mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	ovl: Provide a mount option metacopy=on/off for metadata copyup
By default metadata only copy up is disabled. Provide a mount option so that users can choose one way or other. Also provide a kernel config and module option to enable/disable metacopy feature. metacopy feature requires redirect_dir=on when upper is present. Otherwise, it requires redirect_dir=follow atleast. As of now, metacopy does not work with nfs_export=on. So if both metacopy=on and nfs_export=on then nfs_export is disabled. Signed-off-by: Vivek Goyal <vgoyal@redhat.com> Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
		
							parent
							
								
									d6eac03913
								
							
						
					
					
						commit
						d5791044d2
					
				
					 4 changed files with 92 additions and 4 deletions
				
			
		| 
						 | 
					@ -262,6 +262,30 @@ rightmost one and going left.  In the above example lower1 will be the
 | 
				
			||||||
top, lower2 the middle and lower3 the bottom layer.
 | 
					top, lower2 the middle and lower3 the bottom layer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Metadata only copy up
 | 
				
			||||||
 | 
					--------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When metadata only copy up feature is enabled, overlayfs will only copy
 | 
				
			||||||
 | 
					up metadata (as opposed to whole file), when a metadata specific operation
 | 
				
			||||||
 | 
					like chown/chmod is performed. Full file will be copied up later when
 | 
				
			||||||
 | 
					file is opened for WRITE operation.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In other words, this is delayed data copy up operation and data is copied
 | 
				
			||||||
 | 
					up when there is a need to actually modify data.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					There are multiple ways to enable/disable this feature. A config option
 | 
				
			||||||
 | 
					CONFIG_OVERLAY_FS_METACOPY can be set/unset to enable/disable this feature
 | 
				
			||||||
 | 
					by default. Or one can enable/disable it at module load time with module
 | 
				
			||||||
 | 
					parameter metacopy=on/off. Lastly, there is also a per mount option
 | 
				
			||||||
 | 
					metacopy=on/off to enable/disable this feature per mount.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Do not use metacopy=on with untrusted upper/lower directories. Otherwise
 | 
				
			||||||
 | 
					it is possible that an attacker can create a handcrafted file with
 | 
				
			||||||
 | 
					appropriate REDIRECT and METACOPY xattrs, and gain access to file on lower
 | 
				
			||||||
 | 
					pointed by REDIRECT. This should not be possible on local system as setting
 | 
				
			||||||
 | 
					"trusted." xattrs will require CAP_SYS_ADMIN. But it should be possible
 | 
				
			||||||
 | 
					for untrusted layers like from a pen drive.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Sharing and copying layers
 | 
					Sharing and copying layers
 | 
				
			||||||
--------------------------
 | 
					--------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -280,7 +304,7 @@ though it will not result in a crash or deadlock.
 | 
				
			||||||
Mounting an overlay using an upper layer path, where the upper layer path
 | 
					Mounting an overlay using an upper layer path, where the upper layer path
 | 
				
			||||||
was previously used by another mounted overlay in combination with a
 | 
					was previously used by another mounted overlay in combination with a
 | 
				
			||||||
different lower layer path, is allowed, unless the "inodes index" feature
 | 
					different lower layer path, is allowed, unless the "inodes index" feature
 | 
				
			||||||
is enabled.
 | 
					or "metadata only copy up" feature is enabled.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
With the "inodes index" feature, on the first time mount, an NFS file
 | 
					With the "inodes index" feature, on the first time mount, an NFS file
 | 
				
			||||||
handle of the lower layer root directory, along with the UUID of the lower
 | 
					handle of the lower layer root directory, along with the UUID of the lower
 | 
				
			||||||
| 
						 | 
					@ -293,6 +317,10 @@ lower root origin, mount will fail with ESTALE.  An overlayfs mount with
 | 
				
			||||||
does not support NFS export, lower filesystem does not have a valid UUID or
 | 
					does not support NFS export, lower filesystem does not have a valid UUID or
 | 
				
			||||||
