]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
Merge tag 'vfs-6.19-rc1.directory.locking' of git://git.kernel.org/pub/scm/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 2 Dec 2025 00:13:46 +0000 (16:13 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 2 Dec 2025 00:13:46 +0000 (16:13 -0800)
Pull directory locking updates from Christian Brauner:
 "This contains the work to add centralized APIs for directory locking
  operations.

  This series is part of a larger effort to change directory operation
  locking to allow multiple concurrent operations in a directory. The
  ultimate goal is to lock the target dentry(s) rather than the whole
  parent directory.

  To help with changing the locking protocol, this series centralizes
  locking and lookup in new helper functions. The helpers establish a
  pattern where it is the dentry that is being locked and unlocked
  (currently the lock is held on dentry->d_parent->d_inode, but that can
  change in the future).

  This also changes vfs_mkdir() to unlock the parent on failure, as well
  as dput()ing the dentry. This allows end_creating() to only require
  the target dentry (which may be IS_ERR() after vfs_mkdir()), not the
  parent"

* tag 'vfs-6.19-rc1.directory.locking' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs:
  nfsd: fix end_creating() conversion
  VFS: introduce end_creating_keep()
  VFS: change vfs_mkdir() to unlock on failure.
  ecryptfs: use new start_creating/start_removing APIs
  Add start_renaming_two_dentries()
  VFS/ovl/smb: introduce start_renaming_dentry()
  VFS/nfsd/ovl: introduce start_renaming() and end_renaming()
  VFS: add start_creating_killable() and start_removing_killable()
  VFS: introduce start_removing_dentry()
  smb/server: use end_removing_noperm for for target of smb2_create_link()
  VFS: introduce start_creating_noperm() and start_removing_noperm()
  VFS/nfsd/cachefiles/ovl: introduce start_removing() and end_removing()
  VFS/nfsd/cachefiles/ovl: add start_creating() and end_creating()
  VFS: tidy up do_unlinkat()
  VFS: introduce start_dirop() and end_dirop()
  debugfs: rename end_creating() to debugfs_end_creating()

20 files changed:
1  2 
Documentation/filesystems/porting.rst
fs/btrfs/ioctl.c
fs/cachefiles/namei.c
fs/ecryptfs/inode.c
fs/fuse/dir.c
fs/libfs.c
fs/namei.c
fs/nfsd/nfs3proc.c
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4recover.c
fs/nfsd/vfs.c
fs/overlayfs/copy_up.c
fs/overlayfs/dir.c
fs/overlayfs/overlayfs.h
fs/overlayfs/util.c
fs/smb/server/smb2pdu.c
fs/smb/server/vfs.c
fs/xfs/scrub/orphanage.c
include/linux/fs.h
include/linux/namei.h

Simple merge
Simple merge
index 50c0f9c76d1fd4c05db90d7d0d1bad574523ead0,ef22ac19545b98f6e7778879c79e2ed75680ee64..e5ec90dccc27f71dc19219f7632f3e48eaf51545
@@@ -129,10 -128,12 +128,12 @@@ retry
                if (ret < 0)
                        goto mkdir_error;
                ret = cachefiles_inject_write_error();
-               if (ret == 0)
+               if (ret == 0) {
 -                      subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700);
 +                      subdir = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), subdir, 0700, NULL);
