[Devel] [PATCH v6] overlayfs: add dynamic path resolving in mount options

Kirill Tkhai ktkhai at virtuozzo.com
Tue May 26 19:45:26 MSK 2020


On 26.05.2020 12:47, Alexander Mikhalitsyn wrote:
> This patch adds OVERLAY_FS_DYNAMIC_RESOLVE_PATH_OPTIONS compile-time option,
> and "dyn_path_opts" runtime module option. These options corresponds
> "dynamic path resolving in lowerdir, upperdir, workdir mount options" mode.
> If enabled, user may see real full paths relatively to the mount namespace
> in lowerdir, upperdir, workdir options (/proc/mounts, /proc/<fd>/mountinfo).
> 
> This patch is very helpful to checkpoint/restore functionality of overlayfs
> mounts. With this patch and CRIU it's real to C/R Docker containers with
> overlayfs storage driver.
> 
> Note: d_path function from dcache.c is used to resolve full path in mount
> namespace. This function also adds "(deleted)" suffix if dentry was deleted.
> So, If one of dentries in lowerdir, upperdir, workdir options is deleted,
> we will see "(deleted)" suffix in corresponding path.
> 
> https://jira.sw.ru/browse/PSBM-58614
> 
> Signed-off-by: Alexander Mikhalitsyn <alexander.mikhalitsyn at virtuozzo.com>

Reviewed-by: Kirill Tkhai <ktkhai at virtuozzo.com>

> 
> v2: print_paths_option helper now uses only one additional page, code
> refactored according review comments
> 
> v3: print_paths_option helper now correctly escapes special symbols in
> paths, fixed ofs->lowerpaths[i] paths leak on error path in
> ovl_get_lowerstack function
> 
> v4: fixed ofs->lowerpaths[i] paths double put on error path in
> ovl_get_lowerstack function
> 
> v5: removed redundant path_put_init(&ofs->upperpath) in ovl_fill_super
> function
> 
> v6: print_path_option, print_paths_option reworked using seq_path() helper
> 
> ---
>  fs/overlayfs/Kconfig     | 30 +++++++++++++++++
>  fs/overlayfs/overlayfs.h |  4 +++
>  fs/overlayfs/ovl_entry.h |  6 ++--
>  fs/overlayfs/super.c     | 83 ++++++++++++++++++++++++++----------------------
>  fs/overlayfs/util.c      | 21 ++++++++++++
>  5 files changed, 104 insertions(+), 40 deletions(-)
> 
> diff --git a/fs/overlayfs/Kconfig b/fs/overlayfs/Kconfig
> index 9384164..15e8fa5 100644
> --- a/fs/overlayfs/Kconfig
> +++ b/fs/overlayfs/Kconfig
> @@ -103,3 +103,33 @@ config OVERLAY_FS_XINO_AUTO
>  	  For more information, see Documentation/filesystems/overlayfs.txt
>  
>  	  If unsure, say N.
> +
> +config OVERLAY_FS_DYNAMIC_RESOLVE_PATH_OPTIONS
> +	bool "Overlayfs: all mount paths options resolves dynamically on options show"
> +	default y
> +	depends on OVERLAY_FS
> +	help
> +	  This option helps checkpoint/restore of overlayfs mounts.
> +	  If N selected, old behavior is saved. In this case lowerdir, upperdir,
> +	  workdir options shows in /proc/fd/mountinfo, /proc/mounts as it given
> +	  by user on mount. User may specify relative paths in these options, then
> +	  we couldn't determine from options which full paths correspond these
> +	  relative paths. Also, after pivot_root syscall these paths (even full)
> +	  will not rebuild according to root change.
> +
> +	  If this config option is enabled then overlay filesystems lowerdir, upperdir,
> +	  workdir options paths will dynamically recalculated as full paths in corresponding
> +	  mount namespaces by default.
> +
> +	  It's also possible to change this behavior on overlayfs module loading.
> +
> +	  Disable this to get a backward compatible with previous kernels configuration,
> +	  but in this case checkpoint/restore functionality for overlayfs mounts
> +	  will not work.
> +
> +	  If backward compatibility is not an issue, then it is safe and
> +	  recommended to say Y here.
> +
> +	  For more information, see Documentation/filesystems/overlayfs.txt
> +
> +	  If unsure, say N.
> \ No newline at end of file
> diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
> index fa999d9..f508cd2 100644
> --- a/fs/overlayfs/overlayfs.h
> +++ b/fs/overlayfs/overlayfs.h
> @@ -253,6 +253,10 @@ int ovl_nlink_start(struct dentry *dentry, bool *locked);
>  void ovl_nlink_end(struct dentry *dentry, bool locked);
>  int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir);
>  
> +void print_path_option(struct seq_file *m, const char *name, struct path *path);
> +void print_paths_option(struct seq_file *m, const char *name,
> +			struct path *paths, unsigned int num);
> +
>  static inline bool ovl_is_impuredir(struct dentry *dentry)
>  {
>  	return ovl_check_dir_xattr(dentry, OVL_XATTR_IMPURE);
> diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h
> index 05459d3..a2738e9 100644
> --- a/fs/overlayfs/ovl_entry.h
> +++ b/fs/overlayfs/ovl_entry.h
> @@ -43,13 +43,15 @@ struct ovl_path {
>  /* private information held for overlayfs's superblock */
>  struct ovl_fs {
>  	struct vfsmount *upper_mnt;
> +	struct path upperpath;
>  	unsigned int numlower;
>  	/* Number of unique lower sb that differ from upper sb */
>  	unsigned int numlowerfs;
> +	struct path *lowerpaths;
>  	struct ovl_layer *lower_layers;
>  	struct ovl_sb *lower_fs;
> -	/* workbasedir is the path at workdir= mount option */
> -	struct dentry *workbasedir;
> +	/* workbasepath is the path at workdir= mount option */
> +	struct path workbasepath;
>  	/* workdir is the 'work' directory under workbasedir */
>  	struct dentry *workdir;
>  	/* index directory listing overlay inodes by origin file handle */
> diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
> index 50965ca..7c4f18a 100644
> --- a/fs/overlayfs/super.c
> +++ b/fs/overlayfs/super.c
> @@ -57,6 +57,10 @@ module_param_named(xino_auto, ovl_xino_auto_def, bool, 0644);
>  MODULE_PARM_DESC(ovl_xino_auto_def,
>  		 "Auto enable xino feature");
>  
> +static bool ovl_dyn_path_opts = IS_ENABLED(CONFIG_OVERLAY_FS_DYNAMIC_RESOLVE_PATH_OPTIONS);
> +module_param_named(dyn_path_opts, ovl_dyn_path_opts, bool, 0444);
> +MODULE_PARM_DESC(dyn_path_opts, "dyn_path_opts feature enabled");
> +
>  static void ovl_entry_stack_free(struct ovl_entry *oe)
>  {
>  	unsigned int i;
> @@ -245,11 +249,15 @@ static void ovl_free_fs(struct ovl_fs *ofs)
>  	dput(ofs->indexdir);
>  	dput(ofs->workdir);
>  	if (ofs->workdir_locked)
> -		ovl_inuse_unlock(ofs->workbasedir);
> -	dput(ofs->workbasedir);
> +		ovl_inuse_unlock(ofs->workbasepath.dentry);
> +	path_put(&ofs->workbasepath);
>  	if (ofs->upperdir_locked)
>  		ovl_inuse_unlock(ofs->upper_mnt->mnt_root);
>  	mntput(ofs->upper_mnt);
> +	path_put(&ofs->upperpath);
> +	for (i = 0; i < ofs->numlower; i++)
> +		path_put(&ofs->lowerpaths[i]);
> +	kfree(ofs->lowerpaths);
>  	for (i = 0; i < ofs->numlower; i++)
>  		mntput(ofs->lower_layers[i].mnt);
>  	for (i = 0; i < ofs->numlowerfs; i++)
> @@ -368,11 +376,20 @@ 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;
>  
> -	seq_show_option(m, "lowerdir", ofs->config.lowerdir);
> -	if (ofs->config.upperdir) {
> -		seq_show_option(m, "upperdir", ofs->config.upperdir);
> -		seq_show_option(m, "workdir", ofs->config.workdir);
> +	if (ovl_dyn_path_opts) {
> +		print_paths_option(m, "lowerdir", ofs->lowerpaths, ofs->numlower);
> +		if (ofs->config.upperdir) {
> +			print_path_option(m, "upperdir", &ofs->upperpath);
> +			print_path_option(m, "workdir", &ofs->workbasepath);
> +		}
> +	} else {
> +		seq_show_option(m, "lowerdir", ofs->config.lowerdir);
> +		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 (strcmp(ofs->config.redirect_mode, ovl_redirect_mode_def()) != 0)
> @@ -586,7 +603,7 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
>  static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
>  					 const char *name, bool persist)
>  {
> -	struct inode *dir =  ofs->workbasedir->d_inode;
> +	struct inode *dir =  ofs->workbasepath.dentry->d_inode;
>  	struct vfsmount *mnt = ofs->upper_mnt;
>  	struct dentry *work;
>  	int err;
> @@ -597,7 +614,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
>  	locked = true;
>  
>  retry:
> -	work = lookup_one_len(name, ofs->workbasedir, strlen(name));
> +	work = lookup_one_len(name, ofs->workbasepath.dentry, strlen(name));
>  
>  	if (!IS_ERR(work)) {
>  		struct iattr attr = {
> @@ -1009,7 +1026,7 @@ out:
>  	return err;
>  }
>  
> -static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
> +static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workbasepath)
>  {
>  	struct vfsmount *mnt = ofs->upper_mnt;
>  	struct dentry *temp;
> @@ -1030,7 +1047,7 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
>  	 * workdir. This check requires successful creation of workdir in
>  	 * previous step.
>  	 */
> -	err = ovl_check_d_type_supported(workpath);
> +	err = ovl_check_d_type_supported(workbasepath);
>  	if (err < 0)
>  		goto out;
>  
> @@ -1087,26 +1104,23 @@ out:
>  static int ovl_get_workdir(struct ovl_fs *ofs, struct path *upperpath)
>  {
>  	int err;
> -	struct path workpath = { };
>  
> -	err = ovl_mount_dir(ofs->config.workdir, &workpath);
> +	err = ovl_mount_dir(ofs->config.workdir, &ofs->workbasepath);
>  	if (err)
>  		goto out;
>  
>  	err = -EINVAL;
> -	if (upperpath->mnt != workpath.mnt) {
> +	if (upperpath->mnt != ofs->workbasepath.mnt) {
>  		pr_err("overlayfs: workdir and upperdir must reside under the same mount\n");
>  		goto out;
>  	}
> -	if (!ovl_workdir_ok(workpath.dentry, upperpath->dentry)) {
> +	if (!ovl_workdir_ok(ofs->workbasepath.dentry, upperpath->dentry)) {
>  		pr_err("overlayfs: workdir and upperdir must be separate subtrees\n");
>  		goto out;
>  	}
>  
> -	ofs->workbasedir = dget(workpath.dentry);
> -
>  	err = -EBUSY;
> -	if (ovl_inuse_trylock(ofs->workbasedir)) {
> +	if (ovl_inuse_trylock(ofs->workbasepath.dentry)) {
>  		ofs->workdir_locked = true;
>  	} else if (ofs->config.index) {
>  		pr_err("overlayfs: workdir is in-use by another mount, mount with '-o index=off' to override exclusive workdir protection.\n");
> @@ -1115,14 +1129,12 @@ static int ovl_get_workdir(struct ovl_fs *ofs, struct path *upperpath)
>  		pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n");
>  	}
>  
> -	err = ovl_make_workdir(ofs, &workpath);
> +	err = ovl_make_workdir(ofs, &ofs->workbasepath);
>  	if (err)
>  		goto out;
>  
>  	err = 0;
>  out:
> -	path_put(&workpath);
> -
>  	return err;
>  }
>  
> @@ -1290,7 +1302,6 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
>  {
>  	int err;
>  	char *lowertmp, *lower;
> -	struct path *stack = NULL;
>  	unsigned int stacklen, numlower = 0, i;
>  	bool remote = false;
>  	struct ovl_entry *oe;
> @@ -1316,14 +1327,14 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
>  	}
>  
>  	err = -ENOMEM;
> -	stack = kcalloc(stacklen, sizeof(struct path), GFP_KERNEL);
> -	if (!stack)
> +	ofs->lowerpaths = kcalloc(stacklen, sizeof(struct path), GFP_KERNEL);
> +	if (!ofs->lowerpaths)
>  		goto out_err;
>  
>  	err = -EINVAL;
>  	lower = lowertmp;
>  	for (numlower = 0; numlower < stacklen; numlower++) {
> -		err = ovl_lower_dir(lower, &stack[numlower], ofs,
> +		err = ovl_lower_dir(lower, &ofs->lowerpaths[numlower], ofs,
>  				    overlay_stack_depth, &remote);
>  		if (err)
>  			goto out_err;
> @@ -1338,7 +1349,7 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
>  		goto out_err;
>  	}
>  
> -	err = ovl_get_lower_layers(ofs, stack, numlower);
> +	err = ovl_get_lower_layers(ofs, ofs->lowerpaths, numlower);
>  	if (err)
>  		goto out_err;
>  
> @@ -1348,7 +1359,7 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
>  		goto out_err;
>  
>  	for (i = 0; i < numlower; i++) {
> -		oe->lowerstack[i].dentry = dget(stack[i].dentry);
> +		oe->lowerstack[i].dentry = dget(ofs->lowerpaths[i].dentry);
>  		oe->lowerstack[i].layer = &ofs->lower_layers[i];
>  	}
>  
> @@ -1358,21 +1369,19 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
>  		sb->s_d_op = &ovl_dentry_operations.ops;
>  
>  out:
> -	for (i = 0; i < numlower; i++)
> -		path_put(&stack[i]);
> -	kfree(stack);
>  	kfree(lowertmp);
>  
>  	return oe;
>  
>  out_err:
> +	for (i = 0; i < numlower; i++)
> +		path_put_init(&ofs->lowerpaths[i]);
>  	oe = ERR_PTR(err);
>  	goto out;
>  }
>  
>  static int ovl_fill_super(struct super_block *sb, void *data, int silent)
>  {
> -	struct path upperpath = { };
>  	struct dentry *root_dentry;
>  	struct ovl_entry *oe;
>  	struct ovl_fs *ofs;
> @@ -1423,11 +1432,11 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
>  			goto out_err;
>  		}
>  
> -		err = ovl_get_upper(ofs, &upperpath);
> +		err = ovl_get_upper(ofs, &ofs->upperpath);
>  		if (err)
>  			goto out_err;
>  
> -		err = ovl_get_workdir(ofs, &upperpath);
> +		err = ovl_get_workdir(ofs, &ofs->upperpath);
>  		if (err)
>  			goto out_err;
>  
> @@ -1454,7 +1463,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
>  		sb->s_flags |= MS_RDONLY;
>  
>  	if (!(ovl_force_readonly(ofs)) && ofs->config.index) {
> -		err = ovl_get_indexdir(ofs, oe, &upperpath);
> +		err = ovl_get_indexdir(ofs, oe, &ofs->upperpath);
>  		if (err)
>  			goto out_free_oe;
>  
> @@ -1495,17 +1504,16 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
>  
>  	root_dentry->d_fsdata = oe;
>  
> -	mntput(upperpath.mnt);
> -	if (upperpath.dentry) {
> +	if (ofs->upperpath.dentry) {
>  		ovl_dentry_set_upper_alias(root_dentry);
> -		if (ovl_is_impuredir(upperpath.dentry))
> +		if (ovl_is_impuredir(ofs->upperpath.dentry))
>  			ovl_set_flag(OVL_IMPURE, d_inode(root_dentry));
>  	}
>  
>  	/* Root is always merge -> can have whiteouts */
>  	ovl_set_flag(OVL_WHITEOUTS, d_inode(root_dentry));
>  	ovl_dentry_set_flag(OVL_E_CONNECTED, root_dentry);
> -	ovl_inode_init(d_inode(root_dentry), upperpath.dentry,
> +	ovl_inode_init(d_inode(root_dentry), dget(ofs->upperpath.dentry),
>  		       ovl_dentry_lower(root_dentry));
>  
>  	sb->s_root = root_dentry;
> @@ -1516,7 +1524,6 @@ out_free_oe:
>  	ovl_entry_stack_free(oe);
>  	kfree(oe);
>  out_err:
> -	path_put(&upperpath);
>  	ovl_free_fs(ofs);
>  out:
>  	return err;
> diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
> index 838c371..bb670ee 100644
> --- a/fs/overlayfs/util.c
> +++ b/fs/overlayfs/util.c
> @@ -17,6 +17,7 @@
>  #include <linux/uuid.h>
>  #include <linux/namei.h>
>  #include <linux/ratelimit.h>
> +#include <linux/seq_file.h>
>  #include "overlayfs.h"
>  
>  int ovl_want_write(struct dentry *dentry)
> @@ -678,3 +679,23 @@ err:
>  	pr_err("overlayfs: failed to lock workdir+upperdir\n");
>  	return -EIO;
>  }
> +
> +void print_path_option(struct seq_file *m, const char *name, struct path *path)
> +{
> +	seq_show_option(m, name, "");
> +	seq_path(m, path, ", \t\n\\");
> +}
> +
> +void print_paths_option(struct seq_file *m, const char *name,
> +			struct path *paths, unsigned int num)
> +{
> +	int i;
> +
> +	seq_show_option(m, name, "");
> +
> +	for (i = 0; i < num; i++) {
> +		if (i)
> +			seq_putc(m, ':');
> +		seq_path(m, &paths[i], ", \t\n\\");
> +	}
> +}
> 



More information about the Devel mailing list