summaryrefslogtreecommitdiff
path: root/fs/overlayfs/super.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/overlayfs/super.c')
-rw-r--r--fs/overlayfs/super.c125
1 files changed, 97 insertions, 28 deletions
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 76440feb79f6..9ee37c76091d 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -45,6 +45,11 @@ module_param_named(index, ovl_index_def, bool, 0644);
MODULE_PARM_DESC(ovl_index_def,
"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(ovl_nfs_export_def,
+ "Default to on or off for the NFS export feature");
+
static void ovl_entry_stack_free(struct ovl_entry *oe)
{
unsigned int i;
@@ -211,6 +216,7 @@ static void ovl_destroy_inode(struct inode *inode)
struct ovl_inode *oi = OVL_I(inode);
dput(oi->__upperdentry);
+ iput(oi->lower);
kfree(oi->redirect);
ovl_dir_cache_free(inode);
mutex_destroy(&oi->lock);
@@ -341,6 +347,9 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
seq_printf(m, ",redirect_dir=%s", ofs->config.redirect_mode);
if (ofs->config.index != ovl_index_def)
seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off");
+ if (ofs->config.nfs_export != ovl_nfs_export_def)
+ seq_printf(m, ",nfs_export=%s", ofs->config.nfs_export ?
+ "on" : "off");
return 0;
}
@@ -373,6 +382,8 @@ enum {
OPT_REDIRECT_DIR,
OPT_INDEX_ON,
OPT_INDEX_OFF,
+ OPT_NFS_EXPORT_ON,
+ OPT_NFS_EXPORT_OFF,
OPT_ERR,
};
@@ -384,6 +395,8 @@ static const match_table_t ovl_tokens = {
{OPT_REDIRECT_DIR, "redirect_dir=%s"},
{OPT_INDEX_ON, "index=on"},
{OPT_INDEX_OFF, "index=off"},
+ {OPT_NFS_EXPORT_ON, "nfs_export=on"},
+ {OPT_NFS_EXPORT_OFF, "nfs_export=off"},
{OPT_ERR, NULL}
};
@@ -490,6 +503,14 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
config->index = false;
break;
+ case OPT_NFS_EXPORT_ON:
+ config->nfs_export = true;
+ break;
+
+ case OPT_NFS_EXPORT_OFF:
+ config->nfs_export = false;
+ break;
+
default:
pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p);
return -EINVAL;
@@ -520,10 +541,6 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
bool retried = false;
bool locked = false;
- err = mnt_want_write(mnt);
- if (err)
- goto out_err;
-
inode_lock_nested(dir, I_MUTEX_PARENT);
locked = true;
@@ -588,7 +605,6 @@ retry:
goto out_err;
}
out_unlock:
- mnt_drop_write(mnt);
if (locked)
inode_unlock(dir);
@@ -700,12 +716,16 @@ static int ovl_lower_dir(const char *name, struct path *path,
*remote = true;
/*
- * The inodes index feature needs to encode and decode file
- * handles, so it requires that all layers support them.
+ * The inodes index feature and NFS export need to encode and decode
+ * file handles, so they require that all layers support them.
*/
- if (ofs->config.index && !ovl_can_decode_fh(path->dentry->d_sb)) {
+ if ((ofs->config.nfs_export ||
+ (ofs->config.index && ofs->config.upperdir)) &&
+ !ovl_can_decode_fh(path->dentry->d_sb)) {
ofs->config.index = false;
- pr_warn("overlayfs: fs on '%s' does not support file handles, falling back to index=off.\n", name);
+ ofs->config.nfs_export = false;
+ pr_warn("overlayfs: fs on '%s' does not support file handles, falling back to index=off,nfs_export=off.\n",
+ name);
}
return 0;
@@ -929,12 +949,17 @@ out:
static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
{
+ struct vfsmount *mnt = ofs->upper_mnt;
struct dentry *temp;
int err;
+ err = mnt_want_write(mnt);
+ if (err)
+ return err;
+
ofs->workdir = ovl_workdir_create(ofs, OVL_WORKDIR_NAME, false);
if (!ofs->workdir)
- return 0;
+ goto out;
/*
* Upper should support d_type, else whiteouts are visible. Given
@@ -944,7 +969,7 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
*/
err = ovl_check_d_type_supported(workpath);
if (err < 0)
- return err;
+ goto out;
/*
* We allowed this configuration and don't want to break users over
@@ -967,7 +992,9 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
err = ovl_do_setxattr(ofs->workdir, OVL_XATTR_OPAQUE, "0", 1, 0);
if (err) {
ofs->noxattr = true;
- pr_warn("overlayfs: upper fs does not support xattr.\n");
+ ofs->config.index = false;
+ pr_warn("overlayfs: upper fs does not support xattr, falling back to index=off.\n");
+ err = 0;
} else {
vfs_removexattr(ofs->workdir, OVL_XATTR_OPAQUE);
}
@@ -979,7 +1006,15 @@ static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
pr_warn("overlayfs: upper fs does not support file handles, falling back to index=off.\n");
}
- return 0;
+ /* NFS export of r/w mount depends on index */
+ if (ofs->config.nfs_export && !ofs->config.index) {
+ pr_warn("overlayfs: NFS export requires \"index=on\", falling back to nfs_export=off.\n");
+ ofs->config.nfs_export = false;
+ }
+
+out:
+ mnt_drop_write(mnt);
+ return err;
}
static int ovl_get_workdir(struct ovl_fs *ofs, struct path *upperpath)
@@ -1026,11 +1061,16 @@ out:
static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe,
struct path *upperpath)
{
+ struct vfsmount *mnt = ofs->upper_mnt;
int err;
+ err = mnt_want_write(mnt);
+ if (err)
+ return err;
+
/* Verify lower root is upper root origin */
err = ovl_verify_origin(upperpath->dentry, oe->lowerstack[0].dentry,
- false, true);
+ true);
if (err) {
pr_err("overlayfs: failed to verify upper root origin\n");
goto out;
@@ -1038,23 +1078,33 @@ static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe,
ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
if (ofs->indexdir) {
- /* Verify upper root is index dir origin */
- err = ovl_verify_origin(ofs->indexdir, upperpath->dentry,
- true, true);
+ /*
+ * Verify upper root is exclusively associated with index dir.
+ * Older kernels stored upper fh in "trusted.overlay.origin"
+ * xattr. If that xattr exists, verify that it is a match to
+ * upper dir file handle. In any case, verify or set xattr
+ * "trusted.overlay.upper" to indicate that index may have
+ * directory entries.
+ */
+ if (ovl_check_origin_xattr(ofs->indexdir)) {
+ err = ovl_verify_set_fh(ofs->indexdir, OVL_XATTR_ORIGIN,
+ upperpath->dentry, true, false);
+ if (err)
+ pr_err("overlayfs: failed to verify index dir 'origin' xattr\n");
+ }
+ err = ovl_verify_upper(ofs->indexdir, upperpath->dentry, true);
if (err)
- pr_err("overlayfs: failed to verify index dir origin\n");
+ pr_err("overlayfs: failed to verify index dir 'upper' xattr\n");
/* Cleanup bad/stale/orphan index entries */
if (!err)
- err = ovl_indexdir_cleanup(ofs->indexdir,
- ofs->upper_mnt,
- oe->lowerstack,
- oe->numlower);
+ err = ovl_indexdir_cleanup(ofs);
}
if (err || !ofs->indexdir)
pr_warn("overlayfs: try deleting index dir or mounting with '-o index=off' to disable inodes index.\n");
out:
+ mnt_drop_write(mnt);
return err;
}
@@ -1094,6 +1144,7 @@ static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack,
ofs->lower_layers[ofs->numlower].mnt = mnt;
ofs->lower_layers[ofs->numlower].pseudo_dev = dev;
+ ofs->lower_layers[ofs->numlower].idx = i + 1;
ofs->numlower++;
/* Check if all lower layers are on same sb */
@@ -1131,6 +1182,10 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
} else if (!ofs->config.upperdir && stacklen == 1) {
pr_err("overlayfs: at least 2 lowerdir are needed while upperdir nonexistent\n");
goto out_err;
+ } else if (!ofs->config.upperdir && ofs->config.nfs_export &&
+ ofs->config.redirect_follow) {
+ pr_warn("overlayfs: NFS export requires \"redirect_dir=nofollow\" on non-upper mount, falling back to nfs_export=off.\n");
+ ofs->config.nfs_export = false;
}
err = -ENOMEM;
@@ -1207,6 +1262,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
goto out_err;
ofs->config.index = ovl_index_def;
+ ofs->config.nfs_export = ovl_nfs_export_def;
err = ovl_parse_opt((char *) data, &ofs->config);
if (err)
goto out_err;
@@ -1257,13 +1313,26 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
if (err)
goto out_free_oe;
- if (!ofs->indexdir)
+ /* Force r/o mount with no index dir */
+ if (!ofs->indexdir) {
+ dput(ofs->workdir);
+ ofs->workdir = NULL;
sb->s_flags |= SB_RDONLY;
+ }
+
}
- /* Show index=off/on in /proc/mounts for any of the reasons above */
- if (!ofs->indexdir)
+ /* Show index=off in /proc/mounts for forced r/o mount */
+ if (!ofs->indexdir) {
ofs->config.index = false;
+ if (ofs->upper_mnt && ofs->config.nfs_export) {
+ pr_warn("overlayfs: NFS export requires an index dir, falling back to nfs_export=off.\n");
+ ofs->config.nfs_export = false;
+ }
+ }
+
+ if (ofs->config.nfs_export)
+ sb->s_export_op = &ovl_export_operations;
/* Never override disk quota limits or use reserved space */
cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);
@@ -1279,15 +1348,15 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
if (!root_dentry)
goto out_free_oe;
+ root_dentry->d_fsdata = oe;
+
mntput(upperpath.mnt);
if (upperpath.dentry) {
- oe->has_upper = true;
+ ovl_dentry_set_upper_alias(root_dentry);
if (ovl_is_impuredir(upperpath.dentry))
ovl_set_flag(OVL_IMPURE, d_inode(root_dentry));
}
- root_dentry->d_fsdata = oe;
-
/* Root is always merge -> can have whiteouts */
ovl_set_flag(OVL_WHITEOUTS, d_inode(root_dentry));
ovl_inode_init(d_inode(root_dentry), upperpath.dentry,