Use try_lookup_noperm() instead of d_hash_and_lookup() outside of VFS
authorNeilBrown <neilb@suse.de>
Wed, 19 Mar 2025 03:01:36 +0000 (14:01 +1100)
committerChristian Brauner <brauner@kernel.org>
Tue, 8 Apr 2025 09:24:41 +0000 (11:24 +0200)
try_lookup_noperm() and d_hash_and_lookup() are nearly identical.  The
former does some validation of the name where the latter doesn't.
Outside of the VFS that validation is likely valuable, and having only
one exported function for this task is certainly a good idea.

So make d_hash_and_lookup() local to VFS files and change all other
callers to try_lookup_noperm().  Note that the arguments are swapped.

Signed-off-by: NeilBrown <neilb@suse.de>
Link: https://lore.kernel.org/r/20250319031545.2999807-6-neil@brown.name
Signed-off-by: Christian Brauner <brauner@kernel.org>
Documentation/filesystems/porting.rst
fs/dcache.c
fs/efivarfs/super.c
fs/internal.h
fs/proc/base.c
fs/smb/client/readdir.c
fs/xfs/scrub/orphanage.c
include/linux/dcache.h
net/sunrpc/rpc_pipe.c
security/selinux/selinuxfs.c

index 9150de7f64f142b1067abc3eda2f8c4cb1fc76ab..3111ef5592f31e92970b4dded2f6f98b21c8cebc 100644 (file)
@@ -1232,3 +1232,14 @@ checked that the caller has 'X' permission on the parent.  They must
 ONLY be used internally by a filesystem on itself when it knows that
 permissions are irrelevant or in a context where permission checks have
 already been performed such as after vfs_path_parent_lookup()
+
+---
+
+** mandatory**
+
+d_hash_and_lookup() is no longer exported or available outside the VFS.
+Use try_lookup_noperm() instead.  This adds name validation and takes
+arguments in the opposite order but is otherwise identical.
+
+Using try_lookup_noperm() will require linux/namei.h to be included.
+
index bd5aa136153acca8e3ad8c9199ce3803a8ae6ec6..89f4acab08c0c24c47a12dc9df1047d001ab12bf 100644 (file)
@@ -2412,7 +2412,6 @@ struct dentry *d_hash_and_lookup(struct dentry *dir, struct qstr *name)
        }
        return d_lookup(dir, name);
 }
-EXPORT_SYMBOL(d_hash_and_lookup);
 
 /*
  * When a file is deleted, we have two options:
index 0486e9b68bc6e277933a19a17c95e6dc0e93115e..b2de4079864c93577146e2ff5850df6ba81cf274 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/statfs.h>
 #include <linux/notifier.h>
 #include <linux/printk.h>
+#include <linux/namei.h>
 
 #include "internal.h"
 
@@ -204,7 +205,6 @@ bool efivarfs_variable_is_present(efi_char16_t *variable_name,
        char *name = efivar_get_utf8name(variable_name, vendor);
        struct super_block *sb = data;
        struct dentry *dentry;
-       struct qstr qstr;
 
        if (!name)
                /*
@@ -217,9 +217,7 @@ bool efivarfs_variable_is_present(efi_char16_t *variable_name,
                 */
                return true;
 
-       qstr.name = name;
-       qstr.len = strlen(name);
-       dentry = d_hash_and_lookup(sb->s_root, &qstr);
+       dentry = try_lookup_noperm(&QSTR(name), sb->s_root);
        kfree(name);
        if (!IS_ERR_OR_NULL(dentry))
                dput(dentry);
