]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
vfs: make vfs_symlink break delegations on parent dir
authorJeff Layton <jlayton@kernel.org>
Tue, 11 Nov 2025 14:12:53 +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 must break delegations
on the parent on any change to the directory.

Add a delegated_inode parameter to vfs_symlink() and have it break the
delegation. do_symlinkat() can then wait on the delegation break before
proceeding.

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-12-52f3feebb2f2@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/ecryptfs/inode.c
fs/init.c
fs/namei.c
fs/nfsd/vfs.c
fs/overlayfs/overlayfs.h
include/linux/fs.h

index 83f06452476dec4025cc8f50e082ae6ccbbc7914..ba15e7359dfa6e150b577205991010873a633511 100644 (file)
@@ -479,7 +479,7 @@ static int ecryptfs_symlink(struct mnt_idmap *idmap,
        if (rc)
                goto out_lock;
        rc = vfs_symlink(&nop_mnt_idmap, lower_dir, lower_dentry,
-                        encoded_symname);
+                        encoded_symname, NULL);
        kfree(encoded_symname);
        if (rc || d_really_is_negative(lower_dentry))
                goto out_lock;
index 4f02260dd65b0dfcbfbf5812d2ec6a33444a3b1f..e0f5429c0a49d046bd3f231a260954ed0f90ef44 100644 (file)
--- a/fs/init.c
+++ b/fs/init.c
@@ -209,7 +209,7 @@ int __init init_symlink(const char *oldname, const char *newname)
        error = security_path_symlink(&path, dentry, oldname);
        if (!error)
                error = vfs_symlink(mnt_idmap(path.mnt), path.dentry->d_inode,
-                                   dentry, oldname);
+                                   dentry, oldname, NULL);
        end_creating_path(&path, dentry);
        return error;
 }
index e9616134390fb7f0d09a759be69bf677f8800bc5..d5ab28947b2b6c6e19c7bb4a9140ccec407dc07c 100644 (file)
@@ -4845,6 +4845,7 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname)
  * @dir:       inode of the parent directory
  * @dentry:    dentry of the child symlink file
  * @oldname:   name of the file to link to
+ * @delegated_inode: returns victim inode, if the inode is delegated.
  *
  * Create a symlink.
  *
@@ -4855,7 +4856,8 @@ SYSCALL_DEFINE1(unlink, const char __user *, pathname)
  * raw inode simply pass @nop_mnt_idmap.
  */
 int vfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
-               struct dentry *dentry, const char *oldname)
+               struct dentry *dentry, const char *oldname,
+               struct delegated_inode *delegated_inode)
 {
        int error;
 
@@ -4870,6 +4872,10 @@ int vfs_symlink(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->symlink(idmap, dir, dentry, oldname);
        if (!error)
                fsnotify_create(dir, dentry);
@@ -4883,6 +4889,7 @@ int do_symlinkat(struct filename *from, int newdfd, struct filename *to)
        struct dentry *dentry;
        struct path path;
        unsigned int lookup_flags = 0;
+       struct delegated_inode delegated_inode = { };
 
        if (IS_ERR(from)) {
                error = PTR_ERR(from);
@@ -4897,8 +4904,13 @@ retry:
        error = security_path_symlink(&path, dentry, from->name);
        if (!error)
                error = vfs_symlink(mnt_idmap(path.mnt), path.dentry->d_inode,
-                                   dentry, from->name);
+                                   dentry, from->name, &delegated_inode);
        end_creating_path(&path, dentry);
+       if (is_delegated(&delegated_inode)) {
+               error = break_deleg_wait(&delegated_inode);
+               if (!error)
+                       goto retry;
+       }
        if (retry_estale(error, lookup_flags)) {
                lookup_flags |= LOOKUP_REVAL;
                goto retry;
index 6684935007b17ed40723ff0a744045fd187ace5e..28710da4cce7cc7fc1e14d29420239dc357316f6 100644 (file)
@@ -1742,7 +1742,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
        err = fh_fill_pre_attrs(fhp);
        if (err != nfs_ok)
                goto out_unlock;
-       host_err = vfs_symlink(&nop_mnt_idmap, d_inode(dentry), dnew, path);
+       host_err = vfs_symlink(&nop_mnt_idmap, d_inode(dentry), dnew, path, NULL);
        err = nfserrno(host_err);
        cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp);
        if (!err)
index afd95721f76ea761cfe7133c3902028550f3e359..5065961bd370628c9afe914ea570d9998a096660 100644 (file)
@@ -267,7 +267,7 @@ static inline int ovl_do_symlink(struct ovl_fs *ofs,
                                 struct inode *dir, struct dentry *dentry,
                                 const char *oldname)
 {
-       int err = vfs_symlink(ovl_upper_mnt_idmap(ofs), dir, dentry, oldname);
+       int err = vfs_symlink(ovl_upper_mnt_idmap(ofs), dir, dentry, oldname, NULL);
 
        pr_debug("symlink(\"%s\", %pd2) = %i\n", oldname, dentry, err);
        return err;
index 1a5d86cfafaa97fc89d15cd1a156968b8c3dd377..64323e618724bc20dc101db13035b042f5f88e4d 100644 (file)
@@ -2118,7 +2118,7 @@ struct dentry *vfs_mkdir(struct mnt_idmap *, struct inode *,
 int vfs_mknod(struct mnt_idmap *, struct inode *, struct dentry *,
              umode_t, dev_t, struct delegated_inode *);
 int vfs_symlink(struct mnt_idmap *, struct inode *,
-               struct dentry *, const char *);
+               struct dentry *, const char *, struct delegated_inode *);
 int vfs_link(struct dentry *, struct mnt_idmap *, struct inode *,
             struct dentry *, struct delegated_inode *);
 int vfs_rmdir(struct mnt_idmap *, struct inode *, struct dentry *,