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>
This commit is contained in:
parent
5ffdbe8bf1
commit
8d3095f4ad
3 changed files with 54 additions and 0 deletions
|
@ -98,6 +98,29 @@ int ovl_permission(struct inode *inode, int mask)
|
|||
|
||||
realdentry = ovl_entry_real(oe, &is_upper);
|
||||
|
||||
if (ovl_is_default_permissions(inode)) {
|
||||
struct kstat stat;
|
||||
struct path realpath = { .dentry = realdentry };
|
||||
|
||||
if (mask & MAY_NOT_BLOCK)
|
||||
return -ECHILD;
|
||||
|
||||
realpath.mnt = ovl_entry_mnt_real(oe, inode, is_upper);
|
||||
|
||||
err = vfs_getattr(&realpath, &stat);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if ((stat.mode ^ inode->i_mode) & S_IFMT)
|
||||
return -ESTALE;
|
||||
|
||||
inode->i_mode = stat.mode;
|
||||
inode->i_uid = stat.uid;
|
||||
inode->i_gid = stat.gid;
|
||||
|
||||
return generic_permission(inode, mask);
|
||||
}
|
||||
|
||||
/* Careful in RCU walk mode */
|
||||
realinode = ACCESS_ONCE(realdentry->d_inode);
|
||||
if (!realinode) {
|
||||
|
|
|
@ -142,7 +142,10 @@ struct dentry *ovl_dentry_upper(struct dentry *dentry);
|
|||
struct dentry *ovl_dentry_lower(struct dentry *dentry);
|
||||
struct dentry *ovl_dentry_real(struct dentry *dentry);
|
||||
struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper);
|
||||
struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode,
|
||||
bool is_upper);
|
||||
struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry);
|
||||
bool ovl_is_default_permissions(struct inode *inode);
|
||||
void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache);
|
||||
struct dentry *ovl_workdir(struct dentry *dentry);
|
||||
int ovl_want_write(struct dentry *dentry);
|
||||
|
|
|
@ -30,6 +30,7 @@ struct ovl_config {
|
|||
char *lowerdir;
|
||||
char *upperdir;
|
||||
char *workdir;
|
||||
bool default_permissions;
|
||||
};
|
||||
|
||||
/* private information held for overlayfs's superblock */
|
||||
|
@ -154,6 +155,18 @@ struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper)
|
|||
return realdentry;
|
||||
}
|
||||
|
||||
struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode,
|
||||
bool is_upper)
|
||||
{
|
||||
if (is_upper) {
|
||||
struct ovl_fs *ofs = inode->i_sb->s_fs_info;
|
||||
|
||||
return ofs->upper_mnt;
|
||||
} else {
|
||||
return oe->numlower ? oe->lowerstack[0].mnt : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
@ -161,6 +174,13 @@ struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry)
|
|||
return oe->cache;
|
||||
}
|
||||
|
||||
bool ovl_is_default_permissions(struct inode *inode)
|
||||
{
|
||||
struct ovl_fs *ofs = inode->i_sb->s_fs_info;
|
||||
|
||||
return ofs->config.default_permissions;
|
||||
}
|
||||
|
||||
void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
@ -594,6 +614,8 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
|
|||
seq_printf(m, ",upperdir=%s", ufs->config.upperdir);
|
||||
seq_printf(m, ",workdir=%s", ufs->config.workdir);
|
||||
}
|
||||
if (ufs->config.default_permissions)
|
||||
seq_puts(m, ",default_permissions");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -618,6 +640,7 @@ enum {
|
|||
OPT_LOWERDIR,
|
||||
OPT_UPPERDIR,
|
||||
OPT_WORKDIR,
|
||||
OPT_DEFAULT_PERMISSIONS,
|
||||
OPT_ERR,
|
||||
};
|
||||
|
||||
|
@ -625,6 +648,7 @@ static const match_table_t ovl_tokens = {
|
|||
{OPT_LOWERDIR, "lowerdir=%s"},
|
||||
{OPT_UPPERDIR, "upperdir=%s"},
|
||||
{OPT_WORKDIR, "workdir=%s"},
|
||||
{OPT_DEFAULT_PERMISSIONS, "default_permissions"},
|
||||
{OPT_ERR, NULL}
|
||||
};
|
||||
|
||||
|
@ -685,6 +709,10 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
|
|||
return -ENOMEM;
|
||||
break;
|
||||
|
||||
case OPT_DEFAULT_PERMISSIONS:
|
||||
config->default_permissions = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p);
|
||||
return -EINVAL;
|
||||
|
|
Loading…
Reference in a new issue