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. Rename vfs_rmdir as __vfs_rmdir, make it static and add a new delegated_inode parameter. Add a vfs_rmdir wrapper that passes in a NULL pointer for it. Add the necessary try_break_deleg calls to __vfs_rmdir(). Convert do_rmdir to use __vfs_rmdir and wait for the delegation break to complete before proceeding. Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx> --- fs/namei.c | 51 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 7c9e237ed1b1a535934ffe5e523424bb035e7ae0..2211ed9f427cc97391d068b1a33ce388266a3e02 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4438,22 +4438,8 @@ SYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode) return do_mkdirat(AT_FDCWD, getname(pathname), 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 - * - * Remove a directory. - * - * If the inode has been found through an idmapped mount the idmap of - * the vfsmount must be passed through @idmap. This function will then take - * care to map the inode according to @idmap before checking permissions. - * On non-idmapped mounts or if permission checking is to be performed on the - * raw inode simply pass @nop_mnt_idmap. - */ -int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir, - struct dentry *dentry) +static int __vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, struct inode **delegated_inode) { int error = may_delete(idmap, dir, dentry, 1); @@ -4475,6 +4461,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; @@ -4491,6 +4481,26 @@ int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir, d_delete_notify(dir, dentry); return error; } + +/** + * 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 + * + * Remove a directory. + * + * If the inode has been found through an idmapped mount the idmap of + * the vfsmount must be passed through @idmap. This function will then take + * care to map the inode according to @idmap before checking permissions. + * On non-idmapped mounts or if permission checking is to be performed on the + * raw inode simply pass @nop_mnt_idmap. + */ +int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry) +{ + return __vfs_rmdir(idmap, dir, dentry, NULL); +} EXPORT_SYMBOL(vfs_rmdir); int do_rmdir(int dfd, struct filename *name) @@ -4501,6 +4511,7 @@ int do_rmdir(int dfd, struct filename *name) struct qstr last; int type; unsigned int lookup_flags = 0; + struct inode *delegated_inode = NULL; retry: error = filename_parentat(dfd, name, lookup_flags, &path, &last, &type); if (error) @@ -4530,7 +4541,8 @@ int do_rmdir(int dfd, struct filename *name) 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: @@ -4538,6 +4550,11 @@ int do_rmdir(int dfd, struct filename *name) mnt_drop_write(path.mnt); exit2: path_put(&path); + if (delegated_inode) { + error = break_deleg_wait(&delegated_inode); + if (!error) + goto retry; + } if (retry_estale(error, lookup_flags)) { lookup_flags |= LOOKUP_REVAL; goto retry; -- 2.49.0