Re: [RFC PATCH v3 6/8] fuse: implementation of lookup_handle+statx compound operation

From: Joanne Koong

Date: Wed Apr 08 2026 - 11:20:14 EST


On Wed, Apr 8, 2026 at 3:22 AM Luis Henriques <luis@xxxxxxxxxx> wrote:
>
> On Tue, Apr 07 2026, Joanne Koong wrote:
>
> > On Tue, Apr 7, 2026 at 4:06 PM Joanne Koong <joannelkoong@xxxxxxxxx> wrote:
> >>
> >> On Tue, Apr 7, 2026 at 2:20 PM Luis Henriques <luis@xxxxxxxxxx> wrote:
> >> >
> >> > Hi Joanne,
> >> >
> >> > On Tue, Apr 07 2026, Joanne Koong wrote:
> >> >
> >> > > On Wed, Feb 25, 2026 at 3:25 AM Luis Henriques <luis@xxxxxxxxxx> wrote:
> >> > >>
> >> > >> The implementation of lookup_handle+statx compound operation extends the
> >> > >> lookup operation so that a file handle is be passed into the kernel. It
> >> > >> also needs to include an extra inarg, so that the parent directory file
> >> > >> handle can be sent to user-space. This extra inarg is added as an extension
> >> > >> header to the request.
> >> > >>
> >> > >> By having a separate statx including in a compound operation allows the
> >> > >> attr to be dropped from the lookup_handle request, simplifying the
> >> > >> traditional FUSE lookup operation.
> >> > >>
> >> > >> Signed-off-by: Luis Henriques <luis@xxxxxxxxxx>
> >> > >> ---
> >> > >> fs/fuse/dir.c | 294 +++++++++++++++++++++++++++++++++++---
> >> > >> fs/fuse/fuse_i.h | 23 ++-
> >> > >> fs/fuse/inode.c | 48 +++++--
> >> > >> fs/fuse/readdir.c | 2 +-
> >> > >> include/uapi/linux/fuse.h | 23 ++-
> >> > >> 5 files changed, 355 insertions(+), 35 deletions(-)
> >> > >>
> >> > >> diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
> >> > >> index 113583c4efb6..89e6176abe25 100644
> >> > >> --- a/include/uapi/linux/fuse.h
> >> > >> +++ b/include/uapi/linux/fuse.h
> >> > >>
> >> > >> enum fuse_opcode {
> >> > >> @@ -671,6 +676,8 @@ enum fuse_opcode {
> >> > >> */
> >> > >> FUSE_COMPOUND = 54,
> >> > >>
> >> > >> + FUSE_LOOKUP_HANDLE = 55,
> >> > >> +
> >> > >> /* CUSE specific operations */
> >> > >> CUSE_INIT = 4096,
> >> > >>
> >> > >> @@ -707,6 +714,20 @@ struct fuse_entry_out {
> >> > >> struct fuse_attr attr;
> >> > >> };
> >> > >>
> >> > >> +struct fuse_entry2_out {
> >> > >> + uint64_t nodeid;
> >> > >> + uint64_t generation;
> >> > >> + uint64_t entry_valid;
> >> > >> + uint32_t entry_valid_nsec;
> >> > >> + uint32_t flags;
> >> > >> + uint64_t spare;
> >> > >> +};
> >> > >
> >> > > Hi Luis,
> >> > >
> >> > > Could you explain why we need a new struct fuse_entry2_out instead of
> >> > > reusing struct fuse_entry_out? From what i see, the only differences
> >> > > between them are that fuse_entry2_out drops attr_valid,
> >> > > attr_valid_nsec, and struct fuse_attr. Is this done so that it saves
> >> > > the ~100 bytes per lookup? Would it be cleaner from an abi perspective
> >> > > to just reuse fuse_entry_out and ignore the attr fields if they're not
> >> > > necessary? The reason I'm asking is because I'm looking at how you're
> >> > > doing the lookup request reply to see if the fuse passthrough stuff
> >> > > for metadata/directory operations can be combined with it. But I'm not
> >> > > fully understanding why fuse_entry2_out is needed here.
> >> > >
> >> > > I'm also a bit confused by why the compound with statx is needed here,
> >> > > could you explain this part? I see the call to fuse_statx_to_attr()
> >> > > after do_lookup_handle_statx(), but fuse_statx_to_attr() converts the
> >> > > statx reply right back to a struct fuse_attr for inode setup, so if
> >> > > FUSE_LOOKUP_HANDLE returned struct fuse_entry_out instead of struct
> >> > > fuse_entry2_out, doesn't this solve the problem of needing to compound
> >> > > it with a statx or getattr request? I also noticed that the statx part
> >> > > uses FUSE_ROOT_ID as a workaround for the node id because the actual
> >> > > nodeid isn't known yet, this seems like another sign that the
> >> > > attributes stuff should just be part of the lookup response itself
> >> > > rather than a separate operation?
> >> >
> >> > First of all, thanks a lot for looking into this patchset. Much
> >> > appreciated!
> >> >
> >> > The main reason for swapping the usage of attr by statx is that statx
> >> > includes some attributes that attr does not (e.g. btime). And since I was
> >> > adding a new FUSE operation, it would be a good time for using statx
> >> > instead. (Moreover, as new attributes may be added to statx in the
> >> > future, the benefits of using statx could eventually be even greater.)
> >> >
> >> > This was suggested by Miklos here[0], before converting the whole thing to
> >> > use compound commands. So, I was going to use fuse_statx in the _out args
> >> > for lookup_handle. However, because the interface was getting a bit
> >> > complex with extra args (and ext headers!), Miklos ended up suggesting[1]
> >> > to remove attr completely from the lookup_handle operation, and use
> >> > compounds instead to have the full functionality.
> >>
> >> Thank you for the context and links, Luis!
> >>
> >> Using fuse_statx over fuse_getattr makes sense to me for the new
> >> FUSE_LOOKUP_HANDLE op but the part I am confused about is why it needs
> >> to be compounded. If we are adding a new struct (struct
> >> fuse_entry2_out) to the abi, why is it not simpler to just make the
> >> new struct something like:
> >>
> >> struct fuse_entry_handle_out {
> >
> > Thinking more about how passthrough will use this... I'm thinking
> > something like this would be ideal (though please correct me if I'm
> > completely off track here, Amir):
> >
> > struct fuse_entry_handle_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;
> > struct fuse_statx statx;
> > };
> >
> > where if the inode is not passed through, the kernel uses the stax
> > field for the attributes but if the inode is passed through and the
> > server sets a bitmask specifying that attributes should be derived
> > from the backing file, then the statx field will be ignored by the
> > server and kernel).
>
> If we end up deciding to revert back to not using compound commands I'll
> have to take a closer look into this, as it's not clear what the above
> means. For example, when you say:
>
> "... if the inode is not passed through"
>
> does this "inode" refer to the backing_id? I guess this is some index to
> backing ids being used by FUSE, not exactly an inode, but I'm not very
> familiar with the code.

Ah sorry for the confusing wording. By "... if the inode is not passed
through" what I meant is if the server chooses not to have passthrough
enabled on that inode. If the server does wish for any operations on
that inode to be passed through to a backing inode, it will supply the
backing_id that corresponds to the backing inode (it gets the id from
the FUSE_DEV_IOC_BACKING_OPEN ioctl registration call). If backing_id
is not set (left as 0), then that inode will not have passthrough
enabled on it.

Thanks,
Joanne

>
> Cheers,
> --
> Luís