Commit b9ad1c4396 for qemu.org

commit b9ad1c4396e7eb376f0f8dc7d20538980cfa08f2
Author: Fiona Ebner <f.ebner@proxmox.com>
Date:   Thu Jul 2 15:22:35 2026 +0200

    block/export/fuse: fix regression with O_TRUNC when export is not growable

    Before commit a94a1d7699 ("fuse: Manually process requests (without
    libfuse)"), the O_TRUNC flag when open()-ing an export would be
    ignored. This is because libfuse sets FUSE_CAP_ATOMIC_O_TRUNC, so the
    kernel lets user space handle the O_TRUNC flag, which is ignored by
    the fuse code for export. After the commit, FUSE_CAP_ATOMIC_O_TRUNC is
    not set anymore, so the O_TRUNC flag is handled by the kernel, which
    executes a truncate.

    For blockdev-based exports, this causes a regression, because opening
    with O_TRUNC would previously work, but results in an ENOTSUP after
    commit a94a1d7699. For file-based exports, the fact that truncate is
    executed can be considered an improvement in general. However, in
    combination with growable=off, this still results in a practical
    regression in combination with virt-fw-vars, which opens its output
    file with O_TRUNC and previously worked with a file-based export with
    growable=off. After commit a94a1d7699, the file is truncated upon open
    and then cannot grow, meaning virt-fw-vars won't be able to write the
    output.

    To fix these regressions, while keeping the improved behavior for
    file-based exports with growable=on, set the FUSE_CAP_ATOMIC_O_TRUNC
    flag again if growable=off.

    Cc: qemu-stable@nongnu.org
    Fixes: a94a1d7699 ("fuse: Manually process requests (without libfuse)")
    Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
    Message-ID: <20260702132256.661429-2-f.ebner@proxmox.com>
    Reviewed-by: Kevin Wolf <kwolf@redhat.com>
    Signed-off-by: Kevin Wolf <kwolf@redhat.com>

diff --git a/block/export/fuse.c b/block/export/fuse.c
index c0e8dfb643..9ae22a2e00 100644
--- a/block/export/fuse.c
+++ b/block/export/fuse.c
@@ -859,6 +859,18 @@ fuse_co_init(FuseExport *exp, struct fuse_init_out *out,
     uint32_t supported_flags = FUSE_ASYNC_READ | FUSE_ASYNC_DIO;
     uint32_t flags2 = 0;

+    if (!exp->growable) {
+        /*
+         * Back when libfuse was used, it would always set this flag and thus
+         * the kernel did not execute a truncate itself and passed along O_TRUNC
+         * to user space. Continue setting the flag for backwards compatibility
+         * when the export is not growable to avoid issues with O_TRUNC, i.e.
+         * blockdev-based exports running into ENOTSUP and file-based exports
+         * with growable=off to be truncated and then stuck with size 0.
+         */
+        supported_flags = FUSE_ATOMIC_O_TRUNC;
+    }
+
     if (in->major != 7) {
         error_report("FUSE major version mismatch: We have 7, but kernel has %"
                      PRIu32, in->major);