@@ -404,8 +402,8 @@ static bool efivarfs_actor(struct dir_context *ctx, const char *name, int len,
 {
        unsigned long size;
        struct efivarfs_ctx *ectx = container_of(ctx, struct efivarfs_ctx, ctx);
-       struct qstr qstr = { .name = name, .len = len };
-       struct dentry *dentry = d_hash_and_lookup(ectx->sb->s_root, &qstr);
+       struct dentry *dentry = try_lookup_noperm(&QSTR_LEN(name, len),
+                                                 ectx->sb->s_root);
        struct inode *inode;
        struct efivar_entry *entry;
        int err;
@@ -441,7 +439,6 @@ static int efivarfs_check_missing(efi_char16_t *name16, efi_guid_t vendor,
        char *name;
        struct super_block *sb = data;
        struct dentry *dentry;
-       struct qstr qstr;
        int err;
 
        if (guid_equal(&vendor, &LINUX_EFI_RANDOM_SEED_TABLE_GUID))
@@ -451,9 +448,7 @@ static int efivarfs_check_missing(efi_char16_t *name16, efi_guid_t vendor,
        if (!name)
                return -ENOMEM;
 
-       qstr.name = name;
-       qstr.len = strlen(name);
-       dentry = d_hash_and_lookup(sb->s_root, &qstr);
+       dentry = try_lookup_noperm(&QSTR(name), sb->s_root);
        if (IS_ERR(dentry)) {
                err = PTR_ERR(dentry);
                goto out;
index b9b3e29a73fd8ca5d965f4b267910041dde9a5ec..213bf3226213c1a86c5e9d91d64b52c6d85eb508 100644 (file)
@@ -66,6 +66,7 @@ int do_linkat(int olddfd, struct filename *old, int newdfd,
 int vfs_tmpfile(struct mnt_idmap *idmap,
                const struct path *parentpath,
                struct file *file, umode_t mode);
+struct dentry *d_hash_and_lookup(struct dentry *, struct qstr *);
 
 /*
  * namespace.c
index b0d4e1908b22fc4147e86a3abea0905610ee833b..fe33a5843fbd934567efa2de3c10d9736839aa6b 100644 (file)
@@ -2121,7 +2121,7 @@ bool proc_fill_cache(struct file *file, struct dir_context *ctx,
        unsigned type = DT_UNKNOWN;
        ino_t ino = 1;
 
-       child = d_hash_and_lookup(dir, &qname);
+       child = try_lookup_noperm(&qname, dir);
        if (!child) {
                DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
                child = d_alloc_parallel(dir, &qname, &wq);
index 50f96259d9adc2e9da723ea262adb6a16906b34d..7329ec532bcf6b1c71b0b362cb1f13287627b54b 100644 (file)
@@ -9,6 +9,7 @@
  *
  */
 #include <linux/fs.h>
+#include <linux/namei.h>
 #include <linux/pagemap.h>
 #include <linux/slab.h>
 #include <linux/stat.h>
@@ -78,7 +79,7 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
 
        cifs_dbg(FYI, "%s: for %s\n", __func__, name->name);
 
-       dentry = d_hash_and_lookup(parent, name);
+       dentry = try_lookup_noperm(name, parent);
        if (!dentry) {
                /*
                 * If we know that the inode will need to be revalidated
index 475bb899b2c6a7cfb428bf1ef5d74e228d6a3277..9c12cb8442311ca26b169e4d1567939ae44a5be0 100644 (file)
@@ -444,7 +444,7 @@ xrep_adoption_check_dcache(
        if (!d_orphanage)
                return 0;
 
-       d_child = d_hash_and_lookup(d_orphanage, &qname);
+       d_child = try_lookup_noperm(&qname, d_orphanage);
        if (d_child) {
                trace_xrep_adoption_check_child(sc->mp, d_child);
 
@@ -481,7 +481,7 @@ xrep_adoption_zap_dcache(
        if (!d_orphanage)
                return;
 
-       d_child = d_hash_and_lookup(d_orphanage, &qname);
+       d_child = try_lookup_noperm(&qname, d_orphanage);
        while (d_child != NULL) {
                trace_xrep_adoption_invalidate_child(sc->mp, d_child);
 
index e974e63bcdbcba928866323fbd8303fa0a84f127..a324f82df5625852ea7363a4f239aab223866c50 100644 (file)
@@ -288,7 +288,6 @@ extern void d_exchange(struct dentry *, struct dentry *);
 extern struct dentry *d_ancestor(struct dentry *, struct dentry *);
 
 extern struct dentry *d_lookup(const struct dentry *, const struct qstr *);
-extern struct dentry *d_hash_and_lookup(struct dentry *, struct qstr *);
 
 static inline unsigned d_count(const struct dentry *dentry)
 {
index eadc00410ebc51c51774edab7dec8d64c08e4d79..98f78cd559055b232d33981ccc54c9326df90db3 100644 (file)
@@ -631,7 +631,7 @@ static struct dentry *__rpc_lookup_create_exclusive(struct dentry *parent,
                                          const char *name)
 {
        struct qstr q = QSTR(name);
-       struct dentry *dentry = d_hash_and_lookup(parent, &q);
+       struct dentry *dentry = try_lookup_noperm(&q, parent);
        if (!dentry) {
                dentry = d_alloc(parent, &q);
                if (!dentry)
@@ -658,7 +658,7 @@ static void __rpc_depopulate(struct dentry *parent,
        for (i = start; i < eof; i++) {
                name.name = files[i].name;
                name.len = strlen(files[i].name);
-               dentry = d_hash_and_lookup(parent, &name);
+               dentry = try_lookup_noperm(&name, parent);
 
                if (dentry == NULL)
                        continue;
@@ -1190,7 +1190,7 @@ static const struct rpc_filelist files[] = {
 struct dentry *rpc_d_lookup_sb(const struct super_block *sb,
                               const unsigned char *dir_name)
 {
-       return d_hash_and_lookup(sb->s_root, &QSTR(dir_name));
+       return try_lookup_noperm(&QSTR(dir_name), sb->s_root);
 }
 EXPORT_SYMBOL_GPL(rpc_d_lookup_sb);
 
@@ -1301,7 +1301,7 @@ rpc_gssd_dummy_populate(struct dentry *root, struct rpc_pipe *pipe_data)
        struct dentry *pipe_dentry = NULL;
 
        /* We should never get this far if "gssd" doesn't exist */
-       gssd_dentry = d_hash_and_lookup(root, &QSTR(files[RPCAUTH_gssd].name));
+       gssd_dentry = try_lookup_noperm(&QSTR(files[RPCAUTH_gssd].name), root);
        if (!gssd_dentry)
                return ERR_PTR(-ENOENT);
 
@@ -1311,8 +1311,8 @@ rpc_gssd_dummy_populate(struct dentry *root, struct rpc_pipe *pipe_data)
                goto out;
        }
 
-       clnt_dentry = d_hash_and_lookup(gssd_dentry,
-                                       &QSTR(gssd_dummy_clnt_dir[0].name));
+       clnt_dentry = try_lookup_noperm(&QSTR(gssd_dummy_clnt_dir[0].name),
+                                         gssd_dentry);
        if (!clnt_dentry) {
                __rpc_depopulate(gssd_dentry, gssd_dummy_clnt_dir, 0, 1);
                pipe_dentry = ERR_PTR(-ENOENT);
index 47480eb2189b393588a4a9fcb4b6314febe56493..e67a8ce4b64c2dd2e828849f47f242e4a7e869e6 100644 (file)
@@ -2158,8 +2158,8 @@ static int __init init_sel_fs(void)
                return err;
        }
 
-       selinux_null.dentry = d_hash_and_lookup(selinux_null.mnt->mnt_root,
-                                               &null_name);
+       selinux_null.dentry = try_lookup_noperm(&null_name,
+                                                 selinux_null.mnt->mnt_root);
        if (IS_ERR(selinux_null.dentry)) {
                pr_err("selinuxfs:  could not lookup null!\n");
                err = PTR_ERR(selinux_null.dentry);