Skip to content

Commit 8d3095f

Browse files
committed
ovl: default permissions
Add mount option "default_permissions" to alter the way permissions are calculated. Without this option and prior to this patch permissions were calculated by underlying lower or upper filesystem. With this option the permissions are calculated by overlayfs based on the file owner, group and mode bits. This has significance for example when a read-only exported NFS filesystem is used as a lower layer. In this case the underlying NFS filesystem will reply with EROFS, in which case all we know is that the filesystem is read-only. But that's not what we are interested in, we are interested in whether the access would be allowed if the filesystem wasn't read-only; the server doesn't tell us that, and would need updating at various levels, which doesn't seem practicable. Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
1 parent 5ffdbe8 commit 8d3095f

File tree

3 files changed

+54
-0
lines changed

3 files changed

+54
-0
lines changed

fs/overlayfs/inode.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,29 @@ int ovl_permission(struct inode *inode, int mask)
9898

9999
realdentry = ovl_entry_real(oe, &is_upper);
100100

101+
if (ovl_is_default_permissions(inode)) {
102+
struct kstat stat;
103+
struct path realpath = { .dentry = realdentry };
104+
105+
if (mask & MAY_NOT_BLOCK)
106+
return -ECHILD;
107+
108+
realpath.mnt = ovl_entry_mnt_real(oe, inode, is_upper);
109+
110+
err = vfs_getattr(&realpath, &stat);
111+
if (err)
112+
return err;
113+
114+
if ((stat.mode ^ inode->i_mode) & S_IFMT)
115+
return -ESTALE;
116+
117+
inode->i_mode = stat.mode;
118+
inode->i_uid = stat.uid;
119+
inode->i_gid = stat.gid;
120+
121+
return generic_permission(inode, mask);
122+
}
123+
101124
/* Careful in RCU walk mode */
102125
realinode = ACCESS_ONCE(realdentry->d_inode);
103126
if (!realinode) {

fs/overlayfs/overlayfs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,10 @@ struct dentry *ovl_dentry_upper(struct dentry *dentry);
142142
struct dentry *ovl_dentry_lower(struct dentry *dentry);
143143
struct dentry *ovl_dentry_real(struct dentry *dentry);
144144
struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper);
145+
struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode,
146+
bool is_upper);
145147
struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry);
148+
bool ovl_is_default_permissions(struct inode *inode);
146149
void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache);
147150
struct dentry *ovl_workdir(struct dentry *dentry);
148151
int ovl_want_write(struct dentry *dentry);

fs/overlayfs/super.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ struct ovl_config {
3030
char *lowerdir;
3131
char *upperdir;
3232
char *workdir;
33+
bool default_permissions;
3334
};
3435

3536
/* private information held for overlayfs's superblock */
@@ -154,13 +155,32 @@ struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper)
154155
return realdentry;
155156
}
156157

158+
struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode,
159+
bool is_upper)
160+
{
161+
if (is_upper) {
162+
struct ovl_fs *ofs = inode->i_sb->s_fs_info;
163+
164+
return ofs->upper_mnt;
165+
} else {
166+
return oe->numlower ? oe->lowerstack[0].mnt : NULL;
167+
}
168+
}
169+
157170
struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry)
158171
{
159172
struct ovl_entry *oe = dentry->d_fsdata;
160173

161174
return oe->cache;
162175
}
163176

177+
bool ovl_is_default_permissions(struct inode *inode)
178+
{
179+
struct ovl_fs *ofs = inode->i_sb->s_fs_info;
180+
181+
return ofs->config.default_permissions;
182+
}
183+
164184
void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache)
165185
{
166186
struct ovl_entry *oe = dentry->d_fsdata;
@@ -594,6 +614,8 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
594614
seq_printf(m, ",upperdir=%s", ufs->config.upperdir);
595615
seq_printf(m, ",workdir=%s", ufs->config.workdir);
596616
}
617+
if (ufs->config.default_permissions)
618+
seq_puts(m, ",default_permissions");
597619
return 0;
598620
}
599621

@@ -618,13 +640,15 @@ enum {
618640
OPT_LOWERDIR,
619641
OPT_UPPERDIR,
620642
OPT_WORKDIR,
643+
OPT_DEFAULT_PERMISSIONS,
621644
OPT_ERR,
622645
};
623646

624647
static const match_table_t ovl_tokens = {
625648
{OPT_LOWERDIR, "lowerdir=%s"},
626649
{OPT_UPPERDIR, "upperdir=%s"},
627650
{OPT_WORKDIR, "workdir=%s"},
651+
{OPT_DEFAULT_PERMISSIONS, "default_permissions"},
628652
{OPT_ERR, NULL}
629653
};
630654

@@ -685,6 +709,10 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
685709
return -ENOMEM;
686710
break;
687711

712+
case OPT_DEFAULT_PERMISSIONS:
713+
config->default_permissions = true;
714+
break;
715+
688716
default:
689717
pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p);
690718
return -EINVAL;

0 commit comments

Comments
 (0)