From 7fb7998b599a2e1f3744fbd34a3e7145da841ed1 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 26 Jun 2023 12:23:36 +0200 Subject: ovl: move all parameter handling into params.{c,h} While initially I thought that we couldn't move all new mount api handling into params.{c,h} it turns out it is possible. So this just moves a good chunk of code out of super.c and into params.{c,h}. Signed-off-by: Christian Brauner Signed-off-by: Amir Goldstein --- fs/overlayfs/super.c | 530 +-------------------------------------------------- 1 file changed, 2 insertions(+), 528 deletions(-) (limited to 'fs/overlayfs/super.c') diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index c14c52560fd6..5b069f1a1e44 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -19,6 +19,7 @@ #include #include #include "overlayfs.h" +#include "params.h" MODULE_AUTHOR("Miklos Szeredi "); MODULE_DESCRIPTION("Overlay filesystem"); @@ -27,38 +28,6 @@ MODULE_LICENSE("GPL"); struct ovl_dir_cache; -static bool ovl_redirect_dir_def = IS_ENABLED(CONFIG_OVERLAY_FS_REDIRECT_DIR); -module_param_named(redirect_dir, ovl_redirect_dir_def, bool, 0644); -MODULE_PARM_DESC(redirect_dir, - "Default to on or off for the redirect_dir feature"); - -static bool ovl_redirect_always_follow = - IS_ENABLED(CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW); -module_param_named(redirect_always_follow, ovl_redirect_always_follow, - bool, 0644); -MODULE_PARM_DESC(redirect_always_follow, - "Follow redirects even if redirect_dir feature is turned off"); - -static bool ovl_index_def = IS_ENABLED(CONFIG_OVERLAY_FS_INDEX); -module_param_named(index, ovl_index_def, bool, 0644); -MODULE_PARM_DESC(index, - "Default to on or off for the inodes index feature"); - -static bool ovl_nfs_export_def = IS_ENABLED(CONFIG_OVERLAY_FS_NFS_EXPORT); -module_param_named(nfs_export, ovl_nfs_export_def, bool, 0644); -MODULE_PARM_DESC(nfs_export, - "Default to on or off for the NFS export feature"); - -static bool ovl_xino_auto_def = IS_ENABLED(CONFIG_OVERLAY_FS_XINO_AUTO); -module_param_named(xino_auto, ovl_xino_auto_def, bool, 0644); -MODULE_PARM_DESC(xino_auto, - "Auto enable xino feature"); - -static bool ovl_metacopy_def = IS_ENABLED(CONFIG_OVERLAY_FS_METACOPY); -module_param_named(metacopy, ovl_metacopy_def, bool, 0644); -MODULE_PARM_DESC(metacopy, - "Default to on or off for the metadata only copy up feature"); - static struct dentry *ovl_d_real(struct dentry *dentry, const struct inode *inode) { @@ -211,43 +180,6 @@ static void ovl_destroy_inode(struct inode *inode) kfree(oi->lowerdata_redirect); } -static void ovl_free_fs(struct ovl_fs *ofs) -{ - struct vfsmount **mounts; - unsigned i; - - iput(ofs->workbasedir_trap); - iput(ofs->indexdir_trap); - iput(ofs->workdir_trap); - dput(ofs->whiteout); - dput(ofs->indexdir); - dput(ofs->workdir); - if (ofs->workdir_locked) - ovl_inuse_unlock(ofs->workbasedir); - dput(ofs->workbasedir); - if (ofs->upperdir_locked) - ovl_inuse_unlock(ovl_upper_mnt(ofs)->mnt_root); - - /* Hack! Reuse ofs->layers as a vfsmount array before freeing it */ - mounts = (struct vfsmount **) ofs->layers; - for (i = 0; i < ofs->numlayer; i++) { - iput(ofs->layers[i].trap); - mounts[i] = ofs->layers[i].mnt; - kfree(ofs->layers[i].name); - } - kern_unmount_array(mounts, ofs->numlayer); - kfree(ofs->layers); - for (i = 0; i < ofs->numfs; i++) - free_anon_bdev(ofs->fs[i].pseudo_dev); - kfree(ofs->fs); - - kfree(ofs->config.upperdir); - kfree(ofs->config.workdir); - if (ofs->creator_cred) - put_cred(ofs->creator_cred); - kfree(ofs); -} - static void ovl_put_super(struct super_block *sb) { struct ovl_fs *ofs = sb->s_fs_info; @@ -323,122 +255,6 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf) return err; } -/* Will this overlay be forced to mount/remount ro? */ -static bool ovl_force_readonly(struct ovl_fs *ofs) -{ - return (!ovl_upper_mnt(ofs) || !ofs->workdir); -} - -static const struct constant_table ovl_parameter_redirect_dir[] = { - { "off", OVL_REDIRECT_OFF }, - { "follow", OVL_REDIRECT_FOLLOW }, - { "nofollow", OVL_REDIRECT_NOFOLLOW }, - { "on", OVL_REDIRECT_ON }, - {} -}; - -static const char *ovl_redirect_mode(struct ovl_config *config) -{ - return ovl_parameter_redirect_dir[config->redirect_mode].name; -} - -static int ovl_redirect_mode_def(void) -{ - return ovl_redirect_dir_def ? OVL_REDIRECT_ON : - ovl_redirect_always_follow ? OVL_REDIRECT_FOLLOW : - OVL_REDIRECT_NOFOLLOW; -} - -static const struct constant_table ovl_parameter_xino[] = { - { "off", OVL_XINO_OFF }, - { "auto", OVL_XINO_AUTO }, - { "on", OVL_XINO_ON }, - {} -}; - -static const char *ovl_xino_mode(struct ovl_config *config) -{ - return ovl_parameter_xino[config->xino].name; -} - -static inline int ovl_xino_def(void) -{ - return ovl_xino_auto_def ? OVL_XINO_AUTO : OVL_XINO_OFF; -} - -/** - * ovl_show_options - * @m: the seq_file handle - * @dentry: The dentry to query - * - * Prints the mount options for a given superblock. - * Returns zero; does not fail. - */ -static int ovl_show_options(struct seq_file *m, struct dentry *dentry) -{ - struct super_block *sb = dentry->d_sb; - struct ovl_fs *ofs = sb->s_fs_info; - size_t nr, nr_merged_lower = ofs->numlayer - ofs->numdatalayer; - const struct ovl_layer *data_layers = &ofs->layers[nr_merged_lower]; - - /* ofs->layers[0] is the upper layer */ - seq_printf(m, ",lowerdir=%s", ofs->layers[1].name); - /* dump regular lower layers */ - for (nr = 2; nr < nr_merged_lower; nr++) - seq_printf(m, ":%s", ofs->layers[nr].name); - /* dump data lower layers */ - for (nr = 0; nr < ofs->numdatalayer; nr++) - seq_printf(m, "::%s", data_layers[nr].name); - if (ofs->config.upperdir) { - seq_show_option(m, "upperdir", ofs->config.upperdir); - seq_show_option(m, "workdir", ofs->config.workdir); - } - if (ofs->config.default_permissions) - seq_puts(m, ",default_permissions"); - if (ofs->config.redirect_mode != ovl_redirect_mode_def()) - seq_printf(m, ",redirect_dir=%s", - ovl_redirect_mode(&ofs->config)); - if (ofs->config.index != ovl_index_def) - seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off"); - if (!ofs->config.uuid) - seq_puts(m, ",uuid=off"); - if (ofs->config.nfs_export != ovl_nfs_export_def) - seq_printf(m, ",nfs_export=%s", ofs->config.nfs_export ? - "on" : "off"); - if (ofs->config.xino != ovl_xino_def() && !ovl_same_fs(ofs)) - seq_printf(m, ",xino=%s", ovl_xino_mode(&ofs->config)); - if (ofs->config.metacopy != ovl_metacopy_def) - seq_printf(m, ",metacopy=%s", - ofs->config.metacopy ? "on" : "off"); - if (ofs->config.ovl_volatile) - seq_puts(m, ",volatile"); - if (ofs->config.userxattr) - seq_puts(m, ",userxattr"); - return 0; -} - -static int ovl_reconfigure(struct fs_context *fc) -{ - struct super_block *sb = fc->root->d_sb; - struct ovl_fs *ofs = sb->s_fs_info; - struct super_block *upper_sb; - int ret = 0; - - if (!(fc->sb_flags & SB_RDONLY) && ovl_force_readonly(ofs)) - return -EROFS; - - if (fc->sb_flags & SB_RDONLY && !sb_rdonly(sb)) { - upper_sb = ovl_upper_mnt(ofs)->mnt_sb; - if (ovl_should_sync(ofs)) { - down_read(&upper_sb->s_umount); - ret = sync_filesystem(upper_sb); - up_read(&upper_sb->s_umount); - } - } - - return ret; -} - static const struct super_operations ovl_super_operations = { .alloc_inode = ovl_alloc_inode, .free_inode = ovl_free_inode, @@ -450,262 +266,6 @@ static const struct super_operations ovl_super_operations = { .show_options = ovl_show_options, }; -enum { - Opt_lowerdir, - Opt_upperdir, - Opt_workdir, - Opt_default_permissions, - Opt_redirect_dir, - Opt_index, - Opt_uuid, - Opt_nfs_export, - Opt_userxattr, - Opt_xino, - Opt_metacopy, - Opt_volatile, -}; - -static const struct constant_table ovl_parameter_bool[] = { - { "on", true }, - { "off", false }, - {} -}; - -#define fsparam_string_empty(NAME, OPT) \ - __fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL) - -static const struct fs_parameter_spec ovl_parameter_spec[] = { - fsparam_string_empty("lowerdir", Opt_lowerdir), - fsparam_string("upperdir", Opt_upperdir), - fsparam_string("workdir", Opt_workdir), - fsparam_flag("default_permissions", Opt_default_permissions), - fsparam_enum("redirect_dir", Opt_redirect_dir, ovl_parameter_redirect_dir), - fsparam_enum("index", Opt_index, ovl_parameter_bool), - fsparam_enum("uuid", Opt_uuid, ovl_parameter_bool), - fsparam_enum("nfs_export", Opt_nfs_export, ovl_parameter_bool), - fsparam_flag("userxattr", Opt_userxattr), - fsparam_enum("xino", Opt_xino, ovl_parameter_xino), - fsparam_enum("metacopy", Opt_metacopy, ovl_parameter_bool), - fsparam_flag("volatile", Opt_volatile), - {} -}; - -static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param) -{ - int err = 0; - struct fs_parse_result result; - struct ovl_fs *ofs = fc->s_fs_info; - struct ovl_config *config = &ofs->config; - struct ovl_fs_context *ctx = fc->fs_private; - int opt; - - if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) { - /* - * On remount overlayfs has always ignored all mount - * options no matter if malformed or not so for - * backwards compatibility we do the same here. - */ - if (fc->oldapi) - return 0; - - /* - * Give us the freedom to allow changing mount options - * with the new mount api in the future. So instead of - * silently ignoring everything we report a proper - * error. This is only visible for users of the new - * mount api. - */ - return invalfc(fc, "No changes allowed in reconfigure"); - } - - opt = fs_parse(fc, ovl_parameter_spec, param, &result); - if (opt < 0) - return opt; - - switch (opt) { - case Opt_lowerdir: - err = ovl_parse_param_lowerdir(param->string, fc); - break; - case Opt_upperdir: - fallthrough; - case Opt_workdir: - err = ovl_parse_param_upperdir(param->string, fc, - (Opt_workdir == opt)); - break; - case Opt_default_permissions: - config->default_permissions = true; - break; - case Opt_redirect_dir: - config->redirect_mode = result.uint_32; - if (config->redirect_mode == OVL_REDIRECT_OFF) { - config->redirect_mode = ovl_redirect_always_follow ? - OVL_REDIRECT_FOLLOW : - OVL_REDIRECT_NOFOLLOW; - } - ctx->set.redirect = true; - break; - case Opt_index: - config->index = result.uint_32; - ctx->set.index = true; - break; - case Opt_uuid: - config->uuid = result.uint_32; - break; - case Opt_nfs_export: - config->nfs_export = result.uint_32; - ctx->set.nfs_export = true; - break; - case Opt_xino: - config->xino = result.uint_32; - break; - case Opt_metacopy: - config->metacopy = result.uint_32; - ctx->set.metacopy = true; - break; - case Opt_volatile: - config->ovl_volatile = true; - break; - case Opt_userxattr: - config->userxattr = true; - break; - default: - pr_err("unrecognized mount option \"%s\" or missing value\n", - param->key); - return -EINVAL; - } - - return err; -} - -static int ovl_fs_params_verify(const struct ovl_fs_context *ctx, - struct ovl_config *config) -{ - struct ovl_opt_set set = ctx->set; - - if (ctx->nr_data > 0 && !config->metacopy) { - pr_err("lower data-only dirs require metacopy support.\n"); - return -EINVAL; - } - - /* Workdir/index are useless in non-upper mount */ - if (!config->upperdir) { - if (config->workdir) { - pr_info("option \"workdir=%s\" is useless in a non-upper mount, ignore\n", - config->workdir); - kfree(config->workdir); - config->workdir = NULL; - } - if (config->index && set.index) { - pr_info("option \"index=on\" is useless in a non-upper mount, ignore\n"); - set.index = false; - } - config->index = false; - } - - if (!config->upperdir && config->ovl_volatile) { - pr_info("option \"volatile\" is meaningless in a non-upper mount, ignoring it.\n"); - config->ovl_volatile = false; - } - - /* - * This is to make the logic below simpler. It doesn't make any other - * difference, since redirect_dir=on is only used for upper. - */ - if (!config->upperdir && config->redirect_mode == OVL_REDIRECT_FOLLOW) - config->redirect_mode = OVL_REDIRECT_ON; - - /* Resolve metacopy -> redirect_dir dependency */ - if (config->metacopy && config->redirect_mode != OVL_REDIRECT_ON) { - if (set.metacopy && set.redirect) { - pr_err("conflicting options: metacopy=on,redirect_dir=%s\n", - ovl_redirect_mode(config)); - return -EINVAL; - } - if (set.redirect) { - /* - * There was an explicit redirect_dir=... that resulted - * in this conflict. - */ - pr_info("disabling metacopy due to redirect_dir=%s\n", - ovl_redirect_mode(config)); - config->metacopy = false; - } else { - /* Automatically enable redirect otherwise. */ - config->redirect_mode = OVL_REDIRECT_ON; - } - } - - /* Resolve nfs_export -> index dependency */ - if (config->nfs_export && !config->index) { - if (!config->upperdir && - config->redirect_mode != OVL_REDIRECT_NOFOLLOW) { - pr_info("NFS export requires \"redirect_dir=nofollow\" on non-upper mount, falling back to nfs_export=off.\n"); - config->nfs_export = false; - } else if (set.nfs_export && set.index) { - pr_err("conflicting options: nfs_export=on,index=off\n"); - return -EINVAL; - } else if (set.index) { - /* - * There was an explicit index=off that resulted - * in this conflict. - */ - pr_info("disabling nfs_export due to index=off\n"); - config->nfs_export = false; - } else { - /* Automatically enable index otherwise. */ - config->index = true; - } - } - - /* Resolve nfs_export -> !metacopy dependency */ - if (config->nfs_export && config->metacopy) { - if (set.nfs_export && set.metacopy) { - pr_err("conflicting options: nfs_export=on,metacopy=on\n"); - return -EINVAL; - } - if (set.metacopy) { - /* - * There was an explicit metacopy=on that resulted - * in this conflict. - */ - pr_info("disabling nfs_export due to metacopy=on\n"); - config->nfs_export = false; - } else { - /* - * There was an explicit nfs_export=on that resulted - * in this conflict. - */ - pr_info("disabling metacopy due to nfs_export=on\n"); - config->metacopy = false; - } - } - - - /* Resolve userxattr -> !redirect && !metacopy dependency */ - if (config->userxattr) { - if (set.redirect && - config->redirect_mode != OVL_REDIRECT_NOFOLLOW) { - pr_err("conflicting options: userxattr,redirect_dir=%s\n", - ovl_redirect_mode(config)); - return -EINVAL; - } - if (config->metacopy && set.metacopy) { - pr_err("conflicting options: userxattr,metacopy=on\n"); - return -EINVAL; - } - /* - * Silently disable default setting of redirect and metacopy. - * This shall be the default in the future as well: these - * options must be explicitly enabled if used together with - * userxattr. - */ - config->redirect_mode = OVL_REDIRECT_NOFOLLOW; - config->metacopy = false; - } - - return 0; -} - #define OVL_WORKDIR_NAME "work" #define OVL_INDEXDIR_NAME "index" @@ -1758,7 +1318,7 @@ static struct dentry *ovl_get_root(struct super_block *sb, return root; } -static int ovl_fill_super(struct super_block *sb, struct fs_context *fc) +int ovl_fill_super(struct super_block *sb, struct fs_context *fc) { struct ovl_fs *ofs = sb->s_fs_info; struct ovl_fs_context *ctx = fc->fs_private; @@ -1919,92 +1479,6 @@ out_err: return err; } -static int ovl_get_tree(struct fs_context *fc) -{ - return get_tree_nodev(fc, ovl_fill_super); -} - -static inline void ovl_fs_context_free(struct ovl_fs_context *ctx) -{ - ovl_parse_param_drop_lowerdir(ctx); - path_put(&ctx->upper); - path_put(&ctx->work); - kfree(ctx->lower); - kfree(ctx); -} - -static void ovl_free(struct fs_context *fc) -{ - struct ovl_fs *ofs = fc->s_fs_info; - struct ovl_fs_context *ctx = fc->fs_private; - - /* - * ofs is stored in the fs_context when it is initialized. - * ofs is transferred to the superblock on a successful mount, - * but if an error occurs before the transfer we have to free - * it here. - */ - if (ofs) - ovl_free_fs(ofs); - - if (ctx) - ovl_fs_context_free(ctx); -} - -static const struct fs_context_operations ovl_context_ops = { - .parse_param = ovl_parse_param, - .get_tree = ovl_get_tree, - .reconfigure = ovl_reconfigure, - .free = ovl_free, -}; - -/* - * This is called during fsopen() and will record the user namespace of - * the caller in fc->user_ns since we've raised FS_USERNS_MOUNT. We'll - * need it when we actually create the superblock to verify that the - * process creating the superblock is in the same user namespace as - * process that called fsopen(). - */ -static int ovl_init_fs_context(struct fs_context *fc) -{ - struct ovl_fs_context *ctx; - struct ovl_fs *ofs; - - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL_ACCOUNT); - if (!ctx) - return -ENOMEM; - - /* - * By default we allocate for three lower layers. It's likely - * that it'll cover most users. - */ - ctx->lower = kmalloc_array(3, sizeof(*ctx->lower), GFP_KERNEL_ACCOUNT); - if (!ctx->lower) - goto out_err; - ctx->capacity = 3; - - ofs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL); - if (!ofs) - goto out_err; - - ofs->config.redirect_mode = ovl_redirect_mode_def(); - ofs->config.index = ovl_index_def; - ofs->config.uuid = true; - ofs->config.nfs_export = ovl_nfs_export_def; - ofs->config.xino = ovl_xino_def(); - ofs->config.metacopy = ovl_metacopy_def; - - fc->s_fs_info = ofs; - fc->fs_private = ctx; - fc->ops = &ovl_context_ops; - return 0; - -out_err: - ovl_fs_context_free(ctx); - return -ENOMEM; - -} - static struct file_system_type ovl_fs_type = { .owner = THIS_MODULE, .name = "overlay", -- cgit v1.2.3