Commit d5791044 authored by Vivek Goyal's avatar Vivek Goyal Committed by Miklos Szeredi

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: default avatarVivek Goyal <vgoyal@redhat.com>
Reviewed-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent d6eac039
...@@ -262,6 +262,30 @@ rightmost one and going left. In the above example lower1 will be the ...@@ -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. ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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 { ...@@ -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 = { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment