summaryrefslogtreecommitdiff
path: root/fs/overlayfs/super.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/super.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/super.c')
-rw-r--r--fs/overlayfs/super.c52
1 files changed, 44 insertions, 8 deletions
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 8bc970748b96..e61aab1999d3 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -701,7 +701,7 @@ static int ovl_create_volatile_dirty(struct ovl_fs *ofs)
}
static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
- const struct path *workpath)
+ const struct path *workpath, size_t nr_data)
{
struct vfsmount *mnt = ovl_upper_mnt(ofs);
struct dentry *workdir;
@@ -765,9 +765,11 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
if (err) {
pr_warn("failed to set xattr on upper\n");
ofs->noxattr = true;
- if (ovl_redirect_follow(ofs)) {
- ofs->config.redirect_mode = OVL_REDIRECT_NOFOLLOW;
- pr_warn("...falling back to redirect_dir=nofollow.\n");
+ if (ovl_redirect_follow_lower(ofs)) {
+ ofs->config.redirect_mode =
+ ovl_redirect_follow_mode(false, nr_data);
+ pr_warn("...falling back to redirect_dir=%s.\n",
+ ovl_redirect_mode(&ofs->config));
}
if (ofs->config.metacopy) {
ofs->config.metacopy = false;
@@ -844,7 +846,7 @@ out:
static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ofs,
const struct path *upperpath,
- const struct path *workpath)
+ const struct path *workpath, size_t nr_data)
{
int err;
@@ -873,7 +875,7 @@ static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ofs,
if (err)
return err;
- return ovl_make_workdir(sb, ofs, workpath);
+ return ovl_make_workdir(sb, ofs, workpath, nr_data);
}
static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
@@ -1142,6 +1144,29 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
}
/*
+ * Lower file handles identify the lower fs, but not the layer,
+ * so with different idmaps on the same lower fs, index and
+ * nfs_export may decode a file handle through a layer with the
+ * wrong idmap. Following copy up origin without index is only
+ * used for st_ino, which does not depend on the idmap.
+ */
+ if (i < nr_merged_lower &&
+ (ofs->config.index || ofs->config.nfs_export)) {
+ const struct mnt_idmap *idmap = mnt_idmap(mnt);
+ struct ovl_sb *fs = &ofs->fs[fsid];
+
+ if (!fs->lower_idmap) {
+ fs->lower_idmap = idmap;
+ } else if (fs->lower_idmap != idmap) {
+ pr_err("lower layer '%pd2' conflicts with idmap of another lower layer on the same fs, mount with '-o index=off,nfs_export=off' to override.\n",
+ l->path.dentry);
+ iput(trap);
+ kern_unmount(mnt);
+ return -EINVAL;
+ }
+ }
+
+ /*
* Make lower layers R/O. That way fchmod/fchown on lower file
* will fail instead of modifying lower fs.
*/
@@ -1485,7 +1510,8 @@ static int ovl_fill_super_creds(struct fs_context *fc, struct super_block *sb)
}
}
- err = ovl_get_workdir(sb, ofs, &ctx->upper, &ctx->work);
+ err = ovl_get_workdir(sb, ofs, &ctx->upper, &ctx->work,
+ ctx->nr_data);
if (err)
return err;
@@ -1619,13 +1645,23 @@ out_err:
return err;
}
+static void ovl_kill_sb(struct super_block *sb)
+{
+ struct ovl_fs *ofs = OVL_FS(sb);
+
+ /* Nothing can modify them now, do not hold them across the sync */
+ if (ofs)
+ ovl_inuse_unlock_all(ofs);
+ kill_anon_super(sb);
+}
+
struct file_system_type ovl_fs_type = {
.owner = THIS_MODULE,
.name = "overlay",
.init_fs_context = ovl_init_fs_context,
.parameters = ovl_parameter_spec,
.fs_flags = FS_USERNS_MOUNT | FS_ALLOW_IDMAP,
- .kill_sb = kill_anon_super,
+ .kill_sb = ovl_kill_sb,
};
MODULE_ALIAS_FS("overlay");