To implement O_CREAT|O_DIRECTORY we will have to repeat some of the logic that is now in vfs_mkdir() (e.g. do error checks in the same order). Separate this out in vfs_mkdir_no_perm(), which does all the non-permission related work of vfs_mkdir(). Permission checking for the lookup_open() path is timed differently because we may just be doing an open and no create. Similar considerations give rise to vfs_create_no_perm(). Reviewed-by: NeilBrown Signed-off-by: Jori Koolstra --- fs/namei.c | 78 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 24 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index ab1302b38f46..229a5f7329f0 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -4166,6 +4166,24 @@ static inline umode_t vfs_prepare_mode(struct mnt_idmap *idmap, return mode; } +static inline +int vfs_create_no_perm(struct mnt_idmap *idmap, struct dentry *dentry, + umode_t mode, struct delegated_inode *di) +{ + struct inode *dir = d_inode(dentry->d_parent); + int error; + + error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di); + if (error) + return error; + + error = dir->i_op->create(idmap, dir, dentry, mode); + if (!error) + fsnotify_create(dir, dentry); + + return error; +} + /** * vfs_create - create new file * @idmap: idmap of the mount the inode was found from @@ -4198,13 +4216,8 @@ int vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode, error = security_inode_create(dir, dentry, mode); if (error) return error; - error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di); - if (error) - return error; - error = dir->i_op->create(idmap, dir, dentry, mode); - if (!error) - fsnotify_create(dir, dentry); - return error; + + return vfs_create_no_perm(idmap, dentry, mode, di); } EXPORT_SYMBOL(vfs_create); @@ -4418,6 +4431,7 @@ static struct dentry *atomic_open(const struct path *path, struct dentry *dentry dput(dentry); dentry = ERR_PTR(error); } + return dentry; } @@ -4544,6 +4558,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file, dentry = res; } } + if (dentry->d_inode || !(op->open_flag & O_CREAT)) { /* * No need to create a file. If lookup returned a positive @@ -5358,6 +5373,34 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d return filename_mknodat(AT_FDCWD, name, mode, dev); } +/* Returns the dentry to use (not NULL) or -E on error */ +static inline +struct dentry *vfs_mkdir_no_perm(struct mnt_idmap *idmap, struct inode *dir, + struct dentry *dentry, umode_t mode, + struct delegated_inode *di) +{ + int error; + struct dentry *de; + unsigned max_links = dir->i_sb->s_max_links; + + if (max_links && dir->i_nlink >= max_links) + return ERR_PTR(-EMLINK); + + error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di); + if (error) + return ERR_PTR(error); + + de = dir->i_op->mkdir(idmap, dir, dentry, mode); + if (IS_ERR(de)) + return de; + if (de) { + dput(dentry); + dentry = de; + } + fsnotify_mkdir(dir, dentry); + return dentry; +} + /** * vfs_mkdir - create directory returning correct dentry if possible * @idmap: idmap of the mount the inode was found from @@ -5385,7 +5428,6 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, struct delegated_inode *delegated_inode) { int error; - unsigned max_links = dir->i_sb->s_max_links; struct dentry *de; error = may_create_dentry(idmap, dir, dentry); @@ -5401,24 +5443,12 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir, if (error) goto err; - error = -EMLINK; - if (max_links && dir->i_nlink >= max_links) - goto err; - - error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode); - if (error) + de = vfs_mkdir_no_perm(idmap, dir, dentry, mode, delegated_inode); + if (IS_ERR(de)) { + error = PTR_ERR(de); goto err; - - de = dir->i_op->mkdir(idmap, dir, dentry, mode); - error = PTR_ERR(de); - if (IS_ERR(de)) - goto err; - if (de) { - dput(dentry); - dentry = de; } - fsnotify_mkdir(dir, dentry); - return dentry; + return de; err: end_creating(dentry); -- 2.55.0