if the upper filesystem does not support extended attributes.
 | 
					if the upper filesystem does not support extended attributes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For "metadata only copy up" feature there is no verification mechanism at
 | 
				
			||||||
 | 
					mount time. So if same upper is mounted with different set of lower, mount
 | 
				
			||||||
 | 
					probably will succeed but expect the unexpected later on. So don't do it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
It is quite a common practice to copy overlay layers to a different
 | 
					It is quite a common practice to copy overlay layers to a different
 | 
				
			||||||
directory tree on the same or different underlying filesystem, and even
 | 
					directory tree on the same or different underlying filesystem, and even
 | 
				
			||||||
to a different machine.  With the "inodes index" feature, trying to mount
 | 
					to a different machine.  With the "inodes index" feature, trying to mount
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64,6 +64,7 @@ config OVERLAY_FS_NFS_EXPORT
 | 
				
			||||||
	bool "Overlayfs: turn on NFS export feature by default"
 | 
						bool "Overlayfs: turn on NFS export feature by default"
 | 
				
			||||||
	depends on OVERLAY_FS
 | 
						depends on OVERLAY_FS
 | 
				
			||||||
	depends on OVERLAY_FS_INDEX
 | 
						depends on OVERLAY_FS_INDEX
 | 
				
			||||||
 | 
						depends on !OVERLAY_FS_METACOPY
 | 
				
			||||||
	help
 | 
						help
 | 
				
			||||||
	  If this config option is enabled then overlay filesystems will use
 | 
						  If this config option is enabled then overlay filesystems will use
 | 
				
			||||||
	  the index directory to decode overlay NFS file handles by default.
 | 
						  the index directory to decode overlay NFS file handles by default.
 | 
				
			||||||
