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 parameter to vfs_create. Most callers will set that to NULL, but do_mknodat can use that to synchronously wait for the delegation break to complete. Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx> --- fs/ecryptfs/inode.c | 2 +- fs/namei.c | 15 +++++++++++++-- fs/nfsd/nfs3proc.c | 2 +- fs/nfsd/vfs.c | 2 +- fs/open.c | 2 +- fs/overlayfs/overlayfs.h | 2 +- fs/smb/server/vfs.c | 2 +- include/linux/fs.h | 2 +- 8 files changed, 20 insertions(+), 9 deletions(-) diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index 3d0cddbf037c..a99b1e264c46 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -189,7 +189,7 @@ ecryptfs_do_create(struct inode *directory_inode, rc = lock_parent(ecryptfs_dentry, &lower_dentry, &lower_dir); if (!rc) rc = vfs_create(&nop_mnt_idmap, lower_dir, - lower_dentry, mode, true); + lower_dentry, mode, true, NULL); if (rc) { printk(KERN_ERR "%s: Failure to create dentry in lower fs; " "rc = [%d]\n", __func__, rc); diff --git a/fs/namei.c b/fs/namei.c index 88598a62ec64..01e04cf155eb 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3174,6 +3174,7 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap, * @dentry: pointer to dentry of the base directory * @mode: mode of the new file * @want_excl: whether the file must not yet exist + * @delegated_inode: return pointer for delegated_inode * * Create a new file. * @@ -3184,7 +3185,8 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap, * raw inode simply pass @nop_mnt_idmap. */ int vfs_create(struct mnt_idmap *idmap, struct inode *dir, - struct dentry *dentry, umode_t mode, bool want_excl) + struct dentry *dentry, umode_t mode, bool want_excl, + struct inode **delegated_inode) { int error; @@ -3197,6 +3199,9 @@ int vfs_create(struct mnt_idmap *idmap, struct inode *dir, mode = vfs_prepare_mode(idmap, dir, mode, S_IALLUGO, S_IFREG); error = security_inode_create(dir, dentry, mode); + if (error) + return error; + error = try_break_deleg(dir, delegated_inode); if (error) return error; error = dir->i_op->create(idmap, dir, dentry, mode, want_excl); @@ -4047,6 +4052,7 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode, struct path path; int error; unsigned int lookup_flags = 0; + struct inode *delegated_inode = NULL; error = may_mknod(mode); if (error) @@ -4066,7 +4072,7 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode, switch (mode & S_IFMT) { case 0: case S_IFREG: error = vfs_create(idmap, path.dentry->d_inode, - dentry, mode, true); + dentry, mode, true, &delegated_inode); if (!error) ima_post_path_mknod(idmap, dentry); break; @@ -4081,6 +4087,11 @@ static int do_mknodat(int dfd, struct filename *name, umode_t mode, } out2: done_path_create(&path, dentry); + 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; diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index dfcc957e460d..e920a6291f2d 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c @@ -313,7 +313,7 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp, status = fh_fill_pre_attrs(fhp); if (status != nfs_ok) goto out; - host_err = vfs_create(&nop_mnt_idmap, inode, child, iap->ia_mode, true); + host_err = vfs_create(&nop_mnt_idmap, inode, child, iap->ia_mode, true, NULL); if (host_err < 0) { status = nfserrno(host_err); goto out; diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 34cc2d1a4944..47b8ab1d4b17 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -1491,7 +1491,7 @@ nfsd_create_locked(struct svc_rqst *rqstp, struct svc_fh *fhp, switch (type) { case S_IFREG: host_err = vfs_create(&nop_mnt_idmap, dirp, dchild, - iap->ia_mode, true); + iap->ia_mode, true, NULL); if (!host_err) nfsd_check_ignore_resizing(iap); break; diff --git a/fs/open.c b/fs/open.c index 0a73afe04d34..0b50ea7e8aec 100644 --- a/fs/open.c +++ b/fs/open.c @@ -1137,7 +1137,7 @@ struct file *dentry_create(const struct path *path, int flags, umode_t mode, error = vfs_create(mnt_idmap(path->mnt), d_inode(path->dentry->d_parent), - path->dentry, mode, true); + path->dentry, mode, true, NULL); if (!error) error = vfs_open(path, f); diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 5b1f56294c4d..be2518e6da95 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -232,7 +232,7 @@ static inline int ovl_do_create(struct ovl_fs *ofs, struct inode *dir, struct dentry *dentry, umode_t mode) { - int err = vfs_create(ovl_upper_mnt_idmap(ofs), dir, dentry, mode, true); + int err = vfs_create(ovl_upper_mnt_idmap(ofs), dir, dentry, mode, true, NULL); pr_debug("create(%pd2, 0%o) = %i\n", dentry, mode, err); return err; diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index 5b4e5876c2ac..b313eb5a1d28 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -187,7 +187,7 @@ int ksmbd_vfs_create(struct ksmbd_work *work, const char *name, umode_t mode) mode |= S_IFREG; err = vfs_create(mnt_idmap(path.mnt), d_inode(path.dentry), - dentry, mode, true); + dentry, mode, true, NULL); if (!err) { ksmbd_vfs_inherit_owner(work, d_inode(path.dentry), d_inode(dentry)); diff --git a/include/linux/fs.h b/include/linux/fs.h index e72c825476de..8fb4101fea49 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1837,7 +1837,7 @@ bool inode_owner_or_capable(struct mnt_idmap *idmap, * VFS helper functions.. */ int vfs_create(struct mnt_idmap *, struct inode *, - struct dentry *, umode_t, bool); + struct dentry *, umode_t, bool, struct inode **); int vfs_mkdir(struct mnt_idmap *, struct inode *, struct dentry *, umode_t, struct inode **); int vfs_mknod(struct mnt_idmap *, struct inode *, struct dentry *, -- 2.44.0