]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
vfs: allow rmdir to wait for delegation break on parent
authorJeff Layton <jlayton@kernel.org>
Tue, 11 Nov 2025 14:12:48 +0000 (09:12 -0500)
committerChristian Brauner <brauner@kernel.org>
Wed, 12 Nov 2025 08:38:35 +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 delegated_inode struct to vfs_rmdir() and populate that
pointer with the parent inode if it's non-NULL. Most existing in-kernel
callers pass in a NULL pointer.

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

index 0e79621cb0f79870003b867ca384199171ded4e0..104025104ef75381984fd94dfbd50feeaa8cdd22 100644 (file)
@@ -261,7 +261,7 @@ static int dev_rmdir(const char *name)
                return PTR_ERR(dentry);
        if (d_inode(dentry)->i_private == &thread)
                err = vfs_rmdir(&nop_mnt_idmap, d_inode(parent.dentry),
-                               dentry);
+                               dentry, NULL);
        else
                err = -EPERM;
 
index 35830b3144f8f71374a78b3e7463b864f4fc216e..88631291b32535f623a3fbe4ea9b6ed48a306ca0 100644 (file)
@@ -540,7 +540,7 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
                if (d_unhashed(lower_dentry))
                        rc = -EINVAL;
                else
-                       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));
index 76c0587d991ff7307e3dde69497719d716c8d7b8..9e0393a92091ac522b5324fcdad8c5592a948e8d 100644 (file)
@@ -4522,9 +4522,10 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode)
 
 /**
  * vfs_rmdir - remove directory
- * @idmap:     idmap of the mount the inode was found from
- * @dir:       inode of the parent directory
- * @dentry:    dentry of the child directory
+ * @idmap:             idmap of the mount the inode was found from
+ * @dir:               inode of the parent directory
+ * @dentry:            dentry of the child directory
+ * @delegated_inode:   returns parent inode, if it's delegated.
  *
  * Remove a directory.
  *
@@ -4535,7 +4536,7 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode)
  * raw inode simply pass @nop_mnt_idmap.
  */
 int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
-                    struct dentry *dentry)
+             struct dentry *dentry, struct delegated_inode *delegated_inode)
 {
        int error = may_delete(idmap, dir, dentry, 1);
 
@@ -4557,6 +4558,10 @@ int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
        if (error)
                goto out;
 
+       error = try_break_deleg(dir, delegated_inode);
+       if (error)
+               goto out;
+
        error = dir->i_op->rmdir(dir, dentry);
        if (error)
                goto out;
@@ -4583,6 +4588,7 @@ int do_rmdir(int dfd, struct filename *name)
        struct qstr last;
        int type;
        unsigned int lookup_flags = 0;
+       struct delegated_inode delegated_inode = { };
 retry:
        error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type);
        if (error)
@@ -4612,7 +4618,8 @@ 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);
 exit3:
@@ -4620,6 +4627,11 @@ exit3:
        mnt_drop_write(path.mnt);
 exit2:
        path_put(&path);
+       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 1f56834b2072fcee1d0d400bbb554b0c949ecab4..30bae93931d9c9f8900dfcf96f79403db4f3f458 100644 (file)
@@ -337,7 +337,7 @@ nfsd4_unlink_clid_dir(char *name, struct nfsd_net *nn)
        status = -ENOENT;
        if (d_really_is_negative(dentry))
                goto out;
-       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:
@@ -427,7 +427,7 @@ purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn)
        if (nfs4_has_reclaimed_state(name, nn))
                goto out_free;
 
-       status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child);
+       status = vfs_rmdir(&nop_mnt_idmap, d_inode(parent), child, NULL);
        if (status)
                printk("failed to remove client recovery directory %pd\n",
                                child);
index 97aef140cbf5fca4c41738fdcaccba3b57886463..c400ea94ff2e837fd59719bf2c4b79ef1d064743 100644 (file)
@@ -2108,7 +2108,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
                                break;
                }
        } else {
-               host_err = vfs_rmdir(&nop_mnt_idmap, dirp, rdentry);
+               host_err = vfs_rmdir(&nop_mnt_idmap, dirp, rdentry, NULL);
        }
        fh_fill_post_attrs(fhp);
 
index 0f65f9a5d54d4786b39e4f4f30f416d5b9016e70..d215d7349489686b66bb66e939b27046f7d836f6 100644 (file)
@@ -206,7 +206,7 @@ static inline int ovl_do_notify_change(struct ovl_fs *ofs,
 static inline int ovl_do_rmdir(struct ovl_fs *ofs,
                               struct inode *dir, struct dentry *dentry)
 {
-       int err = vfs_rmdir(ovl_upper_mnt_idmap(ofs), dir, dentry);
+       int err = vfs_rmdir(ovl_upper_mnt_idmap(ofs), dir, dentry, NULL);
 
        pr_debug("rmdir(%pd2) = %i\n", dentry, err);
        return err;
index 3d2190f26623b23ea79c63410905a3c3ad684048..c5f0f3170d586cb2dc4d416b80948c642797fb82 100644 (file)
@@ -609,7 +609,7 @@ int ksmbd_vfs_remove_file(struct ksmbd_work *work, const struct path *path)
 
        idmap = mnt_idmap(path->mnt);
        if (S_ISDIR(d_inode(path->dentry)->i_mode)) {
-               err = vfs_rmdir(idmap, d_inode(parent), path->dentry);
+               err = vfs_rmdir(idmap, d_inode(parent), path->dentry, NULL);
                if (err && err != -ENOTEMPTY)
                        ksmbd_debug(VFS, "rmdir failed, err %d\n", err);
        } else {
@@ -1090,7 +1090,7 @@ int ksmbd_vfs_unlink(struct file *filp)
        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 20bb4c8a4e8e1be7e11047d228c05920ea6c388d..12873214e1c7811735ea5d2dee3d57e2a5604d8f 100644 (file)
@@ -2121,7 +2121,8 @@ int vfs_symlink(struct mnt_idmap *, struct inode *,
                struct dentry *, const char *);
 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 *);
+int vfs_rmdir(struct mnt_idmap *, struct inode *, struct dentry *,
+             struct delegated_inode *);
 int vfs_unlink(struct mnt_idmap *, struct inode *, struct dentry *,
               struct delegated_inode *);