| 
						 | 
					@ -103,3 +104,21 @@ config OVERLAY_FS_XINO_AUTO
 | 
				
			||||||
	  For more information, see Documentation/filesystems/overlayfs.txt
 | 
						  For more information, see Documentation/filesystems/overlayfs.txt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	  If unsure, say N.
 | 
						  If unsure, say N.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					config OVERLAY_FS_METACOPY
 | 
				
			||||||
 | 
						bool "Overlayfs: turn on metadata only copy up feature by default"
 | 
				
			||||||
 | 
						depends on OVERLAY_FS
 | 
				
			||||||
 | 
						select OVERLAY_FS_REDIRECT_DIR
 | 
				
			||||||
 | 
						help
 | 
				
			||||||
 | 
						  If this config option is enabled then overlay filesystems will
 | 
				
			||||||
 | 
						  copy up only metadata where appropriate and data copy up will
 | 
				
			||||||
 | 
						  happen when a file is opened for WRITE operation. It is still
 | 
				
			||||||
 | 
						  possible to turn off this feature globally with the "metacopy=off"
 | 
				
			||||||
 | 
						  module option or on a filesystem instance basis with the
 | 
				
			||||||
 | 
						  "metacopy=off" mount option.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  Note, that this feature is not backward compatible.  That is,
 | 
				
			||||||
 | 
						  mounting an overlay which has metacopy only inodes on a kernel
 | 
				
			||||||
 | 
						  that doesn't support this feature will have unexpected results.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  If unsure, say N.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,6 +19,7 @@ struct ovl_config {
 | 
				
			||||||
	bool index;
 | 
						bool index;
 | 
				
			||||||
	bool nfs_export;
 | 
						bool nfs_export;
 | 
				
			||||||
	int xino;
 | 
						int xino;
 | 
				
			||||||
 | 
						bool metacopy;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct ovl_sb {
 | 
					struct ovl_sb {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -64,6 +64,11 @@ static void ovl_entry_stack_free(struct ovl_entry *oe)
 | 
				
			||||||
		dput(oe->lowerstack[i].dentry);
 | 
							dput(oe->lowerstack[i].dentry);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool ovl_metacopy_def = IS_ENABLED(CONFIG_OVERLAY_FS_METACOPY);
 | 
				
			||||||
 | 
					module_param_named(metacopy, ovl_metacopy_def, bool, 0644);
 | 
				
			||||||
 | 
					MODULE_PARM_DESC(ovl_metacopy_def,
 | 
				
			||||||
 | 
							 "Default to on or off for the metadata only copy up feature");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void ovl_dentry_release(struct dentry *dentry)
 | 
					static void ovl_dentry_release(struct dentry *dentry)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct ovl_entry *oe = dentry->d_fsdata;
 | 
						struct ovl_entry *oe = dentry->d_fsdata;
 | 
				
			||||||
| 
						 | 
					@ -347,6 +352,9 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
 | 
				
			||||||
						"on" : "off");
 | 
											"on" : "off");
 | 
				
			||||||
	if (ofs->config.xino != ovl_xino_def())
 | 
						if (ofs->config.xino != ovl_xino_def())
 | 
				
			||||||
		seq_printf(m, ",xino=%s", ovl_xino_str[ofs->config.xino]);
 | 
							seq_printf(m, ",xino=%s", ovl_xino_str[ofs->config.xino]);
 | 
				
			||||||
 | 
						if (ofs->config.metacopy != ovl_metacopy_def)
 | 
				
			||||||
 | 
							seq_printf(m, ",metacopy=%s",
 | 
				
			||||||
 | 
								   ofs->config.metacopy ? "on" : "off");
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -384,6 +392,8 @@ enum {
 | 
				
			||||||
	OPT_XINO_ON,
 | 
						OPT_XINO_ON,
 | 
				
			||||||
	OPT_XINO_OFF,
 | 
						OPT_XINO_OFF,
 | 
				
			||||||
	OPT_XINO_AUTO,
 | 
						OPT_XINO_AUTO,
 | 
				
			||||||
 | 
						OPT_METACOPY_ON,
 | 
				
			||||||
 | 
						OPT_METACOPY_OFF,
 | 
				
			||||||
	OPT_ERR,
 | 
						OPT_ERR,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -400,6 +410,8 @@ static const match_table_t ovl_tokens = {
 | 
				
			||||||
	{OPT_XINO_ON,			"xino=on"},
 | 
						{OPT_XINO_ON,			"xino=on"},
 | 
				
			||||||
	{OPT_XINO_OFF,			"xino=off"},
 | 
						{OPT_XINO_OFF,			"xino=off"},
 | 
				
			||||||
	{OPT_XINO_AUTO,			"xino=auto"},
 | 
						{OPT_XINO_AUTO,			"xino=auto"},
 | 
				
			||||||
 | 
						{OPT_METACOPY_ON,		"metacopy=on"},
 | 
				
			||||||
 | 
						{OPT_METACOPY_OFF,		"metacopy=off"},
 | 
				
			||||||
	{OPT_ERR,			NULL}
 | 
						{OPT_ERR,			NULL}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -452,6 +464,7 @@ static int ovl_parse_redirect_mode(struct ovl_config *config, const char *mode)
 | 
				
			||||||
static int ovl_parse_opt(char *opt, struct ovl_config *config)
 | 
					static int ovl_parse_opt(char *opt, struct ovl_config *config)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	char *p;
 | 
						char *p;
 | 
				
			||||||
 | 
						int err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	config->redirect_mode = kstrdup(ovl_redirect_mode_def(), GFP_KERNEL);
 | 
						config->redirect_mode = kstrdup(ovl_redirect_mode_def(), GFP_KERNEL);
 | 
				
			||||||
	if (!config->redirect_mode)
 | 
						if (!config->redirect_mode)
 | 
				
			||||||
| 
						 | 
					@ -526,6 +539,14 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
 | 
				
			||||||
			config->xino = OVL_XINO_AUTO;
 | 
								config->xino = OVL_XINO_AUTO;
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case OPT_METACOPY_ON:
 | 
				
			||||||
 | 
								config->metacopy = true;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case OPT_METACOPY_OFF:
 | 
				
			||||||
 | 
								config->metacopy = false;
 | 
				
			||||||
 | 
								break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p);
 | 
								pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p);
 | 
				
			||||||
			return -EINVAL;
 | 
								return -EINVAL;
 | 
				
			||||||
