From: Jeff Layton <jlayton@kernel.org>
To: NeilBrown <neil@brown.name>,
Alexander Viro <viro@zeniv.linux.org.uk>,
Christian Brauner <brauner@kernel.org>, Jan Kara <jack@suse.cz>,
David Howells <dhowells@redhat.com>,
Chuck Lever <chuck.lever@oracle.com>
Cc: linux-nfs@vger.kernel.org, netfs@lists.linux.dev,
linux-fsdevel@vger.kernel.org
Subject: Re: [PATCH 1/6] VFS: improve interface for lookup_one functions
Date: Thu, 20 Mar 2025 06:17:39 -0400 [thread overview]
Message-ID: <f435729f69432b4c6659e1bbff1c8a7866f96bbe.camel@kernel.org> (raw)
In-Reply-To: <20250319031545.2999807-2-neil@brown.name>
On Wed, 2025-03-19 at 14:01 +1100, NeilBrown wrote:
> The family of functions:
> lookup_one()
> lookup_one_unlocked()
> lookup_one_positive_unlocked()
>
> appear designed to be used by external clients of the filesystem rather
> than by filesystems acting on themselves as the lookup_one_len family
> are used.
>
> They are used by:
> btrfs/ioctl - which is a user-space interface rather than an internal
> activity
> exportfs - i.e. from nfsd or the open_by_handle_at interface
> overlayfs - at access the underlying filesystems
> smb/server - for file service
>
> They should be used by nfsd (more than just the exportfs path) and
> cachefs but aren't.
>
> It would help if the documentation didn't claim they should "not be
> called by generic code".
>
I read that as generic VFS code, since this is really intended to used
outside that, but I agree the term "generic" is vague here.
> Also the path component name is passed as "name" and "len" which are
> (confusingly?) separate by the "base". In some cases the len in simply
> "strlen" and so passing a qstr using QSTR() would make the calling
> clearer.
> Other callers do pass separate name and len which are stored in a
> struct. Sometimes these are already stored in a qstr, other times it
> easily could be.
>
> So this patch changes these three functions to receive a 'struct qstr',
> and improves the documentation.
>
> QSTR_LEN() is added to make it easy to pass a QSTR containing a known
> len.
>
> Signed-off-by: NeilBrown <neil@brown.name>
> ---
> Documentation/filesystems/porting.rst | 9 +++++
> fs/btrfs/ioctl.c | 9 ++---
> fs/exportfs/expfs.c | 5 ++-
> fs/namei.c | 51 ++++++++++++---------------
> fs/overlayfs/namei.c | 10 +++---
> fs/overlayfs/overlayfs.h | 2 +-
> fs/overlayfs/readdir.c | 9 ++---
> fs/smb/server/smb2pdu.c | 7 ++--
> include/linux/dcache.h | 3 +-
> include/linux/namei.h | 9 +++--
> 10 files changed, 57 insertions(+), 57 deletions(-)
>
> diff --git a/Documentation/filesystems/porting.rst b/Documentation/filesystems/porting.rst
> index 6817614e0820..06296ffd1e81 100644
> --- a/Documentation/filesystems/porting.rst
> +++ b/Documentation/filesystems/porting.rst
> @@ -1196,3 +1196,12 @@ should use d_drop();d_splice_alias() and return the result of the latter.
> If a positive dentry cannot be returned for some reason, in-kernel
> clients such as cachefiles, nfsd, smb/server may not perform ideally but
> will fail-safe.
> +
> +---
> +
> +** mandatory**
> +
> +lookup_one(), lookup_one_unlocked(), lookup_one_positive_unlocked() now
> +take a qstr instead of a name and len. These, not the "one_len"
> +versions, should be used whenever accessing a filesystem from outside
> +that filesysmtem, through a mount point - which will have a mnt_idmap.
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index 6c18bad53cd3..f94b638f9478 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -911,7 +911,7 @@ static noinline int btrfs_mksubvol(const struct path *parent,
> if (error == -EINTR)
> return error;
>
> - dentry = lookup_one(idmap, name, parent->dentry, namelen);
> + dentry = lookup_one(idmap, QSTR_LEN(name, namelen), parent->dentry);
> error = PTR_ERR(dentry);
> if (IS_ERR(dentry))
> goto out_unlock;
> @@ -2289,7 +2289,6 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
> struct btrfs_ioctl_vol_args_v2 *vol_args2 = NULL;
> struct mnt_idmap *idmap = file_mnt_idmap(file);
> char *subvol_name, *subvol_name_ptr = NULL;
> - int subvol_namelen;
> int ret = 0;
> bool destroy_parent = false;
>
> @@ -2412,10 +2411,8 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
> goto out;
> }
>
> - subvol_namelen = strlen(subvol_name);
> -
> if (strchr(subvol_name, '/') ||
> - strncmp(subvol_name, "..", subvol_namelen) == 0) {
> + strcmp(subvol_name, "..") == 0) {
> ret = -EINVAL;
> goto free_subvol_name;
> }
> @@ -2428,7 +2425,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,
> ret = down_write_killable_nested(&dir->i_rwsem, I_MUTEX_PARENT);
> if (ret == -EINTR)
> goto free_subvol_name;
> - dentry = lookup_one(idmap, subvol_name, parent, subvol_namelen);
> + dentry = lookup_one(idmap, QSTR(subvol_name), parent);
> if (IS_ERR(dentry)) {
> ret = PTR_ERR(dentry);
> goto out_unlock_dir;
> diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c
> index 0c899cfba578..974b432087aa 100644
> --- a/fs/exportfs/expfs.c
> +++ b/fs/exportfs/expfs.c
> @@ -145,7 +145,7 @@ static struct dentry *reconnect_one(struct vfsmount *mnt,
> if (err)
> goto out_err;
> dprintk("%s: found name: %s\n", __func__, nbuf);
> - tmp = lookup_one_unlocked(mnt_idmap(mnt), nbuf, parent, strlen(nbuf));
> + tmp = lookup_one_unlocked(mnt_idmap(mnt), QSTR(nbuf), parent);
> if (IS_ERR(tmp)) {
> dprintk("lookup failed: %ld\n", PTR_ERR(tmp));
> err = PTR_ERR(tmp);
> @@ -551,8 +551,7 @@ exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len,
> }
>
> inode_lock(target_dir->d_inode);
> - nresult = lookup_one(mnt_idmap(mnt), nbuf,
> - target_dir, strlen(nbuf));
> + nresult = lookup_one(mnt_idmap(mnt), QSTR(nbuf), target_dir);
> if (!IS_ERR(nresult)) {
> if (unlikely(nresult->d_inode != result->d_inode)) {
> dput(nresult);
> diff --git a/fs/namei.c b/fs/namei.c
> index b5abf456c5f4..75816fa80028 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -2922,19 +2922,17 @@ struct dentry *lookup_one_len(const char *name, struct dentry *base, int len)
> EXPORT_SYMBOL(lookup_one_len);
>
> /**
> - * lookup_one - filesystem helper to lookup single pathname component
> + * lookup_one - lookup single pathname component
> * @idmap: idmap of the mount the lookup is performed from
> - * @name: pathname component to lookup
> + * @name: qstr holding pathname component to lookup
> * @base: base directory to lookup from
> - * @len: maximum length @len should be interpreted to
> *
> - * Note that this routine is purely a helper for filesystem usage and should
> - * not be called by generic code.
> + * This can be used for in-kernel filesystem clients such as file servers.
> *
> * The caller must hold base->i_mutex.
> */
> -struct dentry *lookup_one(struct mnt_idmap *idmap, const char *name,
> - struct dentry *base, int len)
> +struct dentry *lookup_one(struct mnt_idmap *idmap, struct qstr name,
> + struct dentry *base)
> {
> struct dentry *dentry;
> struct qstr this;
> @@ -2942,7 +2940,7 @@ struct dentry *lookup_one(struct mnt_idmap *idmap, const char *name,
>
> WARN_ON_ONCE(!inode_is_locked(base->d_inode));
>
> - err = lookup_one_common(idmap, name, base, len, &this);
> + err = lookup_one_common(idmap, name.name, base, name.len, &this);
> if (err)
> return ERR_PTR(err);
>
> @@ -2952,27 +2950,24 @@ struct dentry *lookup_one(struct mnt_idmap *idmap, const char *name,
> EXPORT_SYMBOL(lookup_one);
>
> /**
> - * lookup_one_unlocked - filesystem helper to lookup single pathname component
> + * lookup_one_unlocked - lookup single pathname component
> * @idmap: idmap of the mount the lookup is performed from
> - * @name: pathname component to lookup
> + * @name: qstr olding pathname component to lookup
> * @base: base directory to lookup from
> - * @len: maximum length @len should be interpreted to
> *
> - * Note that this routine is purely a helper for filesystem usage and should
> - * not be called by generic code.
> + * This can be used for in-kernel filesystem clients such as file servers.
> *
> * Unlike lookup_one_len, it should be called without the parent
> - * i_mutex held, and will take the i_mutex itself if necessary.
> + * i_rwsem held, and will take the i_rwsem itself if necessary.
> */
> struct dentry *lookup_one_unlocked(struct mnt_idmap *idmap,
> - const char *name, struct dentry *base,
> - int len)
> + struct qstr name, struct dentry *base)
> {
> struct qstr this;
> int err;
> struct dentry *ret;
>
> - err = lookup_one_common(idmap, name, base, len, &this);
> + err = lookup_one_common(idmap, name.name, base, name.len, &this);
> if (err)
> return ERR_PTR(err);
>
> @@ -2984,12 +2979,10 @@ struct dentry *lookup_one_unlocked(struct mnt_idmap *idmap,
> EXPORT_SYMBOL(lookup_one_unlocked);
>
> /**
> - * lookup_one_positive_unlocked - filesystem helper to lookup single
> - * pathname component
> + * lookup_one_positive_unlocked - lookup single pathname component
> * @idmap: idmap of the mount the lookup is performed from
> - * @name: pathname component to lookup
> + * @name: qstr holding pathname component to lookup
> * @base: base directory to lookup from
> - * @len: maximum length @len should be interpreted to
> *
> * This helper will yield ERR_PTR(-ENOENT) on negatives. The helper returns
> * known positive or ERR_PTR(). This is what most of the users want.
> @@ -2998,16 +2991,15 @@ EXPORT_SYMBOL(lookup_one_unlocked);
> * time, so callers of lookup_one_unlocked() need to be very careful; pinned
> * positives have >d_inode stable, so this one avoids such problems.
> *
> - * Note that this routine is purely a helper for filesystem usage and should
> - * not be called by generic code.
> + * This can be used for in-kernel filesystem clients such as file servers.
> *
> - * The helper should be called without i_mutex held.
> + * The helper should be called without i_rwsem held.
> */
> struct dentry *lookup_one_positive_unlocked(struct mnt_idmap *idmap,
> - const char *name,
> - struct dentry *base, int len)
> + struct qstr name,
> + struct dentry *base)
> {
> - struct dentry *ret = lookup_one_unlocked(idmap, name, base, len);
> + struct dentry *ret = lookup_one_unlocked(idmap, name, base);
>
> if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) {
> dput(ret);
> @@ -3032,7 +3024,7 @@ EXPORT_SYMBOL(lookup_one_positive_unlocked);
> struct dentry *lookup_one_len_unlocked(const char *name,
> struct dentry *base, int len)
> {
> - return lookup_one_unlocked(&nop_mnt_idmap, name, base, len);
> + return lookup_one_unlocked(&nop_mnt_idmap, QSTR_LEN(name, len), base);
> }
> EXPORT_SYMBOL(lookup_one_len_unlocked);
>
> @@ -3047,7 +3039,8 @@ EXPORT_SYMBOL(lookup_one_len_unlocked);
> struct dentry *lookup_positive_unlocked(const char *name,
> struct dentry *base, int len)
> {
> - return lookup_one_positive_unlocked(&nop_mnt_idmap, name, base, len);
> + return lookup_one_positive_unlocked(&nop_mnt_idmap, QSTR_LEN(name, len),
> + base);
> }
> EXPORT_SYMBOL(lookup_positive_unlocked);
>
> diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
> index be5c65d6f848..6a6301e4bba5 100644
> --- a/fs/overlayfs/namei.c
> +++ b/fs/overlayfs/namei.c
> @@ -205,8 +205,8 @@ static struct dentry *ovl_lookup_positive_unlocked(struct ovl_lookup_data *d,
> struct dentry *base, int len,
> bool drop_negative)
> {
> - struct dentry *ret = lookup_one_unlocked(mnt_idmap(d->layer->mnt), name,
> - base, len);
> + struct dentry *ret = lookup_one_unlocked(mnt_idmap(d->layer->mnt),
> + QSTR_LEN(name, len), base);
>
> if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) {
> if (drop_negative && ret->d_lockref.count == 1) {
> @@ -789,8 +789,8 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
> if (err)
> return ERR_PTR(err);
>
> - index = lookup_one_positive_unlocked(ovl_upper_mnt_idmap(ofs), name.name,
> - ofs->workdir, name.len);
> + index = lookup_one_positive_unlocked(ovl_upper_mnt_idmap(ofs), name,
> + ofs->workdir);
> if (IS_ERR(index)) {
> err = PTR_ERR(index);
> if (err == -ENOENT) {
> @@ -1396,7 +1396,7 @@ bool ovl_lower_positive(struct dentry *dentry)
>
> this = lookup_one_positive_unlocked(
> mnt_idmap(parentpath->layer->mnt),
> - name->name, parentpath->dentry, name->len);
> + *name, parentpath->dentry);
> if (IS_ERR(this)) {
> switch (PTR_ERR(this)) {
> case -ENOENT:
> diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
> index 6f2f8f4cfbbc..ceaf4eb199c7 100644
> --- a/fs/overlayfs/overlayfs.h
> +++ b/fs/overlayfs/overlayfs.h
> @@ -402,7 +402,7 @@ static inline struct dentry *ovl_lookup_upper(struct ovl_fs *ofs,
> const char *name,
> struct dentry *base, int len)
> {
> - return lookup_one(ovl_upper_mnt_idmap(ofs), name, base, len);
> + return lookup_one(ovl_upper_mnt_idmap(ofs), QSTR_LEN(name, len), base);
> }
>
> static inline bool ovl_open_flags_need_copy_up(int flags)
> diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c
> index 881ec5592da5..68df61f4bba7 100644
> --- a/fs/overlayfs/readdir.c
> +++ b/fs/overlayfs/readdir.c
> @@ -271,7 +271,6 @@ static bool ovl_fill_merge(struct dir_context *ctx, const char *name,
> static int ovl_check_whiteouts(const struct path *path, struct ovl_readdir_data *rdd)
> {
> int err;
> - struct ovl_cache_entry *p;
> struct dentry *dentry, *dir = path->dentry;
> const struct cred *old_cred;
>
> @@ -280,9 +279,11 @@ static int ovl_check_whiteouts(const struct path *path, struct ovl_readdir_data
> err = down_write_killable(&dir->d_inode->i_rwsem);
> if (!err) {
> while (rdd->first_maybe_whiteout) {
> - p = rdd->first_maybe_whiteout;
> + struct ovl_cache_entry *p =
> + rdd->first_maybe_whiteout;
> rdd->first_maybe_whiteout = p->next_maybe_whiteout;
> - dentry = lookup_one(mnt_idmap(path->mnt), p->name, dir, p->len);
> + dentry = lookup_one(mnt_idmap(path->mnt),
> + QSTR_LEN(p->name, p->len), dir);
> if (!IS_ERR(dentry)) {
> p->is_whiteout = ovl_is_whiteout(dentry);
> dput(dentry);
> @@ -492,7 +493,7 @@ static int ovl_cache_update(const struct path *path, struct ovl_cache_entry *p,
> }
> }
> /* This checks also for xwhiteouts */
> - this = lookup_one(mnt_idmap(path->mnt), p->name, dir, p->len);
> + this = lookup_one(mnt_idmap(path->mnt), QSTR_LEN(p->name, p->len), dir);
> if (IS_ERR_OR_NULL(this) || !this->d_inode) {
> /* Mark a stale entry */
> p->is_whiteout = true;
> diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
> index f1efcd027475..c862e3bd4531 100644
> --- a/fs/smb/server/smb2pdu.c
> +++ b/fs/smb/server/smb2pdu.c
> @@ -4091,9 +4091,10 @@ static int process_query_dir_entries(struct smb2_query_dir_private *priv)
> return -EINVAL;
>
> lock_dir(priv->dir_fp);
> - dent = lookup_one(idmap, priv->d_info->name,
> - priv->dir_fp->filp->f_path.dentry,
> - priv->d_info->name_len);
> + dent = lookup_one(idmap,
> + QSTR_LEN(priv->d_info->name,
> + priv->d_info->name_len),
> + priv->dir_fp->filp->f_path.dentry);
> unlock_dir(priv->dir_fp);
>
> if (IS_ERR(dent)) {
> diff --git a/include/linux/dcache.h b/include/linux/dcache.h
> index 45bff10d3773..1f01f4e734c5 100644
> --- a/include/linux/dcache.h
> +++ b/include/linux/dcache.h
> @@ -57,7 +57,8 @@ struct qstr {
> };
>
> #define QSTR_INIT(n,l) { { { .len = l } }, .name = n }
> -#define QSTR(n) (struct qstr)QSTR_INIT(n, strlen(n))
> +#define QSTR_LEN(n,l) (struct qstr)QSTR_INIT(n,l)
> +#define QSTR(n) QSTR_LEN(n, strlen(n))
>
> extern const struct qstr empty_name;
> extern const struct qstr slash_name;
> diff --git a/include/linux/namei.h b/include/linux/namei.h
> index e3042176cdf4..508dae67e3c5 100644
> --- a/include/linux/namei.h
> +++ b/include/linux/namei.h
> @@ -73,13 +73,12 @@ 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);
> -struct dentry *lookup_one(struct mnt_idmap *, const char *, struct dentry *, int);
> +struct dentry *lookup_one(struct mnt_idmap *, struct qstr, struct dentry *);
> struct dentry *lookup_one_unlocked(struct mnt_idmap *idmap,
> - const char *name, struct dentry *base,
> - int len);
> + struct qstr name, struct dentry *base);
> struct dentry *lookup_one_positive_unlocked(struct mnt_idmap *idmap,
> - const char *name,
> - struct dentry *base, int len);
> + struct qstr name,
> + struct dentry *base);
>
> extern int follow_down_one(struct path *);
> extern int follow_down(struct path *path, unsigned int flags);
Reviewed-by: Jeff Layton <jlayton@kernel.org>
next prev parent reply other threads:[~2025-03-20 10:17 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-19 3:01 [PATCH 0/6 RFC v2] tidy up various VFS lookup functions NeilBrown
2025-03-19 3:01 ` [PATCH 1/6] VFS: improve interface for lookup_one functions NeilBrown
2025-03-19 8:40 ` Christian Brauner
2025-03-20 10:17 ` Jeff Layton [this message]
2025-03-22 0:27 ` Al Viro
2025-03-28 1:14 ` NeilBrown
2025-03-19 3:01 ` [PATCH 2/6] nfsd: Use lookup_one() rather than lookup_one_len() NeilBrown
2025-03-19 13:04 ` Chuck Lever
2025-03-20 10:19 ` Jeff Layton
2025-03-19 3:01 ` [PATCH 3/6] cachefiles: " NeilBrown
2025-03-20 10:22 ` Jeff Layton
2025-03-20 12:05 ` Christian Brauner
2025-03-20 13:49 ` David Howells
2025-03-20 14:04 ` Christian Brauner
2025-03-19 3:01 ` [PATCH 4/6] VFS: rename lookup_one_len family to lookup_noperm and remove permission check NeilBrown
2025-03-20 10:29 ` Jeff Layton
2025-03-22 0:34 ` Al Viro
2025-03-28 1:31 ` NeilBrown
2025-03-19 3:01 ` [PATCH 5/6] Use try_lookup_noperm() instead of d_hash_and_lookup() outside of VFS NeilBrown
2025-03-20 10:45 ` Jeff Layton
2025-03-22 0:39 ` Al Viro
2025-03-19 3:01 ` [PATCH 6/6] VFS: change lookup_one_common and lookup_noperm_common to take a qstr NeilBrown
2025-03-20 10:46 ` Jeff Layton
2025-03-19 8:42 ` [PATCH 0/6 RFC v2] tidy up various VFS lookup functions Christian Brauner
2025-03-19 9:23 ` NeilBrown
2025-03-20 14:04 ` [PATCH 1/6] VFS: improve interface for lookup_one functions David Howells
2025-03-22 0:29 ` Al Viro
2025-03-28 1:18 ` NeilBrown
2025-04-04 13:41 ` Christian Brauner
2025-04-04 13:46 ` Christian Brauner
2025-04-04 23:00 ` NeilBrown
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=f435729f69432b4c6659e1bbff1c8a7866f96bbe.camel@kernel.org \
--to=jlayton@kernel.org \
--cc=brauner@kernel.org \
--cc=chuck.lever@oracle.com \
--cc=dhowells@redhat.com \
--cc=jack@suse.cz \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=neil@brown.name \
--cc=netfs@lists.linux.dev \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).