-               else
+               } else {
+                       end_creating(subdir);
                        subdir = ERR_PTR(ret);
+               }
                if (IS_ERR(subdir)) {
                        trace_cachefiles_vfs_error(NULL, d_inode(dir), ret,
                                                   cachefiles_trace_mkdir_error);
index dc3ee0cbd77a457b57b58f50606f4ef78315df7a,2ad1db2cd2ece9147a98d2663b24cbbce1af6a3d..3978248247dc2197b4441957a331c9711a6190bb
@@@ -186,9 -190,12 +190,11 @@@ ecryptfs_do_create(struct inode *direct
        struct inode *lower_dir;
        struct inode *inode;
  
-       rc = lock_parent(ecryptfs_dentry, &lower_dentry, &lower_dir);
-       if (!rc)
-               rc = vfs_create(&nop_mnt_idmap, lower_dentry, mode, NULL);
+       lower_dentry = ecryptfs_start_creating_dentry(ecryptfs_dentry);
+       if (IS_ERR(lower_dentry))
+               return ERR_CAST(lower_dentry);
+       lower_dir = lower_dentry->d_parent->d_inode;
 -      rc = vfs_create(&nop_mnt_idmap, lower_dir,
 -                      lower_dentry, mode, true);
++      rc = vfs_create(&nop_mnt_idmap, lower_dentry, mode, NULL);
        if (rc) {
                printk(KERN_ERR "%s: Failure to create dentry in lower fs; "
                       "rc = [%d]\n", __func__, rc);
@@@ -500,14 -511,16 +510,16 @@@ static struct dentry *ecryptfs_mkdir(st
  {
        int rc;
        struct dentry *lower_dentry;
+       struct dentry *lower_dir_dentry;
        struct inode *lower_dir;
  
-       rc = lock_parent(dentry, &lower_dentry, &lower_dir);
-       if (rc)
-               goto out;
+       lower_dentry = ecryptfs_start_creating_dentry(dentry);
+       if (IS_ERR(lower_dentry))
+               return lower_dentry;
+       lower_dir_dentry = dget(lower_dentry->d_parent);
+       lower_dir = lower_dir_dentry->d_inode;
        lower_dentry = vfs_mkdir(&nop_mnt_idmap, lower_dir,
 -                               lower_dentry, mode);
 +                               lower_dentry, mode, NULL);
        rc = PTR_ERR(lower_dentry);
        if (IS_ERR(lower_dentry))
                goto out;
@@@ -533,14 -546,12 +545,12 @@@ static int ecryptfs_rmdir(struct inode 
        struct inode *lower_dir;
        int rc;
  
-       rc = lock_parent(dentry, &lower_dentry, &lower_dir);
-       dget(lower_dentry);     // don't even try to make the lower negative
-       if (!rc) {
-               if (d_unhashed(lower_dentry))
-                       rc = -EINVAL;
-               else
-                       rc = vfs_rmdir(&nop_mnt_idmap, lower_dir, lower_dentry, NULL);
-       }
+       lower_dentry = ecryptfs_start_removing_dentry(dentry);
+       if (IS_ERR(lower_dentry))
+               return PTR_ERR(lower_dentry);
+       lower_dir = lower_dentry->d_parent->d_inode;
 -      rc = vfs_rmdir(&nop_mnt_idmap, lower_dir, lower_dentry);
++      rc = vfs_rmdir(&nop_mnt_idmap, lower_dir, lower_dentry, NULL);
        if (!rc) {
                clear_nlink(d_inode(dentry));
                fsstack_copy_attr_times(dir, lower_dir);
@@@ -561,10 -571,12 +570,12 @@@ ecryptfs_mknod(struct mnt_idmap *idmap
        struct dentry *lower_dentry;
        struct inode *lower_dir;
  
-       rc = lock_parent(dentry, &lower_dentry, &lower_dir);
-       if (!rc)
-               rc = vfs_mknod(&nop_mnt_idmap, lower_dir,
-                              lower_dentry, mode, dev, NULL);
+       lower_dentry = ecryptfs_start_creating_dentry(dentry);
+       if (IS_ERR(lower_dentry))
+               return PTR_ERR(lower_dentry);
+       lower_dir = lower_dentry->d_parent->d_inode;
 -      rc = vfs_mknod(&nop_mnt_idmap, lower_dir, lower_dentry, mode, dev);
++      rc = vfs_mknod(&nop_mnt_idmap, lower_dir, lower_dentry, mode, dev, NULL);
        if (rc || d_really_is_negative(lower_dentry))
                goto out;
        rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb);
diff --cc fs/fuse/dir.c
Simple merge
diff --cc fs/libfs.c
Simple merge
diff --cc fs/namei.c
index 13041756d9419e64da0397715e847eda6986e0b8,d284ebae41bf116cd62c769f508e9519bbcc1908..bf0f66f0e9b92c106fc92759d22f8158187c29d1
@@@ -4717,12 -5171,10 +5288,11 @@@ retry
        error = security_path_rmdir(&path, dentry);
        if (error)
                goto exit4;
 -      error = vfs_rmdir(mnt_idmap(path.mnt), path.dentry->d_inode, dentry);
 +      error = vfs_rmdir(mnt_idmap(path.mnt), path.dentry->d_inode,
 +                        dentry, &delegated_inode);
  exit4:
-       dput(dentry);
+       end_dirop(dentry);
  exit3:
-       inode_unlock(path.dentry->d_inode);
        mnt_drop_write(path.mnt);
  exit2:
        path_put(&path);
@@@ -4831,8 -5275,8 +5401,8 @@@ int do_unlinkat(int dfd, struct filenam
        struct path path;
        struct qstr last;
        int type;
-       struct inode *inode = NULL;
+       struct inode *inode;
 -      struct inode *delegated_inode = NULL;
 +      struct delegated_inode delegated_inode = { };
        unsigned int lookup_flags = 0;
  retry:
        error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type);
  
        error = mnt_want_write(path.mnt);
        if (error)
-               goto exit2;
+               goto exit_path_put;
  retry_deleg:
-       inode_lock_nested(path.dentry->d_inode, I_MUTEX_PARENT);
-       dentry = lookup_one_qstr_excl(&last, path.dentry, lookup_flags);
+       dentry = start_dirop(path.dentry, &last, lookup_flags);
        error = PTR_ERR(dentry);
-       if (!IS_ERR(dentry)) {
+       if (IS_ERR(dentry))
+               goto exit_drop_write;
  
-               /* Why not before? Because we want correct error value */
-               if (last.name[last.len])
-                       goto slashes;
-               inode = dentry->d_inode;
-               ihold(inode);
-               error = security_path_unlink(&path, dentry);
-               if (error)
-                       goto exit3;
-               error = vfs_unlink(mnt_idmap(path.mnt), path.dentry->d_inode,
-                                  dentry, &delegated_inode);
- exit3:
-               dput(dentry);
+       /* Why not before? Because we want correct error value */
+       if (unlikely(last.name[last.len])) {
+               if (d_is_dir(dentry))
+                       error = -EISDIR;
+               else
+                       error = -ENOTDIR;
+               end_dirop(dentry);
+               goto exit_drop_write;
        }
-       inode_unlock(path.dentry->d_inode);
-       if (inode)
-               iput(inode);    /* truncate the inode here */
-       inode = NULL;
+       inode = dentry->d_inode;
+       ihold(inode);
+       error = security_path_unlink(&path, dentry);
+       if (error)
+               goto exit_end_dirop;
+       error = vfs_unlink(mnt_idmap(path.mnt), path.dentry->d_inode,
+                          dentry, &delegated_inode);
+ exit_end_dirop:
+       end_dirop(dentry);
+       iput(inode);    /* truncate the inode here */
 -      if (delegated_inode) {
 +      if (is_delegated(&delegated_inode)) {
                error = break_deleg_wait(&delegated_inode);
                if (!error)
                        goto retry_deleg;
@@@ -5407,11 -5824,8 +5972,8 @@@ int do_renameat2(int olddfd, struct fil
        struct path old_path, new_path;
        struct qstr old_last, new_last;
        int old_type, new_type;
 -      struct inode *delegated_inode = NULL;
 +      struct delegated_inode delegated_inode = { };
-       unsigned int lookup_flags = 0, target_flags =
-               LOOKUP_RENAME_TARGET | LOOKUP_CREATE;
+       unsigned int lookup_flags = 0;
        bool should_retry = false;
        int error = -EINVAL;
  
@@@ -5480,44 -5883,24 +6031,24 @@@ retry_deleg
                }
        }
        /* unless the source is a directory trailing slashes give -ENOTDIR */
-       if (!d_is_dir(old_dentry)) {
+       if (!d_is_dir(rd.old_dentry)) {
                error = -ENOTDIR;
                if (old_last.name[old_last.len])
-                       goto exit5;
+                       goto exit_unlock;
                if (!(flags & RENAME_EXCHANGE) && new_last.name[new_last.len])
-                       goto exit5;
+                       goto exit_unlock;
        }
-       /* source should not be ancestor of target */
-       error = -EINVAL;
-       if (old_dentry == trap)
-               goto exit5;
-       /* target should not be an ancestor of source */
-       if (!(flags & RENAME_EXCHANGE))
-               error = -ENOTEMPTY;
-       if (new_dentry == trap)
-               goto exit5;
  
-       error = security_path_rename(&old_path, old_dentry,
-                                    &new_path, new_dentry, flags);
+       error = security_path_rename(&old_path, rd.old_dentry,
+                                    &new_path, rd.new_dentry, flags);
        if (error)
-               goto exit5;
+               goto exit_unlock;
  
-       rd.old_parent      = old_path.dentry;
-       rd.old_dentry      = old_dentry;
-       rd.mnt_idmap       = mnt_idmap(old_path.mnt);
-       rd.new_parent      = new_path.dentry;
-       rd.new_dentry      = new_dentry;
-       rd.delegated_inode = &delegated_inode;
-       rd.flags           = flags;
        error = vfs_rename(&rd);
- exit5:
-       dput(new_dentry);
- exit4:
-       dput(old_dentry);
- exit3:
-       unlock_rename(new_path.dentry, old_path.dentry);
+ exit_unlock:
+       end_renaming(&rd);
  exit_lock_rename:
 -      if (delegated_inode) {
 +      if (is_delegated(&delegated_inode)) {
                error = break_deleg_wait(&delegated_inode);
                if (!error)
                        goto retry_deleg;
Simple merge
Simple merge
index 30bae93931d9c9f8900dfcf96f79403db4f3f458,18c08395b273308f79e3b83deeb021a2b1b90244..b39d4cbdfd35e2fc23635fd6a4898e068f95d632
@@@ -212,15 -210,13 +210,13 @@@ nfsd4_create_clid_dir(struct nfs4_clien
                 * In the 4.0 case, we should never get here; but we may
                 * as well be forgiving and just succeed silently.
                 */
-               goto out_put;
+               goto out_end;
 -      dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, S_IRWXU);
 +      dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, 0700, NULL);
        if (IS_ERR(dentry))
                status = PTR_ERR(dentry);
- out_put:
-       if (!status)
-               dput(dentry);
- out_unlock:
-       inode_unlock(d_inode(dir));
+ out_end:
+       end_creating(dentry);
+ out:
        if (status == 0) {
                if (nn->in_grace)
                        __nfsd4_create_reclaim_record_grace(clp, dname,
@@@ -328,20 -324,12 +324,12 @@@ nfsd4_unlink_clid_dir(char *name, struc
        dprintk("NFSD: nfsd4_unlink_clid_dir. name %s\n", name);
  
        dir = nn->rec_file->f_path.dentry;
-       inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
-       dentry = lookup_one(&nop_mnt_idmap, &QSTR(name), dir);
-       if (IS_ERR(dentry)) {
-               status = PTR_ERR(dentry);
-               goto out_unlock;
-       }
-       status = -ENOENT;
-       if (d_really_is_negative(dentry))
-               goto out;
+       dentry = start_removing(&nop_mnt_idmap, dir, &QSTR(name));
+       if (IS_ERR(dentry))
+               return PTR_ERR(dentry);
 -      status = vfs_rmdir(&nop_mnt_idmap, d_inode(dir), dentry);
 +      status = vfs_rmdir(&nop_mnt_idmap, d_inode(dir), dentry, NULL);
- out:
-       dput(dentry);
- out_unlock:
-       inode_unlock(d_inode(dir));
+       end_removing(dentry);
        return status;
  }
  
diff --cc fs/nfsd/vfs.c
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 3747851b61c8bad4c00e49866c380c32e9f53a4b,ea5ab5b0adb194528b50787a4c8b3cebd959796f..03fd7409be79fbfc190be23b9bf8d89b5e1fa24b
@@@ -1083,13 -1024,13 +1023,13 @@@ int ksmbd_vfs_unlink(struct file *filp
                return err;
  
        dir = dget_parent(dentry);
-       err = ksmbd_vfs_lock_parent(dir, dentry);
-       if (err)
+       dentry = start_removing_dentry(dir, dentry);
+       err = PTR_ERR(dentry);
+       if (IS_ERR(dentry))
                goto out;
-       dget(dentry);
  
        if (S_ISDIR(d_inode(dentry)->i_mode))
 -              err = vfs_rmdir(idmap, d_inode(dir), dentry);
 +              err = vfs_rmdir(idmap, d_inode(dir), dentry, NULL);
        else
                err = vfs_unlink(idmap, d_inode(dir), dentry, NULL);
  
index 91c9d07b97f306f57aebb9b69ba564b0c2cb8c17,b77c2b6b6d44dc11b9c38f7e431e9a0e93f27f75..4e550a1d5353b7b8abe616b44e35aa7469ea9df2
@@@ -167,10 -166,10 +166,10 @@@ xrep_orphanage_create
         */
        if (d_really_is_negative(orphanage_dentry)) {
                orphanage_dentry = vfs_mkdir(&nop_mnt_idmap, root_inode,
 -                                           orphanage_dentry, 0750);
 +                                           orphanage_dentry, 0750, NULL);
                error = PTR_ERR(orphanage_dentry);
                if (IS_ERR(orphanage_dentry))
-                       goto out_unlock_root;
+                       goto out_dput_orphanage;
        }
  
        /* Not a directory? Bail out. */
index f4b8eb6a584d0fdc76f90c23412a7e891116f850,f4543612ef1e76f50ce2b020cfbe026d3ab8c0d8..d2cacf9721eef83cfe83977f3e6723e975fe806f
@@@ -3172,9 -3606,11 +3172,11 @@@ extern void drop_super_exclusive(struc
  extern void iterate_supers(void (*f)(struct super_block *, void *), void *arg);
  extern void iterate_supers_type(struct file_system_type *,
                                void (*)(struct super_block *, void *), void *);
 -void filesystems_freeze(void);
 +void filesystems_freeze(bool freeze_all);
  void filesystems_thaw(void);
  
+ void end_dirop(struct dentry *de);
  extern int dcache_dir_open(struct inode *, struct file *);
  extern int dcache_dir_close(struct inode *, struct file *);
  extern loff_t dcache_dir_lseek(struct file *, loff_t, int);
Simple merge