Skip to content

Commit

Permalink
new helper: lookup_positive_unlocked()
Browse files Browse the repository at this point in the history
Most of the callers of lookup_one_len_unlocked() treat negatives are
ERR_PTR(-ENOENT).  Provide a helper that would do just that.  Note
that a pinned positive dentry remains positive - it's ->d_inode is
stable, etc.; a pinned _negative_ dentry can become positive at any
point as long as you are not holding its parent at least shared.
So using lookup_one_len_unlocked() needs to be careful;
lookup_positive_unlocked() is safer and that's what the callers
end up open-coding anyway.

Signed-off-by: Al Viro <[email protected]>
  • Loading branch information
Al Viro committed Nov 15, 2019
1 parent d41efb5 commit 6c2d479
Show file tree
Hide file tree
Showing 9 changed files with 35 additions and 47 deletions.
7 changes: 1 addition & 6 deletions fs/cifs/cifsfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -719,11 +719,6 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
struct inode *dir = d_inode(dentry);
struct dentry *child;

if (!dir) {
dput(dentry);
dentry = ERR_PTR(-ENOENT);
break;
}
if (!S_ISDIR(dir->i_mode)) {
dput(dentry);
dentry = ERR_PTR(-ENOTDIR);
Expand All @@ -740,7 +735,7 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
while (*s && *s != sep)
s++;

child = lookup_one_len_unlocked(p, dentry, s - p);
child = lookup_positive_unlocked(p, dentry, s - p);
dput(dentry);
dentry = child;
} while (!IS_ERR(dentry));
Expand Down
6 changes: 1 addition & 5 deletions fs/debugfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -299,13 +299,9 @@ struct dentry *debugfs_lookup(const char *name, struct dentry *parent)
if (!parent)
parent = debugfs_mount->mnt_root;

dentry = lookup_one_len_unlocked(name, parent, strlen(name));
dentry = lookup_positive_unlocked(name, parent, strlen(name));
if (IS_ERR(dentry))
return NULL;
if (!d_really_is_positive(dentry)) {
dput(dentry);
return NULL;
}
return dentry;
}
EXPORT_SYMBOL_GPL(debugfs_lookup);
Expand Down
2 changes: 1 addition & 1 deletion fs/kernfs/mount.c
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ struct dentry *kernfs_node_dentry(struct kernfs_node *kn,
dput(dentry);
return ERR_PTR(-EINVAL);
}
dtmp = lookup_one_len_unlocked(kntmp->name, dentry,
dtmp = lookup_positive_unlocked(kntmp->name, dentry,
strlen(kntmp->name));
dput(dentry);
if (IS_ERR(dtmp))
Expand Down
20 changes: 20 additions & 0 deletions fs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -2557,6 +2557,26 @@ struct dentry *lookup_one_len_unlocked(const char *name,
}
EXPORT_SYMBOL(lookup_one_len_unlocked);

/*
* Like lookup_one_len_unlocked(), except that it yields ERR_PTR(-ENOENT)
* on negatives. Returns known positive or ERR_PTR(); that's what
* most of the users want. Note that pinned negative with unlocked parent
* _can_ become positive at any time, so callers of lookup_one_len_unlocked()
* need to be very careful; pinned positives have ->d_inode stable, so
* this one avoids such problems.
*/
struct dentry *lookup_positive_unlocked(const char *name,
struct dentry *base, int len)
{
struct dentry *ret = lookup_one_len_unlocked(name, base, len);
if (!IS_ERR(ret) && d_is_negative(ret)) {
dput(ret);
ret = ERR_PTR(-ENOENT);
}
return ret;
}
EXPORT_SYMBOL(lookup_positive_unlocked);

#ifdef CONFIG_UNIX98_PTYS
int path_pts(struct path *path)
{
Expand Down
4 changes: 1 addition & 3 deletions fs/nfsd/nfs3xdr.c
Original file line number Diff line number Diff line change
Expand Up @@ -863,13 +863,11 @@ compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp,
} else
dchild = dget(dparent);
} else
dchild = lookup_one_len_unlocked(name, dparent, namlen);
dchild = lookup_positive_unlocked(name, dparent, namlen);
if (IS_ERR(dchild))
return rv;
if (d_mountpoint(dchild))
goto out;
if (d_really_is_negative(dchild))
goto out;
if (dchild->d_inode->i_ino != ino)
goto out;
rv = fh_compose(fhp, exp, dchild, &cd->fh);
Expand Down
11 changes: 1 addition & 10 deletions fs/nfsd/nfs4xdr.c
Original file line number Diff line number Diff line change
Expand Up @@ -2991,18 +2991,9 @@ nfsd4_encode_dirent_fattr(struct xdr_stream *xdr, struct nfsd4_readdir *cd,
__be32 nfserr;
int ignore_crossmnt = 0;

dentry = lookup_one_len_unlocked(name, cd->rd_fhp->fh_dentry, namlen);
dentry = lookup_positive_unlocked(name, cd->rd_fhp->fh_dentry, namlen);
if (IS_ERR(dentry))
return nfserrno(PTR_ERR(dentry));
if (d_really_is_negative(dentry)) {
/*
* we're not holding the i_mutex here, so there's
* a window where this directory entry could have gone
* away.
*/
dput(dentry);
return nfserr_noent;
}

exp_get(exp);
/*
Expand Down
24 changes: 8 additions & 16 deletions fs/overlayfs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -200,16 +200,14 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
int err;
bool last_element = !post[0];

this = lookup_one_len_unlocked(name, base, namelen);
this = lookup_positive_unlocked(name, base, namelen);
if (IS_ERR(this)) {
err = PTR_ERR(this);
this = NULL;
if (err == -ENOENT || err == -ENAMETOOLONG)
goto out;
goto out_err;
}
if (!this->d_inode)
goto put_and_out;

if (ovl_dentry_weird(this)) {
/* Don't support traversing automounts and other weirdness */
Expand Down Expand Up @@ -651,17 +649,15 @@ struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh)
if (err)
return ERR_PTR(err);

