Skip to content

Commit 3789a0a

Browse files
committed
Merge patch series "VFS: change kern_path_locked() and user_path_locked_at() to never return negative dentry"
NeilBrown <[email protected]> says: I found these opportunities for simplification as part of my work to enhance filesystem directory operations to not require an exclusive lock on the directory. There are quite a collection of users of these interfaces incluing NFS, smb/server, bcachefs, devtmpfs, and audit. Hence the long Cc line. * patches from https://lore.kernel.org/r/[email protected]: VFS: add common error checks to lookup_one_qstr_excl() VFS: change kern_path_locked() and user_path_locked_at() to never return negative dentry Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
2 parents 2c3230f + 204a575 commit 3789a0a

File tree

7 files changed

+90
-98
lines changed

7 files changed

+90
-98
lines changed

Documentation/filesystems/porting.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,3 +1157,24 @@ in normal case it points into the pathname being looked up.
11571157
NOTE: if you need something like full path from the root of filesystem,
11581158
you are still on your own - this assists with simple cases, but it's not
11591159
magic.
1160+
1161+
---
1162+
1163+
** recommended**
1164+
1165+
kern_path_locked() and user_path_locked() no longer return a negative
1166+
dentry so this doesn't need to be checked. If the name cannot be found,
1167+
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.

drivers/base/devtmpfs.c

