@samitouri / QOSamiQemu / commits / 817cc2d045

block/export/fuse: use struct fuse_init_in

The code is switched to use the current 'struct fuse_init_in' in preparation to use the FUSE_DIRECT_IO_ALLOW_MMAP feature, which is part of the flags2 member that got added in protocol version 5.36. To not break compatibility with older kernels, the check for whether the full header of an operation was read in co_read_from_fuse_fd() needs to be adapted. In particular, for a FUSE_INIT operation, the protocol version must be considered, because the length of the header changed with protocol version 7.36. Always using the length of the old, shorter struct was inaccurate, since for newer protocol versions this might mean accepting a truncated read for FUSE_INIT. Users of the init header that want to use parts of the extended structure must check with the using_old_fuse_init_in() helper function if they may do so. Cc: qemu-stable@nongnu.org Fixes: a94a1d7699 ("fuse: Manually process requests (without libfuse)") Signed-off-by: Fiona Ebner <f.ebner@proxmox.com> Message-ID: <20260506145424.10249-2-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>

Fiona Ebner committed May 6, 2026 at 16:49 UTC 817cc2d045d25c980f20c2377d4a301f68d3e3e2
1 file changed +42 -14
block/export/fuse.c
+42 -14
@@ -51,23 +51,16 @@
51 #define FUSE_MAX_READ_BYTES (MIN(BDRV_REQUEST_MAX_BYTES, 1 * 1024 * 1024))
52 #define FUSE_MAX_WRITE_BYTES (64 * 1024)
53
54 -/*
55 - * fuse_init_in structure before 7.36. We don't need the flags2 field added
56 - * there, so we can work with the smaller older structure to stay compatible
57 - * with older kernels.
58 - */
59 -struct fuse_init_in_compat {
60 - uint32_t major;
61 - uint32_t minor;
62 - uint32_t max_readahead;
63 - uint32_t flags;
64 -};
65 -
54 typedef struct FuseRequestInHeader {
55 struct fuse_in_header common;
56 /* All supported requests */
57 union {
70 - struct fuse_init_in_compat init;
58 + /*
59 + * When using_old_fuse_init_in() is true, then the smaller older struct
60 + * is used by the kernel. The flags2 member and other new members must
61 + * be treated as absent then.
62 + */
63 + struct fuse_init_in init;
64 struct fuse_open_in open;
65 struct fuse_setattr_in setattr;
66 struct fuse_read_in read;
@@ -629,6 +622,16 @@ static int clone_fuse_fd(int fd, Error **errp)
622 return new_fd;
623 }
624
625 +/**
626 + * Check whether the smaller older fuse_init_in structure from before protocol
627 + * version 7.36 is used. The flags2 member and other new members must be treated
628 + * as absent then.
629 + */
630 +static bool using_old_fuse_init_in(const struct fuse_init_in *in)
631 +{
632 + return in->major < 7 || (in->major == 7 && in->minor < 36);
633 +}
634 +
635 /**
636 * Try to read a single request from the FUSE FD.
637 * Takes a FuseQueue pointer in `opaque`.
@@ -693,6 +696,31 @@ static void coroutine_fn co_read_from_fuse_fd(void *opaque)
696 goto no_request;
697 }
698
699 + /*
700 + * If the request is of type FUSE_INIT, need to check the version to
701 + * actually determine the length of the fuse_init_in structure used by the
702 + * kernel. In protocol version 7.36, the structure was extended.
703 + */
704 + if (in_hdr->common.opcode == FUSE_INIT) {
705 + /* Length of the fuse_init_in structure before 7.36. */
706 + size_t old_init_hdr_len = 16;
707 +
708 + /*
709 + * Expect at least the size of the smaller older structure to ensure the
710 + * version can be checked.
711 + */
712 + if (unlikely(ret < sizeof(in_hdr->common) + old_init_hdr_len)) {
713 + error_report("FUSE_INIT request truncated, read only %zi bytes",
714 + ret);
715 + fuse_write_err(fuse_fd, &in_hdr->common, -EINVAL);
716 + goto no_request;
717 + }
718 +
719 + if (using_old_fuse_init_in(&in_hdr->init)) {
720 + op_hdr_len = old_init_hdr_len;
721 + }
722 + }
723 +
724 if (unlikely(ret < sizeof(in_hdr->common) + op_hdr_len)) {
725 error_report("FUSE request truncated, expected %zu bytes, read %zi "
726 "bytes",
@@ -826,7 +854,7 @@ static bool is_regular_file(const char *path, Error **errp)
854 */
855 static ssize_t coroutine_fn GRAPH_RDLOCK
856 fuse_co_init(FuseExport *exp, struct fuse_init_out *out,
829 - const struct fuse_init_in_compat *in)
857 + const struct fuse_init_in *in)
858 {
859 const uint32_t supported_flags = FUSE_ASYNC_READ | FUSE_ASYNC_DIO;
860