index = lookup_one_len_unlocked(name.name, ofs->indexdir, name.len);
index = lookup_positive_unlocked(name.name, ofs->indexdir, name.len);
kfree(name.name);
if (IS_ERR(index)) {
if (PTR_ERR(index) == -ENOENT)
index = NULL;
return index;
}

if (d_is_negative(index))
err = 0;
else if (ovl_is_whiteout(index))
if (ovl_is_whiteout(index))
err = -ESTALE;
else if (ovl_dentry_weird(index))
err = -EIO;
Expand All @@ -685,7 +681,7 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
if (err)
return ERR_PTR(err);

index = lookup_one_len_unlocked(name.name, ofs->indexdir, name.len);
index = lookup_positive_unlocked(name.name, ofs->indexdir, name.len);
if (IS_ERR(index)) {
err = PTR_ERR(index);
if (err == -ENOENT) {
Expand All @@ -700,9 +696,7 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
}

inode = d_inode(index);
if (d_is_negative(index)) {
goto out_dput;
} else if (ovl_is_whiteout(index) && !verify) {
if (ovl_is_whiteout(index) && !verify) {
/*
* When index lookup is called with !verify for decoding an
* overlay file handle, a whiteout index implies that decode
Expand Down Expand Up @@ -1131,7 +1125,7 @@ bool ovl_lower_positive(struct dentry *dentry)
struct dentry *this;
struct dentry *lowerdir = poe->lowerstack[i].dentry;

this = lookup_one_len_unlocked(name->name, lowerdir,
this = lookup_positive_unlocked(name->name, lowerdir,
name->len);
if (IS_ERR(this)) {
switch (PTR_ERR(this)) {
Expand All @@ -1148,10 +1142,8 @@ bool ovl_lower_positive(struct dentry *dentry)
break;
}
} else {
if (this->d_inode) {
positive = !ovl_is_whiteout(this);
done = true;
}
positive = !ovl_is_whiteout(this);
done = true;
dput(this);
}
}
Expand Down
7 changes: 1 addition & 6 deletions fs/quota/dquot.c
Original file line number Diff line number Diff line change
Expand Up @@ -2507,15 +2507,10 @@ int dquot_quota_on_mount(struct super_block *sb, char *qf_name,
struct dentry *dentry;
int error;

dentry = lookup_one_len_unlocked(qf_name, sb->s_root, strlen(qf_name));
dentry = lookup_positive_unlocked(qf_name, sb->s_root, strlen(qf_name));
if (IS_ERR(dentry))
return PTR_ERR(dentry);

if (d_really_is_negative(dentry)) {
error = -ENOENT;
goto out;
}

error = security_quota_on(dentry);
if (!error)
error = vfs_load_quota_inode(d_inode(dentry), type, format_id,
Expand Down
1 change: 1 addition & 0 deletions include/linux/namei.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int);
extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int);
extern struct dentry *lookup_positive_unlocked(const char *, struct dentry *, int);

extern int follow_down_one(struct path *);
extern int follow_down(struct path *);
Expand Down

0 comments on commit 6c2d479

Please sign in to comment.