Skip to content

Commit 204a575

Browse files
neilbrownbrauner
authored andcommitted
VFS: add common error checks to lookup_one_qstr_excl()
Callers of lookup_one_qstr_excl() often check if the result is negative or positive. These changes can easily be moved into lookup_one_qstr_excl() by checking the lookup flags: LOOKUP_CREATE means it is NOT an error if the name doesn't exist. LOOKUP_EXCL means it IS an error if the name DOES exist. This patch adds these checks, then removes error checks from callers, and ensures that appropriate flags are passed. This subtly changes the meaning of LOOKUP_EXCL. Previously it could only accompany LOOKUP_CREATE. Now it can accompany LOOKUP_RENAME_TARGET as well. A couple of small changes are needed to accommodate this. The NFS change is functionally a no-op but ensures nfs_is_exclusive_create() does exactly what the name says. Signed-off-by: NeilBrown <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Christian Brauner <[email protected]>
1 parent 1c3cb50 commit 204a575

File tree

4 files changed

+46
-57
lines changed

4 files changed

+46
-57
lines changed

Documentation/filesystems/porting.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,3 +1165,16 @@ magic.
11651165
kern_path_locked() and user_path_locked() no longer return a negative
11661166
dentry so this doesn't need to be checked. If the name cannot be found,
11671167
ERR_PTR(-ENOENT) is returned.
1168+
1169+
** recommend**
1170+
1171+
lookup_one_qstr_excl() is changed to return errors in more cases, so
1172+
these conditions don't require explicit checks:
1173+
1174+
- if LOOKUP_CREATE is NOT given, then the dentry won't be negative,
1175+
ERR_PTR(-ENOENT) is returned instead
1176+
- if LOOKUP_EXCL IS given, then the dentry won't be positive,
1177+
ERR_PTR(-EEXIST) is rreturned instread
1178+
1179+
LOOKUP_EXCL now means "target must not exist". It can be combined with
1180+
LOOK_CREATE or LOOKUP_RENAME_TARGET.

