]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
vfs: make vfs_mknod break delegations on parent directory
authorJeff Layton <jlayton@kernel.org>
Tue, 11 Nov 2025 14:12:52 +0000 (09:12 -0500)
committerChristian Brauner <brauner@kernel.org>
Wed, 12 Nov 2025 08:38:36 +0000 (09:38 +0100)
In order to add directory delegation support, we need to break
delegations on the parent whenever there is going to be a change in the
directory.

Add a new delegated_inode pointer to vfs_mknod() and have the
appropriate callers wait when there is an outstanding delegation. All
other callers just set the pointer to NULL.

Reviewed-by: Jan Kara <jack@suse.cz>
Reviewed-by: NeilBrown <neil@brown.name>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Link: https://patch.msgid.link/20251111-dir-deleg-ro-v6-11-52f3feebb2f2@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
drivers/base/devtmpfs.c
fs/ecryptfs/inode.c
fs/init.c
fs/namei.c
fs/nfsd/vfs.c
fs/overlayfs/overlayfs.h
include/linux/fs.h
net/unix/af_unix.c

index 104025104ef75381984fd94dfbd50feeaa8cdd22..2f576ecf18324f767cd5ac6cbd28adbf9f46b958 100644 (file)
@@ -231,7 +231,7 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid,
                return PTR_ERR(dentry);
 
        err = vfs_mknod(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode,
-                       dev->devt);
+                       dev->devt, NULL);
        if (!err) {
                struct iattr newattrs;
 
index 3341f00dd08753c8feab184dd82b8bfa63d3724a..83f06452476dec4025cc8f50e082ae6ccbbc7914 100644 (file)
@@ -564,7 +564,7 @@ ecryptfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
        rc = lock_parent(dentry, &lower_dentry, &lower_dir);
        if (!rc)
                rc = vfs_mknod(&nop_mnt_idmap, lower_dir,
-                              lower_dentry, mode, dev);
+                              lower_dentry, mode, dev, NULL);
        if (rc || d_really_is_negative(lower_dentry))
                goto out;
        rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb);
index 895f8a09a71acfd03e11164e3b441a7d4e2de146..4f02260dd65b0dfcbfbf5812d2ec6a33444a3b1f 100644 (file)
--- a/fs/init.c
+++ b/fs/init.c
@@ -157,7 +157,7 @@ int __init init_mknod(const char *filename, umode_t mode, unsigned int dev)
        error = security_path_mknod(&path, dentry, mode, dev);
        if (!error)
                error = vfs_mknod(mnt_idmap(path.mnt), path.dentry->d_inode,
-                                 dentry, mode, new_decode_dev(dev));
+                                 dentry, mode, new_decode_dev(dev), NULL);
        end_creating_path(&path, dentry);
        return error;
 }
index b20f053374a578d36fb764e0df80fb7db9230dbe..e9616134390fb7f0d09a759be69bf677f8800bc5 100644 (file)
@@ -4295,13 +4295,15 @@ inline struct dentry *start_creating_user_path(
 }
 EXPORT_SYMBOL(start_creating_user_path);
 
+
 /**
  * vfs_mknod - create device node or file
- * @idmap:     idmap of the mount the inode was found from
- * @dir:       inode of the parent directory
- * @dentry:    dentry of the child device node
- * @mode:      mode of the child device node
- * @dev:       device number of device to create
+ * @idmap:             idmap of the mount the inode was found from
+ * @dir:               inode of the parent directory
+ * @dentry:            dentry of the child device node
+ * @mode:              mode of the child device node
+ * @dev:               device number of device to create
+ * @delegated_inode:   returns parent inode, if the inode is delegated.
  *
  * Create a device node or file.
  *
@@ -4312,7 +4314,8 @@ EXPORT_SYMBOL(start_creating_user_path);
  * raw inode simply pass @nop_mnt_idmap.
  */
 int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
