summaryrefslogtreecommitdiff
path: root/fs/overlayfs/params.c
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-11-13 13:03:45 +0000
committerMark Brown <broonie@kernel.org>2026-11-13 13:03:45 +0000
commit48d7d1c219806ad354744647c33ce35a7cc15bf4 (patch)
tree86d53cac4a54c2f9f0bc6c643f0e608826f5069c /fs/overlayfs/params.c
parente896801285ee3cc166792c6e79a3af1ef5be522c (diff)
parent748739f3171ed1c44a30a98b0e0341b1a8d62d31 (diff)
downloadlinux-next-fs-current.tar.gz
linux-next-fs-current.zip
Merge branch 'ovl-fixes' of https://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs.gitfs-current
Diffstat (limited to 'fs/overlayfs/params.c')
-rw-r--r--fs/overlayfs/params.c91
1 files changed, 71 insertions, 20 deletions
diff --git a/fs/overlayfs/params.c b/fs/overlayfs/params.c
index 5a15e5478a6b..d1ca4a9094f2 100644
--- a/fs/overlayfs/params.c
+++ b/fs/overlayfs/params.c
@@ -107,21 +107,40 @@ static int ovl_xino_def(void)
const struct constant_table ovl_parameter_redirect_dir[] = {
{ "off", OVL_REDIRECT_OFF },
{ "follow", OVL_REDIRECT_FOLLOW },
+ { "followdata", OVL_REDIRECT_FOLLOWDATA },
{ "nofollow", OVL_REDIRECT_NOFOLLOW },
{ "on", OVL_REDIRECT_ON },
{}
};
-static const char *ovl_redirect_mode(struct ovl_config *config)
+static const char *ovl_redirect_mode_name(int redirect_mode)
{
- return ovl_parameter_redirect_dir[config->redirect_mode].name;
+ return ovl_parameter_redirect_dir[redirect_mode].name;
}
+const char *ovl_redirect_mode(struct ovl_config *config)
+{
+ return ovl_redirect_mode_name(config->redirect_mode);
+}
+
+int ovl_redirect_follow_mode(bool follow_lower, size_t nr_data)
+{
+ if (ovl_redirect_always_follow && follow_lower)
+ return OVL_REDIRECT_FOLLOW;
+
+ /* Data-only layers cannot be used without following redirects to them */
+ return nr_data ? OVL_REDIRECT_FOLLOWDATA : OVL_REDIRECT_NOFOLLOW;
+}
+
+/*
+ * The default for show_options() is resolved as if there are no data-only layers,
+ * so that redirect_dir=followdata, which is auto selected with data-only layers,
+ * is displayed in mount options.
+ */
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;
+ return ovl_redirect_dir_def ? OVL_REDIRECT_ON :
+ ovl_redirect_follow_mode(true, 0);
}
static const struct constant_table ovl_parameter_verity[] = {
@@ -659,11 +678,6 @@ static int ovl_parse_param(struct fs_context *fc, struct fs_parameter *param)
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:
@@ -819,7 +833,13 @@ int ovl_init_fs_context(struct fs_context *fc)
if (!ofs)
goto out_err;
- ofs->config.redirect_mode = ovl_redirect_mode_def();
+ /*
+ * The module default determines only if new redirects are created in
+ * upper dir. If they are not, the redirect follow mode will be
+ * determined after parsing mount options.
+ */
+ ofs->config.redirect_mode = ovl_redirect_dir_def ?
+ OVL_REDIRECT_ON : OVL_REDIRECT_OFF;
ofs->config.index = ovl_index_def;
ofs->config.uuid = ovl_uuid_def();
ofs->config.nfs_export = ovl_nfs_export_def;
@@ -845,15 +865,12 @@ void ovl_free_fs(struct ovl_fs *ofs)
struct vfsmount **mounts;
unsigned i;
+ ovl_inuse_unlock_all(ofs);
iput(ofs->workbasedir_trap);
iput(ofs->workdir_trap);
dput(ofs->whiteout);
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);
/* Reuse ofs->config.lowerdirs as a vfsmount array before freeing it */
mounts = (struct vfsmount **) ofs->config.lowerdirs;
@@ -880,6 +897,11 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
struct ovl_config *config)
{
struct ovl_opt_set set = ctx->set;
+ /* The requested redirect_dir=, for reporting conflicts with it */
+ int redirect_opt = config->redirect_mode;
+ /* The redirect follow mode for redirect_dir=off and for the default */
+ int follow_mode = ovl_redirect_follow_mode(!config->userxattr,
+ ctx->nr_data);
/* Workdir/index are useless in non-upper mount */
if (!config->upperdir) {
@@ -907,6 +929,13 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
}
/*
+ * Resolve redirect_dir=off to the default redirect follow mode.
+ * With userxattr, redirects are not followed to lower by default.
+ */
+ if (config->redirect_mode == OVL_REDIRECT_OFF)
+ config->redirect_mode = follow_mode;
+
+ /*
* This is to make the logic below simpler. It doesn't make any other
* difference, since redirect_dir=on is only used for upper.
*/
@@ -917,7 +946,7 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
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));
+ ovl_redirect_mode_name(redirect_opt));
return -EINVAL;
}
if (set.redirect) {
@@ -926,7 +955,7 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
* in this conflict.
*/
pr_info("disabling metacopy due to redirect_dir=%s\n",
- ovl_redirect_mode(config));
+ ovl_redirect_mode_name(redirect_opt));
config->metacopy = false;
} else {
/* Automatically enable redirect otherwise. */
@@ -934,6 +963,16 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
}
}
+ /* Resolve nfs_export -> !data-only layers dependency */
+ if (config->nfs_export && ctx->nr_data) {
+ if (set.nfs_export) {
+ pr_err("NFS export is not supported with data-only layers\n");
+ return -EINVAL;
+ }
+ pr_info("disabling nfs_export due to data-only layers\n");
+ config->nfs_export = false;
+ }
+
/* Resolve nfs_export -> index dependency */
if (config->nfs_export && !config->index) {
if (!config->upperdir &&
@@ -991,9 +1030,10 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
/* Resolve userxattr -> !redirect && !metacopy dependency */
if (config->userxattr) {
if (set.redirect &&
- config->redirect_mode != OVL_REDIRECT_NOFOLLOW) {
+ config->redirect_mode != OVL_REDIRECT_NOFOLLOW &&
+ config->redirect_mode != OVL_REDIRECT_FOLLOWDATA) {
pr_err("conflicting options: userxattr,redirect_dir=%s\n",
- ovl_redirect_mode(config));
+ ovl_redirect_mode_name(redirect_opt));
return -EINVAL;
}
if (config->metacopy && set.metacopy) {
@@ -1006,10 +1046,21 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
* options must be explicitly enabled if used together with
* userxattr.
*/
- config->redirect_mode = OVL_REDIRECT_NOFOLLOW;
+ if (!set.redirect)
+ config->redirect_mode = follow_mode;
config->metacopy = false;
}
+ /* Data-only layers are reached through redirects */
+ if (ctx->nr_data && config->redirect_mode == OVL_REDIRECT_NOFOLLOW) {
+ pr_err("redirect_dir=nofollow is not supported with data-only layers\n");
+ return -EINVAL;
+ }
+ if (!ctx->nr_data && config->redirect_mode == OVL_REDIRECT_FOLLOWDATA) {
+ pr_err("redirect_dir=followdata requires data-only layers\n");
+ return -EINVAL;
+ }
+
/*
* Fail if we don't have trusted xattr capability and a feature was
* explicitly requested that requires them.