From 6f5c84162a30514a795eab3495a12c19306d6f6c Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:32 +0100 Subject: [PATCH 01/54] ovl: add override_creds cleanup guard extension for overlayfs Overlayfs plucks the relevant creds from the superblock. Extend the override_creds cleanup class I added to override_creds_ovl which uses the ovl_override_creds() function as initialization helper. Add with_ovl_creds() based on this new class. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-1-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/overlayfs.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 746bc4ad7b37..362a3485dc53 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -457,6 +457,11 @@ struct dentry *ovl_workdir(struct dentry *dentry); const struct cred *ovl_override_creds(struct super_block *sb); void ovl_revert_creds(const struct cred *old_cred); +EXTEND_CLASS(override_creds, _ovl, ovl_override_creds(sb), struct super_block *sb) + +#define with_ovl_creds(sb) \ + scoped_class(override_creds_ovl, __UNIQUE_ID(label), sb) + static inline const struct cred *ovl_creds(struct super_block *sb) { return OVL_FS(sb)->creator_cred; From 87809f12e057dbd482dc9810d0ad194cdca3d71d Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:33 +0100 Subject: [PATCH 02/54] ovl: port ovl_copy_up_flags() to cred guards Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-2-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/copy_up.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 23216ed01325..859e75daff8e 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -1203,7 +1203,6 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, static int ovl_copy_up_flags(struct dentry *dentry, int flags) { int err = 0; - const struct cred *old_cred; bool disconnected = (dentry->d_flags & DCACHE_DISCONNECTED); /* @@ -1223,7 +1222,6 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags) if (err) return err; - old_cred = ovl_override_creds(dentry->d_sb); while (!err) { struct dentry *next; struct dentry *parent = NULL; @@ -1243,12 +1241,12 @@ static int ovl_copy_up_flags(struct dentry *dentry, int flags) next = parent; } - err = ovl_copy_up_one(parent, next, flags); + with_ovl_creds(dentry->d_sb) + err = ovl_copy_up_one(parent, next, flags); dput(parent); dput(next); } - ovl_revert_creds(old_cred); return err; } From 8c9531edcf60014b7e36739697f5cc15d5e853a1 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:34 +0100 Subject: [PATCH 03/54] ovl: port ovl_create_or_link() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-3-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/dir.c | 72 +++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 10e2b7e41a7a..c3492b040fba 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -618,52 +618,44 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode, struct ovl_cattr *attr, bool origin) { int err; - const struct cred *old_cred, *new_cred = NULL; + const struct cred *new_cred __free(put_cred) = NULL; struct dentry *parent = dentry->d_parent; - old_cred = ovl_override_creds(dentry->d_sb); - - /* - * When linking a file with copy up origin into a new parent, mark the - * new parent dir "impure". - */ - if (origin) { - err = ovl_set_impure(parent, ovl_dentry_upper(parent)); - if (err) - goto out_revert_creds; - } - - if (!attr->hardlink) { + scoped_class(override_creds_ovl, old_cred, dentry->d_sb) { /* - * In the creation cases(create, mkdir, mknod, symlink), - * ovl should transfer current's fs{u,g}id to underlying - * fs. Because underlying fs want to initialize its new - * inode owner using current's fs{u,g}id. And in this - * case, the @inode is a new inode that is initialized - * in inode_init_owner() to current's fs{u,g}id. So use - * the inode's i_{u,g}id to override the cred's fs{u,g}id. - * - * But in the other hardlink case, ovl_link() does not - * create a new inode, so just use the ovl mounter's - * fs{u,g}id. + * When linking a file with copy up origin into a new parent, mark the + * new parent dir "impure". */ - new_cred = ovl_setup_cred_for_create(dentry, inode, attr->mode, - old_cred); - err = PTR_ERR(new_cred); - if (IS_ERR(new_cred)) { - new_cred = NULL; - goto out_revert_creds; + if (origin) { + err = ovl_set_impure(parent, ovl_dentry_upper(parent)); + if (err) + return err; } + + if (!attr->hardlink) { + /* + * In the creation cases(create, mkdir, mknod, symlink), + * ovl should transfer current's fs{u,g}id to underlying + * fs. Because underlying fs want to initialize its new + * inode owner using current's fs{u,g}id. And in this + * case, the @inode is a new inode that is initialized + * in inode_init_owner() to current's fs{u,g}id. So use + * the inode's i_{u,g}id to override the cred's fs{u,g}id. + * + * But in the other hardlink case, ovl_link() does not + * create a new inode, so just use the ovl mounter's + * fs{u,g}id. + */ + new_cred = ovl_setup_cred_for_create(dentry, inode, attr->mode, old_cred); + if (IS_ERR(new_cred)) + return PTR_ERR(new_cred); + } + + if (!ovl_dentry_is_whiteout(dentry)) + return ovl_create_upper(dentry, inode, attr); + + return ovl_create_over_whiteout(dentry, inode, attr); } - - if (!ovl_dentry_is_whiteout(dentry)) - err = ovl_create_upper(dentry, inode, attr); - else - err = ovl_create_over_whiteout(dentry, inode, attr); - -out_revert_creds: - ovl_revert_creds(old_cred); - put_cred(new_cred); return err; } From ff4f6e4689e1f4d9870876651841ea7d996862c9 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:35 +0100 Subject: [PATCH 04/54] ovl: port ovl_set_link_redirect() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-4-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/dir.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index c3492b040fba..170f8dbea4ad 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -731,14 +731,8 @@ static int ovl_symlink(struct mnt_idmap *idmap, struct inode *dir, static int ovl_set_link_redirect(struct dentry *dentry) { - const struct cred *old_cred; - int err; - - old_cred = ovl_override_creds(dentry->d_sb); - err = ovl_set_redirect(dentry, false); - ovl_revert_creds(old_cred); - - return err; + with_ovl_creds(dentry->d_sb) + return ovl_set_redirect(dentry, false); } static int ovl_link(struct dentry *old, struct inode *newdir, From 8368eb837e19d94ed0028a02007f27e928dc3c02 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:36 +0100 Subject: [PATCH 05/54] ovl: port ovl_do_remove() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-5-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/dir.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 170f8dbea4ad..e51c53bbf1b8 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -907,7 +907,6 @@ static void ovl_drop_nlink(struct dentry *dentry) static int ovl_do_remove(struct dentry *dentry, bool is_dir) { int err; - const struct cred *old_cred; bool lower_positive = ovl_lower_positive(dentry); LIST_HEAD(list); @@ -926,12 +925,12 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) if (err) goto out; - old_cred = ovl_override_creds(dentry->d_sb); - if (!lower_positive) - err = ovl_remove_upper(dentry, is_dir, &list); - else - err = ovl_remove_and_whiteout(dentry, &list); - ovl_revert_creds(old_cred); + with_ovl_creds(dentry->d_sb) { + if (!lower_positive) + err = ovl_remove_upper(dentry, is_dir, &list); + else + err = ovl_remove_and_whiteout(dentry, &list); + } if (!err) { if (is_dir) clear_nlink(dentry->d_inode); From 5f51dfe768a1ef3b5f478bdfb0597aacf2e080da Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:37 +0100 Subject: [PATCH 06/54] ovl: port ovl_create_tmpfile() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-6-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/dir.c | 58 +++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index e51c53bbf1b8..ef00fc43ee65 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -1317,7 +1317,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, static int ovl_create_tmpfile(struct file *file, struct dentry *dentry, struct inode *inode, umode_t mode) { - const struct cred *old_cred, *new_cred = NULL; + const struct cred *new_cred __free(put_cred) = NULL; struct path realparentpath; struct file *realfile; struct ovl_file *of; @@ -1326,41 +1326,35 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry, int flags = file->f_flags | OVL_OPEN_FLAGS; int err; - old_cred = ovl_override_creds(dentry->d_sb); - new_cred = ovl_setup_cred_for_create(dentry, inode, mode, old_cred); - err = PTR_ERR(new_cred); - if (IS_ERR(new_cred)) { - new_cred = NULL; - goto out_revert_creds; - } + scoped_class(override_creds_ovl, old_cred, dentry->d_sb) { + new_cred = ovl_setup_cred_for_create(dentry, inode, mode, old_cred); + if (IS_ERR(new_cred)) + return PTR_ERR(new_cred); - ovl_path_upper(dentry->d_parent, &realparentpath); - realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath, - mode, current_cred()); - err = PTR_ERR_OR_ZERO(realfile); - pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err); - if (err) - goto out_revert_creds; + ovl_path_upper(dentry->d_parent, &realparentpath); + realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath, + mode, current_cred()); + err = PTR_ERR_OR_ZERO(realfile); + pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err); + if (err) + return err; - of = ovl_file_alloc(realfile); - if (!of) { - fput(realfile); - err = -ENOMEM; - goto out_revert_creds; - } + of = ovl_file_alloc(realfile); + if (!of) { + fput(realfile); + return -ENOMEM; + } - /* ovl_instantiate() consumes the newdentry reference on success */ - newdentry = dget(realfile->f_path.dentry); - err = ovl_instantiate(dentry, inode, newdentry, false, file); - if (!err) { - file->private_data = of; - } else { - dput(newdentry); - ovl_file_free(of); + /* ovl_instantiate() consumes the newdentry reference on success */ + newdentry = dget(realfile->f_path.dentry); + err = ovl_instantiate(dentry, inode, newdentry, false, file); + if (!err) { + file->private_data = of; + } else { + dput(newdentry); + ovl_file_free(of); + } } -out_revert_creds: - ovl_revert_creds(old_cred); - put_cred(new_cred); return err; } From b27ebb3d4b9c6dc9628131c284e01e45ff0b7a17 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:38 +0100 Subject: [PATCH 07/54] ovl: port ovl_open_realfile() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-7-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/file.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 7ab2c9daffd0..ebcd737e87ef 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -31,7 +31,6 @@ static struct file *ovl_open_realfile(const struct file *file, struct inode *inode = file_inode(file); struct mnt_idmap *real_idmap; struct file *realfile; - const struct cred *old_cred; int flags = file->f_flags | OVL_OPEN_FLAGS; int acc_mode = ACC_MODE(flags); int err; @@ -39,19 +38,19 @@ static struct file *ovl_open_realfile(const struct file *file, if (flags & O_APPEND) acc_mode |= MAY_APPEND; - old_cred = ovl_override_creds(inode->i_sb); - real_idmap = mnt_idmap(realpath->mnt); - err = inode_permission(real_idmap, realinode, MAY_OPEN | acc_mode); - if (err) { - realfile = ERR_PTR(err); - } else { - if (!inode_owner_or_capable(real_idmap, realinode)) - flags &= ~O_NOATIME; + with_ovl_creds(inode->i_sb) { + real_idmap = mnt_idmap(realpath->mnt); + err = inode_permission(real_idmap, realinode, MAY_OPEN | acc_mode); + if (err) { + realfile = ERR_PTR(err); + } else { + if (!inode_owner_or_capable(real_idmap, realinode)) + flags &= ~O_NOATIME; - realfile = backing_file_open(file_user_path(file), - flags, realpath, current_cred()); + realfile = backing_file_open(file_user_path(file), + flags, realpath, current_cred()); + } } - ovl_revert_creds(old_cred); pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n", file, file, ovl_whatisit(inode, realinode), file->f_flags, From 1fc4bc77c7865732694c6d32c98d990b2b1cddc8 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:39 +0100 Subject: [PATCH 08/54] ovl: port ovl_llseek() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-8-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/file.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index ebcd737e87ef..70ddb51297ce 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -243,7 +243,6 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence) { struct inode *inode = file_inode(file); struct file *realfile; - const struct cred *old_cred; loff_t ret; /* @@ -272,9 +271,8 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence) ovl_inode_lock(inode); realfile->f_pos = file->f_pos; - old_cred = ovl_override_creds(inode->i_sb); - ret = vfs_llseek(realfile, offset, whence); - ovl_revert_creds(old_cred); + with_ovl_creds(inode->i_sb) + ret = vfs_llseek(realfile, offset, whence); file->f_pos = realfile->f_pos; ovl_inode_unlock(inode); From 07a891c34676205a12caa002b22d30e6d9bed49d Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:40 +0100 Subject: [PATCH 09/54] ovl: port ovl_fsync() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-9-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/file.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 70ddb51297ce..92de675b697e 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -444,7 +444,6 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync) enum ovl_path_type type; struct path upperpath; struct file *upperfile; - const struct cred *old_cred; int ret; ret = ovl_sync_status(OVL_FS(file_inode(file)->i_sb)); @@ -461,11 +460,8 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync) if (IS_ERR(upperfile)) return PTR_ERR(upperfile); - old_cred = ovl_override_creds(file_inode(file)->i_sb); - ret = vfs_fsync_range(upperfile, start, end, datasync); - ovl_revert_creds(old_cred); - - return ret; + with_ovl_creds(file_inode(file)->i_sb) + return vfs_fsync_range(upperfile, start, end, datasync); } static int ovl_mmap(struct file *file, struct vm_area_struct *vma) From 2468017783027a9fb90fcb336094cc7c880f46ed Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:41 +0100 Subject: [PATCH 10/54] ovl: port ovl_fallocate() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-10-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/file.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 92de675b697e..830a3cc8bbfc 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -479,7 +479,6 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len { struct inode *inode = file_inode(file); struct file *realfile; - const struct cred *old_cred; int ret; inode_lock(inode); @@ -494,9 +493,8 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len if (IS_ERR(realfile)) goto out_unlock; - old_cred = ovl_override_creds(file_inode(file)->i_sb); - ret = vfs_fallocate(realfile, mode, offset, len); - ovl_revert_creds(old_cred); + with_ovl_creds(inode->i_sb) + ret = vfs_fallocate(realfile, mode, offset, len); /* Update size */ ovl_file_modified(file); From 8e8f4df93c1d32e57e3d6cb7ac8233c959423597 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:42 +0100 Subject: [PATCH 11/54] ovl: port ovl_fadvise() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-11-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/file.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 830a3cc8bbfc..73c805851e58 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -508,18 +508,13 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len static int ovl_fadvise(struct file *file, loff_t offset, loff_t len, int advice) { struct file *realfile; - const struct cred *old_cred; - int ret; realfile = ovl_real_file(file); if (IS_ERR(realfile)) return PTR_ERR(realfile); - old_cred = ovl_override_creds(file_inode(file)->i_sb); - ret = vfs_fadvise(realfile, offset, len, advice); - ovl_revert_creds(old_cred); - - return ret; + with_ovl_creds(file_inode(file)->i_sb) + return vfs_fadvise(realfile, offset, len, advice); } enum ovl_copyop { From 9763970984511861505a88b038dbee2c8c5e1623 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:43 +0100 Subject: [PATCH 12/54] ovl: port ovl_flush() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-12-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/file.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 73c805851e58..0ab846d8062e 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -618,7 +618,6 @@ static loff_t ovl_remap_file_range(struct file *file_in, loff_t pos_in, static int ovl_flush(struct file *file, fl_owner_t id) { struct file *realfile; - const struct cred *old_cred; int err = 0; realfile = ovl_real_file(file); @@ -626,9 +625,8 @@ static int ovl_flush(struct file *file, fl_owner_t id) return PTR_ERR(realfile); if (realfile->f_op->flush) { - old_cred = ovl_override_creds(file_inode(file)->i_sb); - err = realfile->f_op->flush(realfile, id); - ovl_revert_creds(old_cred); + with_ovl_creds(file_inode(file)->i_sb) + err = realfile->f_op->flush(realfile, id); } return err; From 7aedfa5a52b09896a0fd8f611966b9d7194fd3fa Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:44 +0100 Subject: [PATCH 13/54] ovl: port ovl_setattr() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-13-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/inode.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index e11f310ce092..7b28318b7f31 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -25,7 +25,6 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry, struct ovl_fs *ofs = OVL_FS(dentry->d_sb); bool full_copy_up = false; struct dentry *upperdentry; - const struct cred *old_cred; err = setattr_prepare(&nop_mnt_idmap, dentry, attr); if (err) @@ -78,9 +77,8 @@ int ovl_setattr(struct mnt_idmap *idmap, struct dentry *dentry, goto out_put_write; inode_lock(upperdentry->d_inode); - old_cred = ovl_override_creds(dentry->d_sb); - err = ovl_do_notify_change(ofs, upperdentry, attr); - ovl_revert_creds(old_cred); + with_ovl_creds(dentry->d_sb) + err = ovl_do_notify_change(ofs, upperdentry, attr); if (!err) ovl_copyattr(dentry->d_inode); inode_unlock(upperdentry->d_inode); From 81707ae827d3af60046544b8e00a7d86ad7660d3 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:45 +0100 Subject: [PATCH 14/54] ovl: port ovl_getattr() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-14-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/inode.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 7b28318b7f31..37c56218f9ac 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -151,13 +151,22 @@ static void ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, int fsid) } } +static inline int ovl_real_getattr_nosec(struct super_block *sb, + const struct path *path, + struct kstat *stat, u32 request_mask, + unsigned int flags) +{ + with_ovl_creds(sb) + return vfs_getattr_nosec(path, stat, request_mask, flags); +} + int ovl_getattr(struct mnt_idmap *idmap, const struct path *path, struct kstat *stat, u32 request_mask, unsigned int flags) { struct dentry *dentry = path->dentry; + struct super_block *sb = dentry->d_sb; enum ovl_path_type type; struct path realpath; - const struct cred *old_cred; struct inode *inode = d_inode(dentry); bool is_dir = S_ISDIR(inode->i_mode); int fsid = 0; @@ -167,10 +176,9 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path, metacopy_blocks = ovl_is_metacopy_dentry(dentry); type = ovl_path_real(dentry, &realpath); - old_cred = ovl_override_creds(dentry->d_sb); - err = vfs_getattr_nosec(&realpath, stat, request_mask, flags); + err = ovl_real_getattr_nosec(sb, &realpath, stat, request_mask, flags); if (err) - goto out; + return err; /* Report the effective immutable/append-only STATX flags */ generic_fill_statx_attr(inode, stat); @@ -193,10 +201,9 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path, (!is_dir ? STATX_NLINK : 0); ovl_path_lower(dentry, &realpath); - err = vfs_getattr_nosec(&realpath, &lowerstat, lowermask, - flags); + err = ovl_real_getattr_nosec(sb, &realpath, &lowerstat, lowermask, flags); if (err) - goto out; + return err; /* * Lower hardlinks may be broken on copy up to different @@ -246,10 +253,10 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path, ovl_path_lowerdata(dentry, &realpath); if (realpath.dentry) { - err = vfs_getattr_nosec(&realpath, &lowerdatastat, - lowermask, flags); + err = ovl_real_getattr_nosec(sb, &realpath, &lowerdatastat, + lowermask, flags); if (err) - goto out; + return err; } else { lowerdatastat.blocks = round_up(stat->size, stat->blksize) >> 9; @@ -277,9 +284,6 @@ int ovl_getattr(struct mnt_idmap *idmap, const struct path *path, if (!is_dir && ovl_test_flag(OVL_INDEX, d_inode(dentry))) stat->nlink = dentry->d_inode->i_nlink; -out: - ovl_revert_creds(old_cred); - return err; } From d81999b40baf669cc2566d4fb102356548f46a91 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:46 +0100 Subject: [PATCH 15/54] ovl: port ovl_permission() to cred guard Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-15-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/inode.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 37c56218f9ac..7dbf6cf2252d 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -293,7 +293,6 @@ int ovl_permission(struct mnt_idmap *idmap, struct inode *upperinode = ovl_inode_upper(inode); struct inode *realinode; struct path realpath; - const struct cred *old_cred; int err; /* Careful in RCU walk mode */ @@ -311,17 +310,15 @@ int ovl_permission(struct mnt_idmap *idmap, if (err) return err; - old_cred = ovl_override_creds(inode->i_sb); if (!upperinode && !special_file(realinode->i_mode) && mask & MAY_WRITE) { mask &= ~(MAY_WRITE | MAY_APPEND); /* Make sure mounter can read file for copy up later */ mask |= MAY_READ; } - err = inode_permission(mnt_idmap(realpath.mnt), realinode, mask); - ovl_revert_creds(old_cred); - return err; + with_ovl_creds(inode->i_sb) + return inode_permission(mnt_idmap(realpath.mnt), realinode, mask); } static const char *ovl_get_link(struct dentry *dentry, From 47eba7f7fd1543dd1a677e6f971b11925e8b9b9b Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:47 +0100 Subject: [PATCH 16/54] ovl: port ovl_get_link() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-16-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/inode.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 7dbf6cf2252d..31e4fb14ac13 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -325,16 +325,11 @@ static const char *ovl_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *done) { - const struct cred *old_cred; - const char *p; - if (!dentry) return ERR_PTR(-ECHILD); - old_cred = ovl_override_creds(dentry->d_sb); - p = vfs_get_link(ovl_dentry_real(dentry), done); - ovl_revert_creds(old_cred); - return p; + with_ovl_creds(dentry->d_sb) + return vfs_get_link(ovl_dentry_real(dentry), done); } #ifdef CONFIG_FS_POSIX_ACL From 71ac28fbcd3309cf6e08b69f611767560f366c0c Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:48 +0100 Subject: [PATCH 17/54] ovl: port do_ovl_get_acl() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-17-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/inode.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 31e4fb14ac13..27c9c3bc77c6 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -459,11 +459,8 @@ struct posix_acl *do_ovl_get_acl(struct mnt_idmap *idmap, acl = get_cached_acl_rcu(realinode, type); } else { - const struct cred *old_cred; - - old_cred = ovl_override_creds(inode->i_sb); - acl = ovl_get_acl_path(&realpath, posix_acl_xattr_name(type), noperm); - ovl_revert_creds(old_cred); + with_ovl_creds(inode->i_sb) + acl = ovl_get_acl_path(&realpath, posix_acl_xattr_name(type), noperm); } return acl; From 8e9698d6e4ef92f378f2cee9f1f8e035eece6cf8 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:49 +0100 Subject: [PATCH 18/54] ovl: port ovl_set_or_remove_acl() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-18-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/inode.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 27c9c3bc77c6..efc0ff84b59b 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -472,7 +472,6 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode, int err; struct path realpath; const char *acl_name; - const struct cred *old_cred; struct ovl_fs *ofs = OVL_FS(dentry->d_sb); struct dentry *upperdentry = ovl_dentry_upper(dentry); struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry); @@ -486,10 +485,8 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode, struct posix_acl *real_acl; ovl_path_lower(dentry, &realpath); - old_cred = ovl_override_creds(dentry->d_sb); - real_acl = vfs_get_acl(mnt_idmap(realpath.mnt), realdentry, - acl_name); - ovl_revert_creds(old_cred); + with_ovl_creds(dentry->d_sb) + real_acl = vfs_get_acl(mnt_idmap(realpath.mnt), realdentry, acl_name); if (IS_ERR(real_acl)) { err = PTR_ERR(real_acl); goto out; @@ -509,12 +506,12 @@ static int ovl_set_or_remove_acl(struct dentry *dentry, struct inode *inode, if (err) goto out; - old_cred = ovl_override_creds(dentry->d_sb); - if (acl) - err = ovl_do_set_acl(ofs, realdentry, acl_name, acl); - else - err = ovl_do_remove_acl(ofs, realdentry, acl_name); - ovl_revert_creds(old_cred); + with_ovl_creds(dentry->d_sb) { + if (acl) + err = ovl_do_set_acl(ofs, realdentry, acl_name, acl); + else + err = ovl_do_remove_acl(ofs, realdentry, acl_name); + } ovl_drop_write(dentry); /* copy c/mtime */ From a3860a808f732384f8104250fba54481e6431f32 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:50 +0100 Subject: [PATCH 19/54] ovl: port ovl_fiemap() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-19-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/inode.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index efc0ff84b59b..aefdb0a27e3b 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -576,9 +576,7 @@ int ovl_update_time(struct inode *inode, int flags) static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 len) { - int err; struct inode *realinode = ovl_inode_realdata(inode); - const struct cred *old_cred; if (!realinode) return -EIO; @@ -586,11 +584,8 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, if (!realinode->i_op->fiemap) return -EOPNOTSUPP; - old_cred = ovl_override_creds(inode->i_sb); - err = realinode->i_op->fiemap(realinode, fieinfo, start, len); - ovl_revert_creds(old_cred); - - return err; + with_ovl_creds(inode->i_sb) + return realinode->i_op->fiemap(realinode, fieinfo, start, len); } /* From af1d5d62f39e1f657ad0276f7d48d95d9d0d1326 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:51 +0100 Subject: [PATCH 20/54] ovl: port ovl_fileattr_set() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-20-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/inode.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index aefdb0a27e3b..53cabd948968 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -636,7 +636,6 @@ int ovl_fileattr_set(struct mnt_idmap *idmap, { struct inode *inode = d_inode(dentry); struct path upperpath; - const struct cred *old_cred; unsigned int flags; int err; @@ -648,18 +647,18 @@ int ovl_fileattr_set(struct mnt_idmap *idmap, if (err) goto out; - old_cred = ovl_override_creds(inode->i_sb); - /* - * Store immutable/append-only flags in xattr and clear them - * in upper fileattr (in case they were set by older kernel) - * so children of "ovl-immutable" directories lower aliases of - * "ovl-immutable" hardlinks could be copied up. - * Clear xattr when flags are cleared. - */ - err = ovl_set_protattr(inode, upperpath.dentry, fa); - if (!err) - err = ovl_real_fileattr_set(&upperpath, fa); - ovl_revert_creds(old_cred); + with_ovl_creds(inode->i_sb) { + /* + * Store immutable/append-only flags in xattr and clear them + * in upper fileattr (in case they were set by older kernel) + * so children of "ovl-immutable" directories lower aliases of + * "ovl-immutable" hardlinks could be copied up. + * Clear xattr when flags are cleared. + */ + err = ovl_set_protattr(inode, upperpath.dentry, fa); + if (!err) + err = ovl_real_fileattr_set(&upperpath, fa); + } ovl_drop_write(dentry); /* From 4975e683c2786c698827aaef18c999a3398bfe6a Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:52 +0100 Subject: [PATCH 21/54] ovl: port ovl_fileattr_get() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-21-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/inode.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 53cabd948968..a3a4924874d2 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -712,15 +712,13 @@ int ovl_fileattr_get(struct dentry *dentry, struct file_kattr *fa) { struct inode *inode = d_inode(dentry); struct path realpath; - const struct cred *old_cred; int err; ovl_path_real(dentry, &realpath); - old_cred = ovl_override_creds(inode->i_sb); - err = ovl_real_fileattr_get(&realpath, fa); + with_ovl_creds(inode->i_sb) + err = ovl_real_fileattr_get(&realpath, fa); ovl_fileattr_prot_flags(inode, fa); - ovl_revert_creds(old_cred); return err; } From b1c47b3abcc6bf81cfc3b4e01faadc23ff261ba2 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:53 +0100 Subject: [PATCH 22/54] ovl: port ovl_maybe_validate_verity() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-22-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/namei.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index e93bcc5727bc..dbacf02423cb 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -979,15 +979,10 @@ static int ovl_maybe_validate_verity(struct dentry *dentry) return err; if (!ovl_test_flag(OVL_VERIFIED_DIGEST, inode)) { - const struct cred *old_cred; - - old_cred = ovl_override_creds(dentry->d_sb); - - err = ovl_validate_verity(ofs, &metapath, &datapath); + with_ovl_creds(dentry->d_sb) + err = ovl_validate_verity(ofs, &metapath, &datapath); if (err == 0) ovl_set_flag(OVL_VERIFIED_DIGEST, inode); - - ovl_revert_creds(old_cred); } ovl_inode_unlock(inode); From cb3c8cbaed041b8cf229f48b9d7503eca7969f97 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:54 +0100 Subject: [PATCH 23/54] ovl: port ovl_maybe_lookup_lowerdata() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-23-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/namei.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index dbacf02423cb..49874525cf52 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -996,7 +996,6 @@ static int ovl_maybe_lookup_lowerdata(struct dentry *dentry) struct inode *inode = d_inode(dentry); const char *redirect = ovl_lowerdata_redirect(inode); struct ovl_path datapath = {}; - const struct cred *old_cred; int err; if (!redirect || ovl_dentry_lowerdata(dentry)) @@ -1014,9 +1013,8 @@ static int ovl_maybe_lookup_lowerdata(struct dentry *dentry) if (ovl_dentry_lowerdata(dentry)) goto out; - old_cred = ovl_override_creds(dentry->d_sb); - err = ovl_lookup_data_layers(dentry, redirect, &datapath); - ovl_revert_creds(old_cred); + with_ovl_creds(dentry->d_sb) + err = ovl_lookup_data_layers(dentry, redirect, &datapath); if (err) goto out_err; From 198d1822884a79e92a6e33284be90db5c09a48df Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:55 +0100 Subject: [PATCH 24/54] ovl: don't override credentials for ovl_check_whiteouts() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function is only called when rdd->dentry is non-NULL: if (!err && rdd->first_maybe_whiteout && rdd->dentry) err = ovl_check_whiteouts(realpath, rdd); | Caller | Sets rdd->dentry? | Can call ovl_check_whiteouts()? | |-------------------------------|-------------------|---------------------------------| | ovl_dir_read_merged() | ✓ Yes (line 430) | ✓ YES | | ovl_dir_read_impure() | ✗ No | ✗ NO | | ovl_check_d_type_supported() | ✗ No | ✗ NO | | ovl_workdir_cleanup_recurse() | ✗ No | ✗ NO | | ovl_indexdir_cleanup() | ✗ No | ✗ NO | VFS layer (.iterate_shared file operation) → ovl_iterate() [CRED OVERRIDE] → ovl_cache_get() → ovl_dir_read_merged() → ovl_dir_read() → ovl_check_whiteouts() [CRED REVERT] ovl_unlink() → ovl_do_remove() → ovl_check_empty_dir() [CRED OVERRIDE] → ovl_dir_read_merged() → ovl_dir_read() → ovl_check_whiteouts() [CRED REVERT] ovl_rename() → ovl_check_empty_dir() [CRED OVERRIDE] → ovl_dir_read_merged() → ovl_dir_read() → ovl_check_whiteouts() [CRED REVERT] All valid callchains already override credentials so drop the override. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-24-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/readdir.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 77ecc39fc33a..2e345d39b193 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -348,11 +348,7 @@ static bool ovl_fill_merge(struct dir_context *ctx, const char *name, static int ovl_check_whiteouts(const struct path *path, struct ovl_readdir_data *rdd) { - int err = 0; struct dentry *dentry, *dir = path->dentry; - const struct cred *old_cred; - - old_cred = ovl_override_creds(rdd->dentry->d_sb); while (rdd->first_maybe_whiteout) { struct ovl_cache_entry *p = @@ -365,13 +361,11 @@ static int ovl_check_whiteouts(const struct path *path, struct ovl_readdir_data p->is_whiteout = ovl_is_whiteout(dentry); dput(dentry); } else if (PTR_ERR(dentry) == -EINTR) { - err = -EINTR; - break; + return -EINTR; } } - ovl_revert_creds(old_cred); - return err; + return 0; } static inline int ovl_dir_read(const struct path *realpath, From d25e4b739f8378419f990983f2542160e79738c5 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:56 +0100 Subject: [PATCH 25/54] ovl: refactor ovl_iterate() and port to cred guard factor out ovl_iterate_merged() and move some code into ovl_iterate_real() for easier use of the scoped ovl cred guard. Signed-off-by: Amir Goldstein Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-25-b31603935724@kernel.org Signed-off-by: Christian Brauner --- fs/overlayfs/readdir.c | 76 +++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 2e345d39b193..e285194306cc 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -832,36 +832,12 @@ static int ovl_iterate_real(struct file *file, struct dir_context *ctx) return err; } - -static int ovl_iterate(struct file *file, struct dir_context *ctx) +static int ovl_iterate_merged(struct file *file, struct dir_context *ctx) { struct ovl_dir_file *od = file->private_data; struct dentry *dentry = file->f_path.dentry; - struct ovl_fs *ofs = OVL_FS(dentry->d_sb); struct ovl_cache_entry *p; - const struct cred *old_cred; - int err; - - old_cred = ovl_override_creds(dentry->d_sb); - if (!ctx->pos) - ovl_dir_reset(file); - - if (od->is_real) { - /* - * If parent is merge, then need to adjust d_ino for '..', if - * dir is impure then need to adjust d_ino for copied up - * entries. - */ - if (ovl_xino_bits(ofs) || - (ovl_same_fs(ofs) && - (ovl_is_impure_dir(file) || - OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent))))) { - err = ovl_iterate_real(file, ctx); - } else { - err = iterate_dir(od->realfile, ctx); - } - goto out; - } + int err = 0; if (!od->cache) { struct ovl_dir_cache *cache; @@ -869,7 +845,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) cache = ovl_cache_get(dentry); err = PTR_ERR(cache); if (IS_ERR(cache)) - goto out; + return err; od->cache = cache; ovl_seek_cursor(od, ctx->pos); @@ -881,7 +857,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) if (!p->ino || p->check_xwhiteout) { err = ovl_cache_update(&file->f_path, p, !p->ino); if (err) - goto out; + return err; } } /* ovl_cache_update() sets is_whiteout on stale entry */ @@ -892,12 +868,50 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) od->cursor = p->l_node.next; ctx->pos++; } - err = 0; -out: - ovl_revert_creds(old_cred); return err; } +static bool ovl_need_adjust_d_ino(struct file *file) +{ + struct dentry *dentry = file->f_path.dentry; + struct ovl_fs *ofs = OVL_FS(dentry->d_sb); + + /* If parent is merge, then need to adjust d_ino for '..' */ + if (ovl_xino_bits(ofs)) + return true; + + /* Can't do consistent inode numbering */ + if (!ovl_same_fs(ofs)) + return false; + + /* If dir is impure then need to adjust d_ino for copied up entries */ + if (ovl_is_impure_dir(file) || + OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent))) + return true; + + /* Pure: no need to adjust d_ino */ + return false; +} + + +static int ovl_iterate(struct file *file, struct dir_context *ctx) +{ + struct ovl_dir_file *od = file->private_data; + + if (!ctx->pos) + ovl_dir_reset(file); + + with_ovl_creds(file_dentry(file)->d_sb) { + if (!od->is_real) + return ovl_iterate_merged(file, ctx); + + if (ovl_need_adjust_d_ino(file)) + return ovl_iterate_real(file, ctx); + + return iterate_dir(od->realfile, ctx); + } +} + static loff_t ovl_dir_llseek(struct file *file, loff_t offset, int origin) { loff_t res; From 5517646e14d18768ee72ae2f4320826f2169329a Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:57 +0100 Subject: [PATCH 26/54] ovl: port ovl_dir_llseek() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-26-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/readdir.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index e285194306cc..28d399de26f3 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -955,14 +955,8 @@ static loff_t ovl_dir_llseek(struct file *file, loff_t offset, int origin) static struct file *ovl_dir_open_realfile(const struct file *file, const struct path *realpath) { - struct file *res; - const struct cred *old_cred; - - old_cred = ovl_override_creds(file_inode(file)->i_sb); - res = ovl_path_open(realpath, O_RDONLY | (file->f_flags & O_LARGEFILE)); - ovl_revert_creds(old_cred); - - return res; + with_ovl_creds(file_inode(file)->i_sb) + return ovl_path_open(realpath, O_RDONLY | (file->f_flags & O_LARGEFILE)); } /* From 67bc75e6f48dcb5b1454a78fcf87e68a68e83e79 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:58 +0100 Subject: [PATCH 27/54] ovl: port ovl_check_empty_dir() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-27-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/readdir.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 28d399de26f3..160960bb0ad0 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -1077,11 +1077,9 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list) int err; struct ovl_cache_entry *p, *n; struct rb_root root = RB_ROOT; - const struct cred *old_cred; - old_cred = ovl_override_creds(dentry->d_sb); - err = ovl_dir_read_merged(dentry, list, &root); - ovl_revert_creds(old_cred); + with_ovl_creds(dentry->d_sb) + err = ovl_dir_read_merged(dentry, list, &root); if (err) return err; From 062c5b48d238f5de63b3e207cbd5153d0e77ed42 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:33:59 +0100 Subject: [PATCH 28/54] ovl: port ovl_nlink_start() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-28-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/util.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 46387aeb6be6..e61eb758349a 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -1147,7 +1147,6 @@ static void ovl_cleanup_index(struct dentry *dentry) int ovl_nlink_start(struct dentry *dentry) { struct inode *inode = d_inode(dentry); - const struct cred *old_cred; int err; if (WARN_ON(!inode)) @@ -1184,15 +1183,14 @@ int ovl_nlink_start(struct dentry *dentry) if (d_is_dir(dentry) || !ovl_test_flag(OVL_INDEX, inode)) return 0; - old_cred = ovl_override_creds(dentry->d_sb); /* * The overlay inode nlink should be incremented/decremented IFF the * upper operation succeeds, along with nlink change of upper inode. * Therefore, before link/unlink/rename, we store the union nlink * value relative to the upper inode nlink in an upper inode xattr. */ - err = ovl_set_nlink_upper(dentry); - ovl_revert_creds(old_cred); + with_ovl_creds(dentry->d_sb) + err = ovl_set_nlink_upper(dentry); if (err) goto out_drop_write; From 9e5ec68f3a706515fe348b674e908edf80e70e63 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:00 +0100 Subject: [PATCH 29/54] ovl: port ovl_nlink_end() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-29-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/util.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index e61eb758349a..0d9487cac0e8 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -1211,11 +1211,8 @@ void ovl_nlink_end(struct dentry *dentry) ovl_drop_write(dentry); if (ovl_test_flag(OVL_INDEX, inode) && inode->i_nlink == 0) { - const struct cred *old_cred; - - old_cred = ovl_override_creds(dentry->d_sb); - ovl_cleanup_index(dentry); - ovl_revert_creds(old_cred); + with_ovl_creds(dentry->d_sb) + ovl_cleanup_index(dentry); } ovl_inode_unlock(inode); From d6053017260113fe20cfaeab35fd84572f5b0e24 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:01 +0100 Subject: [PATCH 30/54] ovl: port ovl_xattr_set() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-30-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/xattrs.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/fs/overlayfs/xattrs.c b/fs/overlayfs/xattrs.c index 88055deca936..787df86acb26 100644 --- a/fs/overlayfs/xattrs.c +++ b/fs/overlayfs/xattrs.c @@ -41,13 +41,11 @@ static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char struct dentry *upperdentry = ovl_i_dentry_upper(inode); struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry); struct path realpath; - const struct cred *old_cred; if (!value && !upperdentry) { ovl_path_lower(dentry, &realpath); - old_cred = ovl_override_creds(dentry->d_sb); - err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0); - ovl_revert_creds(old_cred); + with_ovl_creds(dentry->d_sb) + err = vfs_getxattr(mnt_idmap(realpath.mnt), realdentry, name, NULL, 0); if (err < 0) goto out; } @@ -64,15 +62,14 @@ static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char if (err) goto out; - old_cred = ovl_override_creds(dentry->d_sb); - if (value) { - err = ovl_do_setxattr(ofs, realdentry, name, value, size, - flags); - } else { - WARN_ON(flags != XATTR_REPLACE); - err = ovl_do_removexattr(ofs, realdentry, name); + with_ovl_creds(dentry->d_sb) { + if (value) { + err = ovl_do_setxattr(ofs, realdentry, name, value, size, flags); + } else { + WARN_ON(flags != XATTR_REPLACE); + err = ovl_do_removexattr(ofs, realdentry, name); + } } - ovl_revert_creds(old_cred); ovl_drop_write(dentry); /* copy c/mtime */ From ae64b5418555fa506e74152ebb644bcebc9f2cad Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:02 +0100 Subject: [PATCH 31/54] ovl: port ovl_xattr_get() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-31-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/xattrs.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/fs/overlayfs/xattrs.c b/fs/overlayfs/xattrs.c index 787df86acb26..788182fff3e0 100644 --- a/fs/overlayfs/xattrs.c +++ b/fs/overlayfs/xattrs.c @@ -81,15 +81,11 @@ static int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char static int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name, void *value, size_t size) { - ssize_t res; - const struct cred *old_cred; struct path realpath; ovl_i_path_real(inode, &realpath); - old_cred = ovl_override_creds(dentry->d_sb); - res = vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size); - ovl_revert_creds(old_cred); - return res; + with_ovl_creds(dentry->d_sb) + return vfs_getxattr(mnt_idmap(realpath.mnt), realpath.dentry, name, value, size); } static bool ovl_can_list(struct super_block *sb, const char *s) From 0b5800172c8e0a93776e4ee74e50f56d7625bedc Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:03 +0100 Subject: [PATCH 32/54] ovl: port ovl_listxattr() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-32-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/xattrs.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fs/overlayfs/xattrs.c b/fs/overlayfs/xattrs.c index 788182fff3e0..aa95855c7023 100644 --- a/fs/overlayfs/xattrs.c +++ b/fs/overlayfs/xattrs.c @@ -109,12 +109,10 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) ssize_t res; size_t len; char *s; - const struct cred *old_cred; size_t prefix_len, name_len; - old_cred = ovl_override_creds(dentry->d_sb); - res = vfs_listxattr(realdentry, list, size); - ovl_revert_creds(old_cred); + with_ovl_creds(dentry->d_sb) + res = vfs_listxattr(realdentry, list, size); if (res <= 0 || size == 0) return res; From fb9f31fe9f7b3cecf1a59427a7395c3cf1d26706 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 19 Nov 2025 18:50:56 +0100 Subject: [PATCH 33/54] ovl: introduce struct ovl_renamedata Add a struct ovl_renamedata to group rename-related state that was previously stored in local variables. Embedd struct renamedata directly aligning with the vfs. Signed-off-by: Amir Goldstein Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-33-b31603935724@kernel.org Signed-off-by: Christian Brauner --- fs/overlayfs/dir.c | 78 ++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index ef00fc43ee65..83f5027cf45d 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -1094,6 +1094,14 @@ static int ovl_set_redirect(struct dentry *dentry, bool samedir) return err; } +struct ovl_renamedata { + struct renamedata; + struct dentry *opaquedir; + bool cleanup_whiteout; + bool update_nlink; + bool overwrite; +}; + static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, struct dentry *old, struct inode *newdir, struct dentry *new, unsigned int flags) @@ -1104,49 +1112,53 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, struct renamedata rd = {}; bool old_opaque; bool new_opaque; - bool cleanup_whiteout = false; - bool update_nlink = false; - bool overwrite = !(flags & RENAME_EXCHANGE); bool is_dir = d_is_dir(old); bool new_is_dir = d_is_dir(new); - bool samedir = olddir == newdir; - struct dentry *opaquedir = NULL; + bool samedir = old->d_parent == new->d_parent; struct dentry *whiteout = NULL; const struct cred *old_cred = NULL; struct ovl_fs *ofs = OVL_FS(old->d_sb); + struct ovl_renamedata _ovlrd = { + .old_dentry = old, + .new_dentry = new, + .flags = flags, + .cleanup_whiteout = false, + .overwrite = !(flags & RENAME_EXCHANGE), + }; + struct ovl_renamedata *ovlrd = &_ovlrd; LIST_HEAD(list); err = -EINVAL; - if (flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE)) + if (ovlrd->flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE)) goto out; - flags &= ~RENAME_NOREPLACE; + ovlrd->flags &= ~RENAME_NOREPLACE; /* Don't copy up directory trees */ err = -EXDEV; if (!ovl_can_move(old)) goto out; - if (!overwrite && !ovl_can_move(new)) + if (!ovlrd->overwrite && !ovl_can_move(new)) goto out; - if (overwrite && new_is_dir && !ovl_pure_upper(new)) { + if (ovlrd->overwrite && new_is_dir && !ovl_pure_upper(new)) { err = ovl_check_empty_dir(new, &list); if (err) goto out; } - if (overwrite) { + if (ovlrd->overwrite) { if (ovl_lower_positive(old)) { if (!ovl_dentry_is_whiteout(new)) { /* Whiteout source */ - flags |= RENAME_WHITEOUT; + ovlrd->flags |= RENAME_WHITEOUT; } else { /* Switch whiteouts */ - flags |= RENAME_EXCHANGE; + ovlrd->flags |= RENAME_EXCHANGE; } } else if (is_dir && ovl_dentry_is_whiteout(new)) { - flags |= RENAME_EXCHANGE; - cleanup_whiteout = true; + ovlrd->flags |= RENAME_EXCHANGE; + ovlrd->cleanup_whiteout = true; } } @@ -1157,7 +1169,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, err = ovl_copy_up(new->d_parent); if (err) goto out; - if (!overwrite) { + if (!ovlrd->overwrite) { err = ovl_copy_up(new); if (err) goto out; @@ -1166,10 +1178,10 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, if (err) goto out; - update_nlink = true; + ovlrd->update_nlink = true; } - if (!update_nlink) { + if (!ovlrd->update_nlink) { /* ovl_nlink_start() took ovl_want_write() */ err = ovl_want_write(old); if (err) @@ -1179,10 +1191,10 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, old_cred = ovl_override_creds(old->d_sb); if (!list_empty(&list)) { - opaquedir = ovl_clear_empty(new, &list); - err = PTR_ERR(opaquedir); - if (IS_ERR(opaquedir)) { - opaquedir = NULL; + ovlrd->opaquedir = ovl_clear_empty(new, &list); + err = PTR_ERR(ovlrd->opaquedir); + if (IS_ERR(ovlrd->opaquedir)) { + ovlrd->opaquedir = NULL; goto out_revert_creds; } } @@ -1202,7 +1214,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, if (err) goto out_revert_creds; } - if (!overwrite && ovl_type_origin(new)) { + if (!ovlrd->overwrite && ovl_type_origin(new)) { err = ovl_set_impure(old->d_parent, old_upperdir); if (err) goto out_revert_creds; @@ -1212,7 +1224,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, rd.mnt_idmap = ovl_upper_mnt_idmap(ofs); rd.old_parent = old_upperdir; rd.new_parent = new_upperdir; - rd.flags = flags; + rd.flags = ovlrd->flags; err = start_renaming(&rd, 0, &QSTR_LEN(old->d_name.name, old->d_name.len), @@ -1230,8 +1242,8 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, err = -ESTALE; if (d_inode(new) && ovl_dentry_upper(new)) { - if (opaquedir) { - if (rd.new_dentry != opaquedir) + if (ovlrd->opaquedir) { + if (rd.new_dentry != ovlrd->opaquedir) goto out_unlock; } else { if (!ovl_matches_upper(new, rd.new_dentry)) @@ -1242,7 +1254,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, if (!new_opaque || !ovl_upper_is_whiteout(ofs, rd.new_dentry)) goto out_unlock; } else { - if (flags & RENAME_EXCHANGE) + if (ovlrd->flags & RENAME_EXCHANGE) goto out_unlock; } } @@ -1258,9 +1270,9 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, if (err) goto out_unlock; - if (!overwrite && ovl_type_merge_or_lower(new)) + if (!ovlrd->overwrite && ovl_type_merge_or_lower(new)) err = ovl_set_redirect(new, samedir); - else if (!overwrite && new_is_dir && !new_opaque && + else if (!ovlrd->overwrite && new_is_dir && !new_opaque && ovl_type_merge(old->d_parent)) err = ovl_set_opaque_xerr(new, rd.new_dentry, -EXDEV); if (err) @@ -1268,7 +1280,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, err = ovl_do_rename_rd(&rd); - if (!err && cleanup_whiteout) + if (!err && ovlrd->cleanup_whiteout) whiteout = dget(rd.new_dentry); end_renaming(&rd); @@ -1281,7 +1293,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, dput(whiteout); } - if (overwrite && d_inode(new)) { + if (ovlrd->overwrite && d_inode(new)) { if (new_is_dir) clear_nlink(d_inode(new)); else @@ -1289,7 +1301,7 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, } ovl_dir_modified(old->d_parent, ovl_type_origin(old) || - (!overwrite && ovl_type_origin(new))); + (!ovlrd->overwrite && ovl_type_origin(new))); ovl_dir_modified(new->d_parent, ovl_type_origin(old) || (d_inode(new) && ovl_type_origin(new))); @@ -1300,12 +1312,12 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, out_revert_creds: ovl_revert_creds(old_cred); - if (update_nlink) + if (ovlrd->update_nlink) ovl_nlink_end(new); else ovl_drop_write(old); out: - dput(opaquedir); + dput(ovlrd->opaquedir); ovl_cache_free(&list); return err; From a1da8401987e671ac18720eb26c7ee52e79e3d4c Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 19 Nov 2025 20:48:38 +0100 Subject: [PATCH 34/54] ovl: refactor ovl_rename() Extract the code that runs under overridden credentials into a separate ovl_rename_upper() helper function and the code that runs before/after to ovl_rename_start/end(). Error handling is simplified. The helpers returns errors directly instead of using goto labels. Signed-off-by: Amir Goldstein Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-34-b31603935724@kernel.org Signed-off-by: Christian Brauner --- fs/overlayfs/dir.c | 135 ++++++++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 57 deletions(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 83f5027cf45d..ab1fdd7ccb2e 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -1102,49 +1102,30 @@ struct ovl_renamedata { bool overwrite; }; -static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, - struct dentry *old, struct inode *newdir, - struct dentry *new, unsigned int flags) +static int ovl_rename_start(struct ovl_renamedata *ovlrd, struct list_head *list) { - int err; - struct dentry *old_upperdir; - struct dentry *new_upperdir; - struct renamedata rd = {}; - bool old_opaque; - bool new_opaque; + struct dentry *old = ovlrd->old_dentry; + struct dentry *new = ovlrd->new_dentry; bool is_dir = d_is_dir(old); bool new_is_dir = d_is_dir(new); - bool samedir = old->d_parent == new->d_parent; - struct dentry *whiteout = NULL; - const struct cred *old_cred = NULL; - struct ovl_fs *ofs = OVL_FS(old->d_sb); - struct ovl_renamedata _ovlrd = { - .old_dentry = old, - .new_dentry = new, - .flags = flags, - .cleanup_whiteout = false, - .overwrite = !(flags & RENAME_EXCHANGE), - }; - struct ovl_renamedata *ovlrd = &_ovlrd; - LIST_HEAD(list); + int err; - err = -EINVAL; if (ovlrd->flags & ~(RENAME_EXCHANGE | RENAME_NOREPLACE)) - goto out; + return -EINVAL; ovlrd->flags &= ~RENAME_NOREPLACE; /* Don't copy up directory trees */ err = -EXDEV; if (!ovl_can_move(old)) - goto out; + return err; if (!ovlrd->overwrite && !ovl_can_move(new)) - goto out; + return err; if (ovlrd->overwrite && new_is_dir && !ovl_pure_upper(new)) { - err = ovl_check_empty_dir(new, &list); + err = ovl_check_empty_dir(new, list); if (err) - goto out; + return err; } if (ovlrd->overwrite) { @@ -1164,19 +1145,20 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, err = ovl_copy_up(old); if (err) - goto out; + return err; err = ovl_copy_up(new->d_parent); if (err) - goto out; + return err; + if (!ovlrd->overwrite) { err = ovl_copy_up(new); if (err) - goto out; + return err; } else if (d_inode(new)) { err = ovl_nlink_start(new); if (err) - goto out; + return err; ovlrd->update_nlink = true; } @@ -1185,23 +1167,35 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, /* ovl_nlink_start() took ovl_want_write() */ err = ovl_want_write(old); if (err) - goto out; + return err; } - old_cred = ovl_override_creds(old->d_sb); + return 0; +} - if (!list_empty(&list)) { - ovlrd->opaquedir = ovl_clear_empty(new, &list); - err = PTR_ERR(ovlrd->opaquedir); - if (IS_ERR(ovlrd->opaquedir)) { - ovlrd->opaquedir = NULL; - goto out_revert_creds; - } +static int ovl_rename_upper(struct ovl_renamedata *ovlrd, struct list_head *list) +{ + struct dentry *old = ovlrd->old_dentry; + struct dentry *new = ovlrd->new_dentry; + struct ovl_fs *ofs = OVL_FS(old->d_sb); + struct dentry *old_upperdir = ovl_dentry_upper(old->d_parent); + struct dentry *new_upperdir = ovl_dentry_upper(new->d_parent); + bool is_dir = d_is_dir(old); + bool new_is_dir = d_is_dir(new); + bool samedir = old->d_parent == new->d_parent; + struct renamedata rd = {}; + struct dentry *de; + struct dentry *whiteout = NULL; + bool old_opaque, new_opaque; + int err; + + if (!list_empty(list)) { + de = ovl_clear_empty(new, list); + if (IS_ERR(de)) + return PTR_ERR(de); + ovlrd->opaquedir = de; } - old_upperdir = ovl_dentry_upper(old->d_parent); - new_upperdir = ovl_dentry_upper(new->d_parent); - if (!samedir) { /* * When moving a merge dir or non-dir with copy up origin into @@ -1212,12 +1206,12 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, if (ovl_type_origin(old)) { err = ovl_set_impure(new->d_parent, new_upperdir); if (err) - goto out_revert_creds; + return err; } if (!ovlrd->overwrite && ovl_type_origin(new)) { err = ovl_set_impure(old->d_parent, old_upperdir); if (err) - goto out_revert_creds; + return err; } } @@ -1229,9 +1223,8 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, err = start_renaming(&rd, 0, &QSTR_LEN(old->d_name.name, old->d_name.len), &QSTR_LEN(new->d_name.name, new->d_name.len)); - if (err) - goto out_revert_creds; + return err; err = -ESTALE; if (!ovl_matches_upper(old, rd.old_dentry)) @@ -1283,10 +1276,11 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, if (!err && ovlrd->cleanup_whiteout) whiteout = dget(rd.new_dentry); +out_unlock: end_renaming(&rd); if (err) - goto out_revert_creds; + return err; if (whiteout) { ovl_cleanup(ofs, old_upperdir, whiteout); @@ -1310,20 +1304,47 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, if (d_inode(new) && ovl_dentry_upper(new)) ovl_copyattr(d_inode(new)); -out_revert_creds: - ovl_revert_creds(old_cred); + return err; +} + +static void ovl_rename_end(struct ovl_renamedata *ovlrd) +{ if (ovlrd->update_nlink) - ovl_nlink_end(new); + ovl_nlink_end(ovlrd->new_dentry); else - ovl_drop_write(old); + ovl_drop_write(ovlrd->old_dentry); +} + +static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, + struct dentry *old, struct inode *newdir, + struct dentry *new, unsigned int flags) +{ + const struct cred *old_cred = NULL; + struct ovl_renamedata ovlrd = { + .old_parent = old->d_parent, + .old_dentry = old, + .new_parent = new->d_parent, + .new_dentry = new, + .flags = flags, + .overwrite = !(flags & RENAME_EXCHANGE), + }; + LIST_HEAD(list); + int err; + + err = ovl_rename_start(&ovlrd, &list); + if (err) + goto out; + + old_cred = ovl_override_creds(old->d_sb); + + err = ovl_rename_upper(&ovlrd, &list); + + ovl_revert_creds(old_cred); + ovl_rename_end(&ovlrd); out: dput(ovlrd->opaquedir); ovl_cache_free(&list); return err; - -out_unlock: - end_renaming(&rd); - goto out_revert_creds; } static int ovl_create_tmpfile(struct file *file, struct dentry *dentry, From ca0c657f258089cbb7b6d269cf91936ab83dac3f Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 19 Nov 2025 20:53:17 +0100 Subject: [PATCH 35/54] ovl: port ovl_rename() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-35-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/dir.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index ab1fdd7ccb2e..8e8ede6a1217 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -1319,7 +1319,6 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, struct dentry *old, struct inode *newdir, struct dentry *new, unsigned int flags) { - const struct cred *old_cred = NULL; struct ovl_renamedata ovlrd = { .old_parent = old->d_parent, .old_dentry = old, @@ -1332,17 +1331,13 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, int err; err = ovl_rename_start(&ovlrd, &list); - if (err) - goto out; + if (!err) { + with_ovl_creds(old->d_sb) + err = ovl_rename_upper(&ovlrd, &list); + ovl_rename_end(&ovlrd); + } - old_cred = ovl_override_creds(old->d_sb); - - err = ovl_rename_upper(&ovlrd, &list); - - ovl_revert_creds(old_cred); - ovl_rename_end(&ovlrd); -out: - dput(ovlrd->opaquedir); + dput(ovlrd.opaquedir); ovl_cache_free(&list); return err; } From 14d35fda5b1139bacefb47c69f083fe2cfad211b Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:07 +0100 Subject: [PATCH 36/54] ovl: port ovl_copyfile() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-36-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/file.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 0ab846d8062e..cbae89457234 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -529,7 +529,6 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in, { struct inode *inode_out = file_inode(file_out); struct file *realfile_in, *realfile_out; - const struct cred *old_cred; loff_t ret; inode_lock(inode_out); @@ -551,25 +550,25 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in, if (IS_ERR(realfile_in)) goto out_unlock; - old_cred = ovl_override_creds(file_inode(file_out)->i_sb); - switch (op) { - case OVL_COPY: - ret = vfs_copy_file_range(realfile_in, pos_in, - realfile_out, pos_out, len, flags); - break; + with_ovl_creds(file_inode(file_out)->i_sb) { + switch (op) { + case OVL_COPY: + ret = vfs_copy_file_range(realfile_in, pos_in, + realfile_out, pos_out, len, flags); + break; - case OVL_CLONE: - ret = vfs_clone_file_range(realfile_in, pos_in, - realfile_out, pos_out, len, flags); - break; + case OVL_CLONE: + ret = vfs_clone_file_range(realfile_in, pos_in, + realfile_out, pos_out, len, flags); + break; - case OVL_DEDUPE: - ret = vfs_dedupe_file_range_one(realfile_in, pos_in, - realfile_out, pos_out, len, - flags); - break; + case OVL_DEDUPE: + ret = vfs_dedupe_file_range_one(realfile_in, pos_in, + realfile_out, pos_out, len, + flags); + break; + } } - ovl_revert_creds(old_cred); /* Update size */ ovl_file_modified(file_out); From 15da486ad3bde6119d9b6353bebc2ffb6e9165c1 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:08 +0100 Subject: [PATCH 37/54] ovl: refactor ovl_lookup() Split the core into a separate helper in preparation of converting the caller to the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-37-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/namei.c | 316 ++++++++++++++++++++++--------------------- 1 file changed, 162 insertions(+), 154 deletions(-) diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 49874525cf52..5bed42c079f0 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -1070,57 +1070,44 @@ static bool ovl_check_follow_redirect(struct ovl_lookup_data *d) return true; } -struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, - unsigned int flags) +struct ovl_lookup_ctx { + struct dentry *dentry; + struct ovl_entry *oe; + struct ovl_path *stack; + struct ovl_path *origin_path; + struct dentry *upperdentry; + struct dentry *index; + struct inode *inode; + unsigned int ctr; +}; + +static int ovl_lookup_layers(struct ovl_lookup_ctx *ctx, struct ovl_lookup_data *d) { - struct ovl_entry *oe = NULL; - const struct cred *old_cred; + struct dentry *dentry = ctx->dentry; struct ovl_fs *ofs = OVL_FS(dentry->d_sb); struct ovl_entry *poe = OVL_E(dentry->d_parent); struct ovl_entry *roe = OVL_E(dentry->d_sb->s_root); - struct ovl_path *stack = NULL, *origin_path = NULL; - struct dentry *upperdir, *upperdentry = NULL; - struct dentry *origin = NULL; - struct dentry *index = NULL; - unsigned int ctr = 0; - struct inode *inode = NULL; - bool upperopaque = false; bool check_redirect = (ovl_redirect_follow(ofs) || ofs->numdatalayer); + struct dentry *upperdir; struct dentry *this; - unsigned int i; - int err; + struct dentry *origin = NULL; + bool upperopaque = false; bool uppermetacopy = false; int metacopy_size = 0; - struct ovl_lookup_data d = { - .sb = dentry->d_sb, - .dentry = dentry, - .name = dentry->d_name, - .is_dir = false, - .opaque = false, - .stop = false, - .last = check_redirect ? false : !ovl_numlower(poe), - .redirect = NULL, - .upperredirect = NULL, - .metacopy = 0, - }; + unsigned int i; + int err; - if (dentry->d_name.len > ofs->namelen) - return ERR_PTR(-ENAMETOOLONG); - - old_cred = ovl_override_creds(dentry->d_sb); upperdir = ovl_dentry_upper(dentry->d_parent); if (upperdir) { - d.layer = &ofs->layers[0]; - err = ovl_lookup_layer(upperdir, &d, &upperdentry, true); + d->layer = &ofs->layers[0]; + err = ovl_lookup_layer(upperdir, d, &ctx->upperdentry, true); if (err) - goto out; + return err; - if (upperdentry && upperdentry->d_flags & DCACHE_OP_REAL) { - dput(upperdentry); - err = -EREMOTE; - goto out; - } - if (upperdentry && !d.is_dir) { + if (ctx->upperdentry && ctx->upperdentry->d_flags & DCACHE_OP_REAL) + return -EREMOTE; + + if (ctx->upperdentry && !d->is_dir) { /* * Lookup copy up origin by decoding origin file handle. * We may get a disconnected dentry, which is fine, @@ -1131,50 +1118,50 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, * number - it's the same as if we held a reference * to a dentry in lower layer that was moved under us. */ - err = ovl_check_origin(ofs, upperdentry, &origin_path); + err = ovl_check_origin(ofs, ctx->upperdentry, &ctx->origin_path); if (err) - goto out_put_upper; + return err; - if (d.metacopy) + if (d->metacopy) uppermetacopy = true; - metacopy_size = d.metacopy; + metacopy_size = d->metacopy; } - if (d.redirect) { + if (d->redirect) { err = -ENOMEM; - d.upperredirect = kstrdup(d.redirect, GFP_KERNEL); - if (!d.upperredirect) - goto out_put_upper; - if (d.redirect[0] == '/') + d->upperredirect = kstrdup(d->redirect, GFP_KERNEL); + if (!d->upperredirect) + return err; + if (d->redirect[0] == '/') poe = roe; } - upperopaque = d.opaque; + upperopaque = d->opaque; } - if (!d.stop && ovl_numlower(poe)) { + if (!d->stop && ovl_numlower(poe)) { err = -ENOMEM; - stack = ovl_stack_alloc(ofs->numlayer - 1); - if (!stack) - goto out_put_upper; + ctx->stack = ovl_stack_alloc(ofs->numlayer - 1); + if (!ctx->stack) + return err; } - for (i = 0; !d.stop && i < ovl_numlower(poe); i++) { + for (i = 0; !d->stop && i < ovl_numlower(poe); i++) { struct ovl_path lower = ovl_lowerstack(poe)[i]; - if (!ovl_check_follow_redirect(&d)) { + if (!ovl_check_follow_redirect(d)) { err = -EPERM; - goto out_put; + return err; } if (!check_redirect) - d.last = i == ovl_numlower(poe) - 1; - else if (d.is_dir || !ofs->numdatalayer) - d.last = lower.layer->idx == ovl_numlower(roe); + d->last = i == ovl_numlower(poe) - 1; + else if (d->is_dir || !ofs->numdatalayer) + d->last = lower.layer->idx == ovl_numlower(roe); - d.layer = lower.layer; - err = ovl_lookup_layer(lower.dentry, &d, &this, false); + d->layer = lower.layer; + err = ovl_lookup_layer(lower.dentry, d, &this, false); if (err) - goto out_put; + return err; if (!this) continue; @@ -1183,11 +1170,11 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, * If no origin fh is stored in upper of a merge dir, store fh * of lower dir and set upper parent "impure". */ - if (upperdentry && !ctr && !ofs->noxattr && d.is_dir) { - err = ovl_fix_origin(ofs, dentry, this, upperdentry); + if (ctx->upperdentry && !ctx->ctr && !ofs->noxattr && d->is_dir) { + err = ovl_fix_origin(ofs, dentry, this, ctx->upperdentry); if (err) { dput(this); - goto out_put; + return err; } } @@ -1200,23 +1187,23 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, * matches the dentry found using path based lookup, * otherwise error out. */ - if (upperdentry && !ctr && - ((d.is_dir && ovl_verify_lower(dentry->d_sb)) || - (!d.is_dir && ofs->config.index && origin_path))) { - err = ovl_verify_origin(ofs, upperdentry, this, false); + if (ctx->upperdentry && !ctx->ctr && + ((d->is_dir && ovl_verify_lower(dentry->d_sb)) || + (!d->is_dir && ofs->config.index && ctx->origin_path))) { + err = ovl_verify_origin(ofs, ctx->upperdentry, this, false); if (err) { dput(this); - if (d.is_dir) + if (d->is_dir) break; - goto out_put; + return err; } origin = this; } - if (!upperdentry && !d.is_dir && !ctr && d.metacopy) - metacopy_size = d.metacopy; + if (!ctx->upperdentry && !d->is_dir && !ctx->ctr && d->metacopy) + metacopy_size = d->metacopy; - if (d.metacopy && ctr) { + if (d->metacopy && ctx->ctr) { /* * Do not store intermediate metacopy dentries in * lower chain, except top most lower metacopy dentry. @@ -1226,15 +1213,15 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, dput(this); this = NULL; } else { - stack[ctr].dentry = this; - stack[ctr].layer = lower.layer; - ctr++; + ctx->stack[ctx->ctr].dentry = this; + ctx->stack[ctx->ctr].layer = lower.layer; + ctx->ctr++; } - if (d.stop) + if (d->stop) break; - if (d.redirect && d.redirect[0] == '/' && poe != roe) { + if (d->redirect && d->redirect[0] == '/' && poe != roe) { poe = roe; /* Find the current layer on the root dentry */ i = lower.layer->idx - 1; @@ -1245,12 +1232,12 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, * Defer lookup of lowerdata in data-only layers to first access. * Don't require redirect=follow and metacopy=on in this case. */ - if (d.metacopy && ctr && ofs->numdatalayer && d.absolute_redirect) { - d.metacopy = 0; - ctr++; - } else if (!ovl_check_follow_redirect(&d)) { + if (d->metacopy && ctx->ctr && ofs->numdatalayer && d->absolute_redirect) { + d->metacopy = 0; + ctx->ctr++; + } else if (!ovl_check_follow_redirect(d)) { err = -EPERM; - goto out_put; + return err; } /* @@ -1261,20 +1248,20 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, * For metacopy dentry, path based lookup will find lower dentries. * Just make sure a corresponding data dentry has been found. */ - if (d.metacopy || (uppermetacopy && !ctr)) { + if (d->metacopy || (uppermetacopy && !ctx->ctr)) { pr_warn_ratelimited("metacopy with no lower data found - abort lookup (%pd2)\n", dentry); err = -EIO; - goto out_put; - } else if (!d.is_dir && upperdentry && !ctr && origin_path) { - if (WARN_ON(stack != NULL)) { + return err; + } else if (!d->is_dir && ctx->upperdentry && !ctx->ctr && ctx->origin_path) { + if (WARN_ON(ctx->stack != NULL)) { err = -EIO; - goto out_put; + return err; } - stack = origin_path; - ctr = 1; - origin = origin_path->dentry; - origin_path = NULL; + ctx->stack = ctx->origin_path; + ctx->ctr = 1; + origin = ctx->origin_path->dentry; + ctx->origin_path = NULL; } /* @@ -1296,38 +1283,39 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, * is enabled and if upper had an ORIGIN xattr. * */ - if (!upperdentry && ctr) - origin = stack[0].dentry; + if (!ctx->upperdentry && ctx->ctr) + origin = ctx->stack[0].dentry; if (origin && ovl_indexdir(dentry->d_sb) && - (!d.is_dir || ovl_index_all(dentry->d_sb))) { - index = ovl_lookup_index(ofs, upperdentry, origin, true); - if (IS_ERR(index)) { - err = PTR_ERR(index); - index = NULL; - goto out_put; + (!d->is_dir || ovl_index_all(dentry->d_sb))) { + ctx->index = ovl_lookup_index(ofs, ctx->upperdentry, origin, true); + if (IS_ERR(ctx->index)) { + err = PTR_ERR(ctx->index); + ctx->index = NULL; + return err; } } - if (ctr) { - oe = ovl_alloc_entry(ctr); + if (ctx->ctr) { + ctx->oe = ovl_alloc_entry(ctx->ctr); err = -ENOMEM; - if (!oe) - goto out_put; + if (!ctx->oe) + return err; - ovl_stack_cpy(ovl_lowerstack(oe), stack, ctr); + ovl_stack_cpy(ovl_lowerstack(ctx->oe), ctx->stack, ctx->ctr); } if (upperopaque) ovl_dentry_set_opaque(dentry); - if (d.xwhiteouts) + if (d->xwhiteouts) ovl_dentry_set_xwhiteouts(dentry); - if (upperdentry) + if (ctx->upperdentry) ovl_dentry_set_upper_alias(dentry); - else if (index) { + else if (ctx->index) { + char *upperredirect; struct path upperpath = { - .dentry = upperdentry = dget(index), + .dentry = ctx->upperdentry = dget(ctx->index), .mnt = ovl_upper_mnt(ofs), }; @@ -1336,77 +1324,97 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, * assignment happens only if upperdentry is non-NULL, and * this one only if upperdentry is NULL. */ - d.upperredirect = ovl_get_redirect_xattr(ofs, &upperpath, 0); - if (IS_ERR(d.upperredirect)) { - err = PTR_ERR(d.upperredirect); - d.upperredirect = NULL; - goto out_free_oe; - } + upperredirect = ovl_get_redirect_xattr(ofs, &upperpath, 0); + if (IS_ERR(upperredirect)) + return PTR_ERR(upperredirect); + d->upperredirect = upperredirect; err = ovl_check_metacopy_xattr(ofs, &upperpath, NULL); if (err < 0) - goto out_free_oe; - d.metacopy = uppermetacopy = err; + return err; + d->metacopy = uppermetacopy = err; metacopy_size = err; - if (!ovl_check_follow_redirect(&d)) { + if (!ovl_check_follow_redirect(d)) { err = -EPERM; - goto out_free_oe; + return err; } } - if (upperdentry || ctr) { + if (ctx->upperdentry || ctx->ctr) { + struct inode *inode; struct ovl_inode_params oip = { - .upperdentry = upperdentry, - .oe = oe, - .index = index, - .redirect = d.upperredirect, + .upperdentry = ctx->upperdentry, + .oe = ctx->oe, + .index = ctx->index, + .redirect = d->upperredirect, }; /* Store lowerdata redirect for lazy lookup */ - if (ctr > 1 && !d.is_dir && !stack[ctr - 1].dentry) { - oip.lowerdata_redirect = d.redirect; - d.redirect = NULL; + if (ctx->ctr > 1 && !d->is_dir && !ctx->stack[ctx->ctr - 1].dentry) { + oip.lowerdata_redirect = d->redirect; + d->redirect = NULL; } + inode = ovl_get_inode(dentry->d_sb, &oip); - err = PTR_ERR(inode); if (IS_ERR(inode)) - goto out_free_oe; - if (upperdentry && !uppermetacopy) - ovl_set_flag(OVL_UPPERDATA, inode); + return PTR_ERR(inode); + + ctx->inode = inode; + if (ctx->upperdentry && !uppermetacopy) + ovl_set_flag(OVL_UPPERDATA, ctx->inode); if (metacopy_size > OVL_METACOPY_MIN_SIZE) - ovl_set_flag(OVL_HAS_DIGEST, inode); + ovl_set_flag(OVL_HAS_DIGEST, ctx->inode); } - ovl_dentry_init_reval(dentry, upperdentry, OVL_I_E(inode)); + ovl_dentry_init_reval(dentry, ctx->upperdentry, OVL_I_E(ctx->inode)); + + return 0; +} + +struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + const struct cred *old_cred; + struct ovl_fs *ofs = OVL_FS(dentry->d_sb); + struct ovl_entry *poe = OVL_E(dentry->d_parent); + bool check_redirect = (ovl_redirect_follow(ofs) || ofs->numdatalayer); + int err; + struct ovl_lookup_ctx ctx = { + .dentry = dentry, + }; + struct ovl_lookup_data d = { + .sb = dentry->d_sb, + .dentry = dentry, + .name = dentry->d_name, + .last = check_redirect ? false : !ovl_numlower(poe), + }; + + if (dentry->d_name.len > ofs->namelen) + return ERR_PTR(-ENAMETOOLONG); + + old_cred = ovl_override_creds(dentry->d_sb); + + err = ovl_lookup_layers(&ctx, &d); ovl_revert_creds(old_cred); - if (origin_path) { - dput(origin_path->dentry); - kfree(origin_path); + if (ctx.origin_path) { + dput(ctx.origin_path->dentry); + kfree(ctx.origin_path); } - dput(index); - ovl_stack_free(stack, ctr); + dput(ctx.index); + ovl_stack_free(ctx.stack, ctx.ctr); kfree(d.redirect); - return d_splice_alias(inode, dentry); -out_free_oe: - ovl_free_entry(oe); -out_put: - dput(index); - ovl_stack_free(stack, ctr); -out_put_upper: - if (origin_path) { - dput(origin_path->dentry); - kfree(origin_path); + if (err) { + ovl_free_entry(ctx.oe); + dput(ctx.upperdentry); + kfree(d.upperredirect); + return ERR_PTR(err); } - dput(upperdentry); - kfree(d.upperredirect); -out: - kfree(d.redirect); - ovl_revert_creds(old_cred); - return ERR_PTR(err); + + return d_splice_alias(ctx.inode, dentry); } bool ovl_lower_positive(struct dentry *dentry) From 6b6ef7d16fbb63179b33f98b19818d0d2710be64 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:09 +0100 Subject: [PATCH 38/54] ovl: port ovl_lookup() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-38-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/namei.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 5bed42c079f0..61c2dbd49b6f 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -1376,7 +1376,6 @@ static int ovl_lookup_layers(struct ovl_lookup_ctx *ctx, struct ovl_lookup_data struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { - const struct cred *old_cred; struct ovl_fs *ofs = OVL_FS(dentry->d_sb); struct ovl_entry *poe = OVL_E(dentry->d_parent); bool check_redirect = (ovl_redirect_follow(ofs) || ofs->numdatalayer); @@ -1394,11 +1393,9 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, if (dentry->d_name.len > ofs->namelen) return ERR_PTR(-ENAMETOOLONG); - old_cred = ovl_override_creds(dentry->d_sb); + with_ovl_creds(dentry->d_sb) + err = ovl_lookup_layers(&ctx, &d); - err = ovl_lookup_layers(&ctx, &d); - - ovl_revert_creds(old_cred); if (ctx.origin_path) { dput(ctx.origin_path->dentry); kfree(ctx.origin_path); From db7cfe87832d34bd9720d961f0e9176721c456a8 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:10 +0100 Subject: [PATCH 39/54] ovl: port ovl_lower_positive() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-39-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/namei.c | 70 +++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 61c2dbd49b6f..e9a69c95be91 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -1418,7 +1418,6 @@ bool ovl_lower_positive(struct dentry *dentry) { struct ovl_entry *poe = OVL_E(dentry->d_parent); const struct qstr *name = &dentry->d_name; - const struct cred *old_cred; unsigned int i; bool positive = false; bool done = false; @@ -1434,46 +1433,45 @@ bool ovl_lower_positive(struct dentry *dentry) if (!ovl_dentry_upper(dentry)) return true; - old_cred = ovl_override_creds(dentry->d_sb); - /* Positive upper -> have to look up lower to see whether it exists */ - for (i = 0; !done && !positive && i < ovl_numlower(poe); i++) { - struct dentry *this; - struct ovl_path *parentpath = &ovl_lowerstack(poe)[i]; + with_ovl_creds(dentry->d_sb) { + /* Positive upper -> have to look up lower to see whether it exists */ + for (i = 0; !done && !positive && i < ovl_numlower(poe); i++) { + struct dentry *this; + struct ovl_path *parentpath = &ovl_lowerstack(poe)[i]; - /* - * We need to make a non-const copy of dentry->d_name, - * because lookup_one_positive_unlocked() will hash name - * with parentpath base, which is on another (lower fs). - */ - this = lookup_one_positive_unlocked( - mnt_idmap(parentpath->layer->mnt), - &QSTR_LEN(name->name, name->len), - parentpath->dentry); - if (IS_ERR(this)) { - switch (PTR_ERR(this)) { - case -ENOENT: - case -ENAMETOOLONG: - break; + /* + * We need to make a non-const copy of dentry->d_name, + * because lookup_one_positive_unlocked() will hash name + * with parentpath base, which is on another (lower fs). + */ + this = lookup_one_positive_unlocked(mnt_idmap(parentpath->layer->mnt), + &QSTR_LEN(name->name, name->len), + parentpath->dentry); + if (IS_ERR(this)) { + switch (PTR_ERR(this)) { + case -ENOENT: + case -ENAMETOOLONG: + break; - default: - /* - * Assume something is there, we just couldn't - * access it. - */ - positive = true; - break; + default: + /* + * Assume something is there, we just couldn't + * access it. + */ + positive = true; + break; + } + } else { + struct path path = { + .dentry = this, + .mnt = parentpath->layer->mnt, + }; + positive = !ovl_path_is_whiteout(OVL_FS(dentry->d_sb), &path); + done = true; + dput(this); } - } else { - struct path path = { - .dentry = this, - .mnt = parentpath->layer->mnt, - }; - positive = !ovl_path_is_whiteout(OVL_FS(dentry->d_sb), &path); - done = true; - dput(this); } } - ovl_revert_creds(old_cred); return positive; } From fc95cda6739381123ee8c53ab0b880b9853cb8f0 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:11 +0100 Subject: [PATCH 40/54] ovl: refactor ovl_fill_super() Split the core into a separate helper in preparation of converting the caller to the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-40-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/super.c | 95 +++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 7b8fc1cab6eb..a3e54db441bd 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1364,53 +1364,35 @@ static void ovl_set_d_op(struct super_block *sb) set_default_d_op(sb, &ovl_dentry_operations); } -int ovl_fill_super(struct super_block *sb, struct fs_context *fc) +static int ovl_fill_super_creds(struct fs_context *fc, struct super_block *sb) { struct ovl_fs *ofs = sb->s_fs_info; + struct cred *creator_cred = (struct cred *)ofs->creator_cred; struct ovl_fs_context *ctx = fc->fs_private; - const struct cred *old_cred = NULL; - struct dentry *root_dentry; - struct ovl_entry *oe; struct ovl_layer *layers; - struct cred *cred; + struct ovl_entry *oe = NULL; int err; - err = -EIO; - if (WARN_ON(fc->user_ns != current_user_ns())) - goto out_err; - - ovl_set_d_op(sb); - - err = -ENOMEM; - if (!ofs->creator_cred) - ofs->creator_cred = cred = prepare_creds(); - else - cred = (struct cred *)ofs->creator_cred; - if (!cred) - goto out_err; - - old_cred = ovl_override_creds(sb); - err = ovl_fs_params_verify(ctx, &ofs->config); if (err) - goto out_err; + return err; err = -EINVAL; if (ctx->nr == 0) { if (!(fc->sb_flags & SB_SILENT)) pr_err("missing 'lowerdir'\n"); - goto out_err; + return err; } err = -ENOMEM; layers = kcalloc(ctx->nr + 1, sizeof(struct ovl_layer), GFP_KERNEL); if (!layers) - goto out_err; + return err; ofs->config.lowerdirs = kcalloc(ctx->nr + 1, sizeof(char *), GFP_KERNEL); if (!ofs->config.lowerdirs) { kfree(layers); - goto out_err; + return err; } ofs->layers = layers; /* @@ -1443,12 +1425,12 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) err = -EINVAL; if (!ofs->config.workdir) { pr_err("missing 'workdir'\n"); - goto out_err; + return err; } err = ovl_get_upper(sb, ofs, &layers[0], &ctx->upper); if (err) - goto out_err; + return err; upper_sb = ovl_upper_mnt(ofs)->mnt_sb; if (!ovl_should_sync(ofs)) { @@ -1456,13 +1438,13 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) if (errseq_check(&upper_sb->s_wb_err, ofs->errseq)) { err = -EIO; pr_err("Cannot mount volatile when upperdir has an unseen error. Sync upperdir fs to clear state.\n"); - goto out_err; + return err; } } err = ovl_get_workdir(sb, ofs, &ctx->upper, &ctx->work); if (err) - goto out_err; + return err; if (!ofs->workdir) sb->s_flags |= SB_RDONLY; @@ -1473,7 +1455,7 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) oe = ovl_get_lowerstack(sb, ctx, ofs, layers); err = PTR_ERR(oe); if (IS_ERR(oe)) - goto out_err; + return err; /* If the upper fs is nonexistent, we mark overlayfs r/o too */ if (!ovl_upper_mnt(ofs)) @@ -1526,7 +1508,7 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_export_op = &ovl_export_fid_operations; /* Never override disk quota limits or use reserved space */ - cap_lower(cred->cap_effective, CAP_SYS_RESOURCE); + cap_lower(creator_cred->cap_effective, CAP_SYS_RESOURCE); sb->s_magic = OVERLAYFS_SUPER_MAGIC; sb->s_xattr = ovl_xattr_handlers(ofs); @@ -1544,27 +1526,50 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) sb->s_iflags |= SB_I_EVM_HMAC_UNSUPPORTED; err = -ENOMEM; - root_dentry = ovl_get_root(sb, ctx->upper.dentry, oe); - if (!root_dentry) + sb->s_root = ovl_get_root(sb, ctx->upper.dentry, oe); + if (!sb->s_root) goto out_free_oe; - sb->s_root = root_dentry; - - ovl_revert_creds(old_cred); return 0; out_free_oe: ovl_free_entry(oe); + return err; +} + +int ovl_fill_super(struct super_block *sb, struct fs_context *fc) +{ + struct ovl_fs *ofs = sb->s_fs_info; + const struct cred *old_cred = NULL; + struct cred *cred; + int err; + + err = -EIO; + if (WARN_ON(fc->user_ns != current_user_ns())) + goto out_err; + + ovl_set_d_op(sb); + + err = -ENOMEM; + if (!ofs->creator_cred) + ofs->creator_cred = cred = prepare_creds(); + else + cred = (struct cred *)ofs->creator_cred; + if (!cred) + goto out_err; + + old_cred = ovl_override_creds(sb); + + err = ovl_fill_super_creds(fc, sb); + + ovl_revert_creds(old_cred); + out_err: - /* - * Revert creds before calling ovl_free_fs() which will call - * put_cred() and put_cred() requires that the cred's that are - * put are not the caller's creds, i.e., current->cred. - */ - if (old_cred) - ovl_revert_creds(old_cred); - ovl_free_fs(ofs); - sb->s_fs_info = NULL; + if (err) { + ovl_free_fs(ofs); + sb->s_fs_info = NULL; + } + return err; } From 217e78d1b7eccf53d9e4fb3dedf7168b880bbcae Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:12 +0100 Subject: [PATCH 41/54] ovl: port ovl_fill_super() to cred guard Use the scoped ovl cred guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-41-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/super.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index a3e54db441bd..28b2f707cfbc 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1540,8 +1540,6 @@ static int ovl_fill_super_creds(struct fs_context *fc, struct super_block *sb) int ovl_fill_super(struct super_block *sb, struct fs_context *fc) { struct ovl_fs *ofs = sb->s_fs_info; - const struct cred *old_cred = NULL; - struct cred *cred; int err; err = -EIO; @@ -1550,19 +1548,15 @@ int ovl_fill_super(struct super_block *sb, struct fs_context *fc) ovl_set_d_op(sb); - err = -ENOMEM; - if (!ofs->creator_cred) - ofs->creator_cred = cred = prepare_creds(); - else - cred = (struct cred *)ofs->creator_cred; - if (!cred) - goto out_err; + if (!ofs->creator_cred) { + err = -ENOMEM; + ofs->creator_cred = prepare_creds(); + if (!ofs->creator_cred) + goto out_err; + } - old_cred = ovl_override_creds(sb); - - err = ovl_fill_super_creds(fc, sb); - - ovl_revert_creds(old_cred); + with_ovl_creds(sb) + err = ovl_fill_super_creds(fc, sb); out_err: if (err) { From 850e32512a8c92a5da3fb216ccd2ea45052fdb33 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:13 +0100 Subject: [PATCH 42/54] ovl: remove ovl_revert_creds() The wrapper isn't needed anymore. Overlayfs completely relies on its cleanup guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-v4-42-b31603935724@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/overlayfs.h | 1 - fs/overlayfs/util.c | 5 ----- 2 files changed, 6 deletions(-) diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 362a3485dc53..35829cfbe6b2 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -455,7 +455,6 @@ int ovl_want_write(struct dentry *dentry); void ovl_drop_write(struct dentry *dentry); struct dentry *ovl_workdir(struct dentry *dentry); const struct cred *ovl_override_creds(struct super_block *sb); -void ovl_revert_creds(const struct cred *old_cred); EXTEND_CLASS(override_creds, _ovl, ovl_override_creds(sb), struct super_block *sb) diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 0d9487cac0e8..a499af6faab4 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -69,11 +69,6 @@ const struct cred *ovl_override_creds(struct super_block *sb) return override_creds(ofs->creator_cred); } -void ovl_revert_creds(const struct cred *old_cred) -{ - revert_creds(old_cred); -} - /* * Check if underlying fs supports file handles and try to determine encoding * type, in order to deduce maximum inode number used by fs. From f37b334728814b14745e15d46f9eab73750b67ec Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:38 +0100 Subject: [PATCH 43/54] ovl: add ovl_override_creator_creds cred guard The current code to override credentials for creation operations is pretty difficult to understand. We effectively override the credentials twice: (1) override with the mounter's credentials (2) copy the mounts credentials and override the fs{g,u}id with the inode {u,g}id And then we elide the revert because it would be an idempotent revert. That elision doesn't buy us anything anymore though because I've made it all work without any reference counting anyway. All it does is mix the two credential overrides together. We can use a cleanup guard to clarify the creation codepaths and make them easier to understand. This just introduces the cleanup guard keeping the patch reviewable. We'll convert the caller in follow-up patches and then drop the duplicated code. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-prepare-v2-1-bd1c97a36d7b@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/dir.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 8e8ede6a1217..c61976180a83 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -581,6 +581,42 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, goto out_dput; } +static const struct cred *ovl_override_creator_creds(struct dentry *dentry, struct inode *inode, umode_t mode) +{ + int err; + + if (WARN_ON_ONCE(current->cred != ovl_creds(dentry->d_sb))) + return ERR_PTR(-EINVAL); + + CLASS(prepare_creds, override_cred)(); + if (!override_cred) + return ERR_PTR(-ENOMEM); + + override_cred->fsuid = inode->i_uid; + override_cred->fsgid = inode->i_gid; + + err = security_dentry_create_files_as(dentry, mode, &dentry->d_name, + current->cred, override_cred); + if (err) + return ERR_PTR(err); + + return override_creds(no_free_ptr(override_cred)); +} + +static void ovl_revert_creator_creds(const struct cred *old_cred) +{ + const struct cred *override_cred; + + override_cred = revert_creds(old_cred); + put_cred(override_cred); +} + +DEFINE_CLASS(ovl_override_creator_creds, + const struct cred *, + if (!IS_ERR_OR_NULL(_T)) ovl_revert_creator_creds(_T), + ovl_override_creator_creds(dentry, inode, mode), + struct dentry *dentry, struct inode *inode, umode_t mode) + static const struct cred *ovl_setup_cred_for_create(struct dentry *dentry, struct inode *inode, umode_t mode, From 8d7fc461e45abc7f67c455d908a2e709dec9e3b9 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:39 +0100 Subject: [PATCH 44/54] ovl: port ovl_create_tmpfile() to new ovl_override_creator_creds cleanup guard This clearly indicates the double-credential override and makes the code a lot easier to grasp with one glance. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-prepare-v2-2-bd1c97a36d7b@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/dir.c | 50 +++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index c61976180a83..68f6617b6a77 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -1381,7 +1381,6 @@ static int ovl_rename(struct mnt_idmap *idmap, struct inode *olddir, static int ovl_create_tmpfile(struct file *file, struct dentry *dentry, struct inode *inode, umode_t mode) { - const struct cred *new_cred __free(put_cred) = NULL; struct path realparentpath; struct file *realfile; struct ovl_file *of; @@ -1390,33 +1389,34 @@ static int ovl_create_tmpfile(struct file *file, struct dentry *dentry, int flags = file->f_flags | OVL_OPEN_FLAGS; int err; - scoped_class(override_creds_ovl, old_cred, dentry->d_sb) { - new_cred = ovl_setup_cred_for_create(dentry, inode, mode, old_cred); - if (IS_ERR(new_cred)) - return PTR_ERR(new_cred); + with_ovl_creds(dentry->d_sb) { + scoped_class(ovl_override_creator_creds, cred, dentry, inode, mode) { + if (IS_ERR(cred)) + return PTR_ERR(cred); - ovl_path_upper(dentry->d_parent, &realparentpath); - realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath, - mode, current_cred()); - err = PTR_ERR_OR_ZERO(realfile); - pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err); - if (err) - return err; + ovl_path_upper(dentry->d_parent, &realparentpath); + realfile = backing_tmpfile_open(&file->f_path, flags, &realparentpath, + mode, current_cred()); + err = PTR_ERR_OR_ZERO(realfile); + pr_debug("tmpfile/open(%pd2, 0%o) = %i\n", realparentpath.dentry, mode, err); + if (err) + return err; - of = ovl_file_alloc(realfile); - if (!of) { - fput(realfile); - return -ENOMEM; - } + of = ovl_file_alloc(realfile); + if (!of) { + fput(realfile); + return -ENOMEM; + } - /* ovl_instantiate() consumes the newdentry reference on success */ - newdentry = dget(realfile->f_path.dentry); - err = ovl_instantiate(dentry, inode, newdentry, false, file); - if (!err) { - file->private_data = of; - } else { - dput(newdentry); - ovl_file_free(of); + /* ovl_instantiate() consumes the newdentry reference on success */ + newdentry = dget(realfile->f_path.dentry); + err = ovl_instantiate(dentry, inode, newdentry, false, file); + if (!err) { + file->private_data = of; + } else { + dput(newdentry); + ovl_file_free(of); + } } } return err; From d6ef072d09b2341e606aeeaf14c3510dec329c63 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:40 +0100 Subject: [PATCH 45/54] ovl: reflow ovl_create_or_link() Reflow the creation routine in preparation of porting it to a guard. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-prepare-v2-3-bd1c97a36d7b@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/dir.c | 51 +++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 68f6617b6a77..9eafddea8192 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -650,6 +650,16 @@ static const struct cred *ovl_setup_cred_for_create(struct dentry *dentry, return override_cred; } +static int ovl_create_handle_whiteouts(struct dentry *dentry, + struct inode *inode, + struct ovl_cattr *attr) +{ + if (!ovl_dentry_is_whiteout(dentry)) + return ovl_create_upper(dentry, inode, attr); + + return ovl_create_over_whiteout(dentry, inode, attr); +} + static int ovl_create_or_link(struct dentry *dentry, struct inode *inode, struct ovl_cattr *attr, bool origin) { @@ -668,29 +678,28 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode, return err; } - if (!attr->hardlink) { - /* - * In the creation cases(create, mkdir, mknod, symlink), - * ovl should transfer current's fs{u,g}id to underlying - * fs. Because underlying fs want to initialize its new - * inode owner using current's fs{u,g}id. And in this - * case, the @inode is a new inode that is initialized - * in inode_init_owner() to current's fs{u,g}id. So use - * the inode's i_{u,g}id to override the cred's fs{u,g}id. - * - * But in the other hardlink case, ovl_link() does not - * create a new inode, so just use the ovl mounter's - * fs{u,g}id. - */ - new_cred = ovl_setup_cred_for_create(dentry, inode, attr->mode, old_cred); - if (IS_ERR(new_cred)) - return PTR_ERR(new_cred); - } + /* + * In the creation cases(create, mkdir, mknod, symlink), + * ovl should transfer current's fs{u,g}id to underlying + * fs. Because underlying fs want to initialize its new + * inode owner using current's fs{u,g}id. And in this + * case, the @inode is a new inode that is initialized + * in inode_init_owner() to current's fs{u,g}id. So use + * the inode's i_{u,g}id to override the cred's fs{u,g}id. + * + * But in the other hardlink case, ovl_link() does not + * create a new inode, so just use the ovl mounter's + * fs{u,g}id. + */ - if (!ovl_dentry_is_whiteout(dentry)) - return ovl_create_upper(dentry, inode, attr); + if (attr->hardlink) + return ovl_create_handle_whiteouts(dentry, inode, attr); - return ovl_create_over_whiteout(dentry, inode, attr); + new_cred = ovl_setup_cred_for_create(dentry, inode, attr->mode, old_cred); + if (IS_ERR(new_cred)) + return PTR_ERR(new_cred); + + return ovl_create_handle_whiteouts(dentry, inode, attr); } return err; } From 8a227c2766177db9733f86388d9d811df81e44ac Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:41 +0100 Subject: [PATCH 46/54] ovl: mark ovl_setup_cred_for_create() as unused temporarily The function will become unused in the next patch. We'll remove it in later patches to keep the diff legible. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-prepare-v2-4-bd1c97a36d7b@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 9eafddea8192..b21db1eb34bc 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -617,7 +617,7 @@ DEFINE_CLASS(ovl_override_creator_creds, ovl_override_creator_creds(dentry, inode, mode), struct dentry *dentry, struct inode *inode, umode_t mode) -static const struct cred *ovl_setup_cred_for_create(struct dentry *dentry, +static const __maybe_unused struct cred *ovl_setup_cred_for_create(struct dentry *dentry, struct inode *inode, umode_t mode, const struct cred *old_cred) From e566bff963220ba0f740da42d46dd55c34ef745e Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:42 +0100 Subject: [PATCH 47/54] ovl: port ovl_create_or_link() to new ovl_override_creator_creds cleanup guard This clearly indicates the double-credential override and makes the code a lot easier to grasp with one glance. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-prepare-v2-5-bd1c97a36d7b@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/dir.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index b21db1eb34bc..6cfa7857b352 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -664,10 +664,9 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode, struct ovl_cattr *attr, bool origin) { int err; - const struct cred *new_cred __free(put_cred) = NULL; struct dentry *parent = dentry->d_parent; - scoped_class(override_creds_ovl, old_cred, dentry->d_sb) { + with_ovl_creds(dentry->d_sb) { /* * When linking a file with copy up origin into a new parent, mark the * new parent dir "impure". @@ -695,11 +694,11 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode, if (attr->hardlink) return ovl_create_handle_whiteouts(dentry, inode, attr); - new_cred = ovl_setup_cred_for_create(dentry, inode, attr->mode, old_cred); - if (IS_ERR(new_cred)) - return PTR_ERR(new_cred); - - return ovl_create_handle_whiteouts(dentry, inode, attr); + scoped_class(ovl_override_creator_creds, cred, dentry, inode, attr->mode) { + if (IS_ERR(cred)) + return PTR_ERR(cred); + return ovl_create_handle_whiteouts(dentry, inode, attr); + } } return err; } From 89a11f004f5e3806966cb0e522c4b975bbccc3a4 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Mon, 17 Nov 2025 10:34:43 +0100 Subject: [PATCH 48/54] ovl: drop ovl_setup_cred_for_create() It is now unused and can be removed. Link: https://patch.msgid.link/20251117-work-ovl-cred-guard-prepare-v2-6-bd1c97a36d7b@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/dir.c | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 6cfa7857b352..0f01e005b915 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -617,39 +617,6 @@ DEFINE_CLASS(ovl_override_creator_creds, ovl_override_creator_creds(dentry, inode, mode), struct dentry *dentry, struct inode *inode, umode_t mode) -static const __maybe_unused struct cred *ovl_setup_cred_for_create(struct dentry *dentry, - struct inode *inode, - umode_t mode, - const struct cred *old_cred) -{ - int err; - struct cred *override_cred; - - override_cred = prepare_creds(); - if (!override_cred) - return ERR_PTR(-ENOMEM); - - override_cred->fsuid = inode->i_uid; - override_cred->fsgid = inode->i_gid; - err = security_dentry_create_files_as(dentry, mode, &dentry->d_name, - old_cred, override_cred); - if (err) { - put_cred(override_cred); - return ERR_PTR(err); - } - - /* - * Caller is going to match this with revert_creds() and drop - * referenec on the returned creds. - * We must be called with creator creds already, otherwise we risk - * leaking creds. - */ - old_cred = override_creds(override_cred); - WARN_ON_ONCE(old_cred != ovl_creds(dentry->d_sb)); - - return override_cred; -} - static int ovl_create_handle_whiteouts(struct dentry *dentry, struct inode *inode, struct ovl_cattr *attr) From 81b77b5b0a2984e767674c50a35b71ca218da7e7 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 14 Nov 2025 23:45:22 +0100 Subject: [PATCH 49/54] ovl: add copy up credential guard Add a credential guard for copy up. This will allows us to waste struct struct ovl_cu_creds and simplify the code. Link: https://patch.msgid.link/20251114-work-ovl-cred-guard-copyup-v1-1-ea3fb15cf427@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/copy_up.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 859e75daff8e..a7f70dadf94f 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -752,6 +752,33 @@ static void ovl_revert_cu_creds(struct ovl_cu_creds *cc) } } +static const struct cred *ovl_prepare_copy_up_creds(struct dentry *dentry) +{ + struct cred *copy_up_cred = NULL; + int err; + + err = security_inode_copy_up(dentry, ©_up_cred); + if (err < 0) + return ERR_PTR(err); + + if (!copy_up_cred) + return NULL; + + return override_creds(copy_up_cred); +} + +static void ovl_revert_copy_up_creds(const struct cred *orig_cred) +{ + const struct cred *copy_up_cred; + + copy_up_cred = revert_creds(orig_cred); + put_cred(copy_up_cred); +} + +DEFINE_CLASS(copy_up_creds, const struct cred *, + if (!IS_ERR_OR_NULL(_T)) ovl_revert_copy_up_creds(_T), + ovl_prepare_copy_up_creds(dentry), struct dentry *dentry) + /* * Copyup using workdir to prepare temp file. Used when copying up directories, * special files or when upper fs doesn't support O_TMPFILE. From bdba9c79c8ba6d194fb2821f504222e1630d5c5f Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Wed, 19 Nov 2025 20:27:21 +0100 Subject: [PATCH 50/54] ovl: port ovl_copy_up_workdir() to cred guard Remove the complicated struct ovl_cu_creds dance and use our new copy up cred guard. Link: https://patch.msgid.link/20251114-work-ovl-cred-guard-copyup-v1-2-ea3fb15cf427@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/copy_up.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index a7f70dadf94f..28fdbf131db6 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -790,7 +790,6 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c) struct path path = { .mnt = ovl_upper_mnt(ofs) }; struct renamedata rd = {}; struct dentry *temp; - struct ovl_cu_creds cc; int err; struct ovl_cattr cattr = { /* Can't properly set mode on creation because of the umask */ @@ -799,14 +798,14 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c) .link = c->link }; - err = ovl_prep_cu_creds(c->dentry, &cc); - if (err) - return err; + scoped_class(copy_up_creds, copy_up_creds, c->dentry) { + if (IS_ERR(copy_up_creds)) + return PTR_ERR(copy_up_creds); - ovl_start_write(c->dentry); - temp = ovl_create_temp(ofs, c->workdir, &cattr); - ovl_end_write(c->dentry); - ovl_revert_cu_creds(&cc); + ovl_start_write(c->dentry); + temp = ovl_create_temp(ofs, c->workdir, &cattr); + ovl_end_write(c->dentry); + } if (IS_ERR(temp)) return PTR_ERR(temp); From 643b8a2c0a5bf88faf9f324b9617d2640d887a0f Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 14 Nov 2025 23:45:24 +0100 Subject: [PATCH 51/54] ovl: mark *_cu_creds() as unused temporarily They will become unused in the next patch and we'll drop them after the conversion is finished together with the struct. This keeps the changes small and reviewable. Link: https://patch.msgid.link/20251114-work-ovl-cred-guard-copyup-v1-3-ea3fb15cf427@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/copy_up.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 28fdbf131db6..2ec7d3ce1f0c 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -729,7 +729,7 @@ struct ovl_cu_creds { struct cred *new; }; -static int ovl_prep_cu_creds(struct dentry *dentry, struct ovl_cu_creds *cc) +static int __maybe_unused ovl_prep_cu_creds(struct dentry *dentry, struct ovl_cu_creds *cc) { int err; @@ -744,7 +744,7 @@ static int ovl_prep_cu_creds(struct dentry *dentry, struct ovl_cu_creds *cc) return 0; } -static void ovl_revert_cu_creds(struct ovl_cu_creds *cc) +static void __maybe_unused ovl_revert_cu_creds(struct ovl_cu_creds *cc) { if (cc->new) { revert_creds(cc->old); From 72f098f0dd045e18938284ec210256b7e601ccf7 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 14 Nov 2025 23:45:25 +0100 Subject: [PATCH 52/54] ovl: port ovl_copy_up_tmpfile() to cred guard Remove the complicated struct ovl_cu_creds dance and use our new copy up cred guard. Link: https://patch.msgid.link/20251114-work-ovl-cred-guard-copyup-v1-4-ea3fb15cf427@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/copy_up.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 2ec7d3ce1f0c..71e222e5044d 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -883,17 +883,17 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) struct inode *udir = d_inode(c->destdir); struct dentry *temp, *upper; struct file *tmpfile; - struct ovl_cu_creds cc; int err; - err = ovl_prep_cu_creds(c->dentry, &cc); - if (err) - return err; + scoped_class(copy_up_creds, copy_up_creds, c->dentry) { + if (IS_ERR(copy_up_creds)) + return PTR_ERR(copy_up_creds); + + ovl_start_write(c->dentry); + tmpfile = ovl_do_tmpfile(ofs, c->workdir, c->stat.mode); + ovl_end_write(c->dentry); + } - ovl_start_write(c->dentry); - tmpfile = ovl_do_tmpfile(ofs, c->workdir, c->stat.mode); - ovl_end_write(c->dentry); - ovl_revert_cu_creds(&cc); if (IS_ERR(tmpfile)) return PTR_ERR(tmpfile); From 2c42b6ce4a3ba5b781b0138517c89fb2a736ed8f Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 14 Nov 2025 23:45:26 +0100 Subject: [PATCH 53/54] ovl: remove struct ovl_cu_creds and associated functions Now that we have this all ported to a cred guard remove the struct and the associated helpers. Link: https://patch.msgid.link/20251114-work-ovl-cred-guard-copyup-v1-5-ea3fb15cf427@kernel.org Reviewed-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/copy_up.c | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 71e222e5044d..758611ee4475 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -724,34 +724,6 @@ static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp) return err; } -struct ovl_cu_creds { - const struct cred *old; - struct cred *new; -}; - -static int __maybe_unused ovl_prep_cu_creds(struct dentry *dentry, struct ovl_cu_creds *cc) -{ - int err; - - cc->old = cc->new = NULL; - err = security_inode_copy_up(dentry, &cc->new); - if (err < 0) - return err; - - if (cc->new) - cc->old = override_creds(cc->new); - - return 0; -} - -static void __maybe_unused ovl_revert_cu_creds(struct ovl_cu_creds *cc) -{ - if (cc->new) { - revert_creds(cc->old); - put_cred(cc->new); - } -} - static const struct cred *ovl_prepare_copy_up_creds(struct dentry *dentry) { struct cred *copy_up_cred = NULL; From 2579e21be532457742d4100bbda1c2a5b81cbdef Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 18 Nov 2025 10:48:00 +0800 Subject: [PATCH 54/54] ovl: remove unneeded semicolon Remove unnecessary semicolons reported by Coccinelle/coccicheck and the semantic patch at scripts/coccinelle/misc/semicolon.cocci. Signed-off-by: Chen Ni Fixed: 7ab96df840e60 ("VFS/nfsd/cachefiles/ovl: add start_creating() and end_creating()") Signed-off-by: Amir Goldstein Signed-off-by: Christian Brauner --- fs/overlayfs/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 0f01e005b915..3c0672d29e01 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -105,7 +105,7 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs) whiteout = dget(link); end_creating(link); if (!err) - return whiteout;; + return whiteout; if (err != -EMLINK) { pr_warn("Failed to link whiteout - disabling whiteout inode sharing(nlink=%u, err=%u)\n",