-             struct dentry *dentry, umode_t mode, dev_t dev)
+             struct dentry *dentry, umode_t mode, dev_t dev,
+             struct delegated_inode *delegated_inode)
 {
        bool is_whiteout = S_ISCHR(mode) && dev == WHITEOUT_DEV;
        int error = may_create(idmap, dir, dentry);
@@ -4336,6 +4339,10 @@ int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
        if (error)
                return error;
 
+       error = try_break_deleg(dir, delegated_inode);
+       if (error)
+               return error;
+
        error = dir->i_op->mknod(idmap, dir, dentry, mode, dev);
        if (!error)
                fsnotify_create(dir, dentry);
@@ -4393,11 +4400,11 @@ retry:
                        break;
                case S_IFCHR: case S_IFBLK:
                        error = vfs_mknod(idmap, path.dentry->d_inode,
-                                         dentry, mode, new_decode_dev(dev));
+                                         dentry, mode, new_decode_dev(dev), &di);
                        break;
                case S_IFIFO: case S_IFSOCK:
                        error = vfs_mknod(idmap, path.dentry->d_inode,
-                                         dentry, mode, 0);
+                                         dentry, mode, 0, &di);
                        break;
        }
 out2:
index de5f46f8c6d3ab24fabddeed9bd59adbe7a486df..6684935007b17ed40723ff0a744045fd187ace5e 100644 (file)
@@ -1573,7 +1573,7 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp,
        case S_IFIFO:
        case S_IFSOCK:
                host_err = vfs_mknod(&nop_mnt_idmap, dirp, dchild,
-                                    iap->ia_mode, rdev);
+                                    iap->ia_mode, rdev, NULL);
                break;
        default:
                printk(KERN_WARNING "nfsd: bad file type %o in nfsd_create\n",
index e30441cc9c63ff8b6c055db80be7974501d0023b..afd95721f76ea761cfe7133c3902028550f3e359 100644 (file)
@@ -257,7 +257,7 @@ static inline int ovl_do_mknod(struct ovl_fs *ofs,
                               struct inode *dir, struct dentry *dentry,
                               umode_t mode, dev_t dev)
 {
-       int err = vfs_mknod(ovl_upper_mnt_idmap(ofs), dir, dentry, mode, dev);
+       int err = vfs_mknod(ovl_upper_mnt_idmap(ofs), dir, dentry, mode, dev, NULL);
 
        pr_debug("mknod(%pd2, 0%o, 0%o) = %i\n", dentry, mode, dev, err);
        return err;
index 83b05aec4e10c846d3168018fb62284e45ceb1a8..1a5d86cfafaa97fc89d15cd1a156968b8c3dd377 100644 (file)
@@ -2116,7 +2116,7 @@ int vfs_create(struct mnt_idmap *, struct dentry *, umode_t,
 struct dentry *vfs_mkdir(struct mnt_idmap *, struct inode *,
                         struct dentry *, umode_t, struct delegated_inode *);
 int vfs_mknod(struct mnt_idmap *, struct inode *, struct dentry *,
-              umode_t, dev_t);
+             umode_t, dev_t, struct delegated_inode *);
 int vfs_symlink(struct mnt_idmap *, struct inode *,
                struct dentry *, const char *);
 int vfs_link(struct dentry *, struct mnt_idmap *, struct inode *,
@@ -2152,7 +2152,7 @@ static inline int vfs_whiteout(struct mnt_idmap *idmap,
                               struct inode *dir, struct dentry *dentry)
 {
        return vfs_mknod(idmap, dir, dentry, S_IFCHR | WHITEOUT_MODE,
-                        WHITEOUT_DEV);
+                        WHITEOUT_DEV, NULL);
 }
 
 struct file *kernel_tmpfile_open(struct mnt_idmap *idmap,
index 768098dec2310008632558ae928703b37c3cc8ef..db1fd8d6a84c2c7c0d45b43d9c5a936b3d491b7b 100644 (file)
@@ -1399,7 +1399,7 @@ static int unix_bind_bsd(struct sock *sk, struct sockaddr_un *sunaddr,
        idmap = mnt_idmap(parent.mnt);
        err = security_path_mknod(&parent, dentry, mode, 0);
        if (!err)
-               err = vfs_mknod(idmap, d_inode(parent.dentry), dentry, mode, 0);
+               err = vfs_mknod(idmap, d_inode(parent.dentry), dentry, mode, 0, NULL);
        if (err)
                goto out_path;
        err = mutex_lock_interruptible(&u->bindlock);