| 
						 | 
					@ -540,7 +561,20 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
 | 
				
			||||||
		config->workdir = NULL;
 | 
							config->workdir = NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return ovl_parse_redirect_mode(config, config->redirect_mode);
 | 
						err = ovl_parse_redirect_mode(config, config->redirect_mode);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
							return err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* metacopy feature with upper requires redirect_dir=on */
 | 
				
			||||||
 | 
						if (config->upperdir && config->metacopy && !config->redirect_dir) {
 | 
				
			||||||
 | 
							pr_warn("overlayfs: metadata only copy up requires \"redirect_dir=on\", falling back to metacopy=off.\n");
 | 
				
			||||||
 | 
							config->metacopy = false;
 | 
				
			||||||
 | 
						} else if (config->metacopy && !config->redirect_follow) {
 | 
				
			||||||
 | 
							pr_warn("overlayfs: metadata only copy up requires \"redirect_dir=follow\" on non-upper mount, falling back to metacopy=off.\n");
 | 
				
			||||||
 | 
							config->metacopy = false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define OVL_WORKDIR_NAME "work"
 | 
					#define OVL_WORKDIR_NAME "work"
 | 
				
			||||||
| 
						 | 
					@ -1013,7 +1047,8 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
 | 
				
			||||||
	if (err) {
 | 
						if (err) {
 | 
				
			||||||
		ofs->noxattr = true;
 | 
							ofs->noxattr = true;
 | 
				
			||||||
		ofs->config.index = false;
 | 
							ofs->config.index = false;
 | 
				
			||||||
		pr_warn("overlayfs: upper fs does not support xattr, falling back to index=off.\n");
 | 
							ofs->config.metacopy = false;
 | 
				
			||||||
 | 
							pr_warn("overlayfs: upper fs does not support xattr, falling back to index=off and metacopy=off.\n");
 | 
				
			||||||
		err = 0;
 | 
							err = 0;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		vfs_removexattr(ofs->workdir, OVL_XATTR_OPAQUE);
 | 
							vfs_removexattr(ofs->workdir, OVL_XATTR_OPAQUE);
 | 
				
			||||||
| 
						 | 
					@ -1035,7 +1070,6 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
 | 
				
			||||||
		pr_warn("overlayfs: NFS export requires \"index=on\", falling back to nfs_export=off.\n");
 | 
							pr_warn("overlayfs: NFS export requires \"index=on\", falling back to nfs_export=off.\n");
 | 
				
			||||||
		ofs->config.nfs_export = false;
 | 
							ofs->config.nfs_export = false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
out:
 | 
					out:
 | 
				
			||||||
	mnt_drop_write(mnt);
 | 
						mnt_drop_write(mnt);
 | 
				
			||||||
	return err;
 | 
						return err;
 | 
				
			||||||
| 
						 | 
					@ -1346,6 +1380,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 | 
				
			||||||
	ofs->config.index = ovl_index_def;
 | 
						ofs->config.index = ovl_index_def;
 | 
				
			||||||
	ofs->config.nfs_export = ovl_nfs_export_def;
 | 
						ofs->config.nfs_export = ovl_nfs_export_def;
 | 
				
			||||||
	ofs->config.xino = ovl_xino_def();
 | 
						ofs->config.xino = ovl_xino_def();
 | 
				
			||||||
 | 
						ofs->config.metacopy = ovl_metacopy_def;
 | 
				
			||||||
	err = ovl_parse_opt((char *) data, &ofs->config);
 | 
						err = ovl_parse_opt((char *) data, &ofs->config);
 | 
				
			||||||
	if (err)
 | 
						if (err)
 | 
				
			||||||
		goto out_err;
 | 
							goto out_err;
 | 
				
			||||||
| 
						 | 
					@ -1416,6 +1451,11 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ofs->config.metacopy && ofs->config.nfs_export) {
 | 
				
			||||||
 | 
							pr_warn("overlayfs: NFS export is not supported with metadata only copy up, falling back to nfs_export=off.\n");
 | 
				
			||||||
 | 
							ofs->config.nfs_export = false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (ofs->config.nfs_export)
 | 
						if (ofs->config.nfs_export)
 | 
				
			||||||
		sb->s_export_op = &ovl_export_operations;
 | 
							sb->s_export_op = &ovl_export_operations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue