From: Joanne Koong <joannelkoong@gmail.com>
To: amir73il@gmail.com, miklos@szeredi.hu
Cc: fuse-devel@lists.linux.dev, linux-unionfs@vger.kernel.org
Subject: [PATCH v2 12/21] fuse: add struct fuse_entry2_out and helpers for extended entry replies
Date: Fri, 15 May 2026 17:39:55 -0700 [thread overview]
Message-ID: <20260516004004.1455526-13-joannelkoong@gmail.com> (raw)
In-Reply-To: <20260516004004.1455526-1-joannelkoong@gmail.com>
Add struct fuse_entry2_out, which is a new extended entry reply struct
that carries a backing_id and statx attributes. This will be necessary
for setting fuse passthrough on inodes.
Add helpers that subsequent commits will use to process fuse_entry2_out
for passthrough support for lookup, revalidate, and create.
fuse_statx_to_attr() is also moved to earlier in the file to avoid
forward declaring.
Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
---
fs/fuse/dir.c | 116 +++++++++++++++++++++++++++++---------
fs/fuse/fuse_i.h | 5 ++
include/uapi/linux/fuse.h | 14 +++++
3 files changed, 107 insertions(+), 28 deletions(-)
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index a338f2a06b50..b7a9d2b0476a 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -355,11 +355,18 @@ static void fuse_invalidate_entry(struct dentry *entry)
fuse_invalidate_entry_cache(entry);
}
-static void fuse_lookup_init(struct fuse_args *args, u64 nodeid,
- const struct qstr *name,
- struct fuse_entry_out *outarg)
+static void fuse_lookup_init(struct fuse_conn *fc, struct fuse_args *args,
+ u64 nodeid, const struct qstr *name,
+ struct fuse_entry_out *outarg,
+ struct fuse_entry2_out *outarg2)
{
- memset(outarg, 0, sizeof(struct fuse_entry_out));
+ bool use_entry2 = fuse_use_entry2(fc);
+
+ if (use_entry2)
+ memset(outarg2, 0, sizeof(struct fuse_entry2_out));
+ else
+ memset(outarg, 0, sizeof(struct fuse_entry_out));
+
args->opcode = FUSE_LOOKUP;
args->nodeid = nodeid;
args->in_numargs = 3;
@@ -369,8 +376,79 @@ static void fuse_lookup_init(struct fuse_args *args, u64 nodeid,
args->in_args[2].size = 1;
args->in_args[2].value = "";
args->out_numargs = 1;
- args->out_args[0].size = sizeof(struct fuse_entry_out);
- args->out_args[0].value = outarg;
+
+ if (use_entry2) {
+ args->out_args[0].size = sizeof(struct fuse_entry2_out);
+ args->out_args[0].value = outarg2;
+ } else {
+ args->out_args[0].size = sizeof(struct fuse_entry_out);
+ args->out_args[0].value = outarg;
+ }
+}
+
+static void fuse_statx_to_attr(struct fuse_statx *sx, struct fuse_attr *attr)
+{
+ memset(attr, 0, sizeof(*attr));
+ attr->ino = sx->ino;
+ attr->size = sx->size;
+ attr->blocks = sx->blocks;
+ attr->atime = sx->atime.tv_sec;
+ attr->mtime = sx->mtime.tv_sec;
+ attr->ctime = sx->ctime.tv_sec;
+ attr->atimensec = sx->atime.tv_nsec;
+ attr->mtimensec = sx->mtime.tv_nsec;
+ attr->ctimensec = sx->ctime.tv_nsec;
+ attr->mode = sx->mode;
+ attr->nlink = sx->nlink;
+ attr->uid = sx->uid;
+ attr->gid = sx->gid;
+ attr->rdev = new_encode_dev(MKDEV(sx->rdev_major, sx->rdev_minor));
+ attr->blksize = sx->blksize;
+}
+
+static void fuse_entry2_to_entry(struct fuse_entry2_out *outarg2,
+ struct fuse_entry_out *outarg)
+{
+ memset(outarg, 0, sizeof(struct fuse_entry_out));
+ outarg->nodeid = outarg2->nodeid;
+ outarg->generation = outarg2->generation;
+ outarg->entry_valid = outarg2->entry_valid;
+ outarg->attr_valid = outarg2->attr_valid;
+ outarg->entry_valid_nsec = outarg2->entry_valid_nsec;
+ outarg->attr_valid_nsec = outarg2->attr_valid_nsec;
+ fuse_statx_to_attr(&outarg2->statx, &outarg->attr);
+ outarg->attr.flags = outarg2->flags;
+}
+
+static __maybe_unused int fuse_process_entry2(struct fuse_conn *fc,
+ struct fuse_entry2_out *outarg2,
+ struct fuse_entry_out *outarg,
+ struct fuse_statx **sxp)
+{
+ if (!fuse_use_entry2(fc))
+ return 0;
+
+ /*
+ * Convert entry2 format to entry format before error checking since the
+ * caller needs outarg->nodeid field even on error
+ */
+ fuse_entry2_to_entry(outarg2, outarg);
+
+ if (outarg2->reserved)
+ return -EINVAL;
+
+ /* error */
+ if (outarg2->backing_id < 0)
+ return outarg2->backing_id;
+
+ /*
+ * If passthrough is enabled (backing_id > 0), statx attributes are not
+ * cached because passthrough getattr fetches them directly from the
+ * backing inode
+ */
+ if (!outarg2->backing_id)
+ *sxp = &outarg2->statx;
+ return outarg2->backing_id;
}
/*
@@ -389,6 +467,7 @@ static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name,
struct fuse_conn *fc;
struct fuse_inode *fi;
struct fuse_entry_out outarg;
+ struct fuse_entry2_out outarg2;
struct fuse_forget_link *forget;
FUSE_ARGS(args);
u64 attr_version;
@@ -431,7 +510,7 @@ static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name,
attr_version = fuse_get_attr_version(fc);
- fuse_lookup_init(&args, get_node_id(dir), name, &outarg);
+ fuse_lookup_init(fc, &args, get_node_id(dir), name, &outarg, &outarg2);
ret = fuse_simple_request(get_fuse_mount(inode), &args);
if (ret || !outarg.nodeid) {
kfree(forget);
@@ -551,6 +630,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
FUSE_ARGS(args);
struct fuse_forget_link *forget;
u64 attr_version, evict_ctr;
+ struct fuse_entry2_out outarg2;
int err;
*inode = NULL;
@@ -567,7 +647,7 @@ int fuse_lookup_name(struct super_block *sb, u64 nodeid, const struct qstr *name
attr_version = fuse_get_attr_version(fm->fc);
evict_ctr = fuse_get_evict_ctr(fm->fc);
- fuse_lookup_init(&args, nodeid, name, outarg);
+ fuse_lookup_init(fm->fc, &args, nodeid, name, outarg, &outarg2);
err = fuse_simple_request(fm, &args);
/* Zero nodeid is same as -ENOENT, but with valid timeout */
if (err || !outarg->nodeid)
@@ -1386,26 +1466,6 @@ static void fuse_fillattr(struct mnt_idmap *idmap, struct inode *inode,
stat->blksize = 1 << blkbits;
}
-static void fuse_statx_to_attr(struct fuse_statx *sx, struct fuse_attr *attr)
-{
- memset(attr, 0, sizeof(*attr));
- attr->ino = sx->ino;
- attr->size = sx->size;
- attr->blocks = sx->blocks;
- attr->atime = sx->atime.tv_sec;
- attr->mtime = sx->mtime.tv_sec;
- attr->ctime = sx->ctime.tv_sec;
- attr->atimensec = sx->atime.tv_nsec;
- attr->mtimensec = sx->mtime.tv_nsec;
- attr->ctimensec = sx->ctime.tv_nsec;
- attr->mode = sx->mode;
- attr->nlink = sx->nlink;
- attr->uid = sx->uid;
- attr->gid = sx->gid;
- attr->rdev = new_encode_dev(MKDEV(sx->rdev_major, sx->rdev_minor));
- attr->blksize = sx->blksize;
-}
-
void fuse_kstat_to_attr(struct fuse_conn *fc, const struct kstat *stat,
struct fuse_attr *attr)
{
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 1f2c849ea4e3..89c9333e9702 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1368,6 +1368,11 @@ static inline bool fuse_passthrough_op(struct inode *inode, enum fuse_opcode op)
int fuse_passthrough_getattr(struct inode *inode, struct kstat *stat,
u32 request_mask, unsigned int flags);
+static inline bool fuse_use_entry2(struct fuse_conn *fc)
+{
+ return fc->passthrough_ino;
+}
+
#ifdef CONFIG_SYSCTL
extern int fuse_sysctl_register(void);
extern void fuse_sysctl_unregister(void);
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index 6404ed95c758..3963631558f9 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -244,6 +244,7 @@
* 7.46
* - add FUSE_PASSTHROUGH_INO
* - add ops_mask field to struct fuse_backing_map
+ * - add fuse_entry2_out
*/
#ifndef _LINUX_FUSE_H
@@ -705,6 +706,19 @@ struct fuse_entry_out {
struct fuse_attr attr;
};
+struct fuse_entry2_out {
+ uint64_t nodeid;
+ uint64_t generation;
+ uint64_t entry_valid;
+ uint64_t attr_valid;
+ uint32_t entry_valid_nsec;
+ uint32_t attr_valid_nsec;
+ int32_t backing_id;
+ uint32_t flags;
+ uint64_t reserved;
+ struct fuse_statx statx;
+};
+
struct fuse_forget_in {
uint64_t nlookup;
};
--
2.52.0
next prev parent reply other threads:[~2026-05-16 0:52 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-16 0:39 [PATCH v2 00/21] fuse: extend passthrough to inode operations Joanne Koong
2026-05-16 0:39 ` [PATCH v2 01/21] fuse: introduce FUSE_PASSTHROUGH_INO mode Joanne Koong
2026-05-16 0:39 ` [PATCH v2 02/21] fuse: prepare for passthrough of inode operations Joanne Koong
2026-05-16 1:34 ` Joanne Koong
2026-05-16 0:39 ` [PATCH v2 03/21] fuse: prepare for readdir passthrough on directories Joanne Koong
2026-05-16 0:39 ` [PATCH v2 04/21] fuse: implement passthrough for readdir Joanne Koong
2026-05-16 0:39 ` [PATCH v2 05/21] fuse: prepare for long lived reference on backing file Joanne Koong
2026-05-16 0:39 ` [PATCH v2 06/21] fuse: implement passthrough for getattr/statx Joanne Koong
2026-05-16 0:39 ` [PATCH v2 07/21] fuse: prepare to setup backing inode passthrough on lookup Joanne Koong
2026-05-16 0:39 ` [PATCH v2 08/21] fuse: handle zero ops_mask in FUSE_DEV_IOC_BACKING_OPEN Joanne Koong
2026-05-16 0:39 ` [PATCH v2 09/21] fuse: handle partial io passthrough for read/write, splice, and mmap Joanne Koong
2026-05-16 0:39 ` [PATCH v2 10/21] fuse: prepare to cache statx attributes from entry replies Joanne Koong
2026-05-16 0:39 ` [PATCH v2 11/21] fuse: clean up fuse_dentry_revalidate() Joanne Koong
2026-05-16 0:39 ` Joanne Koong [this message]
2026-05-16 0:39 ` [PATCH v2 13/21] fuse: add passthrough lookup Joanne Koong
2026-05-16 0:39 ` [PATCH v2 14/21] fuse: add passthrough support for entry creation Joanne Koong
2026-05-16 0:39 ` [PATCH v2 15/21] fuse: add passthrough support for create+open Joanne Koong
2026-05-16 0:39 ` [PATCH v2 16/21] fuse: allow backing_id=0 in open to inherit inode's backing file Joanne Koong
2026-05-16 0:40 ` [PATCH v2 17/21] backing-inode: add backing_inode_copyattr() Joanne Koong
2026-05-16 0:40 ` [PATCH v2 18/21] backing-inode: add backing_inode_setattr() Joanne Koong
2026-05-16 0:40 ` [PATCH v2 19/21] fuse: add passthrough setattr Joanne Koong
2026-05-16 1:04 ` Joanne Koong
2026-05-16 0:40 ` [PATCH v2 20/21] fuse: use passthrough getattr in setattr suid/sgid handling Joanne Koong
2026-05-16 1:20 ` Joanne Koong
2026-05-16 0:40 ` [PATCH v2 21/21] docs: fuse: document extended passthrough (FUSE_PASSTHROUGH_INO) Joanne Koong
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=20260516004004.1455526-13-joannelkoong@gmail.com \
--to=joannelkoong@gmail.com \
--cc=amir73il@gmail.com \
--cc=fuse-devel@lists.linux.dev \
--cc=linux-unionfs@vger.kernel.org \
--cc=miklos@szeredi.hu \
/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