Lines changed: 30 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -245,15 +245,12 @@ static int dev_rmdir(const char *name)
245245
dentry = kern_path_locked(name, &parent);
246246
if (IS_ERR(dentry))
247247
return PTR_ERR(dentry);
248-
if (d_really_is_positive(dentry)) {
249-
if (d_inode(dentry)->i_private == &thread)
250-
err = vfs_rmdir(&nop_mnt_idmap, d_inode(parent.dentry),
251-
dentry);
252-
else
253-
err = -EPERM;
254-
} else {
255-
err = -ENOENT;
256-
}
248+
if (d_inode(dentry)->i_private == &thread)
249+
err = vfs_rmdir(&nop_mnt_idmap, d_inode(parent.dentry),
250+
dentry);
251+
else
252+
err = -EPERM;
253+
257254
dput(dentry);
258255
inode_unlock(d_inode(parent.dentry));
259256
path_put(&parent);
@@ -310,39 +307,37 @@ static int handle_remove(const char *nodename, struct device *dev)
310307
{
311308
struct path parent;
312309
struct dentry *dentry;
310+
struct kstat stat;
311+
struct path p;
313312
int deleted = 0;
314313
int err;
315314

316315
dentry = kern_path_locked(nodename, &parent);
317316
if (IS_ERR(dentry))
318317
return PTR_ERR(dentry);
319318

320-
if (d_really_is_positive(dentry)) {
321-
struct kstat stat;
322-
struct path p = {.mnt = parent.mnt, .dentry = dentry};
323-
err = vfs_getattr(&p, &stat, STATX_TYPE | STATX_MODE,
324-
AT_STATX_SYNC_AS_STAT);
325-
if (!err && dev_mynode(dev, d_inode(dentry), &stat)) {
326-
struct iattr newattrs;
327-
/*
328-
* before unlinking this node, reset permissions
329-
* of possible references like hardlinks
330-
*/
331-
newattrs.ia_uid = GLOBAL_ROOT_UID;
332-
newattrs.ia_gid = GLOBAL_ROOT_GID;
333-
newattrs.ia_mode = stat.mode & ~0777;
334-
newattrs.ia_valid =
335-
ATTR_UID|ATTR_GID|ATTR_MODE;
336-
inode_lock(d_inode(dentry));
337-
notify_change(&nop_mnt_idmap, dentry, &newattrs, NULL);
338-
inode_unlock(d_inode(dentry));
339-
err = vfs_unlink(&nop_mnt_idmap, d_inode(parent.dentry),
340-
dentry, NULL);
341-
if (!err || err == -ENOENT)
342-
deleted = 1;
343-
}
344-
} else {
345-
err = -ENOENT;
319+
p.mnt = parent.mnt;
320+
p.dentry = dentry;
321+
err = vfs_getattr(&p, &stat, STATX_TYPE | STATX_MODE,
322+
AT_STATX_SYNC_AS_STAT);
323+
if (!err && dev_mynode(dev, d_inode(dentry), &stat)) {
324+
struct iattr newattrs;
325+
/*
326+
* before unlinking this node, reset permissions
327+
* of possible references like hardlinks
328+
*/
329+
newattrs.ia_uid = GLOBAL_ROOT_UID;
330+
newattrs.ia_gid = GLOBAL_ROOT_GID;
331+
newattrs.ia_mode = stat.mode & ~0777;
332+
newattrs.ia_valid =
333+
ATTR_UID|ATTR_GID|ATTR_MODE;
334+
inode_lock(d_inode(dentry));
335+
notify_change(&nop_mnt_idmap, dentry, &newattrs, NULL);
336+
inode_unlock(d_inode(dentry));
337+
err = vfs_unlink(&nop_mnt_idmap, d_inode(parent.dentry),
338+
dentry, NULL);
339+
if (!err || err == -ENOENT)
340+
deleted = 1;
346341
}
347342
dput(dentry);
348343
inode_unlock(d_inode(parent.dentry));

fs/bcachefs/fs-ioctl.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -511,10 +511,6 @@ static long bch2_ioctl_subvolume_destroy(struct bch_fs *c, struct file *filp,
511511
ret = -EXDEV;
512512
goto err;
513513
}
514-
if (!d_is_positive(victim)) {
515-
ret = -ENOENT;
516-
goto err;
517-
}
518514
ret = __bch2_unlink(dir, victim, true);
519515
if (!ret) {
520516
fsnotify_rmdir(dir, victim);

fs/namei.c

Lines changed: 21 additions & 36 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);
@@ -4078,27 +4091,13 @@ static struct dentry *filename_create(int dfd, struct filename *name,
40784091
* '/', and a directory wasn't requested.
40794092
*/
40804093
if (last.name[last.len] && !want_dir)
4081-
create_flags = 0;
4094+
create_flags &= ~LOOKUP_CREATE;
40824095
inode_lock_nested(path->dentry->d_inode, I_MUTEX_PARENT);
40834096
dentry = lookup_one_qstr_excl(&last, path->dentry,
40844097
reval_flag | create_flags);
40854098
if (IS_ERR(dentry))
40864099
goto unlock;
40874100

4088-
error = -EEXIST;
4089-
if (d_is_positive(dentry))
4090-
goto fail;
4091-
4092-
/*
4093-
* Special case - lookup gave negative, but... we had foo/bar/
4094-
* From the vfs_mknod() POV we just have a negative dentry -
4095-
* all is fine. Let's be bastards - you had / on the end, you've
4096-
* been asking for (non-existent) directory. -ENOENT for you.
4097-
*/
4098-
if (unlikely(!create_flags)) {
4099-
error = -ENOENT;
4100-
goto fail;
4101-
}
41024101
if (unlikely(err2)) {
41034102
error = err2;
41044103
goto fail;
@@ -4445,10 +4444,6 @@ int do_rmdir(int dfd, struct filename *name)
44454444
error = PTR_ERR(dentry);
44464445
if (IS_ERR(dentry))
44474446
goto exit3;
4448-
if (!dentry->d_inode) {
4449-
error = -ENOENT;
4450-
goto exit4;
4451-
}
44524447
error = security_path_rmdir(&path, dentry);
44534448
if (error)
44544449
goto exit4;
@@ -4579,7 +4574,7 @@ int do_unlinkat(int dfd, struct filename *name)
45794574
if (!IS_ERR(dentry)) {
45804575

45814576
/* Why not before? Because we want correct error value */
4582-
if (last.name[last.len] || d_is_negative(dentry))
4577+
if (last.name[last.len])
45834578
goto slashes;
45844579
inode = dentry->d_inode;
45854580
ihold(inode);
@@ -4613,9 +4608,7 @@ int do_unlinkat(int dfd, struct filename *name)
46134608
return error;
46144609

46154610
slashes:
4616-
if (d_is_negative(dentry))
4617-
error = -ENOENT;
4618-
else if (d_is_dir(dentry))
4611+
if (d_is_dir(dentry))
46194612
error = -EISDIR;
46204613
else
46214614
error = -ENOTDIR;
@@ -5115,7 +5108,8 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
51155108
struct qstr old_last, new_last;
51165109
int old_type, new_type;
51175110
struct inode *delegated_inode = NULL;
5118-
unsigned int lookup_flags = 0, target_flags = LOOKUP_RENAME_TARGET;
5111+
unsigned int lookup_flags = 0, target_flags =
5112+
LOOKUP_RENAME_TARGET | LOOKUP_CREATE;
51195113
bool should_retry = false;
51205114
int error = -EINVAL;
51215115

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

51295123
if (flags & RENAME_EXCHANGE)
51305124
target_flags = 0;
5125+
if (flags & RENAME_NOREPLACE)
5126+
target_flags |= LOOKUP_EXCL;
51315127

51325128
retry:
51335129
error = filename_parentat(olddfd, from, lookup_flags, &old_path,
@@ -5169,23 +5165,12 @@ int do_renameat2(int olddfd, struct filename *from, int newdfd,
51695165
error = PTR_ERR(old_dentry);
51705166
if (IS_ERR(old_dentry))
51715167
goto exit3;
5172-
/* source must exist */
5173-
error = -ENOENT;
5174-
if (d_is_negative(old_dentry))
5175-
goto exit4;
51765168
new_dentry = lookup_one_qstr_excl(&new_last, new_path.dentry,
51775169
lookup_flags | target_flags);
51785170
error = PTR_ERR(new_dentry);
51795171
if (IS_ERR(new_dentry))
51805172
goto exit4;
5181-
error = -EEXIST;
5182-
if ((flags & RENAME_NOREPLACE) && d_is_positive(new_dentry))
5183-
goto exit5;
51845173
if (flags & RENAME_EXCHANGE) {
5185-
error = -ENOENT;
5186-
if (d_is_negative(new_dentry))
5187-
goto exit5;
5188-
51895174
if (!d_is_dir(new_dentry)) {
51905175
error = -ENOTDIR;
51915176
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;

kernel/audit_watch.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -350,11 +350,10 @@ static int audit_get_nd(struct audit_watch *watch, struct path *parent)
350350
struct dentry *d = kern_path_locked(watch->path, parent);
351351
if (IS_ERR(d))
352352
return PTR_ERR(d);
353-
if (d_is_positive(d)) {
354-
/* update watch filter fields */
355-
watch->dev = d->d_sb->s_dev;
356-
watch->ino = d_backing_inode(d)->i_ino;
357-
}
353+
/* update watch filter fields */
354+
watch->dev = d->d_sb->s_dev;
355+
watch->ino = d_backing_inode(d)->i_ino;
356+
358357
inode_unlock(d_backing_inode(parent->dentry));
359358
dput(d);
360359
return 0;
@@ -419,10 +418,11 @@ int audit_add_watch(struct audit_krule *krule, struct list_head **list)
419418
/* caller expects mutex locked */
420419
mutex_lock(&audit_filter_mutex);
421420

422-
if (ret) {
421+
if (ret && ret != -ENOENT) {
423422
audit_put_watch(watch);
424423
return ret;
425424
}
425+
ret = 0;
426426

427427
/* either find an old parent or attach a new one */
428428
parent = audit_find_parent(d_backing_inode(parent_path.dentry));

0 commit comments

Comments
 (0)