fs/namei.c

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,6 +1670,8 @@ static struct dentry *lookup_dcache(const struct qstr *name,
16701670
* dentries - as the matter of fact, this only gets called
16711671
* when directory is guaranteed to have no in-lookup children
16721672
* at all.
1673+
* Will return -ENOENT if name isn't found and LOOKUP_CREATE wasn't passed.
1674+
* Will return -EEXIST if name is found and LOOKUP_EXCL was passed.
16731675
*/
16741676
struct dentry *lookup_one_qstr_excl(const struct qstr *name,
16751677
struct dentry *base,
@@ -1680,7 +1682,7 @@ struct dentry *lookup_one_qstr_excl(const struct qstr *name,
16801682
struct inode *dir = base->d_inode;
16811683

16821684
if (dentry)
1683-
return dentry;
1685+
goto found;
16841686

16851687
/* Don't create child dentry for a dead directory. */
16861688
if (unlikely(IS_DEADDIR(dir)))
@@ -1695,6 +1697,17 @@ struct dentry *lookup_one_qstr_excl(const struct qstr *name,
16951697
dput(dentry);
16961698
dentry = old;
16971699
}
1700+
found:
1701+
if (IS_ERR(dentry))
1702+
return dentry;
1703+
if (d_is_negative(dentry) && !(flags & LOOKUP_CREATE)) {
1704+
dput(dentry);
1705+
return ERR_PTR(-ENOENT);
1706+
}
1707+
if (d_is_positive(dentry) && (flags & LOOKUP_EXCL)) {
1708+
dput(dentry);
1709+
return ERR_PTR(-EEXIST);
1710+
}
16981711
return dentry;
16991712
}
17001713
EXPORT_SYMBOL(lookup_one_qstr_excl);
@@ -2741,10 +2754,6 @@ static struct dentry *__kern_path_locked(int dfd, struct filename *name, struct
27412754
}
27422755
inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
27432756
d = lookup_one_qstr_excl(&last, path->dentry, 0);
2744-
if (!IS_ERR(d) && d_is_negative(d)) {
2745-
dput(d);
2746-
d = ERR_PTR(-ENOENT);
2747-
}
27482757
if (IS_ERR(d)) {
27492758
inode_unlock(path->dentry->d_inode);
27502759
path_put(path);
@@ -4082,27 +4091,13 @@ static struct dentry *filename_create(int dfd, struct filename *name,
40824091
* '/', and a directory wasn't requested.
40834092
*/
40844093
if (last.name[last.len] && !want_dir)
4085-
create_flags = 0;
4094+
create_flags &= ~LOOKUP_CREATE;
40864095
inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
40874096
dentry = lookup_one_qstr_excl(&last, path->dentry,
40884097
reval_flag | create_flags);
40894098
if (IS_ERR(dentry))
40904099
goto unlock;
40914100

4092-
error = -EEXIST;
4093-
if (d_is_positive(dentry))
4094-
goto fail;
4095-
4096-
/*
4097-
* Special case - lookup gave negative, but... we had foo/bar/
4098-
* From the vfs_mknod() POV we just have a negative dentry -
4099-
* all is fine. Let's be bastards - you had / on the end, you've
4100-
* been asking for (non-existent) directory. -ENOENT for you.
4101-
*/
4102-
if (unlikely(!create_flags)) {
4103-
error = -ENOENT;
4104-
goto fail;
4105-
}
41064101
if (unlikely(err2)) {
41074102
error = err2;
41084103
goto fail;
@@ -4449,10 +4444,6 @@ int do_rmdir(int dfd, struct filename *name)
44494444
error = PTR_ERR(dentry);
44504445
if (IS_ERR(dentry))
44514446
goto exit3;
4452-
if (!dentry->d_inode) {
4453-
error = -ENOENT;
4454-
goto exit4;
4455-
}
44564447
error = security_path_rmdir(&path, dentry);
44574448
if (error)
44584449
goto exit4;
@@ -4583,7 +4574,7 @@ int do_unlinkat(int dfd, struct filename *name)
45834574
if (!IS_ERR(dentry)) {
45844575

45854576
/* Why not before? Because we want correct error value */
4586-
if (last.name[last.len] || d_is_negative(dentry))
4577+
if (last.name[last.len])
45874578
goto slashes;
45884579
inode = dentry->d_inode;
45894580
ihold(inode);
@@ -4617,9 +4608,7 @@ int do_unlinkat(int dfd, struct filename *name)
46174608
return error;
46184609

46194610
slashes:
4620-
if (d_is_negative(dentry))
4621-
error = -ENOENT;
4622-
else if (d_is_dir(dentry))
4611+
if (d_is_dir(dentry))
46234612
error = -EISDIR;
46244613
else
46254614
error = -ENOTDIR;
@@ -5119,7 +5108,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
51195108
struct qstr old_last, new_last;
51205109
int old_type, new_type;
51215110
struct inode *delegated_inode = NULL;
5122-
unsigned int lookup_flags = 0, target_flags = LOOKUP_RENAME_TARGET;
5111+
unsigned int lookup_flags = 0, target_flags =
5112+
LOOKUP_RENAME_TARGET | LOOKUP_CREATE;
51235113
bool should_retry = false;
51245114
int error = -EINVAL;
51255115

@@ -5132,6 +5122,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
51325122

51335123
if (flags & RENAME_EXCHANGE)
51345124
target_flags = 0;
5125+
if (flags & RENAME_NOREPLACE)
5126+
target_flags |= LOOKUP_EXCL;
51355127

51365128
retry:
51375129
error = filename_parentat(olddfd, from, lookup_flags, &old_path,
@@ -5173,23 +5165,12 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
51735165
error = PTR_ERR(old_dentry);
51745166
if (IS_ERR(old_dentry))
51755167
goto exit3;
5176-
/* source must exist */
5177-
error = -ENOENT;
5178-
if (d_is_negative(old_dentry))
5179-
goto exit4;
51805168
new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry,
51815169
lookup_flags | target_flags);
51825170
error = PTR_ERR(new_dentry);
51835171
if (IS_ERR(new_dentry))
51845172
goto exit4;
5185-
error = -EEXIST;
5186-
if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry))
5187-
goto exit5;
51885173
if (flags & RENAME_EXCHANGE) {
5189-
error = -ENOENT;
5190-
if (d_is_negative(new_dentry))
5191-
goto exit5;
5192-
51935174
if (!d_is_dir(new_dentry)) {
51945175
error = -ENOTDIR;
51955176
if (new_last.name[new_last.len])

fs/nfs/dir.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1532,7 +1532,8 @@ static int nfs_is_exclusive_create(struct inode *dir, unsigned int flags)
15321532
{
15331533
if (NFS_PROTO(dir)->version == 2)
15341534
return 0;
1535-
return flags & LOOKUP_EXCL;
1535+
return (flags & (LOOKUP_CREATE | LOOKUP_EXCL)) ==
1536+
(LOOKUP_CREATE | LOOKUP_EXCL);
15361537
}
15371538

15381539
/*

fs/smb/server/vfs.c

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -113,11 +113,6 @@ static int ksmbd_vfs_path_lookup_locked(struct ksmbd_share_config *share_conf,
113113
if (IS_ERR(d))
114114
goto err_out;
115115

116-
if (d_is_negative(d)) {
117-
dput(d);
118-
goto err_out;
119-
}
120-
121116
path->dentry = d;
122117
path->mnt = mntget(parent_path->mnt);
123118

@@ -693,6 +688,7 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
693688
struct ksmbd_file *parent_fp;
694689
int new_type;
695690
int err, lookup_flags = LOOKUP_NO_SYMLINKS;
691+
int target_lookup_flags = LOOKUP_RENAME_TARGET;
696692

697693
if (ksmbd_override_fsids(work))
698694
return -ENOMEM;
@@ -703,6 +699,14 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
703699
goto revert_fsids;
704700
}
705701

702+
/*
703+
* explicitly handle file overwrite case, for compatibility with
704+
* filesystems that may not support rename flags (e.g: fuse)
705+
*/
706+
if (flags & RENAME_NOREPLACE)
707+
target_lookup_flags |= LOOKUP_EXCL;
708+
flags &= ~(RENAME_NOREPLACE);
709+
706710
retry:
707711
err = vfs_path_parent_lookup(to, lookup_flags | LOOKUP_BENEATH,
708712
&new_path, &new_last, &new_type,
@@ -743,7 +747,7 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
743747
}
744748

745749
new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry,
746-
lookup_flags | LOOKUP_RENAME_TARGET);
750+
lookup_flags | target_lookup_flags);
747751
if (IS_ERR(new_dentry)) {
748752
err = PTR_ERR(new_dentry);
749753
goto out3;
@@ -754,16 +758,6 @@ int ksmbd_vfs_rename(struct ksmbd_work *work, const struct path *old_path,
754758
goto out4;
755759
}
756760

757-
/*
758-
* explicitly handle file overwrite case, for compatibility with
759-
* filesystems that may not support rename flags (e.g: fuse)
760-
*/
761-
if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry)) {
762-
err = -EEXIST;
763-
goto out4;
764-
}
765-
flags &= ~(RENAME_NOREPLACE);
766-
767761
if (old_child == trap) {
768762
err = -EINVAL;
769763
goto out4;

0 commit comments

Comments
 (0)