new helper: lookup_positive_unlocked()
authorAl Viro <viro@zeniv.linux.org.uk>
Thu, 31 Oct 2019 05:21:58 +0000 (01:21 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Fri, 15 Nov 2019 18:49:04 +0000 (13:49 -0500)
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 <viro@zeniv.linux.org.uk>
fs/cifs/cifsfs.c
fs/debugfs/inode.c
fs/kernfs/mount.c
fs/namei.c
fs/nfsd/nfs3xdr.c
fs/nfsd/nfs4xdr.c
fs/overlayfs/namei.c
fs/quota/dquot.c
include/linux/namei.h

index c049c7b..8c8600e 100644 (file)
@@ -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);
@@ -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));
index 7b975db..f4d8df5 100644 (file)
@@ -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);
index 6c12fac..d62cec6 100644 (file)
@@ -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))
index ef55155..6f72fb7 100644 (file)
@@ -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)
 {
index 86e5658..195ab7a 100644 (file)
@@ -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);
index 533d0fc..b092374 100644 (file)
@@ -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);
        /*
index e9717c2..c269d60 100644 (file)
@@ -200,7 +200,7 @@ 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;
@@ -208,8 +208,6 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
                        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 */
@@ -651,7 +649,7 @@ 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)
@@ -659,9 +657,7 @@ struct dentry *ovl_get_index_fh(struct ovl_fs *ofs, struct ovl_fh *fh)
                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;
@@ -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) {
@@ -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
@@ -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)) {
@@ -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);
                }
        }
index 6e826b4..a37e1b1 100644 (file)
@@ -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,
index 397a08a..7fe7b87 100644 (file)
@@ -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 *);