[PULL 13/17] block/export/fuse: fix regression with O_TRUNC when export is not growable

Kevin Wolf <[email protected]>
Newsgroups gmane.comp.emulators.qemu,gmane.comp.emulators.qemu.block
Message-ID <[email protected]>
From: Fiona Ebner <[email protected]>

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: [email protected]
Fixes: a94a1d7699 ("fuse: Manually process requests (without libfuse)")
Signed-off-by: Fiona Ebner <[email protected]>
Message-ID: <[email protected]>
Reviewed-by: Kevin Wolf <[email protected]>
Signed-off-by: Kevin Wolf <[email protected]>
---
 block/export/fuse.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/block/export/fuse.c b/block/export/fuse.c
index c0e8dfb6430..9ae22a2e003 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);
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.