From 9db63cb5df5acd76b1881909da78dd73c5b2ea98 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 4 Sep 2019 10:37:55 -0400 Subject: [PATCH 01/32] qemu-4.1.0-2.fc32 gluster 4K block size fixes (bz #1737256) --- ...-posix-Handle-undetectable-alignment.patch | 151 +++++++ ...osix-Always-allocate-the-first-block.patch | 369 ++++++++++++++++++ qemu.spec | 9 +- 3 files changed, 528 insertions(+), 1 deletion(-) create mode 100644 0001-file-posix-Handle-undetectable-alignment.patch create mode 100644 0002-block-posix-Always-allocate-the-first-block.patch diff --git a/0001-file-posix-Handle-undetectable-alignment.patch b/0001-file-posix-Handle-undetectable-alignment.patch new file mode 100644 index 0000000..01ab7b6 --- /dev/null +++ b/0001-file-posix-Handle-undetectable-alignment.patch @@ -0,0 +1,151 @@ +From: Nir Soffer +Date: Tue, 13 Aug 2019 21:21:03 +0300 +Subject: [PATCH] file-posix: Handle undetectable alignment + +In some cases buf_align or request_alignment cannot be detected: + +1. With Gluster, buf_align cannot be detected since the actual I/O is + done on Gluster server, and qemu buffer alignment does not matter. + Since we don't have alignment requirement, buf_align=1 is the best + value. + +2. With local XFS filesystem, buf_align cannot be detected if reading + from unallocated area. In this we must align the buffer, but we don't + know what is the correct size. Using the wrong alignment results in + I/O error. + +3. With Gluster backed by XFS, request_alignment cannot be detected if + reading from unallocated area. In this case we need to use the + correct alignment, and failing to do so results in I/O errors. + +4. With NFS, the server does not use direct I/O, so both buf_align cannot + be detected. In this case we don't need any alignment so we can use + buf_align=1 and request_alignment=1. + +These cases seems to work when storage sector size is 512 bytes, because +the current code starts checking align=512. If the check succeeds +because alignment cannot be detected we use 512. But this does not work +for storage with 4k sector size. + +To determine if we can detect the alignment, we probe first with +align=1. If probing succeeds, maybe there are no alignment requirement +(cases 1, 4) or we are probing unallocated area (cases 2, 3). Since we +don't have any way to tell, we treat this as undetectable alignment. If +probing with align=1 fails with EINVAL, but probing with one of the +expected alignments succeeds, we know that we found a working alignment. + +Practically the alignment requirements are the same for buffer +alignment, buffer length, and offset in file. So in case we cannot +detect buf_align, we can use request alignment. If we cannot detect +request alignment, we can fallback to a safe value. To use this logic, +we probe first request alignment instead of buf_align. + +Here is a table showing the behaviour with current code (the value in +parenthesis is the optimal value). + +Case Sector buf_align (opt) request_alignment (opt) result +====================================================================== +1 512 512 (1) 512 (512) OK +1 4096 512 (1) 4096 (4096) FAIL +---------------------------------------------------------------------- +2 512 512 (512) 512 (512) OK +2 4096 512 (4096) 4096 (4096) FAIL +---------------------------------------------------------------------- +3 512 512 (1) 512 (512) OK +3 4096 512 (1) 512 (4096) FAIL +---------------------------------------------------------------------- +4 512 512 (1) 512 (1) OK +4 4096 512 (1) 512 (1) OK + +Same cases with this change: + +Case Sector buf_align (opt) request_alignment (opt) result +====================================================================== +1 512 512 (1) 512 (512) OK +1 4096 4096 (1) 4096 (4096) OK +---------------------------------------------------------------------- +2 512 512 (512) 512 (512) OK +2 4096 4096 (4096) 4096 (4096) OK +---------------------------------------------------------------------- +3 512 4096 (1) 4096 (512) OK +3 4096 4096 (1) 4096 (4096) OK +---------------------------------------------------------------------- +4 512 4096 (1) 4096 (1) OK +4 4096 4096 (1) 4096 (1) OK + +I tested that provisioning VMs and copying disks on local XFS and +Gluster with 4k bytes sector size work now, resolving bugs [1],[2]. +I tested also on XFS, NFS, Gluster with 512 bytes sector size. + +[1] https://bugzilla.redhat.com/1737256 +[2] https://bugzilla.redhat.com/1738657 + +Signed-off-by: Nir Soffer +Signed-off-by: Kevin Wolf +(cherry picked from commit a6b257a08e3d72219f03e461a52152672fec0612) +--- + block/file-posix.c | 36 +++++++++++++++++++++++++----------- + 1 file changed, 25 insertions(+), 11 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 4479cc7ab4..b8b4dad553 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -323,6 +323,7 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) + BDRVRawState *s = bs->opaque; + char *buf; + size_t max_align = MAX(MAX_BLOCKSIZE, getpagesize()); ++ size_t alignments[] = {1, 512, 1024, 2048, 4096}; + + /* For SCSI generic devices the alignment is not really used. + With buffered I/O, we don't have any restrictions. */ +@@ -349,25 +350,38 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) + } + #endif + +- /* If we could not get the sizes so far, we can only guess them */ +- if (!s->buf_align) { ++ /* ++ * If we could not get the sizes so far, we can only guess them. First try ++ * to detect request alignment, since it is more likely to succeed. Then ++ * try to detect buf_align, which cannot be detected in some cases (e.g. ++ * Gluster). If buf_align cannot be detected, we fallback to the value of ++ * request_alignment. ++ */ ++ ++ if (!bs->bl.request_alignment) { ++ int i; + size_t align; +- buf = qemu_memalign(max_align, 2 * max_align); +- for (align = 512; align <= max_align; align <<= 1) { +- if (raw_is_io_aligned(fd, buf + align, max_align)) { +- s->buf_align = align; ++ buf = qemu_memalign(max_align, max_align); ++ for (i = 0; i < ARRAY_SIZE(alignments); i++) { ++ align = alignments[i]; ++ if (raw_is_io_aligned(fd, buf, align)) { ++ /* Fallback to safe value. */ ++ bs->bl.request_alignment = (align != 1) ? align : max_align; + break; + } + } + qemu_vfree(buf); + } + +- if (!bs->bl.request_alignment) { ++ if (!s->buf_align) { ++ int i; + size_t align; +- buf = qemu_memalign(s->buf_align, max_align); +- for (align = 512; align <= max_align; align <<= 1) { +- if (raw_is_io_aligned(fd, buf, align)) { +- bs->bl.request_alignment = align; ++ buf = qemu_memalign(max_align, 2 * max_align); ++ for (i = 0; i < ARRAY_SIZE(alignments); i++) { ++ align = alignments[i]; ++ if (raw_is_io_aligned(fd, buf + align, max_align)) { ++ /* Fallback to request_aligment. */ ++ s->buf_align = (align != 1) ? align : bs->bl.request_alignment; + break; + } + } diff --git a/0002-block-posix-Always-allocate-the-first-block.patch b/0002-block-posix-Always-allocate-the-first-block.patch new file mode 100644 index 0000000..40a55b7 --- /dev/null +++ b/0002-block-posix-Always-allocate-the-first-block.patch @@ -0,0 +1,369 @@ +From: Nir Soffer +Date: Tue, 27 Aug 2019 04:05:27 +0300 +Subject: [PATCH] block: posix: Always allocate the first block + +When creating an image with preallocation "off" or "falloc", the first +block of the image is typically not allocated. When using Gluster +storage backed by XFS filesystem, reading this block using direct I/O +succeeds regardless of request length, fooling alignment detection. + +In this case we fallback to a safe value (4096) instead of the optimal +value (512), which may lead to unneeded data copying when aligning +requests. Allocating the first block avoids the fallback. + +Since we allocate the first block even with preallocation=off, we no +longer create images with zero disk size: + + $ ./qemu-img create -f raw test.raw 1g + Formatting 'test.raw', fmt=raw size=1073741824 + + $ ls -lhs test.raw + 4.0K -rw-r--r--. 1 nsoffer nsoffer 1.0G Aug 16 23:48 test.raw + +And converting the image requires additional cluster: + + $ ./qemu-img measure -f raw -O qcow2 test.raw + required size: 458752 + fully allocated size: 1074135040 + +When using format like vmdk with multiple files per image, we allocate +one block per file: + + $ ./qemu-img create -f vmdk -o subformat=twoGbMaxExtentFlat test.vmdk 4g + Formatting 'test.vmdk', fmt=vmdk size=4294967296 compat6=off hwversion=undefined subformat=twoGbMaxExtentFlat + + $ ls -lhs test*.vmdk + 4.0K -rw-r--r--. 1 nsoffer nsoffer 2.0G Aug 27 03:23 test-f001.vmdk + 4.0K -rw-r--r--. 1 nsoffer nsoffer 2.0G Aug 27 03:23 test-f002.vmdk + 4.0K -rw-r--r--. 1 nsoffer nsoffer 353 Aug 27 03:23 test.vmdk + +I did quick performance test for copying disks with qemu-img convert to +new raw target image to Gluster storage with sector size of 512 bytes: + + for i in $(seq 10); do + rm -f dst.raw + sleep 10 + time ./qemu-img convert -f raw -O raw -t none -T none src.raw dst.raw + done + +Here is a table comparing the total time spent: + +Type Before(s) After(s) Diff(%) +--------------------------------------- +real 530.028 469.123 -11.4 +user 17.204 10.768 -37.4 +sys 17.881 7.011 -60.7 + +We can see very clear improvement in CPU usage. + +Signed-off-by: Nir Soffer +Message-id: 20190827010528.8818-2-nsoffer@redhat.com +Reviewed-by: Max Reitz +Signed-off-by: Max Reitz +(cherry picked from commit 3a20013fbb26d2a1bd11ef148eefdb1508783787) +--- + block/file-posix.c | 51 ++++++++++++++++++++++++++++++++ + tests/qemu-iotests/059.out | 2 +- + tests/qemu-iotests/150.out | 11 ------- + tests/qemu-iotests/150.out.qcow2 | 11 +++++++ + tests/qemu-iotests/150.out.raw | 12 ++++++++ + tests/qemu-iotests/175 | 19 ++++++++---- + tests/qemu-iotests/175.out | 8 ++--- + tests/qemu-iotests/178.out.qcow2 | 4 +-- + tests/qemu-iotests/221.out | 12 +++++--- + tests/qemu-iotests/253.out | 12 +++++--- + 10 files changed, 110 insertions(+), 32 deletions(-) + delete mode 100644 tests/qemu-iotests/150.out + create mode 100644 tests/qemu-iotests/150.out.qcow2 + create mode 100644 tests/qemu-iotests/150.out.raw + +diff --git a/block/file-posix.c b/block/file-posix.c +index b8b4dad553..8ea98896ce 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -1749,6 +1749,43 @@ static int handle_aiocb_discard(void *opaque) + return ret; + } + ++/* ++ * Help alignment probing by allocating the first block. ++ * ++ * When reading with direct I/O from unallocated area on Gluster backed by XFS, ++ * reading succeeds regardless of request length. In this case we fallback to ++ * safe alignment which is not optimal. Allocating the first block avoids this ++ * fallback. ++ * ++ * fd may be opened with O_DIRECT, but we don't know the buffer alignment or ++ * request alignment, so we use safe values. ++ * ++ * Returns: 0 on success, -errno on failure. Since this is an optimization, ++ * caller may ignore failures. ++ */ ++static int allocate_first_block(int fd, size_t max_size) ++{ ++ size_t write_size = (max_size < MAX_BLOCKSIZE) ++ ? BDRV_SECTOR_SIZE ++ : MAX_BLOCKSIZE; ++ size_t max_align = MAX(MAX_BLOCKSIZE, getpagesize()); ++ void *buf; ++ ssize_t n; ++ int ret; ++ ++ buf = qemu_memalign(max_align, write_size); ++ memset(buf, 0, write_size); ++ ++ do { ++ n = pwrite(fd, buf, write_size, 0); ++ } while (n == -1 && errno == EINTR); ++ ++ ret = (n == -1) ? -errno : 0; ++ ++ qemu_vfree(buf); ++ return ret; ++} ++ + static int handle_aiocb_truncate(void *opaque) + { + RawPosixAIOData *aiocb = opaque; +@@ -1788,6 +1825,17 @@ static int handle_aiocb_truncate(void *opaque) + /* posix_fallocate() doesn't set errno. */ + error_setg_errno(errp, -result, + "Could not preallocate new data"); ++ } else if (current_length == 0) { ++ /* ++ * posix_fallocate() uses fallocate() if the filesystem ++ * supports it, or fallback to manually writing zeroes. If ++ * fallocate() was used, unaligned reads from the fallocated ++ * area in raw_probe_alignment() will succeed, hence we need to ++ * allocate the first block. ++ * ++ * Optimize future alignment probing; ignore failures. ++ */ ++ allocate_first_block(fd, offset); + } + } else { + result = 0; +@@ -1849,6 +1897,9 @@ static int handle_aiocb_truncate(void *opaque) + if (ftruncate(fd, offset) != 0) { + result = -errno; + error_setg_errno(errp, -result, "Could not resize file"); ++ } else if (current_length == 0 && offset > current_length) { ++ /* Optimize future alignment probing; ignore failures. */ ++ allocate_first_block(fd, offset); + } + return result; + default: +diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out +index 4fab42a28c..fe3f861f3c 100644 +--- a/tests/qemu-iotests/059.out ++++ b/tests/qemu-iotests/059.out +@@ -27,7 +27,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824000 subformat=twoGbMax + image: TEST_DIR/t.vmdk + file format: vmdk + virtual size: 0.977 TiB (1073741824000 bytes) +-disk size: 16 KiB ++disk size: 1.97 MiB + Format specific information: + cid: XXXXXXXX + parent cid: XXXXXXXX +diff --git a/tests/qemu-iotests/150.out b/tests/qemu-iotests/150.out +deleted file mode 100644 +index 2a54e8dcfa..0000000000 +--- a/tests/qemu-iotests/150.out ++++ /dev/null +@@ -1,11 +0,0 @@ +-QA output created by 150 +- +-=== Mapping sparse conversion === +- +-Offset Length File +- +-=== Mapping non-sparse conversion === +- +-Offset Length File +-0 0x100000 TEST_DIR/t.IMGFMT +-*** done +diff --git a/tests/qemu-iotests/150.out.qcow2 b/tests/qemu-iotests/150.out.qcow2 +new file mode 100644 +index 0000000000..2a54e8dcfa +--- /dev/null ++++ b/tests/qemu-iotests/150.out.qcow2 +@@ -0,0 +1,11 @@ ++QA output created by 150 ++ ++=== Mapping sparse conversion === ++ ++Offset Length File ++ ++=== Mapping non-sparse conversion === ++ ++Offset Length File ++0 0x100000 TEST_DIR/t.IMGFMT ++*** done +diff --git a/tests/qemu-iotests/150.out.raw b/tests/qemu-iotests/150.out.raw +new file mode 100644 +index 0000000000..3cdc7727a5 +--- /dev/null ++++ b/tests/qemu-iotests/150.out.raw +@@ -0,0 +1,12 @@ ++QA output created by 150 ++ ++=== Mapping sparse conversion === ++ ++Offset Length File ++0 0x1000 TEST_DIR/t.IMGFMT ++ ++=== Mapping non-sparse conversion === ++ ++Offset Length File ++0 0x100000 TEST_DIR/t.IMGFMT ++*** done +diff --git a/tests/qemu-iotests/175 b/tests/qemu-iotests/175 +index 51e62c8276..7ba28b3c1b 100755 +--- a/tests/qemu-iotests/175 ++++ b/tests/qemu-iotests/175 +@@ -37,14 +37,16 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 + # the file size. This function hides the resulting difference in the + # stat -c '%b' output. + # Parameter 1: Number of blocks an empty file occupies +-# Parameter 2: Image size in bytes ++# Parameter 2: Minimal number of blocks in an image ++# Parameter 3: Image size in bytes + _filter_blocks() + { + extra_blocks=$1 +- img_size=$2 ++ min_blocks=$2 ++ img_size=$3 + +- sed -e "s/blocks=$extra_blocks\\(\$\\|[^0-9]\\)/nothing allocated/" \ +- -e "s/blocks=$((extra_blocks + img_size / 512))\\(\$\\|[^0-9]\\)/everything allocated/" ++ sed -e "s/blocks=$min_blocks\\(\$\\|[^0-9]\\)/min allocation/" \ ++ -e "s/blocks=$((extra_blocks + img_size / 512))\\(\$\\|[^0-9]\\)/max allocation/" + } + + # get standard environment, filters and checks +@@ -60,16 +62,21 @@ size=$((1 * 1024 * 1024)) + touch "$TEST_DIR/empty" + extra_blocks=$(stat -c '%b' "$TEST_DIR/empty") + ++# We always write the first byte; check how many blocks this filesystem ++# allocates to match empty image alloation. ++printf "\0" > "$TEST_DIR/empty" ++min_blocks=$(stat -c '%b' "$TEST_DIR/empty") ++ + echo + echo "== creating image with default preallocation ==" + _make_test_img $size | _filter_imgfmt +-stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $size ++stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_blocks $size + + for mode in off full falloc; do + echo + echo "== creating image with preallocation $mode ==" + IMGOPTS=preallocation=$mode _make_test_img $size | _filter_imgfmt +- stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $size ++ stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_blocks $size + done + + # success, all done +diff --git a/tests/qemu-iotests/175.out b/tests/qemu-iotests/175.out +index 6d9a5ed84e..263e521262 100644 +--- a/tests/qemu-iotests/175.out ++++ b/tests/qemu-iotests/175.out +@@ -2,17 +2,17 @@ QA output created by 175 + + == creating image with default preallocation == + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 +-size=1048576, nothing allocated ++size=1048576, min allocation + + == creating image with preallocation off == + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=off +-size=1048576, nothing allocated ++size=1048576, min allocation + + == creating image with preallocation full == + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=full +-size=1048576, everything allocated ++size=1048576, max allocation + + == creating image with preallocation falloc == + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=falloc +-size=1048576, everything allocated ++size=1048576, max allocation + *** done +diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2 +index 55a8dc926f..9e7d8c44df 100644 +--- a/tests/qemu-iotests/178.out.qcow2 ++++ b/tests/qemu-iotests/178.out.qcow2 +@@ -101,7 +101,7 @@ converted image file size in bytes: 196608 + == raw input image with data (human) == + + Formatting 'TEST_DIR/t.qcow2', fmt=IMGFMT size=1073741824 +-required size: 393216 ++required size: 458752 + fully allocated size: 1074135040 + wrote 512/512 bytes at offset 512 + 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +@@ -257,7 +257,7 @@ converted image file size in bytes: 196608 + + Formatting 'TEST_DIR/t.qcow2', fmt=IMGFMT size=1073741824 + { +- "required": 393216, ++ "required": 458752, + "fully-allocated": 1074135040 + } + wrote 512/512 bytes at offset 512 +diff --git a/tests/qemu-iotests/221.out b/tests/qemu-iotests/221.out +index 9f9dd52bb0..dca024a0c3 100644 +--- a/tests/qemu-iotests/221.out ++++ b/tests/qemu-iotests/221.out +@@ -3,14 +3,18 @@ QA output created by 221 + === Check mapping of unaligned raw image === + + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65537 +-[{ "start": 0, "length": 66048, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] +-[{ "start": 0, "length": 66048, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] ++[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, ++{ "start": 4096, "length": 61952, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] ++[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, ++{ "start": 4096, "length": 61952, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] + wrote 1/1 bytes at offset 65536 + 1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +-[{ "start": 0, "length": 65536, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, ++[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, ++{ "start": 4096, "length": 61440, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, + { "start": 65536, "length": 1, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, + { "start": 65537, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] +-[{ "start": 0, "length": 65536, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, ++[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, ++{ "start": 4096, "length": 61440, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, + { "start": 65536, "length": 1, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, + { "start": 65537, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] + *** done +diff --git a/tests/qemu-iotests/253.out b/tests/qemu-iotests/253.out +index 607c0baa0b..3d08b305d7 100644 +--- a/tests/qemu-iotests/253.out ++++ b/tests/qemu-iotests/253.out +@@ -3,12 +3,16 @@ QA output created by 253 + === Check mapping of unaligned raw image === + + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048575 +-[{ "start": 0, "length": 1048576, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] +-[{ "start": 0, "length": 1048576, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] ++[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, ++{ "start": 4096, "length": 1044480, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] ++[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, ++{ "start": 4096, "length": 1044480, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] + wrote 65535/65535 bytes at offset 983040 + 63.999 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +-[{ "start": 0, "length": 983040, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, ++[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, ++{ "start": 4096, "length": 978944, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, + { "start": 983040, "length": 65536, "depth": 0, "zero": false, "data": true, "offset": OFFSET}] +-[{ "start": 0, "length": 983040, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, ++[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, ++{ "start": 4096, "length": 978944, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, + { "start": 983040, "length": 65536, "depth": 0, "zero": false, "data": true, "offset": OFFSET}] + *** done diff --git a/qemu.spec b/qemu.spec index df5b9d7..dffe3b9 100644 --- a/qemu.spec +++ b/qemu.spec @@ -148,7 +148,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 4.1.0 -Release: 1%{?rcrel}%{?dist} +Release: 2%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -172,6 +172,10 @@ Source20: kvm-x86.modprobe.conf # /etc/security/limits.d/95-kvm-ppc64-memlock.conf Source21: 95-kvm-ppc64-memlock.conf +# gluster 4K block size fixes (bz #1737256) +Patch0001: 0001-file-posix-Handle-undetectable-alignment.patch +Patch0002: 0002-block-posix-Always-allocate-the-first-block.patch + # documentation deps BuildRequires: texinfo @@ -1854,6 +1858,9 @@ getent passwd qemu >/dev/null || \ %changelog +* Wed Sep 04 2019 Cole Robinson - 2:4.1.0-2 +- gluster 4K block size fixes (bz #1737256) + * Mon Aug 19 2019 Cole Robinson - 2:4.1.0-1 - Update to qemu-4.1.0 GA From c36918674fb34422b5a4324878cf3afe44b19030 Mon Sep 17 00:00:00 2001 From: Leigh Scott Date: Wed, 11 Sep 2019 11:33:43 +0100 Subject: [PATCH 02/32] Rebuild for new libnfs version --- qemu.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qemu.spec b/qemu.spec index dffe3b9..3f394c6 100644 --- a/qemu.spec +++ b/qemu.spec @@ -148,7 +148,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 4.1.0 -Release: 2%{?rcrel}%{?dist} +Release: 3%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -1858,6 +1858,9 @@ getent passwd qemu >/dev/null || \ %changelog +* Wed Sep 11 2019 Leigh Scott - 2:4.1.0-3 +- Rebuild for new libnfs version + * Wed Sep 04 2019 Cole Robinson - 2:4.1.0-2 - gluster 4K block size fixes (bz #1737256) From 481596d7a6cab3dcf9ed2570a4d51d9ddf07f0ec Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Thu, 3 Oct 2019 13:43:23 -0400 Subject: [PATCH 03/32] qemu-4.1.0-4.fc32 Rebuild for new virglrenderer --- qemu.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qemu.spec b/qemu.spec index 3f394c6..ea32d01 100644 --- a/qemu.spec +++ b/qemu.spec @@ -148,7 +148,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 4.1.0 -Release: 3%{?rcrel}%{?dist} +Release: 4%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -1858,6 +1858,9 @@ getent passwd qemu >/dev/null || \ %changelog +* Thu Oct 03 2019 Cole Robinson - 2:4.1.0-4 +- Rebuild for new virglrenderer + * Wed Sep 11 2019 Leigh Scott - 2:4.1.0-3 - Rebuild for new libnfs version From 964eff6ae88de7b7ae928b3b9965381ba33aeb85 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Fri, 18 Oct 2019 10:51:02 -0400 Subject: [PATCH 04/32] Fix tests on kernel 5.3+ Signed-off-by: Cole Robinson --- ...onitor-test-more-robust-to-event-ord.patch | 134 ++++++++++++++++++ qemu.spec | 2 + 2 files changed, 136 insertions(+) create mode 100644 0003-tests-make-filemonitor-test-more-robust-to-event-ord.patch diff --git a/0003-tests-make-filemonitor-test-more-robust-to-event-ord.patch b/0003-tests-make-filemonitor-test-more-robust-to-event-ord.patch new file mode 100644 index 0000000..bce580d --- /dev/null +++ b/0003-tests-make-filemonitor-test-more-robust-to-event-ord.patch @@ -0,0 +1,134 @@ +From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= +Date: Wed, 21 Aug 2019 16:14:27 +0100 +Subject: [PATCH] tests: make filemonitor test more robust to event ordering +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The ordering of events that are emitted during the rmdir +test have changed with kernel >= 5.3. Semantically both +new & old orderings are correct, so we must be able to +cope with either. + +To cope with this, when we see an unexpected event, we +push it back onto the queue and look and the subsequent +event to see if that matches instead. + +Tested-by: Peter Xu +Tested-by: Wei Yang +Tested-by: Cornelia Huck +Reviewed-by: Thomas Huth +Signed-off-by: Daniel P. Berrangé +(cherry picked from commit bf9e0313c27d8e6ecd7f7de3d63e1cb25d8f6311) +--- + tests/test-util-filemonitor.c | 43 +++++++++++++++++++++++++++-------- + 1 file changed, 34 insertions(+), 9 deletions(-) + +diff --git a/tests/test-util-filemonitor.c b/tests/test-util-filemonitor.c +index 46e781c022..301cd2db61 100644 +--- a/tests/test-util-filemonitor.c ++++ b/tests/test-util-filemonitor.c +@@ -45,6 +45,11 @@ typedef struct { + const char *filedst; + int64_t *watchid; + int eventid; ++ /* ++ * Only valid with OP_EVENT - this event might be ++ * swapped with the next OP_EVENT ++ */ ++ bool swapnext; + } QFileMonitorTestOp; + + typedef struct { +@@ -98,6 +103,10 @@ qemu_file_monitor_test_handler(int64_t id, + QFileMonitorTestData *data = opaque; + QFileMonitorTestRecord *rec = g_new0(QFileMonitorTestRecord, 1); + ++ if (debug) { ++ g_printerr("Queue event id %" PRIx64 " event %d file %s\n", ++ id, event, filename); ++ } + rec->id = id; + rec->event = event; + rec->filename = g_strdup(filename); +@@ -125,7 +134,8 @@ qemu_file_monitor_test_record_free(QFileMonitorTestRecord *rec) + * to wait for the event to be queued for us. + */ + static QFileMonitorTestRecord * +-qemu_file_monitor_test_next_record(QFileMonitorTestData *data) ++qemu_file_monitor_test_next_record(QFileMonitorTestData *data, ++ QFileMonitorTestRecord *pushback) + { + GTimer *timer = g_timer_new(); + QFileMonitorTestRecord *record = NULL; +@@ -139,9 +149,15 @@ qemu_file_monitor_test_next_record(QFileMonitorTestData *data) + } + if (data->records) { + record = data->records->data; +- tmp = data->records; +- data->records = g_list_remove_link(data->records, tmp); +- g_list_free(tmp); ++ if (pushback) { ++ data->records->data = pushback; ++ } else { ++ tmp = data->records; ++ data->records = g_list_remove_link(data->records, tmp); ++ g_list_free(tmp); ++ } ++ } else if (pushback) { ++ qemu_file_monitor_test_record_free(pushback); + } + qemu_mutex_unlock(&data->lock); + +@@ -158,13 +174,15 @@ static bool + qemu_file_monitor_test_expect(QFileMonitorTestData *data, + int64_t id, + QFileMonitorEvent event, +- const char *filename) ++ const char *filename, ++ bool swapnext) + { + QFileMonitorTestRecord *rec; + bool ret = false; + +- rec = qemu_file_monitor_test_next_record(data); ++ rec = qemu_file_monitor_test_next_record(data, NULL); + ++ retry: + if (!rec) { + g_printerr("Missing event watch id %" PRIx64 " event %d file %s\n", + id, event, filename); +@@ -172,6 +190,11 @@ qemu_file_monitor_test_expect(QFileMonitorTestData *data, + } + + if (id != rec->id) { ++ if (swapnext) { ++ rec = qemu_file_monitor_test_next_record(data, rec); ++ swapnext = false; ++ goto retry; ++ } + g_printerr("Expected watch id %" PRIx64 " but got %" PRIx64 "\n", + id, rec->id); + goto cleanup; +@@ -347,7 +370,8 @@ test_file_monitor_events(void) + .filesrc = "fish", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "", .watchid = &watch4, +- .eventid = QFILE_MONITOR_EVENT_IGNORED }, ++ .eventid = QFILE_MONITOR_EVENT_IGNORED, ++ .swapnext = true }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "fish", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, +@@ -493,8 +517,9 @@ test_file_monitor_events(void) + g_printerr("Event id=%" PRIx64 " event=%d file=%s\n", + *op->watchid, op->eventid, op->filesrc); + } +- if (!qemu_file_monitor_test_expect( +- &data, *op->watchid, op->eventid, op->filesrc)) ++ if (!qemu_file_monitor_test_expect(&data, *op->watchid, ++ op->eventid, op->filesrc, ++ op->swapnext)) + goto cleanup; + break; + case QFILE_MONITOR_TEST_OP_CREATE: diff --git a/qemu.spec b/qemu.spec index ea32d01..e7eae22 100644 --- a/qemu.spec +++ b/qemu.spec @@ -175,6 +175,8 @@ Source21: 95-kvm-ppc64-memlock.conf # gluster 4K block size fixes (bz #1737256) Patch0001: 0001-file-posix-Handle-undetectable-alignment.patch Patch0002: 0002-block-posix-Always-allocate-the-first-block.patch +# Fix tests on kernel 5.3+ +Patch0003: 0003-tests-make-filemonitor-test-more-robust-to-event-ord.patch # documentation deps From 918c70b1aa93b435981f93300dd46baf8f3c8644 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Fri, 18 Oct 2019 10:54:16 -0400 Subject: [PATCH 05/32] spec: Disable any -tcg usage for -user builds Signed-off-by: Cole Robinson --- qemu.spec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qemu.spec b/qemu.spec index e7eae22..b672dff 100644 --- a/qemu.spec +++ b/qemu.spec @@ -1032,6 +1032,7 @@ run_configure_disable_everything() { --disable-sparse \ --disable-spice \ --disable-system \ + --disable-tcg \ --disable-tcmalloc \ --disable-tools \ --disable-tpm \ @@ -1090,6 +1091,7 @@ run_configure \ --audio-drv-list=pa,sdl,alsa,oss \ --enable-kvm \ --enable-system \ + --enable-tcg \ --enable-linux-user \ --enable-pie \ --enable-modules \ From f4c127bbc1dcd84e1cac33169416025a9d91f4b3 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Thu, 24 Oct 2019 11:34:55 -0400 Subject: [PATCH 06/32] qemu-4.1.0-5.fc32 Workaround for qcow2 triggered XFS corruption (bz #1763519) --- ...id-recursive-block_status-call-if-po.patch | 224 ++++++++++++++++++ qemu.spec | 7 +- 2 files changed, 230 insertions(+), 1 deletion(-) create mode 100644 0004-Revert-block-avoid-recursive-block_status-call-if-po.patch diff --git a/0004-Revert-block-avoid-recursive-block_status-call-if-po.patch b/0004-Revert-block-avoid-recursive-block_status-call-if-po.patch new file mode 100644 index 0000000..6edadc7 --- /dev/null +++ b/0004-Revert-block-avoid-recursive-block_status-call-if-po.patch @@ -0,0 +1,224 @@ +From: Cole Robinson +Date: Thu, 24 Oct 2019 11:32:46 -0400 +Subject: [PATCH] Revert "block: avoid recursive block_status call if possible" + +This reverts commit 69f47505ee66afaa513305de0c1895a224e52c45. + +Workaround for qcow2 triggered XFS corruption, bug 1763519 +--- + block/io.c | 9 +-------- + block/qcow2-refcount.c | 32 -------------------------------- + block/qcow2.c | 11 ----------- + block/qcow2.h | 4 ---- + include/block/block.h | 8 +------- + tests/qemu-iotests/102 | 2 +- + tests/qemu-iotests/102.out | 3 +-- + tests/qemu-iotests/141.out | 2 +- + tests/qemu-iotests/144.out | 2 +- + 9 files changed, 6 insertions(+), 67 deletions(-) + +diff --git a/block/io.c b/block/io.c +index 06305c6ea6..ef057ba5db 100644 +--- a/block/io.c ++++ b/block/io.c +@@ -2156,12 +2156,6 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, + */ + assert(*pnum && QEMU_IS_ALIGNED(*pnum, align) && + align > offset - aligned_offset); +- if (ret & BDRV_BLOCK_RECURSE) { +- assert(ret & BDRV_BLOCK_DATA); +- assert(ret & BDRV_BLOCK_OFFSET_VALID); +- assert(!(ret & BDRV_BLOCK_ZERO)); +- } +- + *pnum -= offset - aligned_offset; + if (*pnum > bytes) { + *pnum = bytes; +@@ -2192,8 +2186,7 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, + } + } + +- if (want_zero && ret & BDRV_BLOCK_RECURSE && +- local_file && local_file != bs && ++ if (want_zero && local_file && local_file != bs && + (ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO) && + (ret & BDRV_BLOCK_OFFSET_VALID)) { + int64_t file_pnum; +diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c +index ef965d7895..81ecb88f9c 100644 +--- a/block/qcow2-refcount.c ++++ b/block/qcow2-refcount.c +@@ -3448,35 +3448,3 @@ int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size) + "There are no references in the refcount table."); + return -EIO; + } +- +-int qcow2_detect_metadata_preallocation(BlockDriverState *bs) +-{ +- BDRVQcow2State *s = bs->opaque; +- int64_t i, end_cluster, cluster_count = 0, threshold; +- int64_t file_length, real_allocation, real_clusters; +- +- file_length = bdrv_getlength(bs->file->bs); +- if (file_length < 0) { +- return file_length; +- } +- +- real_allocation = bdrv_get_allocated_file_size(bs->file->bs); +- if (real_allocation < 0) { +- return real_allocation; +- } +- +- real_clusters = real_allocation / s->cluster_size; +- threshold = MAX(real_clusters * 10 / 9, real_clusters + 2); +- +- end_cluster = size_to_clusters(s, file_length); +- for (i = 0; i < end_cluster && cluster_count < threshold; i++) { +- uint64_t refcount; +- int ret = qcow2_get_refcount(bs, i, &refcount); +- if (ret < 0) { +- return ret; +- } +- cluster_count += !!refcount; +- } +- +- return cluster_count >= threshold; +-} +diff --git a/block/qcow2.c b/block/qcow2.c +index 039bdc2f7e..86e88f6af4 100644 +--- a/block/qcow2.c ++++ b/block/qcow2.c +@@ -1895,12 +1895,6 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, + unsigned int bytes; + int status = 0; + +- if (!s->metadata_preallocation_checked) { +- ret = qcow2_detect_metadata_preallocation(bs); +- s->metadata_preallocation = (ret == 1); +- s->metadata_preallocation_checked = true; +- } +- + bytes = MIN(INT_MAX, count); + qemu_co_mutex_lock(&s->lock); + ret = qcow2_get_cluster_offset(bs, offset, &bytes, &cluster_offset); +@@ -1923,11 +1917,6 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, + } else if (ret != QCOW2_CLUSTER_UNALLOCATED) { + status |= BDRV_BLOCK_DATA; + } +- if (s->metadata_preallocation && (status & BDRV_BLOCK_DATA) && +- (status & BDRV_BLOCK_OFFSET_VALID)) +- { +- status |= BDRV_BLOCK_RECURSE; +- } + return status; + } + +diff --git a/block/qcow2.h b/block/qcow2.h +index fc1b0d3c1e..567375e56c 100644 +--- a/block/qcow2.h ++++ b/block/qcow2.h +@@ -356,9 +356,6 @@ typedef struct BDRVQcow2State { + int nb_threads; + + BdrvChild *data_file; +- +- bool metadata_preallocation_checked; +- bool metadata_preallocation; + } BDRVQcow2State; + + typedef struct Qcow2COWRegion { +@@ -658,7 +655,6 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, + void *cb_opaque, Error **errp); + int qcow2_shrink_reftable(BlockDriverState *bs); + int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size); +-int qcow2_detect_metadata_preallocation(BlockDriverState *bs); + + /* qcow2-cluster.c functions */ + int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, +diff --git a/include/block/block.h b/include/block/block.h +index 50a07c1c33..269c0931e7 100644 +--- a/include/block/block.h ++++ b/include/block/block.h +@@ -156,15 +156,10 @@ typedef struct HDGeometry { + * BDRV_BLOCK_EOF: the returned pnum covers through end of file for this + * layer, set by block layer + * +- * Internal flags: ++ * Internal flag: + * BDRV_BLOCK_RAW: for use by passthrough drivers, such as raw, to request + * that the block layer recompute the answer from the returned + * BDS; must be accompanied by just BDRV_BLOCK_OFFSET_VALID. +- * BDRV_BLOCK_RECURSE: request that the block layer will recursively search for +- * zeroes in file child of current block node inside +- * returned region. Only valid together with both +- * BDRV_BLOCK_DATA and BDRV_BLOCK_OFFSET_VALID. Should not +- * appear with BDRV_BLOCK_ZERO. + * + * If BDRV_BLOCK_OFFSET_VALID is set, the map parameter represents the + * host offset within the returned BDS that is allocated for the +@@ -189,7 +184,6 @@ typedef struct HDGeometry { + #define BDRV_BLOCK_RAW 0x08 + #define BDRV_BLOCK_ALLOCATED 0x10 + #define BDRV_BLOCK_EOF 0x20 +-#define BDRV_BLOCK_RECURSE 0x40 + #define BDRV_BLOCK_OFFSET_MASK BDRV_SECTOR_MASK + + typedef QSIMPLEQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue; +diff --git a/tests/qemu-iotests/102 b/tests/qemu-iotests/102 +index b898df436f..749ff66b8a 100755 +--- a/tests/qemu-iotests/102 ++++ b/tests/qemu-iotests/102 +@@ -55,7 +55,7 @@ $QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io + $QEMU_IMG resize -f raw --shrink "$TEST_IMG" $((5 * 64 * 1024)) + + $QEMU_IO -c map "$TEST_IMG" +-$QEMU_IMG map "$TEST_IMG" | _filter_qemu_img_map ++$QEMU_IMG map "$TEST_IMG" + + echo + echo '=== Testing map on an image file truncated outside of qemu ===' +diff --git a/tests/qemu-iotests/102.out b/tests/qemu-iotests/102.out +index cd2fdc7f96..4401b08fee 100644 +--- a/tests/qemu-iotests/102.out ++++ b/tests/qemu-iotests/102.out +@@ -7,8 +7,7 @@ wrote 65536/65536 bytes at offset 0 + 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + Image resized. + 64 KiB (0x10000) bytes allocated at offset 0 bytes (0x0) +-Offset Length File +-0 0x10000 TEST_DIR/t.IMGFMT ++Offset Length Mapped to File + + === Testing map on an image file truncated outside of qemu === + +diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out +index 4d71d9dcae..41c7291258 100644 +--- a/tests/qemu-iotests/141.out ++++ b/tests/qemu-iotests/141.out +@@ -42,9 +42,9 @@ Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t. + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} ++{"return": {}} + {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: commit"}} + {"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}} +diff --git a/tests/qemu-iotests/144.out b/tests/qemu-iotests/144.out +index a9a8216bea..55299201e4 100644 +--- a/tests/qemu-iotests/144.out ++++ b/tests/qemu-iotests/144.out +@@ -14,10 +14,10 @@ Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/ + + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "virtio0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "virtio0"}} +-{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "virtio0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} + {"return": {}} ++{"return": {}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "virtio0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "virtio0"}} + {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} diff --git a/qemu.spec b/qemu.spec index b672dff..5c6e672 100644 --- a/qemu.spec +++ b/qemu.spec @@ -148,7 +148,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 4.1.0 -Release: 4%{?rcrel}%{?dist} +Release: 5%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -177,6 +177,8 @@ Patch0001: 0001-file-posix-Handle-undetectable-alignment.patch Patch0002: 0002-block-posix-Always-allocate-the-first-block.patch # Fix tests on kernel 5.3+ Patch0003: 0003-tests-make-filemonitor-test-more-robust-to-event-ord.patch +# Workaround for qcow2 triggered XFS corruption (bz #1763519) +Patch0004: 0004-Revert-block-avoid-recursive-block_status-call-if-po.patch # documentation deps @@ -1862,6 +1864,9 @@ getent passwd qemu >/dev/null || \ %changelog +* Thu Oct 24 2019 Cole Robinson - 2:4.1.0-5 +- Workaround for qcow2 triggered XFS corruption (bz #1763519) + * Thu Oct 03 2019 Cole Robinson - 2:4.1.0-4 - Rebuild for new virglrenderer From fe24ece8af2ec7b1c4f26a495451b8e47ec1427d Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Thu, 24 Oct 2019 14:04:01 -0400 Subject: [PATCH 07/32] Use --enable-tcg for static builds, apparently it's required Signed-off-by: Cole Robinson --- qemu.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/qemu.spec b/qemu.spec index 5c6e672..c4b6f75 100644 --- a/qemu.spec +++ b/qemu.spec @@ -1076,6 +1076,7 @@ run_configure_disable_everything \ --disable-pie \ --enable-attr \ --enable-linux-user \ + --enable-tcg \ --static make V=1 %{?_smp_mflags} $buildldflags From 8e6758e973cf907759acdf08bb7ad8f2315b6498 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Mon, 11 Nov 2019 09:33:46 -0500 Subject: [PATCH 08/32] qemu-4.1.0-6.fc32 Fix compressed qcow2 'qemu-img check' errors (bz #1768541) --- ...ow2-Fix-QCOW2_COMPRESSED_SECTOR_MASK.patch | 27 +++++++++++++++++++ qemu.spec | 7 ++++- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 0005-qcow2-Fix-QCOW2_COMPRESSED_SECTOR_MASK.patch diff --git a/0005-qcow2-Fix-QCOW2_COMPRESSED_SECTOR_MASK.patch b/0005-qcow2-Fix-QCOW2_COMPRESSED_SECTOR_MASK.patch new file mode 100644 index 0000000..eb76ab9 --- /dev/null +++ b/0005-qcow2-Fix-QCOW2_COMPRESSED_SECTOR_MASK.patch @@ -0,0 +1,27 @@ +From: Max Reitz +Date: Mon, 28 Oct 2019 17:18:40 +0100 +Subject: [PATCH] qcow2: Fix QCOW2_COMPRESSED_SECTOR_MASK + +Masks for L2 table entries should have 64 bit. + +Fixes: b6c246942b14d3e0dec46a6c5868ed84e7dbea19 +Buglink: https://bugs.launchpad.net/qemu/+bug/1850000 +Cc: qemu-stable@nongnu.org +Signed-off-by: Max Reitz +--- + block/qcow2.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/block/qcow2.h b/block/qcow2.h +index 567375e56c..45f9585c08 100644 +--- a/block/qcow2.h ++++ b/block/qcow2.h +@@ -77,7 +77,7 @@ + + /* Defined in the qcow2 spec (compressed cluster descriptor) */ + #define QCOW2_COMPRESSED_SECTOR_SIZE 512U +-#define QCOW2_COMPRESSED_SECTOR_MASK (~(QCOW2_COMPRESSED_SECTOR_SIZE - 1)) ++#define QCOW2_COMPRESSED_SECTOR_MASK (~(QCOW2_COMPRESSED_SECTOR_SIZE - 1ULL)) + + /* Must be at least 2 to cover COW */ + #define MIN_L2_CACHE_SIZE 2 /* cache entries */ diff --git a/qemu.spec b/qemu.spec index c4b6f75..b523bcd 100644 --- a/qemu.spec +++ b/qemu.spec @@ -148,7 +148,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 4.1.0 -Release: 5%{?rcrel}%{?dist} +Release: 6%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -179,6 +179,8 @@ Patch0002: 0002-block-posix-Always-allocate-the-first-block.patch Patch0003: 0003-tests-make-filemonitor-test-more-robust-to-event-ord.patch # Workaround for qcow2 triggered XFS corruption (bz #1763519) Patch0004: 0004-Revert-block-avoid-recursive-block_status-call-if-po.patch +# Fix compressed qcow2 'qemu-img check' errors (bz #1768541) +Patch0005: 0005-qcow2-Fix-QCOW2_COMPRESSED_SECTOR_MASK.patch # documentation deps @@ -1865,6 +1867,9 @@ getent passwd qemu >/dev/null || \ %changelog +* Mon Nov 11 2019 Cole Robinson - 2:4.1.0-6 +- Fix compressed qcow2 'qemu-img check' errors (bz #1768541) + * Thu Oct 24 2019 Cole Robinson - 2:4.1.0-5 - Workaround for qcow2 triggered XFS corruption (bz #1763519) From 0038f84388a010a82d2479ff1572ecd7c2e90a26 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 12 Nov 2019 15:47:45 -0500 Subject: [PATCH 09/32] Update to qemu 4.2.0 rc1 Signed-off-by: Cole Robinson --- .gitignore | 1 + ...-posix-Handle-undetectable-alignment.patch | 151 ------- ...osix-Always-allocate-the-first-block.patch | 369 ------------------ ...onitor-test-more-robust-to-event-ord.patch | 134 ------- ...id-recursive-block_status-call-if-po.patch | 224 ----------- ...ow2-Fix-QCOW2_COMPRESSED_SECTOR_MASK.patch | 27 -- qemu.spec | 39 +- sources | 2 +- 8 files changed, 22 insertions(+), 925 deletions(-) delete mode 100644 0001-file-posix-Handle-undetectable-alignment.patch delete mode 100644 0002-block-posix-Always-allocate-the-first-block.patch delete mode 100644 0003-tests-make-filemonitor-test-more-robust-to-event-ord.patch delete mode 100644 0004-Revert-block-avoid-recursive-block_status-call-if-po.patch delete mode 100644 0005-qcow2-Fix-QCOW2_COMPRESSED_SECTOR_MASK.patch diff --git a/.gitignore b/.gitignore index 1da195b..e9e9dc2 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,4 @@ /qemu-4.1.0-rc1.tar.xz /qemu-4.1.0-rc2.tar.xz /qemu-4.1.0.tar.xz +/qemu-4.2.0-rc1.tar.xz diff --git a/0001-file-posix-Handle-undetectable-alignment.patch b/0001-file-posix-Handle-undetectable-alignment.patch deleted file mode 100644 index 01ab7b6..0000000 --- a/0001-file-posix-Handle-undetectable-alignment.patch +++ /dev/null @@ -1,151 +0,0 @@ -From: Nir Soffer -Date: Tue, 13 Aug 2019 21:21:03 +0300 -Subject: [PATCH] file-posix: Handle undetectable alignment - -In some cases buf_align or request_alignment cannot be detected: - -1. With Gluster, buf_align cannot be detected since the actual I/O is - done on Gluster server, and qemu buffer alignment does not matter. - Since we don't have alignment requirement, buf_align=1 is the best - value. - -2. With local XFS filesystem, buf_align cannot be detected if reading - from unallocated area. In this we must align the buffer, but we don't - know what is the correct size. Using the wrong alignment results in - I/O error. - -3. With Gluster backed by XFS, request_alignment cannot be detected if - reading from unallocated area. In this case we need to use the - correct alignment, and failing to do so results in I/O errors. - -4. With NFS, the server does not use direct I/O, so both buf_align cannot - be detected. In this case we don't need any alignment so we can use - buf_align=1 and request_alignment=1. - -These cases seems to work when storage sector size is 512 bytes, because -the current code starts checking align=512. If the check succeeds -because alignment cannot be detected we use 512. But this does not work -for storage with 4k sector size. - -To determine if we can detect the alignment, we probe first with -align=1. If probing succeeds, maybe there are no alignment requirement -(cases 1, 4) or we are probing unallocated area (cases 2, 3). Since we -don't have any way to tell, we treat this as undetectable alignment. If -probing with align=1 fails with EINVAL, but probing with one of the -expected alignments succeeds, we know that we found a working alignment. - -Practically the alignment requirements are the same for buffer -alignment, buffer length, and offset in file. So in case we cannot -detect buf_align, we can use request alignment. If we cannot detect -request alignment, we can fallback to a safe value. To use this logic, -we probe first request alignment instead of buf_align. - -Here is a table showing the behaviour with current code (the value in -parenthesis is the optimal value). - -Case Sector buf_align (opt) request_alignment (opt) result -====================================================================== -1 512 512 (1) 512 (512) OK -1 4096 512 (1) 4096 (4096) FAIL ----------------------------------------------------------------------- -2 512 512 (512) 512 (512) OK -2 4096 512 (4096) 4096 (4096) FAIL ----------------------------------------------------------------------- -3 512 512 (1) 512 (512) OK -3 4096 512 (1) 512 (4096) FAIL ----------------------------------------------------------------------- -4 512 512 (1) 512 (1) OK -4 4096 512 (1) 512 (1) OK - -Same cases with this change: - -Case Sector buf_align (opt) request_alignment (opt) result -====================================================================== -1 512 512 (1) 512 (512) OK -1 4096 4096 (1) 4096 (4096) OK ----------------------------------------------------------------------- -2 512 512 (512) 512 (512) OK -2 4096 4096 (4096) 4096 (4096) OK ----------------------------------------------------------------------- -3 512 4096 (1) 4096 (512) OK -3 4096 4096 (1) 4096 (4096) OK ----------------------------------------------------------------------- -4 512 4096 (1) 4096 (1) OK -4 4096 4096 (1) 4096 (1) OK - -I tested that provisioning VMs and copying disks on local XFS and -Gluster with 4k bytes sector size work now, resolving bugs [1],[2]. -I tested also on XFS, NFS, Gluster with 512 bytes sector size. - -[1] https://bugzilla.redhat.com/1737256 -[2] https://bugzilla.redhat.com/1738657 - -Signed-off-by: Nir Soffer -Signed-off-by: Kevin Wolf -(cherry picked from commit a6b257a08e3d72219f03e461a52152672fec0612) ---- - block/file-posix.c | 36 +++++++++++++++++++++++++----------- - 1 file changed, 25 insertions(+), 11 deletions(-) - -diff --git a/block/file-posix.c b/block/file-posix.c -index 4479cc7ab4..b8b4dad553 100644 ---- a/block/file-posix.c -+++ b/block/file-posix.c -@@ -323,6 +323,7 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) - BDRVRawState *s = bs->opaque; - char *buf; - size_t max_align = MAX(MAX_BLOCKSIZE, getpagesize()); -+ size_t alignments[] = {1, 512, 1024, 2048, 4096}; - - /* For SCSI generic devices the alignment is not really used. - With buffered I/O, we don't have any restrictions. */ -@@ -349,25 +350,38 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) - } - #endif - -- /* If we could not get the sizes so far, we can only guess them */ -- if (!s->buf_align) { -+ /* -+ * If we could not get the sizes so far, we can only guess them. First try -+ * to detect request alignment, since it is more likely to succeed. Then -+ * try to detect buf_align, which cannot be detected in some cases (e.g. -+ * Gluster). If buf_align cannot be detected, we fallback to the value of -+ * request_alignment. -+ */ -+ -+ if (!bs->bl.request_alignment) { -+ int i; - size_t align; -- buf = qemu_memalign(max_align, 2 * max_align); -- for (align = 512; align <= max_align; align <<= 1) { -- if (raw_is_io_aligned(fd, buf + align, max_align)) { -- s->buf_align = align; -+ buf = qemu_memalign(max_align, max_align); -+ for (i = 0; i < ARRAY_SIZE(alignments); i++) { -+ align = alignments[i]; -+ if (raw_is_io_aligned(fd, buf, align)) { -+ /* Fallback to safe value. */ -+ bs->bl.request_alignment = (align != 1) ? align : max_align; - break; - } - } - qemu_vfree(buf); - } - -- if (!bs->bl.request_alignment) { -+ if (!s->buf_align) { -+ int i; - size_t align; -- buf = qemu_memalign(s->buf_align, max_align); -- for (align = 512; align <= max_align; align <<= 1) { -- if (raw_is_io_aligned(fd, buf, align)) { -- bs->bl.request_alignment = align; -+ buf = qemu_memalign(max_align, 2 * max_align); -+ for (i = 0; i < ARRAY_SIZE(alignments); i++) { -+ align = alignments[i]; -+ if (raw_is_io_aligned(fd, buf + align, max_align)) { -+ /* Fallback to request_aligment. */ -+ s->buf_align = (align != 1) ? align : bs->bl.request_alignment; - break; - } - } diff --git a/0002-block-posix-Always-allocate-the-first-block.patch b/0002-block-posix-Always-allocate-the-first-block.patch deleted file mode 100644 index 40a55b7..0000000 --- a/0002-block-posix-Always-allocate-the-first-block.patch +++ /dev/null @@ -1,369 +0,0 @@ -From: Nir Soffer -Date: Tue, 27 Aug 2019 04:05:27 +0300 -Subject: [PATCH] block: posix: Always allocate the first block - -When creating an image with preallocation "off" or "falloc", the first -block of the image is typically not allocated. When using Gluster -storage backed by XFS filesystem, reading this block using direct I/O -succeeds regardless of request length, fooling alignment detection. - -In this case we fallback to a safe value (4096) instead of the optimal -value (512), which may lead to unneeded data copying when aligning -requests. Allocating the first block avoids the fallback. - -Since we allocate the first block even with preallocation=off, we no -longer create images with zero disk size: - - $ ./qemu-img create -f raw test.raw 1g - Formatting 'test.raw', fmt=raw size=1073741824 - - $ ls -lhs test.raw - 4.0K -rw-r--r--. 1 nsoffer nsoffer 1.0G Aug 16 23:48 test.raw - -And converting the image requires additional cluster: - - $ ./qemu-img measure -f raw -O qcow2 test.raw - required size: 458752 - fully allocated size: 1074135040 - -When using format like vmdk with multiple files per image, we allocate -one block per file: - - $ ./qemu-img create -f vmdk -o subformat=twoGbMaxExtentFlat test.vmdk 4g - Formatting 'test.vmdk', fmt=vmdk size=4294967296 compat6=off hwversion=undefined subformat=twoGbMaxExtentFlat - - $ ls -lhs test*.vmdk - 4.0K -rw-r--r--. 1 nsoffer nsoffer 2.0G Aug 27 03:23 test-f001.vmdk - 4.0K -rw-r--r--. 1 nsoffer nsoffer 2.0G Aug 27 03:23 test-f002.vmdk - 4.0K -rw-r--r--. 1 nsoffer nsoffer 353 Aug 27 03:23 test.vmdk - -I did quick performance test for copying disks with qemu-img convert to -new raw target image to Gluster storage with sector size of 512 bytes: - - for i in $(seq 10); do - rm -f dst.raw - sleep 10 - time ./qemu-img convert -f raw -O raw -t none -T none src.raw dst.raw - done - -Here is a table comparing the total time spent: - -Type Before(s) After(s) Diff(%) ---------------------------------------- -real 530.028 469.123 -11.4 -user 17.204 10.768 -37.4 -sys 17.881 7.011 -60.7 - -We can see very clear improvement in CPU usage. - -Signed-off-by: Nir Soffer -Message-id: 20190827010528.8818-2-nsoffer@redhat.com -Reviewed-by: Max Reitz -Signed-off-by: Max Reitz -(cherry picked from commit 3a20013fbb26d2a1bd11ef148eefdb1508783787) ---- - block/file-posix.c | 51 ++++++++++++++++++++++++++++++++ - tests/qemu-iotests/059.out | 2 +- - tests/qemu-iotests/150.out | 11 ------- - tests/qemu-iotests/150.out.qcow2 | 11 +++++++ - tests/qemu-iotests/150.out.raw | 12 ++++++++ - tests/qemu-iotests/175 | 19 ++++++++---- - tests/qemu-iotests/175.out | 8 ++--- - tests/qemu-iotests/178.out.qcow2 | 4 +-- - tests/qemu-iotests/221.out | 12 +++++--- - tests/qemu-iotests/253.out | 12 +++++--- - 10 files changed, 110 insertions(+), 32 deletions(-) - delete mode 100644 tests/qemu-iotests/150.out - create mode 100644 tests/qemu-iotests/150.out.qcow2 - create mode 100644 tests/qemu-iotests/150.out.raw - -diff --git a/block/file-posix.c b/block/file-posix.c -index b8b4dad553..8ea98896ce 100644 ---- a/block/file-posix.c -+++ b/block/file-posix.c -@@ -1749,6 +1749,43 @@ static int handle_aiocb_discard(void *opaque) - return ret; - } - -+/* -+ * Help alignment probing by allocating the first block. -+ * -+ * When reading with direct I/O from unallocated area on Gluster backed by XFS, -+ * reading succeeds regardless of request length. In this case we fallback to -+ * safe alignment which is not optimal. Allocating the first block avoids this -+ * fallback. -+ * -+ * fd may be opened with O_DIRECT, but we don't know the buffer alignment or -+ * request alignment, so we use safe values. -+ * -+ * Returns: 0 on success, -errno on failure. Since this is an optimization, -+ * caller may ignore failures. -+ */ -+static int allocate_first_block(int fd, size_t max_size) -+{ -+ size_t write_size = (max_size < MAX_BLOCKSIZE) -+ ? BDRV_SECTOR_SIZE -+ : MAX_BLOCKSIZE; -+ size_t max_align = MAX(MAX_BLOCKSIZE, getpagesize()); -+ void *buf; -+ ssize_t n; -+ int ret; -+ -+ buf = qemu_memalign(max_align, write_size); -+ memset(buf, 0, write_size); -+ -+ do { -+ n = pwrite(fd, buf, write_size, 0); -+ } while (n == -1 && errno == EINTR); -+ -+ ret = (n == -1) ? -errno : 0; -+ -+ qemu_vfree(buf); -+ return ret; -+} -+ - static int handle_aiocb_truncate(void *opaque) - { - RawPosixAIOData *aiocb = opaque; -@@ -1788,6 +1825,17 @@ static int handle_aiocb_truncate(void *opaque) - /* posix_fallocate() doesn't set errno. */ - error_setg_errno(errp, -result, - "Could not preallocate new data"); -+ } else if (current_length == 0) { -+ /* -+ * posix_fallocate() uses fallocate() if the filesystem -+ * supports it, or fallback to manually writing zeroes. If -+ * fallocate() was used, unaligned reads from the fallocated -+ * area in raw_probe_alignment() will succeed, hence we need to -+ * allocate the first block. -+ * -+ * Optimize future alignment probing; ignore failures. -+ */ -+ allocate_first_block(fd, offset); - } - } else { - result = 0; -@@ -1849,6 +1897,9 @@ static int handle_aiocb_truncate(void *opaque) - if (ftruncate(fd, offset) != 0) { - result = -errno; - error_setg_errno(errp, -result, "Could not resize file"); -+ } else if (current_length == 0 && offset > current_length) { -+ /* Optimize future alignment probing; ignore failures. */ -+ allocate_first_block(fd, offset); - } - return result; - default: -diff --git a/tests/qemu-iotests/059.out b/tests/qemu-iotests/059.out -index 4fab42a28c..fe3f861f3c 100644 ---- a/tests/qemu-iotests/059.out -+++ b/tests/qemu-iotests/059.out -@@ -27,7 +27,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824000 subformat=twoGbMax - image: TEST_DIR/t.vmdk - file format: vmdk - virtual size: 0.977 TiB (1073741824000 bytes) --disk size: 16 KiB -+disk size: 1.97 MiB - Format specific information: - cid: XXXXXXXX - parent cid: XXXXXXXX -diff --git a/tests/qemu-iotests/150.out b/tests/qemu-iotests/150.out -deleted file mode 100644 -index 2a54e8dcfa..0000000000 ---- a/tests/qemu-iotests/150.out -+++ /dev/null -@@ -1,11 +0,0 @@ --QA output created by 150 -- --=== Mapping sparse conversion === -- --Offset Length File -- --=== Mapping non-sparse conversion === -- --Offset Length File --0 0x100000 TEST_DIR/t.IMGFMT --*** done -diff --git a/tests/qemu-iotests/150.out.qcow2 b/tests/qemu-iotests/150.out.qcow2 -new file mode 100644 -index 0000000000..2a54e8dcfa ---- /dev/null -+++ b/tests/qemu-iotests/150.out.qcow2 -@@ -0,0 +1,11 @@ -+QA output created by 150 -+ -+=== Mapping sparse conversion === -+ -+Offset Length File -+ -+=== Mapping non-sparse conversion === -+ -+Offset Length File -+0 0x100000 TEST_DIR/t.IMGFMT -+*** done -diff --git a/tests/qemu-iotests/150.out.raw b/tests/qemu-iotests/150.out.raw -new file mode 100644 -index 0000000000..3cdc7727a5 ---- /dev/null -+++ b/tests/qemu-iotests/150.out.raw -@@ -0,0 +1,12 @@ -+QA output created by 150 -+ -+=== Mapping sparse conversion === -+ -+Offset Length File -+0 0x1000 TEST_DIR/t.IMGFMT -+ -+=== Mapping non-sparse conversion === -+ -+Offset Length File -+0 0x100000 TEST_DIR/t.IMGFMT -+*** done -diff --git a/tests/qemu-iotests/175 b/tests/qemu-iotests/175 -index 51e62c8276..7ba28b3c1b 100755 ---- a/tests/qemu-iotests/175 -+++ b/tests/qemu-iotests/175 -@@ -37,14 +37,16 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 - # the file size. This function hides the resulting difference in the - # stat -c '%b' output. - # Parameter 1: Number of blocks an empty file occupies --# Parameter 2: Image size in bytes -+# Parameter 2: Minimal number of blocks in an image -+# Parameter 3: Image size in bytes - _filter_blocks() - { - extra_blocks=$1 -- img_size=$2 -+ min_blocks=$2 -+ img_size=$3 - -- sed -e "s/blocks=$extra_blocks\\(\$\\|[^0-9]\\)/nothing allocated/" \ -- -e "s/blocks=$((extra_blocks + img_size / 512))\\(\$\\|[^0-9]\\)/everything allocated/" -+ sed -e "s/blocks=$min_blocks\\(\$\\|[^0-9]\\)/min allocation/" \ -+ -e "s/blocks=$((extra_blocks + img_size / 512))\\(\$\\|[^0-9]\\)/max allocation/" - } - - # get standard environment, filters and checks -@@ -60,16 +62,21 @@ size=$((1 * 1024 * 1024)) - touch "$TEST_DIR/empty" - extra_blocks=$(stat -c '%b' "$TEST_DIR/empty") - -+# We always write the first byte; check how many blocks this filesystem -+# allocates to match empty image alloation. -+printf "\0" > "$TEST_DIR/empty" -+min_blocks=$(stat -c '%b' "$TEST_DIR/empty") -+ - echo - echo "== creating image with default preallocation ==" - _make_test_img $size | _filter_imgfmt --stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $size -+stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_blocks $size - - for mode in off full falloc; do - echo - echo "== creating image with preallocation $mode ==" - IMGOPTS=preallocation=$mode _make_test_img $size | _filter_imgfmt -- stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $size -+ stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $min_blocks $size - done - - # success, all done -diff --git a/tests/qemu-iotests/175.out b/tests/qemu-iotests/175.out -index 6d9a5ed84e..263e521262 100644 ---- a/tests/qemu-iotests/175.out -+++ b/tests/qemu-iotests/175.out -@@ -2,17 +2,17 @@ QA output created by 175 - - == creating image with default preallocation == - Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 --size=1048576, nothing allocated -+size=1048576, min allocation - - == creating image with preallocation off == - Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=off --size=1048576, nothing allocated -+size=1048576, min allocation - - == creating image with preallocation full == - Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=full --size=1048576, everything allocated -+size=1048576, max allocation - - == creating image with preallocation falloc == - Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=falloc --size=1048576, everything allocated -+size=1048576, max allocation - *** done -diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2 -index 55a8dc926f..9e7d8c44df 100644 ---- a/tests/qemu-iotests/178.out.qcow2 -+++ b/tests/qemu-iotests/178.out.qcow2 -@@ -101,7 +101,7 @@ converted image file size in bytes: 196608 - == raw input image with data (human) == - - Formatting 'TEST_DIR/t.qcow2', fmt=IMGFMT size=1073741824 --required size: 393216 -+required size: 458752 - fully allocated size: 1074135040 - wrote 512/512 bytes at offset 512 - 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -@@ -257,7 +257,7 @@ converted image file size in bytes: 196608 - - Formatting 'TEST_DIR/t.qcow2', fmt=IMGFMT size=1073741824 - { -- "required": 393216, -+ "required": 458752, - "fully-allocated": 1074135040 - } - wrote 512/512 bytes at offset 512 -diff --git a/tests/qemu-iotests/221.out b/tests/qemu-iotests/221.out -index 9f9dd52bb0..dca024a0c3 100644 ---- a/tests/qemu-iotests/221.out -+++ b/tests/qemu-iotests/221.out -@@ -3,14 +3,18 @@ QA output created by 221 - === Check mapping of unaligned raw image === - - Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=65537 --[{ "start": 0, "length": 66048, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] --[{ "start": 0, "length": 66048, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] -+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -+{ "start": 4096, "length": 61952, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] -+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -+{ "start": 4096, "length": 61952, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] - wrote 1/1 bytes at offset 65536 - 1 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) --[{ "start": 0, "length": 65536, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, -+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -+{ "start": 4096, "length": 61440, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, - { "start": 65536, "length": 1, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, - { "start": 65537, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] --[{ "start": 0, "length": 65536, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, -+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -+{ "start": 4096, "length": 61440, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, - { "start": 65536, "length": 1, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, - { "start": 65537, "length": 511, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] - *** done -diff --git a/tests/qemu-iotests/253.out b/tests/qemu-iotests/253.out -index 607c0baa0b..3d08b305d7 100644 ---- a/tests/qemu-iotests/253.out -+++ b/tests/qemu-iotests/253.out -@@ -3,12 +3,16 @@ QA output created by 253 - === Check mapping of unaligned raw image === - - Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048575 --[{ "start": 0, "length": 1048576, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] --[{ "start": 0, "length": 1048576, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] -+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -+{ "start": 4096, "length": 1044480, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] -+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -+{ "start": 4096, "length": 1044480, "depth": 0, "zero": true, "data": false, "offset": OFFSET}] - wrote 65535/65535 bytes at offset 983040 - 63.999 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) --[{ "start": 0, "length": 983040, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, -+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -+{ "start": 4096, "length": 978944, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, - { "start": 983040, "length": 65536, "depth": 0, "zero": false, "data": true, "offset": OFFSET}] --[{ "start": 0, "length": 983040, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, -+[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, -+{ "start": 4096, "length": 978944, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, - { "start": 983040, "length": 65536, "depth": 0, "zero": false, "data": true, "offset": OFFSET}] - *** done diff --git a/0003-tests-make-filemonitor-test-more-robust-to-event-ord.patch b/0003-tests-make-filemonitor-test-more-robust-to-event-ord.patch deleted file mode 100644 index bce580d..0000000 --- a/0003-tests-make-filemonitor-test-more-robust-to-event-ord.patch +++ /dev/null @@ -1,134 +0,0 @@ -From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= -Date: Wed, 21 Aug 2019 16:14:27 +0100 -Subject: [PATCH] tests: make filemonitor test more robust to event ordering -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The ordering of events that are emitted during the rmdir -test have changed with kernel >= 5.3. Semantically both -new & old orderings are correct, so we must be able to -cope with either. - -To cope with this, when we see an unexpected event, we -push it back onto the queue and look and the subsequent -event to see if that matches instead. - -Tested-by: Peter Xu -Tested-by: Wei Yang -Tested-by: Cornelia Huck -Reviewed-by: Thomas Huth -Signed-off-by: Daniel P. Berrangé -(cherry picked from commit bf9e0313c27d8e6ecd7f7de3d63e1cb25d8f6311) ---- - tests/test-util-filemonitor.c | 43 +++++++++++++++++++++++++++-------- - 1 file changed, 34 insertions(+), 9 deletions(-) - -diff --git a/tests/test-util-filemonitor.c b/tests/test-util-filemonitor.c -index 46e781c022..301cd2db61 100644 ---- a/tests/test-util-filemonitor.c -+++ b/tests/test-util-filemonitor.c -@@ -45,6 +45,11 @@ typedef struct { - const char *filedst; - int64_t *watchid; - int eventid; -+ /* -+ * Only valid with OP_EVENT - this event might be -+ * swapped with the next OP_EVENT -+ */ -+ bool swapnext; - } QFileMonitorTestOp; - - typedef struct { -@@ -98,6 +103,10 @@ qemu_file_monitor_test_handler(int64_t id, - QFileMonitorTestData *data = opaque; - QFileMonitorTestRecord *rec = g_new0(QFileMonitorTestRecord, 1); - -+ if (debug) { -+ g_printerr("Queue event id %" PRIx64 " event %d file %s\n", -+ id, event, filename); -+ } - rec->id = id; - rec->event = event; - rec->filename = g_strdup(filename); -@@ -125,7 +134,8 @@ qemu_file_monitor_test_record_free(QFileMonitorTestRecord *rec) - * to wait for the event to be queued for us. - */ - static QFileMonitorTestRecord * --qemu_file_monitor_test_next_record(QFileMonitorTestData *data) -+qemu_file_monitor_test_next_record(QFileMonitorTestData *data, -+ QFileMonitorTestRecord *pushback) - { - GTimer *timer = g_timer_new(); - QFileMonitorTestRecord *record = NULL; -@@ -139,9 +149,15 @@ qemu_file_monitor_test_next_record(QFileMonitorTestData *data) - } - if (data->records) { - record = data->records->data; -- tmp = data->records; -- data->records = g_list_remove_link(data->records, tmp); -- g_list_free(tmp); -+ if (pushback) { -+ data->records->data = pushback; -+ } else { -+ tmp = data->records; -+ data->records = g_list_remove_link(data->records, tmp); -+ g_list_free(tmp); -+ } -+ } else if (pushback) { -+ qemu_file_monitor_test_record_free(pushback); - } - qemu_mutex_unlock(&data->lock); - -@@ -158,13 +174,15 @@ static bool - qemu_file_monitor_test_expect(QFileMonitorTestData *data, - int64_t id, - QFileMonitorEvent event, -- const char *filename) -+ const char *filename, -+ bool swapnext) - { - QFileMonitorTestRecord *rec; - bool ret = false; - -- rec = qemu_file_monitor_test_next_record(data); -+ rec = qemu_file_monitor_test_next_record(data, NULL); - -+ retry: - if (!rec) { - g_printerr("Missing event watch id %" PRIx64 " event %d file %s\n", - id, event, filename); -@@ -172,6 +190,11 @@ qemu_file_monitor_test_expect(QFileMonitorTestData *data, - } - - if (id != rec->id) { -+ if (swapnext) { -+ rec = qemu_file_monitor_test_next_record(data, rec); -+ swapnext = false; -+ goto retry; -+ } - g_printerr("Expected watch id %" PRIx64 " but got %" PRIx64 "\n", - id, rec->id); - goto cleanup; -@@ -347,7 +370,8 @@ test_file_monitor_events(void) - .filesrc = "fish", }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "", .watchid = &watch4, -- .eventid = QFILE_MONITOR_EVENT_IGNORED }, -+ .eventid = QFILE_MONITOR_EVENT_IGNORED, -+ .swapnext = true }, - { .type = QFILE_MONITOR_TEST_OP_EVENT, - .filesrc = "fish", .watchid = &watch0, - .eventid = QFILE_MONITOR_EVENT_DELETED }, -@@ -493,8 +517,9 @@ test_file_monitor_events(void) - g_printerr("Event id=%" PRIx64 " event=%d file=%s\n", - *op->watchid, op->eventid, op->filesrc); - } -- if (!qemu_file_monitor_test_expect( -- &data, *op->watchid, op->eventid, op->filesrc)) -+ if (!qemu_file_monitor_test_expect(&data, *op->watchid, -+ op->eventid, op->filesrc, -+ op->swapnext)) - goto cleanup; - break; - case QFILE_MONITOR_TEST_OP_CREATE: diff --git a/0004-Revert-block-avoid-recursive-block_status-call-if-po.patch b/0004-Revert-block-avoid-recursive-block_status-call-if-po.patch deleted file mode 100644 index 6edadc7..0000000 --- a/0004-Revert-block-avoid-recursive-block_status-call-if-po.patch +++ /dev/null @@ -1,224 +0,0 @@ -From: Cole Robinson -Date: Thu, 24 Oct 2019 11:32:46 -0400 -Subject: [PATCH] Revert "block: avoid recursive block_status call if possible" - -This reverts commit 69f47505ee66afaa513305de0c1895a224e52c45. - -Workaround for qcow2 triggered XFS corruption, bug 1763519 ---- - block/io.c | 9 +-------- - block/qcow2-refcount.c | 32 -------------------------------- - block/qcow2.c | 11 ----------- - block/qcow2.h | 4 ---- - include/block/block.h | 8 +------- - tests/qemu-iotests/102 | 2 +- - tests/qemu-iotests/102.out | 3 +-- - tests/qemu-iotests/141.out | 2 +- - tests/qemu-iotests/144.out | 2 +- - 9 files changed, 6 insertions(+), 67 deletions(-) - -diff --git a/block/io.c b/block/io.c -index 06305c6ea6..ef057ba5db 100644 ---- a/block/io.c -+++ b/block/io.c -@@ -2156,12 +2156,6 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, - */ - assert(*pnum && QEMU_IS_ALIGNED(*pnum, align) && - align > offset - aligned_offset); -- if (ret & BDRV_BLOCK_RECURSE) { -- assert(ret & BDRV_BLOCK_DATA); -- assert(ret & BDRV_BLOCK_OFFSET_VALID); -- assert(!(ret & BDRV_BLOCK_ZERO)); -- } -- - *pnum -= offset - aligned_offset; - if (*pnum > bytes) { - *pnum = bytes; -@@ -2192,8 +2186,7 @@ static int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, - } - } - -- if (want_zero && ret & BDRV_BLOCK_RECURSE && -- local_file && local_file != bs && -+ if (want_zero && local_file && local_file != bs && - (ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO) && - (ret & BDRV_BLOCK_OFFSET_VALID)) { - int64_t file_pnum; -diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c -index ef965d7895..81ecb88f9c 100644 ---- a/block/qcow2-refcount.c -+++ b/block/qcow2-refcount.c -@@ -3448,35 +3448,3 @@ int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size) - "There are no references in the refcount table."); - return -EIO; - } -- --int qcow2_detect_metadata_preallocation(BlockDriverState *bs) --{ -- BDRVQcow2State *s = bs->opaque; -- int64_t i, end_cluster, cluster_count = 0, threshold; -- int64_t file_length, real_allocation, real_clusters; -- -- file_length = bdrv_getlength(bs->file->bs); -- if (file_length < 0) { -- return file_length; -- } -- -- real_allocation = bdrv_get_allocated_file_size(bs->file->bs); -- if (real_allocation < 0) { -- return real_allocation; -- } -- -- real_clusters = real_allocation / s->cluster_size; -- threshold = MAX(real_clusters * 10 / 9, real_clusters + 2); -- -- end_cluster = size_to_clusters(s, file_length); -- for (i = 0; i < end_cluster && cluster_count < threshold; i++) { -- uint64_t refcount; -- int ret = qcow2_get_refcount(bs, i, &refcount); -- if (ret < 0) { -- return ret; -- } -- cluster_count += !!refcount; -- } -- -- return cluster_count >= threshold; --} -diff --git a/block/qcow2.c b/block/qcow2.c -index 039bdc2f7e..86e88f6af4 100644 ---- a/block/qcow2.c -+++ b/block/qcow2.c -@@ -1895,12 +1895,6 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, - unsigned int bytes; - int status = 0; - -- if (!s->metadata_preallocation_checked) { -- ret = qcow2_detect_metadata_preallocation(bs); -- s->metadata_preallocation = (ret == 1); -- s->metadata_preallocation_checked = true; -- } -- - bytes = MIN(INT_MAX, count); - qemu_co_mutex_lock(&s->lock); - ret = qcow2_get_cluster_offset(bs, offset, &bytes, &cluster_offset); -@@ -1923,11 +1917,6 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, - } else if (ret != QCOW2_CLUSTER_UNALLOCATED) { - status |= BDRV_BLOCK_DATA; - } -- if (s->metadata_preallocation && (status & BDRV_BLOCK_DATA) && -- (status & BDRV_BLOCK_OFFSET_VALID)) -- { -- status |= BDRV_BLOCK_RECURSE; -- } - return status; - } - -diff --git a/block/qcow2.h b/block/qcow2.h -index fc1b0d3c1e..567375e56c 100644 ---- a/block/qcow2.h -+++ b/block/qcow2.h -@@ -356,9 +356,6 @@ typedef struct BDRVQcow2State { - int nb_threads; - - BdrvChild *data_file; -- -- bool metadata_preallocation_checked; -- bool metadata_preallocation; - } BDRVQcow2State; - - typedef struct Qcow2COWRegion { -@@ -658,7 +655,6 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, - void *cb_opaque, Error **errp); - int qcow2_shrink_reftable(BlockDriverState *bs); - int64_t qcow2_get_last_cluster(BlockDriverState *bs, int64_t size); --int qcow2_detect_metadata_preallocation(BlockDriverState *bs); - - /* qcow2-cluster.c functions */ - int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, -diff --git a/include/block/block.h b/include/block/block.h -index 50a07c1c33..269c0931e7 100644 ---- a/include/block/block.h -+++ b/include/block/block.h -@@ -156,15 +156,10 @@ typedef struct HDGeometry { - * BDRV_BLOCK_EOF: the returned pnum covers through end of file for this - * layer, set by block layer - * -- * Internal flags: -+ * Internal flag: - * BDRV_BLOCK_RAW: for use by passthrough drivers, such as raw, to request - * that the block layer recompute the answer from the returned - * BDS; must be accompanied by just BDRV_BLOCK_OFFSET_VALID. -- * BDRV_BLOCK_RECURSE: request that the block layer will recursively search for -- * zeroes in file child of current block node inside -- * returned region. Only valid together with both -- * BDRV_BLOCK_DATA and BDRV_BLOCK_OFFSET_VALID. Should not -- * appear with BDRV_BLOCK_ZERO. - * - * If BDRV_BLOCK_OFFSET_VALID is set, the map parameter represents the - * host offset within the returned BDS that is allocated for the -@@ -189,7 +184,6 @@ typedef struct HDGeometry { - #define BDRV_BLOCK_RAW 0x08 - #define BDRV_BLOCK_ALLOCATED 0x10 - #define BDRV_BLOCK_EOF 0x20 --#define BDRV_BLOCK_RECURSE 0x40 - #define BDRV_BLOCK_OFFSET_MASK BDRV_SECTOR_MASK - - typedef QSIMPLEQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue; -diff --git a/tests/qemu-iotests/102 b/tests/qemu-iotests/102 -index b898df436f..749ff66b8a 100755 ---- a/tests/qemu-iotests/102 -+++ b/tests/qemu-iotests/102 -@@ -55,7 +55,7 @@ $QEMU_IO -c 'write 0 64k' "$TEST_IMG" | _filter_qemu_io - $QEMU_IMG resize -f raw --shrink "$TEST_IMG" $((5 * 64 * 1024)) - - $QEMU_IO -c map "$TEST_IMG" --$QEMU_IMG map "$TEST_IMG" | _filter_qemu_img_map -+$QEMU_IMG map "$TEST_IMG" - - echo - echo '=== Testing map on an image file truncated outside of qemu ===' -diff --git a/tests/qemu-iotests/102.out b/tests/qemu-iotests/102.out -index cd2fdc7f96..4401b08fee 100644 ---- a/tests/qemu-iotests/102.out -+++ b/tests/qemu-iotests/102.out -@@ -7,8 +7,7 @@ wrote 65536/65536 bytes at offset 0 - 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) - Image resized. - 64 KiB (0x10000) bytes allocated at offset 0 bytes (0x0) --Offset Length File --0 0x10000 TEST_DIR/t.IMGFMT -+Offset Length Mapped to File - - === Testing map on an image file truncated outside of qemu === - -diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out -index 4d71d9dcae..41c7291258 100644 ---- a/tests/qemu-iotests/141.out -+++ b/tests/qemu-iotests/141.out -@@ -42,9 +42,9 @@ Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t. - {"return": {}} - {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}} - {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}} --{"return": {}} - {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}} - {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} -+{"return": {}} - {"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: block device is in use by block job: commit"}} - {"return": {}} - {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}} -diff --git a/tests/qemu-iotests/144.out b/tests/qemu-iotests/144.out -index a9a8216bea..55299201e4 100644 ---- a/tests/qemu-iotests/144.out -+++ b/tests/qemu-iotests/144.out -@@ -14,10 +14,10 @@ Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/ - - {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "virtio0"}} - {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "virtio0"}} --{"return": {}} - {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "virtio0"}} - {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} - {"return": {}} -+{"return": {}} - {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "virtio0"}} - {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "virtio0"}} - {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}} diff --git a/0005-qcow2-Fix-QCOW2_COMPRESSED_SECTOR_MASK.patch b/0005-qcow2-Fix-QCOW2_COMPRESSED_SECTOR_MASK.patch deleted file mode 100644 index eb76ab9..0000000 --- a/0005-qcow2-Fix-QCOW2_COMPRESSED_SECTOR_MASK.patch +++ /dev/null @@ -1,27 +0,0 @@ -From: Max Reitz -Date: Mon, 28 Oct 2019 17:18:40 +0100 -Subject: [PATCH] qcow2: Fix QCOW2_COMPRESSED_SECTOR_MASK - -Masks for L2 table entries should have 64 bit. - -Fixes: b6c246942b14d3e0dec46a6c5868ed84e7dbea19 -Buglink: https://bugs.launchpad.net/qemu/+bug/1850000 -Cc: qemu-stable@nongnu.org -Signed-off-by: Max Reitz ---- - block/qcow2.h | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/block/qcow2.h b/block/qcow2.h -index 567375e56c..45f9585c08 100644 ---- a/block/qcow2.h -+++ b/block/qcow2.h -@@ -77,7 +77,7 @@ - - /* Defined in the qcow2 spec (compressed cluster descriptor) */ - #define QCOW2_COMPRESSED_SECTOR_SIZE 512U --#define QCOW2_COMPRESSED_SECTOR_MASK (~(QCOW2_COMPRESSED_SECTOR_SIZE - 1)) -+#define QCOW2_COMPRESSED_SECTOR_MASK (~(QCOW2_COMPRESSED_SECTOR_SIZE - 1ULL)) - - /* Must be at least 2 to cover COW */ - #define MIN_L2_CACHE_SIZE 2 /* cache entries */ diff --git a/qemu.spec b/qemu.spec index b523bcd..9c0f1d6 100644 --- a/qemu.spec +++ b/qemu.spec @@ -115,6 +115,7 @@ %define requires_ui_curses Requires: %{name}-ui-curses = %{evr} %define requires_ui_gtk Requires: %{name}-ui-gtk = %{evr} %define requires_ui_sdl Requires: %{name}-ui-sdl = %{evr} +%define requires_ui_spice_add Requires: %{name}-ui-spice-app = %{evr} %global requires_all_modules \ %{requires_block_curl} \ @@ -138,7 +139,7 @@ %{obsoletes_block_rbd} # Release candidate version tracking -#global rcver rc2 +%global rcver rc1 %if 0%{?rcver:1} %global rcrel .%{rcver} %global rcstr -%{rcver} @@ -147,8 +148,8 @@ Summary: QEMU is a FAST! processor emulator Name: qemu -Version: 4.1.0 -Release: 6%{?rcrel}%{?dist} +Version: 4.2.0 +Release: 0.1%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -172,16 +173,6 @@ Source20: kvm-x86.modprobe.conf # /etc/security/limits.d/95-kvm-ppc64-memlock.conf Source21: 95-kvm-ppc64-memlock.conf -# gluster 4K block size fixes (bz #1737256) -Patch0001: 0001-file-posix-Handle-undetectable-alignment.patch -Patch0002: 0002-block-posix-Always-allocate-the-first-block.patch -# Fix tests on kernel 5.3+ -Patch0003: 0003-tests-make-filemonitor-test-more-robust-to-event-ord.patch -# Workaround for qcow2 triggered XFS corruption (bz #1763519) -Patch0004: 0004-Revert-block-avoid-recursive-block_status-call-if-po.patch -# Fix compressed qcow2 'qemu-img check' errors (bz #1768541) -Patch0005: 0005-qcow2-Fix-QCOW2_COMPRESSED_SECTOR_MASK.patch - # documentation deps BuildRequires: texinfo @@ -526,6 +517,12 @@ Requires: %{name}-common%{?_isa} = %{epoch}:%{version}-%{release} %description ui-sdl This package provides the additional SDL UI for QEMU. +%package ui-spice-app +Summary: QEMU spice-app UI driver +Requires: %{name}-common%{?_isa} = %{epoch}:%{version}-%{release} +%description ui-spice-app +This package provides the additional spice-app UI for QEMU. + %if %{have_kvm} %package kvm @@ -1189,7 +1186,7 @@ popd # Copy some static data into place -install -D -p -m 0644 -t %{buildroot}%{qemudocdir} Changelog README COPYING COPYING.LIB LICENSE +install -D -p -m 0644 -t %{buildroot}%{qemudocdir} Changelog README.rst COPYING COPYING.LIB LICENSE install -D -p -m 0644 qemu.sasl %{buildroot}%{_sysconfdir}/sasl2/qemu.conf @@ -1374,18 +1371,18 @@ getent passwd qemu >/dev/null || \ %files common -f %{name}.lang %dir %{qemudocdir} %doc %{qemudocdir}/Changelog -%doc %{qemudocdir}/COPYING -%doc %{qemudocdir}/COPYING.LIB -%doc %{qemudocdir}/LICENSE %doc %{qemudocdir}/qemu-doc.html %doc %{qemudocdir}/qemu-doc.txt %doc %{qemudocdir}/qemu-ga-ref.html %doc %{qemudocdir}/qemu-ga-ref.txt %doc %{qemudocdir}/qemu-qmp-ref.html %doc %{qemudocdir}/qemu-qmp-ref.txt -%doc %{qemudocdir}/README +%doc %{qemudocdir}/README.rst %doc %{qemudocdir}/interop %doc %{qemudocdir}/specs +%license %{qemudocdir}/COPYING +%license %{qemudocdir}/COPYING.LIB +%license %{qemudocdir}/LICENSE %dir %{_datadir}/%{name}/ %{_datadir}/applications/qemu.desktop %{_datadir}/icons/hicolor/*/apps/* @@ -1495,6 +1492,8 @@ getent passwd qemu >/dev/null || \ %{_libdir}/qemu/ui-gtk.so %files ui-sdl %{_libdir}/qemu/ui-sdl.so +%files ui-spice-app +%{_libdir}/qemu/ui-spice-app.so %files -n ivshmem-tools @@ -1774,7 +1773,6 @@ getent passwd qemu >/dev/null || \ %{_datadir}/%{name}/ppc_rom.bin %{_datadir}/%{name}/qemu_vga.ndrv %{_datadir}/%{name}/skiboot.lid -%{_datadir}/%{name}/spapr-rtas.bin %{_datadir}/%{name}/u-boot.e500 %{_datadir}/%{name}/u-boot-sam460-20100605.bin %ifarch %{power64} @@ -1867,6 +1865,9 @@ getent passwd qemu >/dev/null || \ %changelog +* Tue Nov 12 2019 Cole Robinson - 2:4.2.0-0.1.rc1 +- Update to qemu-4.2.0 rc1 + * Mon Nov 11 2019 Cole Robinson - 2:4.1.0-6 - Fix compressed qcow2 'qemu-img check' errors (bz #1768541) diff --git a/sources b/sources index b509b83..310b89f 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (qemu-4.1.0.tar.xz) = 82fd51702a7b9b1b00b2f1bd3b4a832b80249018dbba1add0b0a73e7d4bee452afd45574b4d8df7ce4477d8711f3bda4ca072a1a6de25895c93eb21cf78fc4b2 +SHA512 (qemu-4.2.0-rc1.tar.xz) = 8ad5e0472fd384a9ba03b2e8fbb1e887169abb47a50a3f130b1943b39f45677a9e65ca5d1deb96338a5b3c3953db67f50e194a6763e9121c0eb5f620896162a9 From 16769836d706583240562c99c5f2966ef786da53 Mon Sep 17 00:00:00 2001 From: Thierry Vignaud Date: Wed, 13 Nov 2019 16:41:11 +0100 Subject: [PATCH 10/32] qemu: actually pull new ui_spice_add subpkg --00000000000065190005973c323b Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Hi The newly added spice_add subpkg is not required despite creating the proper macro This fixes it. See you --=20 Thierry Vignaud -- EMEA ENG OpenStack Management tvignaud@redhat.com irc: tvignaud
Hi
The newly added spice_add subpkg is not required despite creating the proper macro
This fixes it.
See you

--
Thierry Vignaud -- EMEA ENG OpenStack Management
irc: tvignaud
From c610e43d411389e36462607d38c95a85264f2881 Mon Sep 17 00:00:00 2001 From: Thierry Vignaud Date: Wed, 13 Nov 2019 16:39:25 +0100 Subject: [PATCH] actually pull new ui_spice_add subpkg --- qemu.spec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qemu.spec b/qemu.spec index 9c0f1d6..e0c74af 100644 --- a/qemu.spec +++ b/qemu.spec @@ -131,7 +131,8 @@ %{requires_audio_sdl} \ %{requires_ui_curses} \ %{requires_ui_gtk} \ -%{requires_ui_sdl} +%{requires_ui_sdl} \ +%{requires_ui_spice_add} # Modules which can be conditionally built %global obsoletes_some_modules \ From fddfbd96374b03c73e622d20244e5dbed4b1de11 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 13 Nov 2019 12:38:02 -0500 Subject: [PATCH 11/32] spec: Fix spice_app variable naming Signed-off-by: Cole Robinson --- qemu.spec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qemu.spec b/qemu.spec index e0c74af..ec9981a 100644 --- a/qemu.spec +++ b/qemu.spec @@ -115,7 +115,7 @@ %define requires_ui_curses Requires: %{name}-ui-curses = %{evr} %define requires_ui_gtk Requires: %{name}-ui-gtk = %{evr} %define requires_ui_sdl Requires: %{name}-ui-sdl = %{evr} -%define requires_ui_spice_add Requires: %{name}-ui-spice-app = %{evr} +%define requires_ui_spice_app Requires: %{name}-ui-spice-app = %{evr} %global requires_all_modules \ %{requires_block_curl} \ @@ -132,7 +132,7 @@ %{requires_ui_curses} \ %{requires_ui_gtk} \ %{requires_ui_sdl} \ -%{requires_ui_spice_add} +%{requires_ui_spice_app} # Modules which can be conditionally built %global obsoletes_some_modules \ From b4072bd6457db1f299d31820b66da0a5c703117f Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 13 Nov 2019 16:33:52 -0500 Subject: [PATCH 12/32] Fix the test suite Signed-off-by: Cole Robinson --- ...dules-test-duplicate-test-case-error.patch | 40 +++++++++++++++++++ qemu.spec | 3 ++ 2 files changed, 43 insertions(+) create mode 100644 0001-tests-fix-modules-test-duplicate-test-case-error.patch diff --git a/0001-tests-fix-modules-test-duplicate-test-case-error.patch b/0001-tests-fix-modules-test-duplicate-test-case-error.patch new file mode 100644 index 0000000..7c8f385 --- /dev/null +++ b/0001-tests-fix-modules-test-duplicate-test-case-error.patch @@ -0,0 +1,40 @@ +From d64c9aa098cc6e5c0b638438c4959eddfa7e24e2 Mon Sep 17 00:00:00 2001 +Message-Id: +From: Cole Robinson +Date: Wed, 13 Nov 2019 16:05:11 -0500 +Subject: [PATCH] tests: fix modules-test 'duplicate test case' error + +./configure --enable-sdl --audio-drv-list=sdl --enable-modules + +Will generate two identical test names: /$arch/module/load/sdl +Which generates an error like: + +(tests/modules-test:23814): GLib-ERROR **: 18:23:06.359: duplicate test case path: /aarch64//module/load/sdl + +Add the subsystem prefix in the name as well, so instead we get: + +/$arch/module/load/audio-sdl +/$arch/module/load/ui-sdl + +Signed-off-by: Cole Robinson +--- + tests/modules-test.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/tests/modules-test.c b/tests/modules-test.c +index d1a6ace218..88217686e1 100644 +--- a/tests/modules-test.c ++++ b/tests/modules-test.c +@@ -64,7 +64,8 @@ int main(int argc, char *argv[]) + g_test_init(&argc, &argv, NULL); + + for (i = 0; i < G_N_ELEMENTS(modules); i += 2) { +- char *testname = g_strdup_printf("/module/load/%s", modules[i + 1]); ++ char *testname = g_strdup_printf("/module/load/%s%s", ++ modules[i], modules[i + 1]); + qtest_add_data_func(testname, modules + i, test_modules_load); + g_free(testname); + } +-- +2.23.0 + diff --git a/qemu.spec b/qemu.spec index ec9981a..66edf53 100644 --- a/qemu.spec +++ b/qemu.spec @@ -157,6 +157,9 @@ URL: http://www.qemu.org/ Source0: http://wiki.qemu-project.org/download/%{name}-%{version}%{?rcstr}.tar.xz +# Fix a test suite error +Patch1: 0001-tests-fix-modules-test-duplicate-test-case-error.patch + # guest agent service Source10: qemu-guest-agent.service Source17: qemu-ga.sysconfig From 41cffcfad7ee3dd98b07997b1cb38acd6766d37c Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 13 Nov 2019 19:57:10 -0500 Subject: [PATCH 13/32] Add new bios-microvm.bin rom Signed-off-by: Cole Robinson --- qemu.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/qemu.spec b/qemu.spec index 66edf53..e7d0803 100644 --- a/qemu.spec +++ b/qemu.spec @@ -1846,6 +1846,7 @@ getent passwd qemu >/dev/null || \ %{_mandir}/man1/qemu-system-x86_64.1* %{_datadir}/%{name}/bios.bin %{_datadir}/%{name}/bios-256k.bin +%{_datadir}/%{name}/bios-microvm.bin %{_datadir}/%{name}/kvmvapic.bin %{_datadir}/%{name}/linuxboot.bin %{_datadir}/%{name}/linuxboot_dma.bin From 993f4157b672a83032d4583069e7c095a3f3f446 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Fri, 15 Nov 2019 09:39:29 -0500 Subject: [PATCH 14/32] Make spice-app subpackage conditional on with_spice Signed-off-by: Cole Robinson --- qemu.spec | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/qemu.spec b/qemu.spec index e7d0803..a4d1961 100644 --- a/qemu.spec +++ b/qemu.spec @@ -115,7 +115,12 @@ %define requires_ui_curses Requires: %{name}-ui-curses = %{evr} %define requires_ui_gtk Requires: %{name}-ui-gtk = %{evr} %define requires_ui_sdl Requires: %{name}-ui-sdl = %{evr} + +%if %{have_spice} %define requires_ui_spice_app Requires: %{name}-ui-spice-app = %{evr} +%else +%define requires_ui_spice_app Requires: %{nil} +%endif %global requires_all_modules \ %{requires_block_curl} \ @@ -521,11 +526,13 @@ Requires: %{name}-common%{?_isa} = %{epoch}:%{version}-%{release} %description ui-sdl This package provides the additional SDL UI for QEMU. +%if %{have_spice} %package ui-spice-app Summary: QEMU spice-app UI driver Requires: %{name}-common%{?_isa} = %{epoch}:%{version}-%{release} %description ui-spice-app This package provides the additional spice-app UI for QEMU. +%endif %if %{have_kvm} @@ -1496,8 +1503,10 @@ getent passwd qemu >/dev/null || \ %{_libdir}/qemu/ui-gtk.so %files ui-sdl %{_libdir}/qemu/ui-sdl.so +%if %{have_spice} %files ui-spice-app %{_libdir}/qemu/ui-spice-app.so +%endif %files -n ivshmem-tools From ff9bb15b16b34808bd7080f51630067e0359b6d2 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Fri, 15 Nov 2019 09:43:16 -0500 Subject: [PATCH 15/32] Fix last commit Signed-off-by: Cole Robinson --- qemu.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qemu.spec b/qemu.spec index a4d1961..426e560 100644 --- a/qemu.spec +++ b/qemu.spec @@ -119,7 +119,7 @@ %if %{have_spice} %define requires_ui_spice_app Requires: %{name}-ui-spice-app = %{evr} %else -%define requires_ui_spice_app Requires: %{nil} +%define requires_ui_spice_app %{nil} %endif %global requires_all_modules \ From 46eefb217c80423142772c6d7256fb34574577b3 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Fri, 15 Nov 2019 13:26:03 -0500 Subject: [PATCH 16/32] Disable tests, they are consistently failing on ppc64le Signed-off-by: Cole Robinson --- qemu.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qemu.spec b/qemu.spec index 426e560..f03fabf 100644 --- a/qemu.spec +++ b/qemu.spec @@ -1317,7 +1317,10 @@ chmod +x %{buildroot}%{_libdir}/qemu/*.so %global archs_ignore_test_failures 0 # Enable this temporarily if tests are broken -%global temp_skip_check 0 +# 4.1.0-rc1 pcc64le tests consistently failing: +# Memory content inconsistency at 58f6000 first_byte = 46 last_byte = 45 current = 46 hit_edge = 1 +# ERROR - Bail out! ERROR:/builddir/build/BUILD/qemu-4.2.0-rc1/tests/migration-test.c:372:check_guests_ram: assertion failed: (bad == 0) +%global temp_skip_check 1 pushd build-dynamic %ifnarch %{archs_skip_tests} From 6732563c656b4892975b019cc63db37059165683 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 20 Nov 2019 13:06:13 -0500 Subject: [PATCH 17/32] Update to qemu-4.2.0 rc2 Signed-off-by: Cole Robinson --- .gitignore | 1 + ...migration-test-if-dev-kvm-cannot-be-.patch | 49 +++++++++++++++++++ qemu.spec | 9 +++- sources | 2 +- 4 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 0002-pseries-disable-migration-test-if-dev-kvm-cannot-be-.patch diff --git a/.gitignore b/.gitignore index e9e9dc2..f0a4eee 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ /qemu-4.1.0-rc2.tar.xz /qemu-4.1.0.tar.xz /qemu-4.2.0-rc1.tar.xz +/qemu-4.2.0-rc2.tar.xz diff --git a/0002-pseries-disable-migration-test-if-dev-kvm-cannot-be-.patch b/0002-pseries-disable-migration-test-if-dev-kvm-cannot-be-.patch new file mode 100644 index 0000000..a88eb75 --- /dev/null +++ b/0002-pseries-disable-migration-test-if-dev-kvm-cannot-be-.patch @@ -0,0 +1,49 @@ +From 5c84a2c439afb2a6a5aa69ccd2d7e2ecdb1d18cf Mon Sep 17 00:00:00 2001 +Message-Id: <5c84a2c439afb2a6a5aa69ccd2d7e2ecdb1d18cf.1574270987.git.crobinso@redhat.com> +From: Laurent Vivier +Date: Wed, 20 Nov 2019 18:09:55 +0100 +Subject: [PATCH] pseries: disable migration-test if /dev/kvm cannot be used + +On ppc64, migration-test only works with kvm_hv, and we already +have a check to verify the module is loaded. + +kvm_hv module can be loaded in memory and /sys/module/kvm_hv exists, +but on some systems (like build systems) /dev/kvm can be missing +(by administrators choice). + +And as kvm_hv exists test-migration is started but QEMU falls back to +TCG because it cannot be used: + + Could not access KVM kernel module: No such file or directory + failed to initialize KVM: No such file or directory + Back to tcg accelerator + +And as the test is done with TCG, it fails. + +As for s390x, we must check for the existence and the access rights +of /dev/kvm. + +Reported-by: Cole Robinson +Signed-off-by: Laurent Vivier +Signed-off-by: Cole Robinson +--- + tests/migration-test.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/tests/migration-test.c b/tests/migration-test.c +index ac780dffda..2b25ba6d77 100644 +--- a/tests/migration-test.c ++++ b/tests/migration-test.c +@@ -1349,7 +1349,8 @@ int main(int argc, char **argv) + * some reason) + */ + if (g_str_equal(qtest_get_arch(), "ppc64") && +- access("/sys/module/kvm_hv", F_OK)) { ++ (access("/sys/module/kvm_hv", F_OK) || ++ access("/dev/kvm", R_OK | W_OK))) { + g_test_message("Skipping test: kvm_hv not available"); + return g_test_run(); + } +-- +2.23.0 + diff --git a/qemu.spec b/qemu.spec index f03fabf..58ae10a 100644 --- a/qemu.spec +++ b/qemu.spec @@ -145,7 +145,7 @@ %{obsoletes_block_rbd} # Release candidate version tracking -%global rcver rc1 +%global rcver rc2 %if 0%{?rcver:1} %global rcrel .%{rcver} %global rcstr -%{rcver} @@ -155,7 +155,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 4.2.0 -Release: 0.1%{?rcrel}%{?dist} +Release: 0.2%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -164,6 +164,8 @@ Source0: http://wiki.qemu-project.org/download/%{name}-%{version}%{?rcstr}.tar.x # Fix a test suite error Patch1: 0001-tests-fix-modules-test-duplicate-test-case-error.patch +# Properly skip pseries test on not-kvm +Patch2: 0002-pseries-disable-migration-test-if-dev-kvm-cannot-be-.patch # guest agent service Source10: qemu-guest-agent.service @@ -1882,6 +1884,9 @@ getent passwd qemu >/dev/null || \ %changelog +* Wed Nov 20 2019 Cole Robinson - 2:4.2.0-0.2.rc2 +- Update to qemu-4.2.0 rc2 + * Tue Nov 12 2019 Cole Robinson - 2:4.2.0-0.1.rc1 - Update to qemu-4.2.0 rc1 diff --git a/sources b/sources index 310b89f..048bffd 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (qemu-4.2.0-rc1.tar.xz) = 8ad5e0472fd384a9ba03b2e8fbb1e887169abb47a50a3f130b1943b39f45677a9e65ca5d1deb96338a5b3c3953db67f50e194a6763e9121c0eb5f620896162a9 +SHA512 (qemu-4.2.0-rc2.tar.xz) = a02aea8282c3ca49f290180b9749e9583f1e3bf47f86672ab4ed663ec310c491a0a3860192db1b5582737e4c57d14ad3a59f3e85d82c8d7848d3742b3fbd224f From 1d0e437ac8cde0d710599d7c9e1fc97ac36ac7ce Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 20 Nov 2019 13:09:58 -0500 Subject: [PATCH 18/32] Re-enable test suite Signed-off-by: Cole Robinson --- qemu.spec | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/qemu.spec b/qemu.spec index 58ae10a..ef4f6e0 100644 --- a/qemu.spec +++ b/qemu.spec @@ -1319,10 +1319,7 @@ chmod +x %{buildroot}%{_libdir}/qemu/*.so %global archs_ignore_test_failures 0 # Enable this temporarily if tests are broken -# 4.1.0-rc1 pcc64le tests consistently failing: -# Memory content inconsistency at 58f6000 first_byte = 46 last_byte = 45 current = 46 hit_edge = 1 -# ERROR - Bail out! ERROR:/builddir/build/BUILD/qemu-4.2.0-rc1/tests/migration-test.c:372:check_guests_ram: assertion failed: (bad == 0) -%global temp_skip_check 1 +%global temp_skip_check 0 pushd build-dynamic %ifnarch %{archs_skip_tests} From e6e2c63c09ec72a34e3e7f978810c18addc31175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 2 Dec 2019 11:35:47 +0000 Subject: [PATCH 19/32] Disable rdma on arm 32-bit (rhbz #1778517) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniel P. Berrangé --- qemu.spec | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/qemu.spec b/qemu.spec index ef4f6e0..6e9d20f 100644 --- a/qemu.spec +++ b/qemu.spec @@ -87,6 +87,12 @@ %endif %global with_block_gluster 1 +%ifarch %{arm} +%define with_rdma 0 +%else +%define with_rdma 1 +%endif + %define evr %{epoch}:%{version}-%{release} %define requires_block_curl Requires: %{name}-block-curl = %{evr} @@ -155,7 +161,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 4.2.0 -Release: 0.2%{?rcrel}%{?dist} +Release: 0.3%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -279,7 +285,9 @@ BuildRequires: vte291-devel # GTK translations BuildRequires: gettext # RDMA migration +%if %{with_rdma} BuildRequires: rdma-core-devel +%endif %if %{have_xen} # Xen support BuildRequires: xen-devel @@ -1881,6 +1889,9 @@ getent passwd qemu >/dev/null || \ %changelog +* Mon Dec 2 2019 Daniel P. Berrangé - 2:4.2.0-0.3.rc2 +- Disable RDMA on 32-bit arm (rhbz #1778517) + * Wed Nov 20 2019 Cole Robinson - 2:4.2.0-0.2.rc2 - Update to qemu-4.2.0 rc2 From 46ea403d2f0e91055c2295ba4a43a5a22e9ff026 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 10 Dec 2019 16:50:49 -0500 Subject: [PATCH 20/32] Update to qemu-4.2.0 rc5 Signed-off-by: Cole Robinson --- .gitignore | 1 + ...migration-test-if-dev-kvm-cannot-be-.patch | 49 ------------------- qemu.spec | 9 ++-- sources | 2 +- 4 files changed, 7 insertions(+), 54 deletions(-) delete mode 100644 0002-pseries-disable-migration-test-if-dev-kvm-cannot-be-.patch diff --git a/.gitignore b/.gitignore index f0a4eee..4466667 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ /qemu-4.1.0.tar.xz /qemu-4.2.0-rc1.tar.xz /qemu-4.2.0-rc2.tar.xz +/qemu-4.2.0-rc5.tar.xz diff --git a/0002-pseries-disable-migration-test-if-dev-kvm-cannot-be-.patch b/0002-pseries-disable-migration-test-if-dev-kvm-cannot-be-.patch deleted file mode 100644 index a88eb75..0000000 --- a/0002-pseries-disable-migration-test-if-dev-kvm-cannot-be-.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 5c84a2c439afb2a6a5aa69ccd2d7e2ecdb1d18cf Mon Sep 17 00:00:00 2001 -Message-Id: <5c84a2c439afb2a6a5aa69ccd2d7e2ecdb1d18cf.1574270987.git.crobinso@redhat.com> -From: Laurent Vivier -Date: Wed, 20 Nov 2019 18:09:55 +0100 -Subject: [PATCH] pseries: disable migration-test if /dev/kvm cannot be used - -On ppc64, migration-test only works with kvm_hv, and we already -have a check to verify the module is loaded. - -kvm_hv module can be loaded in memory and /sys/module/kvm_hv exists, -but on some systems (like build systems) /dev/kvm can be missing -(by administrators choice). - -And as kvm_hv exists test-migration is started but QEMU falls back to -TCG because it cannot be used: - - Could not access KVM kernel module: No such file or directory - failed to initialize KVM: No such file or directory - Back to tcg accelerator - -And as the test is done with TCG, it fails. - -As for s390x, we must check for the existence and the access rights -of /dev/kvm. - -Reported-by: Cole Robinson -Signed-off-by: Laurent Vivier -Signed-off-by: Cole Robinson ---- - tests/migration-test.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/tests/migration-test.c b/tests/migration-test.c -index ac780dffda..2b25ba6d77 100644 ---- a/tests/migration-test.c -+++ b/tests/migration-test.c -@@ -1349,7 +1349,8 @@ int main(int argc, char **argv) - * some reason) - */ - if (g_str_equal(qtest_get_arch(), "ppc64") && -- access("/sys/module/kvm_hv", F_OK)) { -+ (access("/sys/module/kvm_hv", F_OK) || -+ access("/dev/kvm", R_OK | W_OK))) { - g_test_message("Skipping test: kvm_hv not available"); - return g_test_run(); - } --- -2.23.0 - diff --git a/qemu.spec b/qemu.spec index 6e9d20f..adda418 100644 --- a/qemu.spec +++ b/qemu.spec @@ -151,7 +151,7 @@ %{obsoletes_block_rbd} # Release candidate version tracking -%global rcver rc2 +%global rcver rc5 %if 0%{?rcver:1} %global rcrel .%{rcver} %global rcstr -%{rcver} @@ -161,7 +161,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 4.2.0 -Release: 0.3%{?rcrel}%{?dist} +Release: 0.4%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -170,8 +170,6 @@ Source0: http://wiki.qemu-project.org/download/%{name}-%{version}%{?rcstr}.tar.x # Fix a test suite error Patch1: 0001-tests-fix-modules-test-duplicate-test-case-error.patch -# Properly skip pseries test on not-kvm -Patch2: 0002-pseries-disable-migration-test-if-dev-kvm-cannot-be-.patch # guest agent service Source10: qemu-guest-agent.service @@ -1889,6 +1887,9 @@ getent passwd qemu >/dev/null || \ %changelog +* Tue Dec 10 2019 Cole Robinson - 4.2.0-0.4.rc5 +- Update to qemu-4.2.0 rc5 + * Mon Dec 2 2019 Daniel P. Berrangé - 2:4.2.0-0.3.rc2 - Disable RDMA on 32-bit arm (rhbz #1778517) diff --git a/sources b/sources index 048bffd..0d2a5b2 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (qemu-4.2.0-rc2.tar.xz) = a02aea8282c3ca49f290180b9749e9583f1e3bf47f86672ab4ed663ec310c491a0a3860192db1b5582737e4c57d14ad3a59f3e85d82c8d7848d3742b3fbd224f +SHA512 (qemu-4.2.0-rc5.tar.xz) = 455a5600353db68da2fea891e101b268cd87c23aa872ae2b86ab93e46a7024a093227110a3a1d8fea79af0830aa27eaf16275bcb6e06c47a87046287682fe716 From 57a3231073cdbf53ad7d07f6bcee2b58b72e44d3 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Fri, 13 Dec 2019 09:32:23 -0500 Subject: [PATCH 21/32] Update to qemu-4.2.0 GA Signed-off-by: Cole Robinson --- .gitignore | 1 + qemu.spec | 7 +++++-- sources | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 4466667..d8a4cd1 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ /qemu-4.2.0-rc1.tar.xz /qemu-4.2.0-rc2.tar.xz /qemu-4.2.0-rc5.tar.xz +/qemu-4.2.0.tar.xz diff --git a/qemu.spec b/qemu.spec index adda418..0123d05 100644 --- a/qemu.spec +++ b/qemu.spec @@ -151,7 +151,7 @@ %{obsoletes_block_rbd} # Release candidate version tracking -%global rcver rc5 +#%%global rcver rc5 %if 0%{?rcver:1} %global rcrel .%{rcver} %global rcstr -%{rcver} @@ -161,7 +161,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 4.2.0 -Release: 0.4%{?rcrel}%{?dist} +Release: 1%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -1887,6 +1887,9 @@ getent passwd qemu >/dev/null || \ %changelog +* Fri Dec 13 2019 Cole Robinson - 4.2.0-1 +- Update to qemu-4.2.0 GA + * Tue Dec 10 2019 Cole Robinson - 4.2.0-0.4.rc5 - Update to qemu-4.2.0 rc5 diff --git a/sources b/sources index 0d2a5b2..46350e1 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (qemu-4.2.0-rc5.tar.xz) = 455a5600353db68da2fea891e101b268cd87c23aa872ae2b86ab93e46a7024a093227110a3a1d8fea79af0830aa27eaf16275bcb6e06c47a87046287682fe716 +SHA512 (qemu-4.2.0.tar.xz) = 2a79973c2b07c53e8c57a808ea8add7b6b2cbca96488ed5d4b669ead8c9318907dec2b6109f180fc8ca8f04c0f73a56e82b3a527b5626b799d7e849f2474ec56 From ba6f50c7d70b9fff5091964fe24f34aab76dc597 Mon Sep 17 00:00:00 2001 From: Mohan Boddu Date: Thu, 19 Dec 2019 10:55:04 -0500 Subject: [PATCH 22/32] Rebuild for xen 4.13 Signed-off-by: Mohan Boddu --- qemu.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qemu.spec b/qemu.spec index 0123d05..76a7077 100644 --- a/qemu.spec +++ b/qemu.spec @@ -161,7 +161,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 4.2.0 -Release: 1%{?rcrel}%{?dist} +Release: 2%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -1887,6 +1887,9 @@ getent passwd qemu >/dev/null || \ %changelog +* Thu Dec 19 2019 Mohan Boddu - 4.2.0-2 +- Rebuild for xen 4.13 + * Fri Dec 13 2019 Cole Robinson - 4.2.0-1 - Update to qemu-4.2.0 GA From b98348b41174d982527aca8bff6be1df0e776a59 Mon Sep 17 00:00:00 2001 From: "Richard W.M. Jones" Date: Sat, 25 Jan 2020 09:19:23 +0000 Subject: [PATCH 23/32] Add miscellaneous fixes for RISC-V (RHBZ#1794902). --- ...e_u-fix-a-memory-leak-in-soc_realize.patch | 34 +++++++++++++ 0002-riscv-Set-xPIE-to-1-after-xRET.patch | 43 +++++++++++++++++ ...-target-riscv-Fix-tb-flags-FS-status.patch | 48 +++++++++++++++++++ ...riscv-fsd-fsw-doesn-t-dirty-FP-state.patch | 41 ++++++++++++++++ ...date-mstatus.SD-when-FS-is-set-dirty.patch | 47 ++++++++++++++++++ qemu.spec | 13 ++++- 6 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 0001-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch create mode 100644 0002-riscv-Set-xPIE-to-1-after-xRET.patch create mode 100644 0003-target-riscv-Fix-tb-flags-FS-status.patch create mode 100644 0004-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch create mode 100644 0005-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch diff --git a/0001-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch b/0001-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch new file mode 100644 index 0000000..3f0e784 --- /dev/null +++ b/0001-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch @@ -0,0 +1,34 @@ +From bb8136df698bd565ee4f6c18d26c50dee320bfe4 Mon Sep 17 00:00:00 2001 +From: Pan Nengyuan +Date: Tue, 10 Dec 2019 15:14:37 +0800 +Subject: [PATCH 1/5] riscv/sifive_u: fix a memory leak in soc_realize() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Fix a minor memory leak in riscv_sifive_u_soc_realize() + +Reported-by: Euler Robot +Signed-off-by: Pan Nengyuan +Reviewed-by: Philippe Mathieu-Daudé +Reviewed-by: Alistair Francis +Signed-off-by: Palmer Dabbelt +--- + hw/riscv/sifive_u.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c +index 0140e95732..0e12b3ccef 100644 +--- a/hw/riscv/sifive_u.c ++++ b/hw/riscv/sifive_u.c +@@ -542,6 +542,7 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) + SIFIVE_U_PLIC_CONTEXT_BASE, + SIFIVE_U_PLIC_CONTEXT_STRIDE, + memmap[SIFIVE_U_PLIC].size); ++ g_free(plic_hart_config); + sifive_uart_create(system_memory, memmap[SIFIVE_U_UART0].base, + serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART0_IRQ)); + sifive_uart_create(system_memory, memmap[SIFIVE_U_UART1].base, +-- +2.24.1 + diff --git a/0002-riscv-Set-xPIE-to-1-after-xRET.patch b/0002-riscv-Set-xPIE-to-1-after-xRET.patch new file mode 100644 index 0000000..4bd340a --- /dev/null +++ b/0002-riscv-Set-xPIE-to-1-after-xRET.patch @@ -0,0 +1,43 @@ +From a37f21c27d3e2342c2080aafd4cfe7e949612428 Mon Sep 17 00:00:00 2001 +From: Yiting Wang +Date: Fri, 3 Jan 2020 11:53:42 +0800 +Subject: [PATCH 2/5] riscv: Set xPIE to 1 after xRET + +When executing an xRET instruction, supposing xPP holds the +value y, xIE is set to xPIE; the privilege mode is changed to y; +xPIE is set to 1. But QEMU sets xPIE to 0 incorrectly. + +Signed-off-by: Yiting Wang +Reviewed-by: Bin Meng +Tested-by: Bin Meng +Reviewed-by: Alistair Francis +Signed-off-by: Palmer Dabbelt +--- + target/riscv/op_helper.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c +index 331cc36232..e87c9115bc 100644 +--- a/target/riscv/op_helper.c ++++ b/target/riscv/op_helper.c +@@ -93,7 +93,7 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) + env->priv_ver >= PRIV_VERSION_1_10_0 ? + MSTATUS_SIE : MSTATUS_UIE << prev_priv, + get_field(mstatus, MSTATUS_SPIE)); +- mstatus = set_field(mstatus, MSTATUS_SPIE, 0); ++ mstatus = set_field(mstatus, MSTATUS_SPIE, 1); + mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); + riscv_cpu_set_mode(env, prev_priv); + env->mstatus = mstatus; +@@ -118,7 +118,7 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) + env->priv_ver >= PRIV_VERSION_1_10_0 ? + MSTATUS_MIE : MSTATUS_UIE << prev_priv, + get_field(mstatus, MSTATUS_MPIE)); +- mstatus = set_field(mstatus, MSTATUS_MPIE, 0); ++ mstatus = set_field(mstatus, MSTATUS_MPIE, 1); + mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); + riscv_cpu_set_mode(env, prev_priv); + env->mstatus = mstatus; +-- +2.24.1 + diff --git a/0003-target-riscv-Fix-tb-flags-FS-status.patch b/0003-target-riscv-Fix-tb-flags-FS-status.patch new file mode 100644 index 0000000..93e5287 --- /dev/null +++ b/0003-target-riscv-Fix-tb-flags-FS-status.patch @@ -0,0 +1,48 @@ +From 613fa160e19abe8e1fe44423fcfa8ec73d3d48e5 Mon Sep 17 00:00:00 2001 +From: ShihPo Hung +Date: Tue, 14 Jan 2020 22:17:31 -0800 +Subject: [PATCH 3/5] target/riscv: Fix tb->flags FS status + +It was found that running libquantum on riscv-linux qemu produced an +incorrect result. After investigation, FP registers are not saved +during context switch due to incorrect mstatus.FS. + +In current implementation tb->flags merges all non-disabled state to +dirty. This means the code in mark_fs_dirty in translate.c that +handles initial and clean states is unreachable. + +This patch fixes it and is successfully tested with: + libquantum + +Thanks to Richard for pointing out the actual bug. + +v3: remove the redundant condition +v2: root cause FS problem + +Suggested-by: Richard Henderson +Signed-off-by: ShihPo Hung +Reviewed-by: Richard Henderson +Signed-off-by: Palmer Dabbelt +--- + target/riscv/cpu.h | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h +index e59343e13c..de0a8d893a 100644 +--- a/target/riscv/cpu.h ++++ b/target/riscv/cpu.h +@@ -293,10 +293,7 @@ static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, + #ifdef CONFIG_USER_ONLY + *flags = TB_FLAGS_MSTATUS_FS; + #else +- *flags = cpu_mmu_index(env, 0); +- if (riscv_cpu_fp_enabled(env)) { +- *flags |= TB_FLAGS_MSTATUS_FS; +- } ++ *flags = cpu_mmu_index(env, 0) | (env->mstatus & MSTATUS_FS); + #endif + } + +-- +2.24.1 + diff --git a/0004-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch b/0004-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch new file mode 100644 index 0000000..6b5a896 --- /dev/null +++ b/0004-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch @@ -0,0 +1,41 @@ +From a59796eb6d59bbd74ce28ddbddb1b83e60674e96 Mon Sep 17 00:00:00 2001 +From: ShihPo Hung +Date: Tue, 14 Jan 2020 22:17:32 -0800 +Subject: [PATCH 4/5] target/riscv: fsd/fsw doesn't dirty FP state + +Signed-off-by: ShihPo Hung +Reviewed-by: Richard Henderson +Reviewed-by: Alistair Francis +Signed-off-by: Palmer Dabbelt +--- + target/riscv/insn_trans/trans_rvd.inc.c | 1 - + target/riscv/insn_trans/trans_rvf.inc.c | 1 - + 2 files changed, 2 deletions(-) + +diff --git a/target/riscv/insn_trans/trans_rvd.inc.c b/target/riscv/insn_trans/trans_rvd.inc.c +index 393fa0248c..ea1044f13b 100644 +--- a/target/riscv/insn_trans/trans_rvd.inc.c ++++ b/target/riscv/insn_trans/trans_rvd.inc.c +@@ -43,7 +43,6 @@ static bool trans_fsd(DisasContext *ctx, arg_fsd *a) + + tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], t0, ctx->mem_idx, MO_TEQ); + +- mark_fs_dirty(ctx); + tcg_temp_free(t0); + return true; + } +diff --git a/target/riscv/insn_trans/trans_rvf.inc.c b/target/riscv/insn_trans/trans_rvf.inc.c +index 172dbfa919..e23cd639a6 100644 +--- a/target/riscv/insn_trans/trans_rvf.inc.c ++++ b/target/riscv/insn_trans/trans_rvf.inc.c +@@ -52,7 +52,6 @@ static bool trans_fsw(DisasContext *ctx, arg_fsw *a) + tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], t0, ctx->mem_idx, MO_TEUL); + + tcg_temp_free(t0); +- mark_fs_dirty(ctx); + return true; + } + +-- +2.24.1 + diff --git a/0005-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch b/0005-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch new file mode 100644 index 0000000..b3733a6 --- /dev/null +++ b/0005-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch @@ -0,0 +1,47 @@ +From 82f014671cf057de51c4a577c9e2ad637dcec6f9 Mon Sep 17 00:00:00 2001 +From: ShihPo Hung +Date: Tue, 14 Jan 2020 22:17:33 -0800 +Subject: [PATCH 5/5] target/riscv: update mstatus.SD when FS is set dirty + +remove the check becuase SD bit should summarize FS and XS fields +unconditionally. + +Signed-off-by: ShihPo Hung +Reviewed-by: Richard Henderson +Reviewed-by: Alistair Francis +Signed-off-by: Palmer Dabbelt +--- + target/riscv/csr.c | 3 +-- + target/riscv/translate.c | 2 +- + 2 files changed, 2 insertions(+), 3 deletions(-) + +diff --git a/target/riscv/csr.c b/target/riscv/csr.c +index da02f9f0b1..0e34c292c5 100644 +--- a/target/riscv/csr.c ++++ b/target/riscv/csr.c +@@ -341,8 +341,7 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val) + + mstatus = (mstatus & ~mask) | (val & mask); + +- dirty = (riscv_cpu_fp_enabled(env) && +- ((mstatus & MSTATUS_FS) == MSTATUS_FS)) | ++ dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) | + ((mstatus & MSTATUS_XS) == MSTATUS_XS); + mstatus = set_field(mstatus, MSTATUS_SD, dirty); + env->mstatus = mstatus; +diff --git a/target/riscv/translate.c b/target/riscv/translate.c +index ab6a891dc3..8e40ed3ac4 100644 +--- a/target/riscv/translate.c ++++ b/target/riscv/translate.c +@@ -394,7 +394,7 @@ static void mark_fs_dirty(DisasContext *ctx) + + tmp = tcg_temp_new(); + tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); +- tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); ++ tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS | MSTATUS_SD); + tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); + tcg_temp_free(tmp); + } +-- +2.24.1 + diff --git a/qemu.spec b/qemu.spec index 76a7077..eb965e5 100644 --- a/qemu.spec +++ b/qemu.spec @@ -161,7 +161,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 4.2.0 -Release: 2%{?rcrel}%{?dist} +Release: 3%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -171,6 +171,14 @@ Source0: http://wiki.qemu-project.org/download/%{name}-%{version}%{?rcstr}.tar.x # Fix a test suite error Patch1: 0001-tests-fix-modules-test-duplicate-test-case-error.patch +# Miscellaneous fixes for RISC-V, merged upstream in commit +# ba2ed84fe6a78f64b2da441750fc6e925d94106a. +Patch2: 0001-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch +Patch3: 0002-riscv-Set-xPIE-to-1-after-xRET.patch +Patch4: 0003-target-riscv-Fix-tb-flags-FS-status.patch +Patch5: 0004-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch +Patch6: 0005-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch + # guest agent service Source10: qemu-guest-agent.service Source17: qemu-ga.sysconfig @@ -1887,6 +1895,9 @@ getent passwd qemu >/dev/null || \ %changelog +* Sat Jan 25 2019 Mohan Boddu - 4.2.0-3 +- Add miscellaneous fixes for RISC-V (RHBZ#1794902). + * Thu Dec 19 2019 Mohan Boddu - 4.2.0-2 - Rebuild for xen 4.13 From 1d442bb6123659211e5831a3726cb10ae0cdaf3e Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 28 Jan 2020 09:48:34 -0500 Subject: [PATCH 24/32] qemu-4.2.0-4.fc32 virtio-fs support from upstream --- ...dules-test-duplicate-test-case-error.patch | 5 - ...e_u-fix-a-memory-leak-in-soc_realize.patch | 6 +- ... 0003-riscv-Set-xPIE-to-1-after-xRET.patch | 6 +- ...-target-riscv-Fix-tb-flags-FS-status.patch | 6 +- ...riscv-fsd-fsw-doesn-t-dirty-FP-state.patch | 6 +- ...date-mstatus.SD-when-FS-is-set-dirty.patch | 6 +- ...io-fs-fix-MSI-X-nvectors-calculation.patch | 41 + ...host-user-fs-remove-vhostfd-property.patch | 43 + ...me-CONFIG_LIBCAP-to-CONFIG_LIBCAP_NG.patch | 118 + 0010-virtiofsd-Pull-in-upstream-headers.patch | 4895 +++++ 0011-virtiofsd-Pull-in-kernel-s-fuse.h.patch | 929 + 0012-virtiofsd-Add-auxiliary-.c-s.patch | 1371 ++ 0013-virtiofsd-Add-fuse_lowlevel.c.patch | 3156 ++++ 0014-virtiofsd-Add-passthrough_ll.patch | 1370 ++ 0015-virtiofsd-Trim-down-imported-files.patch | 1565 ++ ...-Format-imported-files-to-qemu-style.patch | 14727 ++++++++++++++++ ...fsd-remove-mountpoint-dummy-argument.patch | 143 + ...d-remove-unused-notify-reply-support.patch | 278 + ...move-unused-enum-fuse_buf_copy_flags.patch | 252 + ...fuse_daemonize-ignored-return-values.patch | 104 + ...mmon-header-and-define-for-QEMU-buil.patch | 147 + ...irtiofsd-Trim-out-compatibility-code.patch | 529 + ...-passthrough_ll-fix-fallocate-ifdefs.patch | 36 + ...sync-work-even-if-only-inode-is-pass.patch | 79 + 0025-virtiofsd-Add-options-for-virtio.patch | 84 + ...fsd-add-o-source-PATH-to-help-output.patch | 30 + ...vhost-connection-instead-of-mounting.patch | 241 + ...virtiofsd-Start-wiring-up-vhost-user.patch | 231 + 0029-virtiofsd-Add-main-virtio-loop.patch | 89 + ...virtiofsd-get-set-features-callbacks.patch | 50 + 0031-virtiofsd-Start-queue-threads.patch | 148 + 0032-virtiofsd-Poll-kick_fd-for-queue.patch | 81 + ...sd-Start-reading-commands-from-queue.patch | 184 + 0034-virtiofsd-Send-replies-to-messages.patch | 183 + 0035-virtiofsd-Keep-track-of-replies.patch | 100 + ...akefile-wiring-for-virtiofsd-contrib.patch | 90 + ...-virtiofsd-Fast-path-for-virtio-read.patch | 220 + ...iofsd-add-fd-FDNUM-fd-passing-option.patch | 154 + ...tiofsd-make-f-foreground-the-default.patch | 60 + 0040-virtiofsd-add-vhost-user.json-file.patch | 57 + ...tiofsd-add-print-capabilities-option.patch | 105 + 0042-virtiofs-Add-maintainers-entry.patch | 36 + ...rough_ll-create-new-files-in-caller-.patch | 179 + ...rough_ll-add-lo_map-for-ino-fh-indir.patch | 162 + ...rough_ll-add-ino_map-to-hide-lo_inod.patch | 376 + ...rough_ll-add-dirp_map-to-hide-lo_dir.patch | 222 + ...rough_ll-add-fd_map-to-hide-file-des.patch | 308 + ...through_ll-add-fallback-for-racy-ops.patch | 284 + 0049-virtiofsd-validate-path-components.patch | 148 + ...-fuse_bufvec-through-to-do_write_buf.patch | 149 + ...Pass-write-iov-s-all-the-way-through.patch | 121 + 0052-virtiofsd-add-fuse_mbuf_iter-API.patch | 115 + ...te-input-buffer-sizes-in-do_write_bu.patch | 117 + ...input-buffer-size-in-fuse_lowlevel.c.patch | 1091 ++ ...fsd-prevent-.-escape-in-lo_do_lookup.patch | 35 + ...sd-prevent-.-escape-in-lo_do_readdir.patch | 89 + ...-proc-self-fd-O_PATH-file-descriptor.patch | 374 + 0058-virtiofsd-sandbox-mount-namespace.patch | 150 + ...d-move-to-an-empty-network-namespace.patch | 50 + ...irtiofsd-move-to-a-new-pid-namespace.patch | 207 + 0061-virtiofsd-add-seccomp-whitelist.patch | 265 + ...ofsd-Parse-flag-FUSE_WRITE_KILL_PRIV.patch | 57 + 0063-virtiofsd-cap-ng-helpers.patch | 159 + ...op-CAP_FSETID-if-client-asked-for-it.patch | 156 + ...ofsd-set-maximum-RLIMIT_NOFILE-limit.patch | 77 + ...tiofsd-fix-libfuse-information-leaks.patch | 306 + ...iofsd-add-syslog-command-line-option.patch | 223 + ...log-only-when-priority-is-high-enoug.patch | 451 + ...-to-the-log-with-FUSE_LOG_DEBUG-leve.patch | 68 + ...mestamp-to-the-log-with-FUSE_LOG_DEB.patch | 56 + 0071-virtiofsd-Handle-reinit.patch | 37 + 0072-virtiofsd-Handle-hard-reboot.patch | 49 + ...Kill-threads-when-queues-are-stopped.patch | 126 + ...Print-unexpected-slave-message-types.patch | 32 + ...ost-user-Protect-slave-fd-with-mutex.patch | 118 + ...passthrough_ll-add-renameat2-support.patch | 33 + ...rough_ll-disable-readdirplus-on-cach.patch | 33 + ...d-passthrough_ll-control-readdirplus.patch | 60 + ...-unref_inode-to-unref_inode_lolocked.patch | 77 + ...hen-parent-inode-isn-t-known-in-lo_d.patch | 66 + ...ract-root-inode-init-into-setup_root.patch | 91 + ...rough_ll-clean-up-cache-related-opti.patch | 121 + ...rtiofsd-passthrough_ll-use-hashtable.patch | 195 + ...virtiofsd-Clean-up-inodes-on-destroy.patch | 69 + ...t-nanosecond-resolution-for-file-tim.patch | 66 + ...virtiofsd-fix-error-handling-in-main.patch | 47 + ...fsd-cleanup-allocated-resource-in-se.patch | 66 + ...rtiofsd-fix-memory-leak-on-lo.source.patch | 47 + ...iofsd-add-helper-for-lo_data-cleanup.patch | 72 + ...t-multiply-running-with-same-vhost_u.patch | 127 + ...d-enable-PARALLEL_DIROPS-during-INIT.patch | 31 + ...correct-error-handling-in-lo_do_look.patch | 27 + ...sd-fix-memory-leak-on-fuse-queueinfo.patch | 44 + ...virtiofsd-Support-remote-posix-locks.patch | 336 + ...se_lowlevel_is_virtio-in-fuse_sessio.patch | 40 + ...t-fv_queue_thread-vs-virtio_loop-rac.patch | 132 + 0097-virtiofsd-make-lo_release-atomic.patch | 46 + ...iofsd-prevent-races-with-lo_dirp_put.patch | 131 + ...name-inode-refcount-to-inode-nlookup.patch | 123 + ...t-user-Fix-some-memtable-remap-cases.patch | 98 + ...rough_ll-fix-refcounting-on-remove-r.patch | 123 + ...uce-inode-refcount-to-prevent-use-af.patch | 569 + ...d-do-not-always-set-FUSE_FLOCK_LOCKS.patch | 38 + ...t-more-fprintf-and-perror-to-use-fus.patch | 83 + ...Reset-O_DIRECT-flag-during-file-open.patch | 56 + ...ta-corruption-with-O_APPEND-write-in.patch | 120 + ...rough_ll-Use-cache_readdir-for-direc.patch | 29 + ...sd-add-definition-of-fuse_buf_writev.patch | 77 + ...se_buf_writev-to-replace-fuse_buf_wr.patch | 66 + ...sd-process-requests-in-a-thread-pool.patch | 514 + ...prevent-FUSE_INIT-FUSE_DESTROY-races.patch | 84 + ...tiofsd-fix-lo_destroy-resource-leaks.patch | 78 + ...ofsd-add-thread-pool-size-NUM-option.patch | 90 + ...t-lo_destroy-to-take-the-lo-mutex-lo.patch | 96 + ...rough_ll-Pass-errno-to-fuse_reply_er.patch | 34 + ...ll-queue-threads-on-exit-in-virtio_l.patch | 52 + ...add-some-options-to-the-help-message.patch | 55 + qemu.spec | 141 +- 118 files changed, 42758 insertions(+), 43 deletions(-) rename 0001-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch => 0002-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch (88%) rename 0002-riscv-Set-xPIE-to-1-after-xRET.patch => 0003-riscv-Set-xPIE-to-1-after-xRET.patch (92%) rename 0003-target-riscv-Fix-tb-flags-FS-status.patch => 0004-target-riscv-Fix-tb-flags-FS-status.patch (91%) rename 0004-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch => 0005-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch (89%) rename 0005-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch => 0006-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch (91%) create mode 100644 0007-virtio-fs-fix-MSI-X-nvectors-calculation.patch create mode 100644 0008-vhost-user-fs-remove-vhostfd-property.patch create mode 100644 0009-build-rename-CONFIG_LIBCAP-to-CONFIG_LIBCAP_NG.patch create mode 100644 0010-virtiofsd-Pull-in-upstream-headers.patch create mode 100644 0011-virtiofsd-Pull-in-kernel-s-fuse.h.patch create mode 100644 0012-virtiofsd-Add-auxiliary-.c-s.patch create mode 100644 0013-virtiofsd-Add-fuse_lowlevel.c.patch create mode 100644 0014-virtiofsd-Add-passthrough_ll.patch create mode 100644 0015-virtiofsd-Trim-down-imported-files.patch create mode 100644 0016-virtiofsd-Format-imported-files-to-qemu-style.patch create mode 100644 0017-virtiofsd-remove-mountpoint-dummy-argument.patch create mode 100644 0018-virtiofsd-remove-unused-notify-reply-support.patch create mode 100644 0019-virtiofsd-Remove-unused-enum-fuse_buf_copy_flags.patch create mode 100644 0020-virtiofsd-Fix-fuse_daemonize-ignored-return-values.patch create mode 100644 0021-virtiofsd-Fix-common-header-and-define-for-QEMU-buil.patch create mode 100644 0022-virtiofsd-Trim-out-compatibility-code.patch create mode 100644 0023-vitriofsd-passthrough_ll-fix-fallocate-ifdefs.patch create mode 100644 0024-virtiofsd-Make-fsync-work-even-if-only-inode-is-pass.patch create mode 100644 0025-virtiofsd-Add-options-for-virtio.patch create mode 100644 0026-virtiofsd-add-o-source-PATH-to-help-output.patch create mode 100644 0027-virtiofsd-Open-vhost-connection-instead-of-mounting.patch create mode 100644 0028-virtiofsd-Start-wiring-up-vhost-user.patch create mode 100644 0029-virtiofsd-Add-main-virtio-loop.patch create mode 100644 0030-virtiofsd-get-set-features-callbacks.patch create mode 100644 0031-virtiofsd-Start-queue-threads.patch create mode 100644 0032-virtiofsd-Poll-kick_fd-for-queue.patch create mode 100644 0033-virtiofsd-Start-reading-commands-from-queue.patch create mode 100644 0034-virtiofsd-Send-replies-to-messages.patch create mode 100644 0035-virtiofsd-Keep-track-of-replies.patch create mode 100644 0036-virtiofsd-Add-Makefile-wiring-for-virtiofsd-contrib.patch create mode 100644 0037-virtiofsd-Fast-path-for-virtio-read.patch create mode 100644 0038-virtiofsd-add-fd-FDNUM-fd-passing-option.patch create mode 100644 0039-virtiofsd-make-f-foreground-the-default.patch create mode 100644 0040-virtiofsd-add-vhost-user.json-file.patch create mode 100644 0041-virtiofsd-add-print-capabilities-option.patch create mode 100644 0042-virtiofs-Add-maintainers-entry.patch create mode 100644 0043-virtiofsd-passthrough_ll-create-new-files-in-caller-.patch create mode 100644 0044-virtiofsd-passthrough_ll-add-lo_map-for-ino-fh-indir.patch create mode 100644 0045-virtiofsd-passthrough_ll-add-ino_map-to-hide-lo_inod.patch create mode 100644 0046-virtiofsd-passthrough_ll-add-dirp_map-to-hide-lo_dir.patch create mode 100644 0047-virtiofsd-passthrough_ll-add-fd_map-to-hide-file-des.patch create mode 100644 0048-virtiofsd-passthrough_ll-add-fallback-for-racy-ops.patch create mode 100644 0049-virtiofsd-validate-path-components.patch create mode 100644 0050-virtiofsd-Plumb-fuse_bufvec-through-to-do_write_buf.patch create mode 100644 0051-virtiofsd-Pass-write-iov-s-all-the-way-through.patch create mode 100644 0052-virtiofsd-add-fuse_mbuf_iter-API.patch create mode 100644 0053-virtiofsd-validate-input-buffer-sizes-in-do_write_bu.patch create mode 100644 0054-virtiofsd-check-input-buffer-size-in-fuse_lowlevel.c.patch create mode 100644 0055-virtiofsd-prevent-.-escape-in-lo_do_lookup.patch create mode 100644 0056-virtiofsd-prevent-.-escape-in-lo_do_readdir.patch create mode 100644 0057-virtiofsd-use-proc-self-fd-O_PATH-file-descriptor.patch create mode 100644 0058-virtiofsd-sandbox-mount-namespace.patch create mode 100644 0059-virtiofsd-move-to-an-empty-network-namespace.patch create mode 100644 0060-virtiofsd-move-to-a-new-pid-namespace.patch create mode 100644 0061-virtiofsd-add-seccomp-whitelist.patch create mode 100644 0062-virtiofsd-Parse-flag-FUSE_WRITE_KILL_PRIV.patch create mode 100644 0063-virtiofsd-cap-ng-helpers.patch create mode 100644 0064-virtiofsd-Drop-CAP_FSETID-if-client-asked-for-it.patch create mode 100644 0065-virtiofsd-set-maximum-RLIMIT_NOFILE-limit.patch create mode 100644 0066-virtiofsd-fix-libfuse-information-leaks.patch create mode 100644 0067-virtiofsd-add-syslog-command-line-option.patch create mode 100644 0068-virtiofsd-print-log-only-when-priority-is-high-enoug.patch create mode 100644 0069-virtiofsd-Add-ID-to-the-log-with-FUSE_LOG_DEBUG-leve.patch create mode 100644 0070-virtiofsd-Add-timestamp-to-the-log-with-FUSE_LOG_DEB.patch create mode 100644 0071-virtiofsd-Handle-reinit.patch create mode 100644 0072-virtiofsd-Handle-hard-reboot.patch create mode 100644 0073-virtiofsd-Kill-threads-when-queues-are-stopped.patch create mode 100644 0074-vhost-user-Print-unexpected-slave-message-types.patch create mode 100644 0075-contrib-libvhost-user-Protect-slave-fd-with-mutex.patch create mode 100644 0076-virtiofsd-passthrough_ll-add-renameat2-support.patch create mode 100644 0077-virtiofsd-passthrough_ll-disable-readdirplus-on-cach.patch create mode 100644 0078-virtiofsd-passthrough_ll-control-readdirplus.patch create mode 100644 0079-virtiofsd-rename-unref_inode-to-unref_inode_lolocked.patch create mode 100644 0080-virtiofsd-fail-when-parent-inode-isn-t-known-in-lo_d.patch create mode 100644 0081-virtiofsd-extract-root-inode-init-into-setup_root.patch create mode 100644 0082-virtiofsd-passthrough_ll-clean-up-cache-related-opti.patch create mode 100644 0083-virtiofsd-passthrough_ll-use-hashtable.patch create mode 100644 0084-virtiofsd-Clean-up-inodes-on-destroy.patch create mode 100644 0085-virtiofsd-support-nanosecond-resolution-for-file-tim.patch create mode 100644 0086-virtiofsd-fix-error-handling-in-main.patch create mode 100644 0087-virtiofsd-cleanup-allocated-resource-in-se.patch create mode 100644 0088-virtiofsd-fix-memory-leak-on-lo.source.patch create mode 100644 0089-virtiofsd-add-helper-for-lo_data-cleanup.patch create mode 100644 0090-virtiofsd-Prevent-multiply-running-with-same-vhost_u.patch create mode 100644 0091-virtiofsd-enable-PARALLEL_DIROPS-during-INIT.patch create mode 100644 0092-virtiofsd-fix-incorrect-error-handling-in-lo_do_look.patch create mode 100644 0093-Virtiofsd-fix-memory-leak-on-fuse-queueinfo.patch create mode 100644 0094-virtiofsd-Support-remote-posix-locks.patch create mode 100644 0095-virtiofsd-use-fuse_lowlevel_is_virtio-in-fuse_sessio.patch create mode 100644 0096-virtiofsd-prevent-fv_queue_thread-vs-virtio_loop-rac.patch create mode 100644 0097-virtiofsd-make-lo_release-atomic.patch create mode 100644 0098-virtiofsd-prevent-races-with-lo_dirp_put.patch create mode 100644 0099-virtiofsd-rename-inode-refcount-to-inode-nlookup.patch create mode 100644 0100-libvhost-user-Fix-some-memtable-remap-cases.patch create mode 100644 0101-virtiofsd-passthrough_ll-fix-refcounting-on-remove-r.patch create mode 100644 0102-virtiofsd-introduce-inode-refcount-to-prevent-use-af.patch create mode 100644 0103-virtiofsd-do-not-always-set-FUSE_FLOCK_LOCKS.patch create mode 100644 0104-virtiofsd-convert-more-fprintf-and-perror-to-use-fus.patch create mode 100644 0105-virtiofsd-Reset-O_DIRECT-flag-during-file-open.patch create mode 100644 0106-virtiofsd-Fix-data-corruption-with-O_APPEND-write-in.patch create mode 100644 0107-virtiofsd-passthrough_ll-Use-cache_readdir-for-direc.patch create mode 100644 0108-virtiofsd-add-definition-of-fuse_buf_writev.patch create mode 100644 0109-virtiofsd-use-fuse_buf_writev-to-replace-fuse_buf_wr.patch create mode 100644 0110-virtiofsd-process-requests-in-a-thread-pool.patch create mode 100644 0111-virtiofsd-prevent-FUSE_INIT-FUSE_DESTROY-races.patch create mode 100644 0112-virtiofsd-fix-lo_destroy-resource-leaks.patch create mode 100644 0113-virtiofsd-add-thread-pool-size-NUM-option.patch create mode 100644 0114-virtiofsd-Convert-lo_destroy-to-take-the-lo-mutex-lo.patch create mode 100644 0115-virtiofsd-passthrough_ll-Pass-errno-to-fuse_reply_er.patch create mode 100644 0116-virtiofsd-stop-all-queue-threads-on-exit-in-virtio_l.patch create mode 100644 0117-virtiofsd-add-some-options-to-the-help-message.patch diff --git a/0001-tests-fix-modules-test-duplicate-test-case-error.patch b/0001-tests-fix-modules-test-duplicate-test-case-error.patch index 7c8f385..2848e42 100644 --- a/0001-tests-fix-modules-test-duplicate-test-case-error.patch +++ b/0001-tests-fix-modules-test-duplicate-test-case-error.patch @@ -1,5 +1,3 @@ -From d64c9aa098cc6e5c0b638438c4959eddfa7e24e2 Mon Sep 17 00:00:00 2001 -Message-Id: From: Cole Robinson Date: Wed, 13 Nov 2019 16:05:11 -0500 Subject: [PATCH] tests: fix modules-test 'duplicate test case' error @@ -35,6 +33,3 @@ index d1a6ace218..88217686e1 100644 qtest_add_data_func(testname, modules + i, test_modules_load); g_free(testname); } --- -2.23.0 - diff --git a/0001-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch b/0002-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch similarity index 88% rename from 0001-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch rename to 0002-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch index 3f0e784..70d4fb1 100644 --- a/0001-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch +++ b/0002-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch @@ -1,7 +1,6 @@ -From bb8136df698bd565ee4f6c18d26c50dee320bfe4 Mon Sep 17 00:00:00 2001 From: Pan Nengyuan Date: Tue, 10 Dec 2019 15:14:37 +0800 -Subject: [PATCH 1/5] riscv/sifive_u: fix a memory leak in soc_realize() +Subject: [PATCH] riscv/sifive_u: fix a memory leak in soc_realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -29,6 +28,3 @@ index 0140e95732..0e12b3ccef 100644 sifive_uart_create(system_memory, memmap[SIFIVE_U_UART0].base, serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART0_IRQ)); sifive_uart_create(system_memory, memmap[SIFIVE_U_UART1].base, --- -2.24.1 - diff --git a/0002-riscv-Set-xPIE-to-1-after-xRET.patch b/0003-riscv-Set-xPIE-to-1-after-xRET.patch similarity index 92% rename from 0002-riscv-Set-xPIE-to-1-after-xRET.patch rename to 0003-riscv-Set-xPIE-to-1-after-xRET.patch index 4bd340a..25a5123 100644 --- a/0002-riscv-Set-xPIE-to-1-after-xRET.patch +++ b/0003-riscv-Set-xPIE-to-1-after-xRET.patch @@ -1,7 +1,6 @@ -From a37f21c27d3e2342c2080aafd4cfe7e949612428 Mon Sep 17 00:00:00 2001 From: Yiting Wang Date: Fri, 3 Jan 2020 11:53:42 +0800 -Subject: [PATCH 2/5] riscv: Set xPIE to 1 after xRET +Subject: [PATCH] riscv: Set xPIE to 1 after xRET When executing an xRET instruction, supposing xPP holds the value y, xIE is set to xPIE; the privilege mode is changed to y; @@ -38,6 +37,3 @@ index 331cc36232..e87c9115bc 100644 mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); riscv_cpu_set_mode(env, prev_priv); env->mstatus = mstatus; --- -2.24.1 - diff --git a/0003-target-riscv-Fix-tb-flags-FS-status.patch b/0004-target-riscv-Fix-tb-flags-FS-status.patch similarity index 91% rename from 0003-target-riscv-Fix-tb-flags-FS-status.patch rename to 0004-target-riscv-Fix-tb-flags-FS-status.patch index 93e5287..d0fc7b5 100644 --- a/0003-target-riscv-Fix-tb-flags-FS-status.patch +++ b/0004-target-riscv-Fix-tb-flags-FS-status.patch @@ -1,7 +1,6 @@ -From 613fa160e19abe8e1fe44423fcfa8ec73d3d48e5 Mon Sep 17 00:00:00 2001 From: ShihPo Hung Date: Tue, 14 Jan 2020 22:17:31 -0800 -Subject: [PATCH 3/5] target/riscv: Fix tb->flags FS status +Subject: [PATCH] target/riscv: Fix tb->flags FS status It was found that running libquantum on riscv-linux qemu produced an incorrect result. After investigation, FP registers are not saved @@ -43,6 +42,3 @@ index e59343e13c..de0a8d893a 100644 #endif } --- -2.24.1 - diff --git a/0004-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch b/0005-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch similarity index 89% rename from 0004-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch rename to 0005-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch index 6b5a896..1ca5360 100644 --- a/0004-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch +++ b/0005-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch @@ -1,7 +1,6 @@ -From a59796eb6d59bbd74ce28ddbddb1b83e60674e96 Mon Sep 17 00:00:00 2001 From: ShihPo Hung Date: Tue, 14 Jan 2020 22:17:32 -0800 -Subject: [PATCH 4/5] target/riscv: fsd/fsw doesn't dirty FP state +Subject: [PATCH] target/riscv: fsd/fsw doesn't dirty FP state Signed-off-by: ShihPo Hung Reviewed-by: Richard Henderson @@ -36,6 +35,3 @@ index 172dbfa919..e23cd639a6 100644 return true; } --- -2.24.1 - diff --git a/0005-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch b/0006-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch similarity index 91% rename from 0005-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch rename to 0006-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch index b3733a6..de413fb 100644 --- a/0005-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch +++ b/0006-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch @@ -1,7 +1,6 @@ -From 82f014671cf057de51c4a577c9e2ad637dcec6f9 Mon Sep 17 00:00:00 2001 From: ShihPo Hung Date: Tue, 14 Jan 2020 22:17:33 -0800 -Subject: [PATCH 5/5] target/riscv: update mstatus.SD when FS is set dirty +Subject: [PATCH] target/riscv: update mstatus.SD when FS is set dirty remove the check becuase SD bit should summarize FS and XS fields unconditionally. @@ -42,6 +41,3 @@ index ab6a891dc3..8e40ed3ac4 100644 tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); tcg_temp_free(tmp); } --- -2.24.1 - diff --git a/0007-virtio-fs-fix-MSI-X-nvectors-calculation.patch b/0007-virtio-fs-fix-MSI-X-nvectors-calculation.patch new file mode 100644 index 0000000..483a444 --- /dev/null +++ b/0007-virtio-fs-fix-MSI-X-nvectors-calculation.patch @@ -0,0 +1,41 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:00:36 +0000 +Subject: [PATCH] virtio-fs: fix MSI-X nvectors calculation + +The following MSI-X vectors are required: + * VIRTIO Configuration Change + * hiprio virtqueue + * requests virtqueues + +Fix the calculation to reserve enough MSI-X vectors. Otherwise guest +drivers fall back to a sub-optional configuration where all virtqueues +share a single vector. + +This change does not break live migration compatibility since +vhost-user-fs-pci devices are not migratable yet. + +Reported-by: Vivek Goyal +Signed-off-by: Stefan Hajnoczi +Message-Id: <20191209110759.35227-1-stefanha@redhat.com> +Reviewed-by: Dr. David Alan Gilbert +Reviewed-by: Michael S. Tsirkin +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 366844f3d1329c6423dd752891a28ccb3ee8fddd) +--- + hw/virtio/vhost-user-fs-pci.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/hw/virtio/vhost-user-fs-pci.c b/hw/virtio/vhost-user-fs-pci.c +index 933a3f265b..e3a649d4a6 100644 +--- a/hw/virtio/vhost-user-fs-pci.c ++++ b/hw/virtio/vhost-user-fs-pci.c +@@ -40,7 +40,8 @@ static void vhost_user_fs_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) + DeviceState *vdev = DEVICE(&dev->vdev); + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { +- vpci_dev->nvectors = dev->vdev.conf.num_request_queues + 1; ++ /* Also reserve config change and hiprio queue vectors */ ++ vpci_dev->nvectors = dev->vdev.conf.num_request_queues + 2; + } + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); diff --git a/0008-vhost-user-fs-remove-vhostfd-property.patch b/0008-vhost-user-fs-remove-vhostfd-property.patch new file mode 100644 index 0000000..c77730d --- /dev/null +++ b/0008-vhost-user-fs-remove-vhostfd-property.patch @@ -0,0 +1,43 @@ +From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= +Date: Mon, 27 Jan 2020 19:00:37 +0000 +Subject: [PATCH] vhost-user-fs: remove "vhostfd" property +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The property doesn't make much sense for a vhost-user device. + +Signed-off-by: Marc-André Lureau +Message-Id: <20191116112016.14872-1-marcandre.lureau@redhat.com> +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 703857348724319735d9be7b5b996e6445c6e6b9) +--- + hw/virtio/vhost-user-fs.c | 1 - + include/hw/virtio/vhost-user-fs.h | 1 - + 2 files changed, 2 deletions(-) + +diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c +index f0df7f4746..ca0b7fc9de 100644 +--- a/hw/virtio/vhost-user-fs.c ++++ b/hw/virtio/vhost-user-fs.c +@@ -263,7 +263,6 @@ static Property vuf_properties[] = { + DEFINE_PROP_UINT16("num-request-queues", VHostUserFS, + conf.num_request_queues, 1), + DEFINE_PROP_UINT16("queue-size", VHostUserFS, conf.queue_size, 128), +- DEFINE_PROP_STRING("vhostfd", VHostUserFS, conf.vhostfd), + DEFINE_PROP_END_OF_LIST(), + }; + +diff --git a/include/hw/virtio/vhost-user-fs.h b/include/hw/virtio/vhost-user-fs.h +index 539885b458..9ff1bdb7cf 100644 +--- a/include/hw/virtio/vhost-user-fs.h ++++ b/include/hw/virtio/vhost-user-fs.h +@@ -28,7 +28,6 @@ typedef struct { + char *tag; + uint16_t num_request_queues; + uint16_t queue_size; +- char *vhostfd; + } VHostUserFSConf; + + typedef struct { diff --git a/0009-build-rename-CONFIG_LIBCAP-to-CONFIG_LIBCAP_NG.patch b/0009-build-rename-CONFIG_LIBCAP-to-CONFIG_LIBCAP_NG.patch new file mode 100644 index 0000000..02ec43e --- /dev/null +++ b/0009-build-rename-CONFIG_LIBCAP-to-CONFIG_LIBCAP_NG.patch @@ -0,0 +1,118 @@ +From: Paolo Bonzini +Date: Mon, 27 Jan 2020 19:00:38 +0000 +Subject: [PATCH] build: rename CONFIG_LIBCAP to CONFIG_LIBCAP_NG + +Since we are actually testing for the newer capng library, rename the +symbol to match. + +Reviewed-by: Dr. David Alan Gilbert +Signed-off-by: Paolo Bonzini +(cherry picked from commit a358bca24026a377e0804e137a4499e4e041918d) +--- + configure | 2 +- + qemu-bridge-helper.c | 6 +++--- + scsi/qemu-pr-helper.c | 12 ++++++------ + 3 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/configure b/configure +index 6099be1d84..afe9393f04 100755 +--- a/configure ++++ b/configure +@@ -6759,7 +6759,7 @@ if test "$l2tpv3" = "yes" ; then + echo "CONFIG_L2TPV3=y" >> $config_host_mak + fi + if test "$cap_ng" = "yes" ; then +- echo "CONFIG_LIBCAP=y" >> $config_host_mak ++ echo "CONFIG_LIBCAP_NG=y" >> $config_host_mak + fi + echo "CONFIG_AUDIO_DRIVERS=$audio_drv_list" >> $config_host_mak + for drv in $audio_drv_list; do +diff --git a/qemu-bridge-helper.c b/qemu-bridge-helper.c +index 3d50ec094c..88b26747fc 100644 +--- a/qemu-bridge-helper.c ++++ b/qemu-bridge-helper.c +@@ -43,7 +43,7 @@ + + #include "net/tap-linux.h" + +-#ifdef CONFIG_LIBCAP ++#ifdef CONFIG_LIBCAP_NG + #include + #endif + +@@ -207,7 +207,7 @@ static int send_fd(int c, int fd) + return sendmsg(c, &msg, 0); + } + +-#ifdef CONFIG_LIBCAP ++#ifdef CONFIG_LIBCAP_NG + static int drop_privileges(void) + { + /* clear all capabilities */ +@@ -246,7 +246,7 @@ int main(int argc, char **argv) + int access_allowed, access_denied; + int ret = EXIT_SUCCESS; + +-#ifdef CONFIG_LIBCAP ++#ifdef CONFIG_LIBCAP_NG + /* if we're run from an suid binary, immediately drop privileges preserving + * cap_net_admin */ + if (geteuid() == 0 && getuid() != geteuid()) { +diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c +index debb18f4aa..0659ceef09 100644 +--- a/scsi/qemu-pr-helper.c ++++ b/scsi/qemu-pr-helper.c +@@ -24,7 +24,7 @@ + #include + #include + +-#ifdef CONFIG_LIBCAP ++#ifdef CONFIG_LIBCAP_NG + #include + #endif + #include +@@ -70,7 +70,7 @@ static int num_active_sockets = 1; + static int noisy; + static int verbose; + +-#ifdef CONFIG_LIBCAP ++#ifdef CONFIG_LIBCAP_NG + static int uid = -1; + static int gid = -1; + #endif +@@ -97,7 +97,7 @@ static void usage(const char *name) + " (default '%s')\n" + " -T, --trace [[enable=]][,events=][,file=]\n" + " specify tracing options\n" +-#ifdef CONFIG_LIBCAP ++#ifdef CONFIG_LIBCAP_NG + " -u, --user=USER user to drop privileges to\n" + " -g, --group=GROUP group to drop privileges to\n" + #endif +@@ -827,7 +827,7 @@ static void close_server_socket(void) + num_active_sockets--; + } + +-#ifdef CONFIG_LIBCAP ++#ifdef CONFIG_LIBCAP_NG + static int drop_privileges(void) + { + /* clear all capabilities */ +@@ -920,7 +920,7 @@ int main(int argc, char **argv) + pidfile = g_strdup(optarg); + pidfile_specified = true; + break; +-#ifdef CONFIG_LIBCAP ++#ifdef CONFIG_LIBCAP_NG + case 'u': { + unsigned long res; + struct passwd *userinfo = getpwnam(optarg); +@@ -1056,7 +1056,7 @@ int main(int argc, char **argv) + exit(EXIT_FAILURE); + } + +-#ifdef CONFIG_LIBCAP ++#ifdef CONFIG_LIBCAP_NG + if (drop_privileges() < 0) { + error_report("Failed to drop privileges: %s", strerror(errno)); + exit(EXIT_FAILURE); diff --git a/0010-virtiofsd-Pull-in-upstream-headers.patch b/0010-virtiofsd-Pull-in-upstream-headers.patch new file mode 100644 index 0000000..cc049ad --- /dev/null +++ b/0010-virtiofsd-Pull-in-upstream-headers.patch @@ -0,0 +1,4895 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:00:39 +0000 +Subject: [PATCH] virtiofsd: Pull in upstream headers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Pull in headers fromlibfuse's upstream fuse-3.8.0 + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit ee46c78901eb7fa78e328e04c0494ad6d207238b) +--- + tools/virtiofsd/fuse.h | 1275 +++++++++++++++ + tools/virtiofsd/fuse_common.h | 823 ++++++++++ + tools/virtiofsd/fuse_i.h | 139 ++ + tools/virtiofsd/fuse_log.h | 82 + + tools/virtiofsd/fuse_lowlevel.h | 2089 +++++++++++++++++++++++++ + tools/virtiofsd/fuse_misc.h | 59 + + tools/virtiofsd/fuse_opt.h | 271 ++++ + tools/virtiofsd/passthrough_helpers.h | 76 + + 8 files changed, 4814 insertions(+) + create mode 100644 tools/virtiofsd/fuse.h + create mode 100644 tools/virtiofsd/fuse_common.h + create mode 100644 tools/virtiofsd/fuse_i.h + create mode 100644 tools/virtiofsd/fuse_log.h + create mode 100644 tools/virtiofsd/fuse_lowlevel.h + create mode 100644 tools/virtiofsd/fuse_misc.h + create mode 100644 tools/virtiofsd/fuse_opt.h + create mode 100644 tools/virtiofsd/passthrough_helpers.h + +diff --git a/tools/virtiofsd/fuse.h b/tools/virtiofsd/fuse.h +new file mode 100644 +index 0000000000..883f6e59fb +--- /dev/null ++++ b/tools/virtiofsd/fuse.h +@@ -0,0 +1,1275 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2007 Miklos Szeredi ++ ++ This program can be distributed under the terms of the GNU LGPLv2. ++ See the file COPYING.LIB. ++*/ ++ ++#ifndef FUSE_H_ ++#define FUSE_H_ ++ ++/** @file ++ * ++ * This file defines the library interface of FUSE ++ * ++ * IMPORTANT: you should define FUSE_USE_VERSION before including this header. ++ */ ++ ++#include "fuse_common.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* ----------------------------------------------------------- * ++ * Basic FUSE API * ++ * ----------------------------------------------------------- */ ++ ++/** Handle for a FUSE filesystem */ ++struct fuse; ++ ++/** ++ * Readdir flags, passed to ->readdir() ++ */ ++enum fuse_readdir_flags { ++ /** ++ * "Plus" mode. ++ * ++ * The kernel wants to prefill the inode cache during readdir. The ++ * filesystem may honour this by filling in the attributes and setting ++ * FUSE_FILL_DIR_FLAGS for the filler function. The filesystem may also ++ * just ignore this flag completely. ++ */ ++ FUSE_READDIR_PLUS = (1 << 0), ++}; ++ ++enum fuse_fill_dir_flags { ++ /** ++ * "Plus" mode: all file attributes are valid ++ * ++ * The attributes are used by the kernel to prefill the inode cache ++ * during a readdir. ++ * ++ * It is okay to set FUSE_FILL_DIR_PLUS if FUSE_READDIR_PLUS is not set ++ * and vice versa. ++ */ ++ FUSE_FILL_DIR_PLUS = (1 << 1), ++}; ++ ++/** Function to add an entry in a readdir() operation ++ * ++ * The *off* parameter can be any non-zero value that enables the ++ * filesystem to identify the current point in the directory ++ * stream. It does not need to be the actual physical position. A ++ * value of zero is reserved to indicate that seeking in directories ++ * is not supported. ++ * ++ * @param buf the buffer passed to the readdir() operation ++ * @param name the file name of the directory entry ++ * @param stat file attributes, can be NULL ++ * @param off offset of the next entry or zero ++ * @param flags fill flags ++ * @return 1 if buffer is full, zero otherwise ++ */ ++typedef int (*fuse_fill_dir_t) (void *buf, const char *name, ++ const struct stat *stbuf, off_t off, ++ enum fuse_fill_dir_flags flags); ++/** ++ * Configuration of the high-level API ++ * ++ * This structure is initialized from the arguments passed to ++ * fuse_new(), and then passed to the file system's init() handler ++ * which should ensure that the configuration is compatible with the ++ * file system implementation. ++ */ ++struct fuse_config { ++ /** ++ * If `set_gid` is non-zero, the st_gid attribute of each file ++ * is overwritten with the value of `gid`. ++ */ ++ int set_gid; ++ unsigned int gid; ++ ++ /** ++ * If `set_uid` is non-zero, the st_uid attribute of each file ++ * is overwritten with the value of `uid`. ++ */ ++ int set_uid; ++ unsigned int uid; ++ ++ /** ++ * If `set_mode` is non-zero, the any permissions bits set in ++ * `umask` are unset in the st_mode attribute of each file. ++ */ ++ int set_mode; ++ unsigned int umask; ++ ++ /** ++ * The timeout in seconds for which name lookups will be ++ * cached. ++ */ ++ double entry_timeout; ++ ++ /** ++ * The timeout in seconds for which a negative lookup will be ++ * cached. This means, that if file did not exist (lookup ++ * retuned ENOENT), the lookup will only be redone after the ++ * timeout, and the file/directory will be assumed to not ++ * exist until then. A value of zero means that negative ++ * lookups are not cached. ++ */ ++ double negative_timeout; ++ ++ /** ++ * The timeout in seconds for which file/directory attributes ++ * (as returned by e.g. the `getattr` handler) are cached. ++ */ ++ double attr_timeout; ++ ++ /** ++ * Allow requests to be interrupted ++ */ ++ int intr; ++ ++ /** ++ * Specify which signal number to send to the filesystem when ++ * a request is interrupted. The default is hardcoded to ++ * USR1. ++ */ ++ int intr_signal; ++ ++ /** ++ * Normally, FUSE assigns inodes to paths only for as long as ++ * the kernel is aware of them. With this option inodes are ++ * instead remembered for at least this many seconds. This ++ * will require more memory, but may be necessary when using ++ * applications that make use of inode numbers. ++ * ++ * A number of -1 means that inodes will be remembered for the ++ * entire life-time of the file-system process. ++ */ ++ int remember; ++ ++ /** ++ * The default behavior is that if an open file is deleted, ++ * the file is renamed to a hidden file (.fuse_hiddenXXX), and ++ * only removed when the file is finally released. This ++ * relieves the filesystem implementation of having to deal ++ * with this problem. This option disables the hiding ++ * behavior, and files are removed immediately in an unlink ++ * operation (or in a rename operation which overwrites an ++ * existing file). ++ * ++ * It is recommended that you not use the hard_remove ++ * option. When hard_remove is set, the following libc ++ * functions fail on unlinked files (returning errno of ++ * ENOENT): read(2), write(2), fsync(2), close(2), f*xattr(2), ++ * ftruncate(2), fstat(2), fchmod(2), fchown(2) ++ */ ++ int hard_remove; ++ ++ /** ++ * Honor the st_ino field in the functions getattr() and ++ * fill_dir(). This value is used to fill in the st_ino field ++ * in the stat(2), lstat(2), fstat(2) functions and the d_ino ++ * field in the readdir(2) function. The filesystem does not ++ * have to guarantee uniqueness, however some applications ++ * rely on this value being unique for the whole filesystem. ++ * ++ * Note that this does *not* affect the inode that libfuse ++ * and the kernel use internally (also called the "nodeid"). ++ */ ++ int use_ino; ++ ++ /** ++ * If use_ino option is not given, still try to fill in the ++ * d_ino field in readdir(2). If the name was previously ++ * looked up, and is still in the cache, the inode number ++ * found there will be used. Otherwise it will be set to -1. ++ * If use_ino option is given, this option is ignored. ++ */ ++ int readdir_ino; ++ ++ /** ++ * This option disables the use of page cache (file content cache) ++ * in the kernel for this filesystem. This has several affects: ++ * ++ * 1. Each read(2) or write(2) system call will initiate one ++ * or more read or write operations, data will not be ++ * cached in the kernel. ++ * ++ * 2. The return value of the read() and write() system calls ++ * will correspond to the return values of the read and ++ * write operations. This is useful for example if the ++ * file size is not known in advance (before reading it). ++ * ++ * Internally, enabling this option causes fuse to set the ++ * `direct_io` field of `struct fuse_file_info` - overwriting ++ * any value that was put there by the file system. ++ */ ++ int direct_io; ++ ++ /** ++ * This option disables flushing the cache of the file ++ * contents on every open(2). This should only be enabled on ++ * filesystems where the file data is never changed ++ * externally (not through the mounted FUSE filesystem). Thus ++ * it is not suitable for network filesystems and other ++ * intermediate filesystems. ++ * ++ * NOTE: if this option is not specified (and neither ++ * direct_io) data is still cached after the open(2), so a ++ * read(2) system call will not always initiate a read ++ * operation. ++ * ++ * Internally, enabling this option causes fuse to set the ++ * `keep_cache` field of `struct fuse_file_info` - overwriting ++ * any value that was put there by the file system. ++ */ ++ int kernel_cache; ++ ++ /** ++ * This option is an alternative to `kernel_cache`. Instead of ++ * unconditionally keeping cached data, the cached data is ++ * invalidated on open(2) if if the modification time or the ++ * size of the file has changed since it was last opened. ++ */ ++ int auto_cache; ++ ++ /** ++ * The timeout in seconds for which file attributes are cached ++ * for the purpose of checking if auto_cache should flush the ++ * file data on open. ++ */ ++ int ac_attr_timeout_set; ++ double ac_attr_timeout; ++ ++ /** ++ * If this option is given the file-system handlers for the ++ * following operations will not receive path information: ++ * read, write, flush, release, fsync, readdir, releasedir, ++ * fsyncdir, lock, ioctl and poll. ++ * ++ * For the truncate, getattr, chmod, chown and utimens ++ * operations the path will be provided only if the struct ++ * fuse_file_info argument is NULL. ++ */ ++ int nullpath_ok; ++ ++ /** ++ * The remaining options are used by libfuse internally and ++ * should not be touched. ++ */ ++ int show_help; ++ char *modules; ++ int debug; ++}; ++ ++ ++/** ++ * The file system operations: ++ * ++ * Most of these should work very similarly to the well known UNIX ++ * file system operations. A major exception is that instead of ++ * returning an error in 'errno', the operation should return the ++ * negated error value (-errno) directly. ++ * ++ * All methods are optional, but some are essential for a useful ++ * filesystem (e.g. getattr). Open, flush, release, fsync, opendir, ++ * releasedir, fsyncdir, access, create, truncate, lock, init and ++ * destroy are special purpose methods, without which a full featured ++ * filesystem can still be implemented. ++ * ++ * In general, all methods are expected to perform any necessary ++ * permission checking. However, a filesystem may delegate this task ++ * to the kernel by passing the `default_permissions` mount option to ++ * `fuse_new()`. In this case, methods will only be called if ++ * the kernel's permission check has succeeded. ++ * ++ * Almost all operations take a path which can be of any length. ++ */ ++struct fuse_operations { ++ /** Get file attributes. ++ * ++ * Similar to stat(). The 'st_dev' and 'st_blksize' fields are ++ * ignored. The 'st_ino' field is ignored except if the 'use_ino' ++ * mount option is given. In that case it is passed to userspace, ++ * but libfuse and the kernel will still assign a different ++ * inode for internal use (called the "nodeid"). ++ * ++ * `fi` will always be NULL if the file is not currently open, but ++ * may also be NULL if the file is open. ++ */ ++ int (*getattr) (const char *, struct stat *, struct fuse_file_info *fi); ++ ++ /** Read the target of a symbolic link ++ * ++ * The buffer should be filled with a null terminated string. The ++ * buffer size argument includes the space for the terminating ++ * null character. If the linkname is too long to fit in the ++ * buffer, it should be truncated. The return value should be 0 ++ * for success. ++ */ ++ int (*readlink) (const char *, char *, size_t); ++ ++ /** Create a file node ++ * ++ * This is called for creation of all non-directory, non-symlink ++ * nodes. If the filesystem defines a create() method, then for ++ * regular files that will be called instead. ++ */ ++ int (*mknod) (const char *, mode_t, dev_t); ++ ++ /** Create a directory ++ * ++ * Note that the mode argument may not have the type specification ++ * bits set, i.e. S_ISDIR(mode) can be false. To obtain the ++ * correct directory type bits use mode|S_IFDIR ++ * */ ++ int (*mkdir) (const char *, mode_t); ++ ++ /** Remove a file */ ++ int (*unlink) (const char *); ++ ++ /** Remove a directory */ ++ int (*rmdir) (const char *); ++ ++ /** Create a symbolic link */ ++ int (*symlink) (const char *, const char *); ++ ++ /** Rename a file ++ * ++ * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If ++ * RENAME_NOREPLACE is specified, the filesystem must not ++ * overwrite *newname* if it exists and return an error ++ * instead. If `RENAME_EXCHANGE` is specified, the filesystem ++ * must atomically exchange the two files, i.e. both must ++ * exist and neither may be deleted. ++ */ ++ int (*rename) (const char *, const char *, unsigned int flags); ++ ++ /** Create a hard link to a file */ ++ int (*link) (const char *, const char *); ++ ++ /** Change the permission bits of a file ++ * ++ * `fi` will always be NULL if the file is not currenlty open, but ++ * may also be NULL if the file is open. ++ */ ++ int (*chmod) (const char *, mode_t, struct fuse_file_info *fi); ++ ++ /** Change the owner and group of a file ++ * ++ * `fi` will always be NULL if the file is not currenlty open, but ++ * may also be NULL if the file is open. ++ * ++ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is ++ * expected to reset the setuid and setgid bits. ++ */ ++ int (*chown) (const char *, uid_t, gid_t, struct fuse_file_info *fi); ++ ++ /** Change the size of a file ++ * ++ * `fi` will always be NULL if the file is not currenlty open, but ++ * may also be NULL if the file is open. ++ * ++ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is ++ * expected to reset the setuid and setgid bits. ++ */ ++ int (*truncate) (const char *, off_t, struct fuse_file_info *fi); ++ ++ /** Open a file ++ * ++ * Open flags are available in fi->flags. The following rules ++ * apply. ++ * ++ * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be ++ * filtered out / handled by the kernel. ++ * ++ * - Access modes (O_RDONLY, O_WRONLY, O_RDWR, O_EXEC, O_SEARCH) ++ * should be used by the filesystem to check if the operation is ++ * permitted. If the ``-o default_permissions`` mount option is ++ * given, this check is already done by the kernel before calling ++ * open() and may thus be omitted by the filesystem. ++ * ++ * - When writeback caching is enabled, the kernel may send ++ * read requests even for files opened with O_WRONLY. The ++ * filesystem should be prepared to handle this. ++ * ++ * - When writeback caching is disabled, the filesystem is ++ * expected to properly handle the O_APPEND flag and ensure ++ * that each write is appending to the end of the file. ++ * ++ * - When writeback caching is enabled, the kernel will ++ * handle O_APPEND. However, unless all changes to the file ++ * come through the kernel this will not work reliably. The ++ * filesystem should thus either ignore the O_APPEND flag ++ * (and let the kernel handle it), or return an error ++ * (indicating that reliably O_APPEND is not available). ++ * ++ * Filesystem may store an arbitrary file handle (pointer, ++ * index, etc) in fi->fh, and use this in other all other file ++ * operations (read, write, flush, release, fsync). ++ * ++ * Filesystem may also implement stateless file I/O and not store ++ * anything in fi->fh. ++ * ++ * There are also some flags (direct_io, keep_cache) which the ++ * filesystem may set in fi, to change the way the file is opened. ++ * See fuse_file_info structure in for more details. ++ * ++ * If this request is answered with an error code of ENOSYS ++ * and FUSE_CAP_NO_OPEN_SUPPORT is set in ++ * `fuse_conn_info.capable`, this is treated as success and ++ * future calls to open will also succeed without being send ++ * to the filesystem process. ++ * ++ */ ++ int (*open) (const char *, struct fuse_file_info *); ++ ++ /** Read data from an open file ++ * ++ * Read should return exactly the number of bytes requested except ++ * on EOF or error, otherwise the rest of the data will be ++ * substituted with zeroes. An exception to this is when the ++ * 'direct_io' mount option is specified, in which case the return ++ * value of the read system call will reflect the return value of ++ * this operation. ++ */ ++ int (*read) (const char *, char *, size_t, off_t, ++ struct fuse_file_info *); ++ ++ /** Write data to an open file ++ * ++ * Write should return exactly the number of bytes requested ++ * except on error. An exception to this is when the 'direct_io' ++ * mount option is specified (see read operation). ++ * ++ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is ++ * expected to reset the setuid and setgid bits. ++ */ ++ int (*write) (const char *, const char *, size_t, off_t, ++ struct fuse_file_info *); ++ ++ /** Get file system statistics ++ * ++ * The 'f_favail', 'f_fsid' and 'f_flag' fields are ignored ++ */ ++ int (*statfs) (const char *, struct statvfs *); ++ ++ /** Possibly flush cached data ++ * ++ * BIG NOTE: This is not equivalent to fsync(). It's not a ++ * request to sync dirty data. ++ * ++ * Flush is called on each close() of a file descriptor, as opposed to ++ * release which is called on the close of the last file descriptor for ++ * a file. Under Linux, errors returned by flush() will be passed to ++ * userspace as errors from close(), so flush() is a good place to write ++ * back any cached dirty data. However, many applications ignore errors ++ * on close(), and on non-Linux systems, close() may succeed even if flush() ++ * returns an error. For these reasons, filesystems should not assume ++ * that errors returned by flush will ever be noticed or even ++ * delivered. ++ * ++ * NOTE: The flush() method may be called more than once for each ++ * open(). This happens if more than one file descriptor refers to an ++ * open file handle, e.g. due to dup(), dup2() or fork() calls. It is ++ * not possible to determine if a flush is final, so each flush should ++ * be treated equally. Multiple write-flush sequences are relatively ++ * rare, so this shouldn't be a problem. ++ * ++ * Filesystems shouldn't assume that flush will be called at any ++ * particular point. It may be called more times than expected, or not ++ * at all. ++ * ++ * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html ++ */ ++ int (*flush) (const char *, struct fuse_file_info *); ++ ++ /** Release an open file ++ * ++ * Release is called when there are no more references to an open ++ * file: all file descriptors are closed and all memory mappings ++ * are unmapped. ++ * ++ * For every open() call there will be exactly one release() call ++ * with the same flags and file handle. It is possible to ++ * have a file opened more than once, in which case only the last ++ * release will mean, that no more reads/writes will happen on the ++ * file. The return value of release is ignored. ++ */ ++ int (*release) (const char *, struct fuse_file_info *); ++ ++ /** Synchronize file contents ++ * ++ * If the datasync parameter is non-zero, then only the user data ++ * should be flushed, not the meta data. ++ */ ++ int (*fsync) (const char *, int, struct fuse_file_info *); ++ ++ /** Set extended attributes */ ++ int (*setxattr) (const char *, const char *, const char *, size_t, int); ++ ++ /** Get extended attributes */ ++ int (*getxattr) (const char *, const char *, char *, size_t); ++ ++ /** List extended attributes */ ++ int (*listxattr) (const char *, char *, size_t); ++ ++ /** Remove extended attributes */ ++ int (*removexattr) (const char *, const char *); ++ ++ /** Open directory ++ * ++ * Unless the 'default_permissions' mount option is given, ++ * this method should check if opendir is permitted for this ++ * directory. Optionally opendir may also return an arbitrary ++ * filehandle in the fuse_file_info structure, which will be ++ * passed to readdir, releasedir and fsyncdir. ++ */ ++ int (*opendir) (const char *, struct fuse_file_info *); ++ ++ /** Read directory ++ * ++ * The filesystem may choose between two modes of operation: ++ * ++ * 1) The readdir implementation ignores the offset parameter, and ++ * passes zero to the filler function's offset. The filler ++ * function will not return '1' (unless an error happens), so the ++ * whole directory is read in a single readdir operation. ++ * ++ * 2) The readdir implementation keeps track of the offsets of the ++ * directory entries. It uses the offset parameter and always ++ * passes non-zero offset to the filler function. When the buffer ++ * is full (or an error happens) the filler function will return ++ * '1'. ++ */ ++ int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t, ++ struct fuse_file_info *, enum fuse_readdir_flags); ++ ++ /** Release directory ++ */ ++ int (*releasedir) (const char *, struct fuse_file_info *); ++ ++ /** Synchronize directory contents ++ * ++ * If the datasync parameter is non-zero, then only the user data ++ * should be flushed, not the meta data ++ */ ++ int (*fsyncdir) (const char *, int, struct fuse_file_info *); ++ ++ /** ++ * Initialize filesystem ++ * ++ * The return value will passed in the `private_data` field of ++ * `struct fuse_context` to all file operations, and as a ++ * parameter to the destroy() method. It overrides the initial ++ * value provided to fuse_main() / fuse_new(). ++ */ ++ void *(*init) (struct fuse_conn_info *conn, ++ struct fuse_config *cfg); ++ ++ /** ++ * Clean up filesystem ++ * ++ * Called on filesystem exit. ++ */ ++ void (*destroy) (void *private_data); ++ ++ /** ++ * Check file access permissions ++ * ++ * This will be called for the access() system call. If the ++ * 'default_permissions' mount option is given, this method is not ++ * called. ++ * ++ * This method is not called under Linux kernel versions 2.4.x ++ */ ++ int (*access) (const char *, int); ++ ++ /** ++ * Create and open a file ++ * ++ * If the file does not exist, first create it with the specified ++ * mode, and then open it. ++ * ++ * If this method is not implemented or under Linux kernel ++ * versions earlier than 2.6.15, the mknod() and open() methods ++ * will be called instead. ++ */ ++ int (*create) (const char *, mode_t, struct fuse_file_info *); ++ ++ /** ++ * Perform POSIX file locking operation ++ * ++ * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW. ++ * ++ * For the meaning of fields in 'struct flock' see the man page ++ * for fcntl(2). The l_whence field will always be set to ++ * SEEK_SET. ++ * ++ * For checking lock ownership, the 'fuse_file_info->owner' ++ * argument must be used. ++ * ++ * For F_GETLK operation, the library will first check currently ++ * held locks, and if a conflicting lock is found it will return ++ * information without calling this method. This ensures, that ++ * for local locks the l_pid field is correctly filled in. The ++ * results may not be accurate in case of race conditions and in ++ * the presence of hard links, but it's unlikely that an ++ * application would rely on accurate GETLK results in these ++ * cases. If a conflicting lock is not found, this method will be ++ * called, and the filesystem may fill out l_pid by a meaningful ++ * value, or it may leave this field zero. ++ * ++ * For F_SETLK and F_SETLKW the l_pid field will be set to the pid ++ * of the process performing the locking operation. ++ * ++ * Note: if this method is not implemented, the kernel will still ++ * allow file locking to work locally. Hence it is only ++ * interesting for network filesystems and similar. ++ */ ++ int (*lock) (const char *, struct fuse_file_info *, int cmd, ++ struct flock *); ++ ++ /** ++ * Change the access and modification times of a file with ++ * nanosecond resolution ++ * ++ * This supersedes the old utime() interface. New applications ++ * should use this. ++ * ++ * `fi` will always be NULL if the file is not currenlty open, but ++ * may also be NULL if the file is open. ++ * ++ * See the utimensat(2) man page for details. ++ */ ++ int (*utimens) (const char *, const struct timespec tv[2], ++ struct fuse_file_info *fi); ++ ++ /** ++ * Map block index within file to block index within device ++ * ++ * Note: This makes sense only for block device backed filesystems ++ * mounted with the 'blkdev' option ++ */ ++ int (*bmap) (const char *, size_t blocksize, uint64_t *idx); ++ ++ /** ++ * Ioctl ++ * ++ * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in ++ * 64bit environment. The size and direction of data is ++ * determined by _IOC_*() decoding of cmd. For _IOC_NONE, ++ * data will be NULL, for _IOC_WRITE data is out area, for ++ * _IOC_READ in area and if both are set in/out area. In all ++ * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes. ++ * ++ * If flags has FUSE_IOCTL_DIR then the fuse_file_info refers to a ++ * directory file handle. ++ * ++ * Note : the unsigned long request submitted by the application ++ * is truncated to 32 bits. ++ */ ++ int (*ioctl) (const char *, unsigned int cmd, void *arg, ++ struct fuse_file_info *, unsigned int flags, void *data); ++ ++ /** ++ * Poll for IO readiness events ++ * ++ * Note: If ph is non-NULL, the client should notify ++ * when IO readiness events occur by calling ++ * fuse_notify_poll() with the specified ph. ++ * ++ * Regardless of the number of times poll with a non-NULL ph ++ * is received, single notification is enough to clear all. ++ * Notifying more times incurs overhead but doesn't harm ++ * correctness. ++ * ++ * The callee is responsible for destroying ph with ++ * fuse_pollhandle_destroy() when no longer in use. ++ */ ++ int (*poll) (const char *, struct fuse_file_info *, ++ struct fuse_pollhandle *ph, unsigned *reventsp); ++ ++ /** Write contents of buffer to an open file ++ * ++ * Similar to the write() method, but data is supplied in a ++ * generic buffer. Use fuse_buf_copy() to transfer data to ++ * the destination. ++ * ++ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is ++ * expected to reset the setuid and setgid bits. ++ */ ++ int (*write_buf) (const char *, struct fuse_bufvec *buf, off_t off, ++ struct fuse_file_info *); ++ ++ /** Store data from an open file in a buffer ++ * ++ * Similar to the read() method, but data is stored and ++ * returned in a generic buffer. ++ * ++ * No actual copying of data has to take place, the source ++ * file descriptor may simply be stored in the buffer for ++ * later data transfer. ++ * ++ * The buffer must be allocated dynamically and stored at the ++ * location pointed to by bufp. If the buffer contains memory ++ * regions, they too must be allocated using malloc(). The ++ * allocated memory will be freed by the caller. ++ */ ++ int (*read_buf) (const char *, struct fuse_bufvec **bufp, ++ size_t size, off_t off, struct fuse_file_info *); ++ /** ++ * Perform BSD file locking operation ++ * ++ * The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN ++ * ++ * Nonblocking requests will be indicated by ORing LOCK_NB to ++ * the above operations ++ * ++ * For more information see the flock(2) manual page. ++ * ++ * Additionally fi->owner will be set to a value unique to ++ * this open file. This same value will be supplied to ++ * ->release() when the file is released. ++ * ++ * Note: if this method is not implemented, the kernel will still ++ * allow file locking to work locally. Hence it is only ++ * interesting for network filesystems and similar. ++ */ ++ int (*flock) (const char *, struct fuse_file_info *, int op); ++ ++ /** ++ * Allocates space for an open file ++ * ++ * This function ensures that required space is allocated for specified ++ * file. If this function returns success then any subsequent write ++ * request to specified range is guaranteed not to fail because of lack ++ * of space on the file system media. ++ */ ++ int (*fallocate) (const char *, int, off_t, off_t, ++ struct fuse_file_info *); ++ ++ /** ++ * Copy a range of data from one file to another ++ * ++ * Performs an optimized copy between two file descriptors without the ++ * additional cost of transferring data through the FUSE kernel module ++ * to user space (glibc) and then back into the FUSE filesystem again. ++ * ++ * In case this method is not implemented, glibc falls back to reading ++ * data from the source and writing to the destination. Effectively ++ * doing an inefficient copy of the data. ++ */ ++ ssize_t (*copy_file_range) (const char *path_in, ++ struct fuse_file_info *fi_in, ++ off_t offset_in, const char *path_out, ++ struct fuse_file_info *fi_out, ++ off_t offset_out, size_t size, int flags); ++ ++ /** ++ * Find next data or hole after the specified offset ++ */ ++ off_t (*lseek) (const char *, off_t off, int whence, struct fuse_file_info *); ++}; ++ ++/** Extra context that may be needed by some filesystems ++ * ++ * The uid, gid and pid fields are not filled in case of a writepage ++ * operation. ++ */ ++struct fuse_context { ++ /** Pointer to the fuse object */ ++ struct fuse *fuse; ++ ++ /** User ID of the calling process */ ++ uid_t uid; ++ ++ /** Group ID of the calling process */ ++ gid_t gid; ++ ++ /** Process ID of the calling thread */ ++ pid_t pid; ++ ++ /** Private filesystem data */ ++ void *private_data; ++ ++ /** Umask of the calling process */ ++ mode_t umask; ++}; ++ ++/** ++ * Main function of FUSE. ++ * ++ * This is for the lazy. This is all that has to be called from the ++ * main() function. ++ * ++ * This function does the following: ++ * - parses command line options, and handles --help and ++ * --version ++ * - installs signal handlers for INT, HUP, TERM and PIPE ++ * - registers an exit handler to unmount the filesystem on program exit ++ * - creates a fuse handle ++ * - registers the operations ++ * - calls either the single-threaded or the multi-threaded event loop ++ * ++ * Most file systems will have to parse some file-system specific ++ * arguments before calling this function. It is recommended to do ++ * this with fuse_opt_parse() and a processing function that passes ++ * through any unknown options (this can also be achieved by just ++ * passing NULL as the processing function). That way, the remaining ++ * options can be passed directly to fuse_main(). ++ * ++ * fuse_main() accepts all options that can be passed to ++ * fuse_parse_cmdline(), fuse_new(), or fuse_session_new(). ++ * ++ * Option parsing skips argv[0], which is assumed to contain the ++ * program name. This element must always be present and is used to ++ * construct a basic ``usage: `` message for the --help ++ * output. argv[0] may also be set to the empty string. In this case ++ * the usage message is suppressed. This can be used by file systems ++ * to print their own usage line first. See hello.c for an example of ++ * how to do this. ++ * ++ * Note: this is currently implemented as a macro. ++ * ++ * The following error codes may be returned from fuse_main(): ++ * 1: Invalid option arguments ++ * 2: No mount point specified ++ * 3: FUSE setup failed ++ * 4: Mounting failed ++ * 5: Failed to daemonize (detach from session) ++ * 6: Failed to set up signal handlers ++ * 7: An error occured during the life of the file system ++ * ++ * @param argc the argument counter passed to the main() function ++ * @param argv the argument vector passed to the main() function ++ * @param op the file system operation ++ * @param private_data Initial value for the `private_data` ++ * field of `struct fuse_context`. May be overridden by the ++ * `struct fuse_operations.init` handler. ++ * @return 0 on success, nonzero on failure ++ * ++ * Example usage, see hello.c ++ */ ++/* ++ int fuse_main(int argc, char *argv[], const struct fuse_operations *op, ++ void *private_data); ++*/ ++#define fuse_main(argc, argv, op, private_data) \ ++ fuse_main_real(argc, argv, op, sizeof(*(op)), private_data) ++ ++/* ----------------------------------------------------------- * ++ * More detailed API * ++ * ----------------------------------------------------------- */ ++ ++/** ++ * Print available options (high- and low-level) to stdout. This is ++ * not an exhaustive list, but includes only those options that may be ++ * of interest to an end-user of a file system. ++ * ++ * The function looks at the argument vector only to determine if ++ * there are additional modules to be loaded (module=foo option), ++ * and attempts to call their help functions as well. ++ * ++ * @param args the argument vector. ++ */ ++void fuse_lib_help(struct fuse_args *args); ++ ++/** ++ * Create a new FUSE filesystem. ++ * ++ * This function accepts most file-system independent mount options ++ * (like context, nodev, ro - see mount(8)), as well as the ++ * FUSE-specific mount options from mount.fuse(8). ++ * ++ * If the --help option is specified, the function writes a help text ++ * to stdout and returns NULL. ++ * ++ * Option parsing skips argv[0], which is assumed to contain the ++ * program name. This element must always be present and is used to ++ * construct a basic ``usage: `` message for the --help output. If ++ * argv[0] is set to the empty string, no usage message is included in ++ * the --help output. ++ * ++ * If an unknown option is passed in, an error message is written to ++ * stderr and the function returns NULL. ++ * ++ * @param args argument vector ++ * @param op the filesystem operations ++ * @param op_size the size of the fuse_operations structure ++ * @param private_data Initial value for the `private_data` ++ * field of `struct fuse_context`. May be overridden by the ++ * `struct fuse_operations.init` handler. ++ * @return the created FUSE handle ++ */ ++#if FUSE_USE_VERSION == 30 ++struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op, ++ size_t op_size, void *private_data); ++#define fuse_new(args, op, size, data) fuse_new_30(args, op, size, data) ++#else ++struct fuse *fuse_new(struct fuse_args *args, const struct fuse_operations *op, ++ size_t op_size, void *private_data); ++#endif ++ ++/** ++ * Mount a FUSE file system. ++ * ++ * @param mountpoint the mount point path ++ * @param f the FUSE handle ++ * ++ * @return 0 on success, -1 on failure. ++ **/ ++int fuse_mount(struct fuse *f, const char *mountpoint); ++ ++/** ++ * Unmount a FUSE file system. ++ * ++ * See fuse_session_unmount() for additional information. ++ * ++ * @param f the FUSE handle ++ **/ ++void fuse_unmount(struct fuse *f); ++ ++/** ++ * Destroy the FUSE handle. ++ * ++ * NOTE: This function does not unmount the filesystem. If this is ++ * needed, call fuse_unmount() before calling this function. ++ * ++ * @param f the FUSE handle ++ */ ++void fuse_destroy(struct fuse *f); ++ ++/** ++ * FUSE event loop. ++ * ++ * Requests from the kernel are processed, and the appropriate ++ * operations are called. ++ * ++ * For a description of the return value and the conditions when the ++ * event loop exits, refer to the documentation of ++ * fuse_session_loop(). ++ * ++ * @param f the FUSE handle ++ * @return see fuse_session_loop() ++ * ++ * See also: fuse_loop_mt() ++ */ ++int fuse_loop(struct fuse *f); ++ ++/** ++ * Flag session as terminated ++ * ++ * This function will cause any running event loops to exit on ++ * the next opportunity. ++ * ++ * @param f the FUSE handle ++ */ ++void fuse_exit(struct fuse *f); ++ ++/** ++ * FUSE event loop with multiple threads ++ * ++ * Requests from the kernel are processed, and the appropriate ++ * operations are called. Request are processed in parallel by ++ * distributing them between multiple threads. ++ * ++ * For a description of the return value and the conditions when the ++ * event loop exits, refer to the documentation of ++ * fuse_session_loop(). ++ * ++ * Note: using fuse_loop() instead of fuse_loop_mt() means you are running in ++ * single-threaded mode, and that you will not have to worry about reentrancy, ++ * though you will have to worry about recursive lookups. In single-threaded ++ * mode, FUSE will wait for one callback to return before calling another. ++ * ++ * Enabling multiple threads, by using fuse_loop_mt(), will cause FUSE to make ++ * multiple simultaneous calls into the various callback functions given by your ++ * fuse_operations record. ++ * ++ * If you are using multiple threads, you can enjoy all the parallel execution ++ * and interactive response benefits of threads, and you get to enjoy all the ++ * benefits of race conditions and locking bugs, too. Ensure that any code used ++ * in the callback function of fuse_operations is also thread-safe. ++ * ++ * @param f the FUSE handle ++ * @param config loop configuration ++ * @return see fuse_session_loop() ++ * ++ * See also: fuse_loop() ++ */ ++#if FUSE_USE_VERSION < 32 ++int fuse_loop_mt_31(struct fuse *f, int clone_fd); ++#define fuse_loop_mt(f, clone_fd) fuse_loop_mt_31(f, clone_fd) ++#else ++int fuse_loop_mt(struct fuse *f, struct fuse_loop_config *config); ++#endif ++ ++/** ++ * Get the current context ++ * ++ * The context is only valid for the duration of a filesystem ++ * operation, and thus must not be stored and used later. ++ * ++ * @return the context ++ */ ++struct fuse_context *fuse_get_context(void); ++ ++/** ++ * Get the current supplementary group IDs for the current request ++ * ++ * Similar to the getgroups(2) system call, except the return value is ++ * always the total number of group IDs, even if it is larger than the ++ * specified size. ++ * ++ * The current fuse kernel module in linux (as of 2.6.30) doesn't pass ++ * the group list to userspace, hence this function needs to parse ++ * "/proc/$TID/task/$TID/status" to get the group IDs. ++ * ++ * This feature may not be supported on all operating systems. In ++ * such a case this function will return -ENOSYS. ++ * ++ * @param size size of given array ++ * @param list array of group IDs to be filled in ++ * @return the total number of supplementary group IDs or -errno on failure ++ */ ++int fuse_getgroups(int size, gid_t list[]); ++ ++/** ++ * Check if the current request has already been interrupted ++ * ++ * @return 1 if the request has been interrupted, 0 otherwise ++ */ ++int fuse_interrupted(void); ++ ++/** ++ * Invalidates cache for the given path. ++ * ++ * This calls fuse_lowlevel_notify_inval_inode internally. ++ * ++ * @return 0 on successful invalidation, negative error value otherwise. ++ * This routine may return -ENOENT to indicate that there was ++ * no entry to be invalidated, e.g., because the path has not ++ * been seen before or has been forgotten; this should not be ++ * considered to be an error. ++ */ ++int fuse_invalidate_path(struct fuse *f, const char *path); ++ ++/** ++ * The real main function ++ * ++ * Do not call this directly, use fuse_main() ++ */ ++int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, ++ size_t op_size, void *private_data); ++ ++/** ++ * Start the cleanup thread when using option "remember". ++ * ++ * This is done automatically by fuse_loop_mt() ++ * @param fuse struct fuse pointer for fuse instance ++ * @return 0 on success and -1 on error ++ */ ++int fuse_start_cleanup_thread(struct fuse *fuse); ++ ++/** ++ * Stop the cleanup thread when using option "remember". ++ * ++ * This is done automatically by fuse_loop_mt() ++ * @param fuse struct fuse pointer for fuse instance ++ */ ++void fuse_stop_cleanup_thread(struct fuse *fuse); ++ ++/** ++ * Iterate over cache removing stale entries ++ * use in conjunction with "-oremember" ++ * ++ * NOTE: This is already done for the standard sessions ++ * ++ * @param fuse struct fuse pointer for fuse instance ++ * @return the number of seconds until the next cleanup ++ */ ++int fuse_clean_cache(struct fuse *fuse); ++ ++/* ++ * Stacking API ++ */ ++ ++/** ++ * Fuse filesystem object ++ * ++ * This is opaque object represents a filesystem layer ++ */ ++struct fuse_fs; ++ ++/* ++ * These functions call the relevant filesystem operation, and return ++ * the result. ++ * ++ * If the operation is not defined, they return -ENOSYS, with the ++ * exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir, ++ * fuse_fs_releasedir and fuse_fs_statfs, which return 0. ++ */ ++ ++int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf, ++ struct fuse_file_info *fi); ++int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, ++ const char *newpath, unsigned int flags); ++int fuse_fs_unlink(struct fuse_fs *fs, const char *path); ++int fuse_fs_rmdir(struct fuse_fs *fs, const char *path); ++int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, ++ const char *path); ++int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath); ++int fuse_fs_release(struct fuse_fs *fs, const char *path, ++ struct fuse_file_info *fi); ++int fuse_fs_open(struct fuse_fs *fs, const char *path, ++ struct fuse_file_info *fi); ++int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size, ++ off_t off, struct fuse_file_info *fi); ++int fuse_fs_read_buf(struct fuse_fs *fs, const char *path, ++ struct fuse_bufvec **bufp, size_t size, off_t off, ++ struct fuse_file_info *fi); ++int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf, ++ size_t size, off_t off, struct fuse_file_info *fi); ++int fuse_fs_write_buf(struct fuse_fs *fs, const char *path, ++ struct fuse_bufvec *buf, off_t off, ++ struct fuse_file_info *fi); ++int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync, ++ struct fuse_file_info *fi); ++int fuse_fs_flush(struct fuse_fs *fs, const char *path, ++ struct fuse_file_info *fi); ++int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf); ++int fuse_fs_opendir(struct fuse_fs *fs, const char *path, ++ struct fuse_file_info *fi); ++int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf, ++ fuse_fill_dir_t filler, off_t off, ++ struct fuse_file_info *fi, enum fuse_readdir_flags flags); ++int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync, ++ struct fuse_file_info *fi); ++int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, ++ struct fuse_file_info *fi); ++int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode, ++ struct fuse_file_info *fi); ++int fuse_fs_lock(struct fuse_fs *fs, const char *path, ++ struct fuse_file_info *fi, int cmd, struct flock *lock); ++int fuse_fs_flock(struct fuse_fs *fs, const char *path, ++ struct fuse_file_info *fi, int op); ++int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode, ++ struct fuse_file_info *fi); ++int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid, ++ struct fuse_file_info *fi); ++int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size, ++ struct fuse_file_info *fi); ++int fuse_fs_utimens(struct fuse_fs *fs, const char *path, ++ const struct timespec tv[2], struct fuse_file_info *fi); ++int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask); ++int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf, ++ size_t len); ++int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode, ++ dev_t rdev); ++int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode); ++int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name, ++ const char *value, size_t size, int flags); ++int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name, ++ char *value, size_t size); ++int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list, ++ size_t size); ++int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, ++ const char *name); ++int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize, ++ uint64_t *idx); ++int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd, ++ void *arg, struct fuse_file_info *fi, unsigned int flags, ++ void *data); ++int fuse_fs_poll(struct fuse_fs *fs, const char *path, ++ struct fuse_file_info *fi, struct fuse_pollhandle *ph, ++ unsigned *reventsp); ++int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode, ++ off_t offset, off_t length, struct fuse_file_info *fi); ++ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in, ++ struct fuse_file_info *fi_in, off_t off_in, ++ const char *path_out, ++ struct fuse_file_info *fi_out, off_t off_out, ++ size_t len, int flags); ++off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence, ++ struct fuse_file_info *fi); ++void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn, ++ struct fuse_config *cfg); ++void fuse_fs_destroy(struct fuse_fs *fs); ++ ++int fuse_notify_poll(struct fuse_pollhandle *ph); ++ ++/** ++ * Create a new fuse filesystem object ++ * ++ * This is usually called from the factory of a fuse module to create ++ * a new instance of a filesystem. ++ * ++ * @param op the filesystem operations ++ * @param op_size the size of the fuse_operations structure ++ * @param private_data Initial value for the `private_data` ++ * field of `struct fuse_context`. May be overridden by the ++ * `struct fuse_operations.init` handler. ++ * @return a new filesystem object ++ */ ++struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, ++ void *private_data); ++ ++/** ++ * Factory for creating filesystem objects ++ * ++ * The function may use and remove options from 'args' that belong ++ * to this module. ++ * ++ * For now the 'fs' vector always contains exactly one filesystem. ++ * This is the filesystem which will be below the newly created ++ * filesystem in the stack. ++ * ++ * @param args the command line arguments ++ * @param fs NULL terminated filesystem object vector ++ * @return the new filesystem object ++ */ ++typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args, ++ struct fuse_fs *fs[]); ++/** ++ * Register filesystem module ++ * ++ * If the "-omodules=*name*_:..." option is present, filesystem ++ * objects are created and pushed onto the stack with the *factory_* ++ * function. ++ * ++ * @param name_ the name of this filesystem module ++ * @param factory_ the factory function for this filesystem module ++ */ ++#define FUSE_REGISTER_MODULE(name_, factory_) \ ++ fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_ ++ ++/** Get session from fuse object */ ++struct fuse_session *fuse_get_session(struct fuse *f); ++ ++/** ++ * Open a FUSE file descriptor and set up the mount for the given ++ * mountpoint and flags. ++ * ++ * @param mountpoint reference to the mount in the file system ++ * @param options mount options ++ * @return the FUSE file descriptor or -1 upon error ++ */ ++int fuse_open_channel(const char *mountpoint, const char *options); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* FUSE_H_ */ +diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h +new file mode 100644 +index 0000000000..2d686b2ac4 +--- /dev/null ++++ b/tools/virtiofsd/fuse_common.h +@@ -0,0 +1,823 @@ ++/* FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2007 Miklos Szeredi ++ ++ This program can be distributed under the terms of the GNU LGPLv2. ++ See the file COPYING.LIB. ++*/ ++ ++/** @file */ ++ ++#if !defined(FUSE_H_) && !defined(FUSE_LOWLEVEL_H_) ++#error "Never include directly; use or instead." ++#endif ++ ++#ifndef FUSE_COMMON_H_ ++#define FUSE_COMMON_H_ ++ ++#include "fuse_opt.h" ++#include "fuse_log.h" ++#include ++#include ++ ++/** Major version of FUSE library interface */ ++#define FUSE_MAJOR_VERSION 3 ++ ++/** Minor version of FUSE library interface */ ++#define FUSE_MINOR_VERSION 2 ++ ++#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) ++#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * Information about an open file. ++ * ++ * File Handles are created by the open, opendir, and create methods and closed ++ * by the release and releasedir methods. Multiple file handles may be ++ * concurrently open for the same file. Generally, a client will create one ++ * file handle per file descriptor, though in some cases multiple file ++ * descriptors can share a single file handle. ++ */ ++struct fuse_file_info { ++ /** Open flags. Available in open() and release() */ ++ int flags; ++ ++ /** In case of a write operation indicates if this was caused ++ by a delayed write from the page cache. If so, then the ++ context's pid, uid, and gid fields will not be valid, and ++ the *fh* value may not match the *fh* value that would ++ have been sent with the corresponding individual write ++ requests if write caching had been disabled. */ ++ unsigned int writepage : 1; ++ ++ /** Can be filled in by open, to use direct I/O on this file. */ ++ unsigned int direct_io : 1; ++ ++ /** Can be filled in by open. It signals the kernel that any ++ currently cached file data (ie., data that the filesystem ++ provided the last time the file was open) need not be ++ invalidated. Has no effect when set in other contexts (in ++ particular it does nothing when set by opendir()). */ ++ unsigned int keep_cache : 1; ++ ++ /** Indicates a flush operation. Set in flush operation, also ++ maybe set in highlevel lock operation and lowlevel release ++ operation. */ ++ unsigned int flush : 1; ++ ++ /** Can be filled in by open, to indicate that the file is not ++ seekable. */ ++ unsigned int nonseekable : 1; ++ ++ /* Indicates that flock locks for this file should be ++ released. If set, lock_owner shall contain a valid value. ++ May only be set in ->release(). */ ++ unsigned int flock_release : 1; ++ ++ /** Can be filled in by opendir. It signals the kernel to ++ enable caching of entries returned by readdir(). Has no ++ effect when set in other contexts (in particular it does ++ nothing when set by open()). */ ++ unsigned int cache_readdir : 1; ++ ++ /** Padding. Reserved for future use*/ ++ unsigned int padding : 25; ++ unsigned int padding2 : 32; ++ ++ /** File handle id. May be filled in by filesystem in create, ++ * open, and opendir(). Available in most other file operations on the ++ * same file handle. */ ++ uint64_t fh; ++ ++ /** Lock owner id. Available in locking operations and flush */ ++ uint64_t lock_owner; ++ ++ /** Requested poll events. Available in ->poll. Only set on kernels ++ which support it. If unsupported, this field is set to zero. */ ++ uint32_t poll_events; ++}; ++ ++/** ++ * Configuration parameters passed to fuse_session_loop_mt() and ++ * fuse_loop_mt(). ++ */ ++struct fuse_loop_config { ++ /** ++ * whether to use separate device fds for each thread ++ * (may increase performance) ++ */ ++ int clone_fd; ++ ++ /** ++ * The maximum number of available worker threads before they ++ * start to get deleted when they become idle. If not ++ * specified, the default is 10. ++ * ++ * Adjusting this has performance implications; a very small number ++ * of threads in the pool will cause a lot of thread creation and ++ * deletion overhead and performance may suffer. When set to 0, a new ++ * thread will be created to service every operation. ++ */ ++ unsigned int max_idle_threads; ++}; ++ ++/************************************************************************** ++ * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' * ++ **************************************************************************/ ++ ++/** ++ * Indicates that the filesystem supports asynchronous read requests. ++ * ++ * If this capability is not requested/available, the kernel will ++ * ensure that there is at most one pending read request per ++ * file-handle at any time, and will attempt to order read requests by ++ * increasing offset. ++ * ++ * This feature is enabled by default when supported by the kernel. ++ */ ++#define FUSE_CAP_ASYNC_READ (1 << 0) ++ ++/** ++ * Indicates that the filesystem supports "remote" locking. ++ * ++ * This feature is enabled by default when supported by the kernel, ++ * and if getlk() and setlk() handlers are implemented. ++ */ ++#define FUSE_CAP_POSIX_LOCKS (1 << 1) ++ ++/** ++ * Indicates that the filesystem supports the O_TRUNC open flag. If ++ * disabled, and an application specifies O_TRUNC, fuse first calls ++ * truncate() and then open() with O_TRUNC filtered out. ++ * ++ * This feature is enabled by default when supported by the kernel. ++ */ ++#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3) ++ ++/** ++ * Indicates that the filesystem supports lookups of "." and "..". ++ * ++ * This feature is disabled by default. ++ */ ++#define FUSE_CAP_EXPORT_SUPPORT (1 << 4) ++ ++/** ++ * Indicates that the kernel should not apply the umask to the ++ * file mode on create operations. ++ * ++ * This feature is disabled by default. ++ */ ++#define FUSE_CAP_DONT_MASK (1 << 6) ++ ++/** ++ * Indicates that libfuse should try to use splice() when writing to ++ * the fuse device. This may improve performance. ++ * ++ * This feature is disabled by default. ++ */ ++#define FUSE_CAP_SPLICE_WRITE (1 << 7) ++ ++/** ++ * Indicates that libfuse should try to move pages instead of copying when ++ * writing to / reading from the fuse device. This may improve performance. ++ * ++ * This feature is disabled by default. ++ */ ++#define FUSE_CAP_SPLICE_MOVE (1 << 8) ++ ++/** ++ * Indicates that libfuse should try to use splice() when reading from ++ * the fuse device. This may improve performance. ++ * ++ * This feature is enabled by default when supported by the kernel and ++ * if the filesystem implements a write_buf() handler. ++ */ ++#define FUSE_CAP_SPLICE_READ (1 << 9) ++ ++/** ++ * If set, the calls to flock(2) will be emulated using POSIX locks and must ++ * then be handled by the filesystem's setlock() handler. ++ * ++ * If not set, flock(2) calls will be handled by the FUSE kernel module ++ * internally (so any access that does not go through the kernel cannot be taken ++ * into account). ++ * ++ * This feature is enabled by default when supported by the kernel and ++ * if the filesystem implements a flock() handler. ++ */ ++#define FUSE_CAP_FLOCK_LOCKS (1 << 10) ++ ++/** ++ * Indicates that the filesystem supports ioctl's on directories. ++ * ++ * This feature is enabled by default when supported by the kernel. ++ */ ++#define FUSE_CAP_IOCTL_DIR (1 << 11) ++ ++/** ++ * Traditionally, while a file is open the FUSE kernel module only ++ * asks the filesystem for an update of the file's attributes when a ++ * client attempts to read beyond EOF. This is unsuitable for ++ * e.g. network filesystems, where the file contents may change ++ * without the kernel knowing about it. ++ * ++ * If this flag is set, FUSE will check the validity of the attributes ++ * on every read. If the attributes are no longer valid (i.e., if the ++ * *attr_timeout* passed to fuse_reply_attr() or set in `struct ++ * fuse_entry_param` has passed), it will first issue a `getattr` ++ * request. If the new mtime differs from the previous value, any ++ * cached file *contents* will be invalidated as well. ++ * ++ * This flag should always be set when available. If all file changes ++ * go through the kernel, *attr_timeout* should be set to a very large ++ * number to avoid unnecessary getattr() calls. ++ * ++ * This feature is enabled by default when supported by the kernel. ++ */ ++#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12) ++ ++/** ++ * Indicates that the filesystem supports readdirplus. ++ * ++ * This feature is enabled by default when supported by the kernel and if the ++ * filesystem implements a readdirplus() handler. ++ */ ++#define FUSE_CAP_READDIRPLUS (1 << 13) ++ ++/** ++ * Indicates that the filesystem supports adaptive readdirplus. ++ * ++ * If FUSE_CAP_READDIRPLUS is not set, this flag has no effect. ++ * ++ * If FUSE_CAP_READDIRPLUS is set and this flag is not set, the kernel ++ * will always issue readdirplus() requests to retrieve directory ++ * contents. ++ * ++ * If FUSE_CAP_READDIRPLUS is set and this flag is set, the kernel ++ * will issue both readdir() and readdirplus() requests, depending on ++ * how much information is expected to be required. ++ * ++ * As of Linux 4.20, the algorithm is as follows: when userspace ++ * starts to read directory entries, issue a READDIRPLUS request to ++ * the filesystem. If any entry attributes have been looked up by the ++ * time userspace requests the next batch of entries continue with ++ * READDIRPLUS, otherwise switch to plain READDIR. This will reasult ++ * in eg plain "ls" triggering READDIRPLUS first then READDIR after ++ * that because it doesn't do lookups. "ls -l" should result in all ++ * READDIRPLUS, except if dentries are already cached. ++ * ++ * This feature is enabled by default when supported by the kernel and ++ * if the filesystem implements both a readdirplus() and a readdir() ++ * handler. ++ */ ++#define FUSE_CAP_READDIRPLUS_AUTO (1 << 14) ++ ++/** ++ * Indicates that the filesystem supports asynchronous direct I/O submission. ++ * ++ * If this capability is not requested/available, the kernel will ensure that ++ * there is at most one pending read and one pending write request per direct ++ * I/O file-handle at any time. ++ * ++ * This feature is enabled by default when supported by the kernel. ++ */ ++#define FUSE_CAP_ASYNC_DIO (1 << 15) ++ ++/** ++ * Indicates that writeback caching should be enabled. This means that ++ * individual write request may be buffered and merged in the kernel ++ * before they are send to the filesystem. ++ * ++ * This feature is disabled by default. ++ */ ++#define FUSE_CAP_WRITEBACK_CACHE (1 << 16) ++ ++/** ++ * Indicates support for zero-message opens. If this flag is set in ++ * the `capable` field of the `fuse_conn_info` structure, then the ++ * filesystem may return `ENOSYS` from the open() handler to indicate ++ * success. Further attempts to open files will be handled in the ++ * kernel. (If this flag is not set, returning ENOSYS will be treated ++ * as an error and signaled to the caller). ++ * ++ * Setting (or unsetting) this flag in the `want` field has *no ++ * effect*. ++ */ ++#define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17) ++ ++/** ++ * Indicates support for parallel directory operations. If this flag ++ * is unset, the FUSE kernel module will ensure that lookup() and ++ * readdir() requests are never issued concurrently for the same ++ * directory. ++ * ++ * This feature is enabled by default when supported by the kernel. ++ */ ++#define FUSE_CAP_PARALLEL_DIROPS (1 << 18) ++ ++/** ++ * Indicates support for POSIX ACLs. ++ * ++ * If this feature is enabled, the kernel will cache and have ++ * responsibility for enforcing ACLs. ACL will be stored as xattrs and ++ * passed to userspace, which is responsible for updating the ACLs in ++ * the filesystem, keeping the file mode in sync with the ACL, and ++ * ensuring inheritance of default ACLs when new filesystem nodes are ++ * created. Note that this requires that the file system is able to ++ * parse and interpret the xattr representation of ACLs. ++ * ++ * Enabling this feature implicitly turns on the ++ * ``default_permissions`` mount option (even if it was not passed to ++ * mount(2)). ++ * ++ * This feature is disabled by default. ++ */ ++#define FUSE_CAP_POSIX_ACL (1 << 19) ++ ++/** ++ * Indicates that the filesystem is responsible for unsetting ++ * setuid and setgid bits when a file is written, truncated, or ++ * its owner is changed. ++ * ++ * This feature is enabled by default when supported by the kernel. ++ */ ++#define FUSE_CAP_HANDLE_KILLPRIV (1 << 20) ++ ++/** ++ * Indicates support for zero-message opendirs. If this flag is set in ++ * the `capable` field of the `fuse_conn_info` structure, then the filesystem ++ * may return `ENOSYS` from the opendir() handler to indicate success. Further ++ * opendir and releasedir messages will be handled in the kernel. (If this ++ * flag is not set, returning ENOSYS will be treated as an error and signalled ++ * to the caller.) ++ * ++ * Setting (or unsetting) this flag in the `want` field has *no effect*. ++ */ ++#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24) ++ ++/** ++ * Ioctl flags ++ * ++ * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine ++ * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed ++ * FUSE_IOCTL_RETRY: retry with new iovecs ++ * FUSE_IOCTL_DIR: is a directory ++ * ++ * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs ++ */ ++#define FUSE_IOCTL_COMPAT (1 << 0) ++#define FUSE_IOCTL_UNRESTRICTED (1 << 1) ++#define FUSE_IOCTL_RETRY (1 << 2) ++#define FUSE_IOCTL_DIR (1 << 4) ++ ++#define FUSE_IOCTL_MAX_IOV 256 ++ ++/** ++ * Connection information, passed to the ->init() method ++ * ++ * Some of the elements are read-write, these can be changed to ++ * indicate the value requested by the filesystem. The requested ++ * value must usually be smaller than the indicated value. ++ */ ++struct fuse_conn_info { ++ /** ++ * Major version of the protocol (read-only) ++ */ ++ unsigned proto_major; ++ ++ /** ++ * Minor version of the protocol (read-only) ++ */ ++ unsigned proto_minor; ++ ++ /** ++ * Maximum size of the write buffer ++ */ ++ unsigned max_write; ++ ++ /** ++ * Maximum size of read requests. A value of zero indicates no ++ * limit. However, even if the filesystem does not specify a ++ * limit, the maximum size of read requests will still be ++ * limited by the kernel. ++ * ++ * NOTE: For the time being, the maximum size of read requests ++ * must be set both here *and* passed to fuse_session_new() ++ * using the ``-o max_read=`` mount option. At some point ++ * in the future, specifying the mount option will no longer ++ * be necessary. ++ */ ++ unsigned max_read; ++ ++ /** ++ * Maximum readahead ++ */ ++ unsigned max_readahead; ++ ++ /** ++ * Capability flags that the kernel supports (read-only) ++ */ ++ unsigned capable; ++ ++ /** ++ * Capability flags that the filesystem wants to enable. ++ * ++ * libfuse attempts to initialize this field with ++ * reasonable default values before calling the init() handler. ++ */ ++ unsigned want; ++ ++ /** ++ * Maximum number of pending "background" requests. A ++ * background request is any type of request for which the ++ * total number is not limited by other means. As of kernel ++ * 4.8, only two types of requests fall into this category: ++ * ++ * 1. Read-ahead requests ++ * 2. Asynchronous direct I/O requests ++ * ++ * Read-ahead requests are generated (if max_readahead is ++ * non-zero) by the kernel to preemptively fill its caches ++ * when it anticipates that userspace will soon read more ++ * data. ++ * ++ * Asynchronous direct I/O requests are generated if ++ * FUSE_CAP_ASYNC_DIO is enabled and userspace submits a large ++ * direct I/O request. In this case the kernel will internally ++ * split it up into multiple smaller requests and submit them ++ * to the filesystem concurrently. ++ * ++ * Note that the following requests are *not* background ++ * requests: writeback requests (limited by the kernel's ++ * flusher algorithm), regular (i.e., synchronous and ++ * buffered) userspace read/write requests (limited to one per ++ * thread), asynchronous read requests (Linux's io_submit(2) ++ * call actually blocks, so these are also limited to one per ++ * thread). ++ */ ++ unsigned max_background; ++ ++ /** ++ * Kernel congestion threshold parameter. If the number of pending ++ * background requests exceeds this number, the FUSE kernel module will ++ * mark the filesystem as "congested". This instructs the kernel to ++ * expect that queued requests will take some time to complete, and to ++ * adjust its algorithms accordingly (e.g. by putting a waiting thread ++ * to sleep instead of using a busy-loop). ++ */ ++ unsigned congestion_threshold; ++ ++ /** ++ * When FUSE_CAP_WRITEBACK_CACHE is enabled, the kernel is responsible ++ * for updating mtime and ctime when write requests are received. The ++ * updated values are passed to the filesystem with setattr() requests. ++ * However, if the filesystem does not support the full resolution of ++ * the kernel timestamps (nanoseconds), the mtime and ctime values used ++ * by kernel and filesystem will differ (and result in an apparent ++ * change of times after a cache flush). ++ * ++ * To prevent this problem, this variable can be used to inform the ++ * kernel about the timestamp granularity supported by the file-system. ++ * The value should be power of 10. The default is 1, i.e. full ++ * nano-second resolution. Filesystems supporting only second resolution ++ * should set this to 1000000000. ++ */ ++ unsigned time_gran; ++ ++ /** ++ * For future use. ++ */ ++ unsigned reserved[22]; ++}; ++ ++struct fuse_session; ++struct fuse_pollhandle; ++struct fuse_conn_info_opts; ++ ++/** ++ * This function parses several command-line options that can be used ++ * to override elements of struct fuse_conn_info. The pointer returned ++ * by this function should be passed to the ++ * fuse_apply_conn_info_opts() method by the file system's init() ++ * handler. ++ * ++ * Before using this function, think twice if you really want these ++ * parameters to be adjustable from the command line. In most cases, ++ * they should be determined by the file system internally. ++ * ++ * The following options are recognized: ++ * ++ * -o max_write=N sets conn->max_write ++ * -o max_readahead=N sets conn->max_readahead ++ * -o max_background=N sets conn->max_background ++ * -o congestion_threshold=N sets conn->congestion_threshold ++ * -o async_read sets FUSE_CAP_ASYNC_READ in conn->want ++ * -o sync_read unsets FUSE_CAP_ASYNC_READ in conn->want ++ * -o atomic_o_trunc sets FUSE_CAP_ATOMIC_O_TRUNC in conn->want ++ * -o no_remote_lock Equivalent to -o no_remote_flock,no_remote_posix_lock ++ * -o no_remote_flock Unsets FUSE_CAP_FLOCK_LOCKS in conn->want ++ * -o no_remote_posix_lock Unsets FUSE_CAP_POSIX_LOCKS in conn->want ++ * -o [no_]splice_write (un-)sets FUSE_CAP_SPLICE_WRITE in conn->want ++ * -o [no_]splice_move (un-)sets FUSE_CAP_SPLICE_MOVE in conn->want ++ * -o [no_]splice_read (un-)sets FUSE_CAP_SPLICE_READ in conn->want ++ * -o [no_]auto_inval_data (un-)sets FUSE_CAP_AUTO_INVAL_DATA in conn->want ++ * -o readdirplus=no unsets FUSE_CAP_READDIRPLUS in conn->want ++ * -o readdirplus=yes sets FUSE_CAP_READDIRPLUS and unsets ++ * FUSE_CAP_READDIRPLUS_AUTO in conn->want ++ * -o readdirplus=auto sets FUSE_CAP_READDIRPLUS and ++ * FUSE_CAP_READDIRPLUS_AUTO in conn->want ++ * -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in conn->want ++ * -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in conn->want ++ * -o time_gran=N sets conn->time_gran ++ * ++ * Known options will be removed from *args*, unknown options will be ++ * passed through unchanged. ++ * ++ * @param args argument vector (input+output) ++ * @return parsed options ++ **/ ++struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args); ++ ++/** ++ * This function applies the (parsed) parameters in *opts* to the ++ * *conn* pointer. It may modify the following fields: wants, ++ * max_write, max_readahead, congestion_threshold, max_background, ++ * time_gran. A field is only set (or unset) if the corresponding ++ * option has been explicitly set. ++ */ ++void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, ++ struct fuse_conn_info *conn); ++ ++/** ++ * Go into the background ++ * ++ * @param foreground if true, stay in the foreground ++ * @return 0 on success, -1 on failure ++ */ ++int fuse_daemonize(int foreground); ++ ++/** ++ * Get the version of the library ++ * ++ * @return the version ++ */ ++int fuse_version(void); ++ ++/** ++ * Get the full package version string of the library ++ * ++ * @return the package version ++ */ ++const char *fuse_pkgversion(void); ++ ++/** ++ * Destroy poll handle ++ * ++ * @param ph the poll handle ++ */ ++void fuse_pollhandle_destroy(struct fuse_pollhandle *ph); ++ ++/* ----------------------------------------------------------- * ++ * Data buffer * ++ * ----------------------------------------------------------- */ ++ ++/** ++ * Buffer flags ++ */ ++enum fuse_buf_flags { ++ /** ++ * Buffer contains a file descriptor ++ * ++ * If this flag is set, the .fd field is valid, otherwise the ++ * .mem fields is valid. ++ */ ++ FUSE_BUF_IS_FD = (1 << 1), ++ ++ /** ++ * Seek on the file descriptor ++ * ++ * If this flag is set then the .pos field is valid and is ++ * used to seek to the given offset before performing ++ * operation on file descriptor. ++ */ ++ FUSE_BUF_FD_SEEK = (1 << 2), ++ ++ /** ++ * Retry operation on file descriptor ++ * ++ * If this flag is set then retry operation on file descriptor ++ * until .size bytes have been copied or an error or EOF is ++ * detected. ++ */ ++ FUSE_BUF_FD_RETRY = (1 << 3), ++}; ++ ++/** ++ * Buffer copy flags ++ */ ++enum fuse_buf_copy_flags { ++ /** ++ * Don't use splice(2) ++ * ++ * Always fall back to using read and write instead of ++ * splice(2) to copy data from one file descriptor to another. ++ * ++ * If this flag is not set, then only fall back if splice is ++ * unavailable. ++ */ ++ FUSE_BUF_NO_SPLICE = (1 << 1), ++ ++ /** ++ * Force splice ++ * ++ * Always use splice(2) to copy data from one file descriptor ++ * to another. If splice is not available, return -EINVAL. ++ */ ++ FUSE_BUF_FORCE_SPLICE = (1 << 2), ++ ++ /** ++ * Try to move data with splice. ++ * ++ * If splice is used, try to move pages from the source to the ++ * destination instead of copying. See documentation of ++ * SPLICE_F_MOVE in splice(2) man page. ++ */ ++ FUSE_BUF_SPLICE_MOVE = (1 << 3), ++ ++ /** ++ * Don't block on the pipe when copying data with splice ++ * ++ * Makes the operations on the pipe non-blocking (if the pipe ++ * is full or empty). See SPLICE_F_NONBLOCK in the splice(2) ++ * man page. ++ */ ++ FUSE_BUF_SPLICE_NONBLOCK= (1 << 4), ++}; ++ ++/** ++ * Single data buffer ++ * ++ * Generic data buffer for I/O, extended attributes, etc... Data may ++ * be supplied as a memory pointer or as a file descriptor ++ */ ++struct fuse_buf { ++ /** ++ * Size of data in bytes ++ */ ++ size_t size; ++ ++ /** ++ * Buffer flags ++ */ ++ enum fuse_buf_flags flags; ++ ++ /** ++ * Memory pointer ++ * ++ * Used unless FUSE_BUF_IS_FD flag is set. ++ */ ++ void *mem; ++ ++ /** ++ * File descriptor ++ * ++ * Used if FUSE_BUF_IS_FD flag is set. ++ */ ++ int fd; ++ ++ /** ++ * File position ++ * ++ * Used if FUSE_BUF_FD_SEEK flag is set. ++ */ ++ off_t pos; ++}; ++ ++/** ++ * Data buffer vector ++ * ++ * An array of data buffers, each containing a memory pointer or a ++ * file descriptor. ++ * ++ * Allocate dynamically to add more than one buffer. ++ */ ++struct fuse_bufvec { ++ /** ++ * Number of buffers in the array ++ */ ++ size_t count; ++ ++ /** ++ * Index of current buffer within the array ++ */ ++ size_t idx; ++ ++ /** ++ * Current offset within the current buffer ++ */ ++ size_t off; ++ ++ /** ++ * Array of buffers ++ */ ++ struct fuse_buf buf[1]; ++}; ++ ++/* Initialize bufvec with a single buffer of given size */ ++#define FUSE_BUFVEC_INIT(size__) \ ++ ((struct fuse_bufvec) { \ ++ /* .count= */ 1, \ ++ /* .idx = */ 0, \ ++ /* .off = */ 0, \ ++ /* .buf = */ { /* [0] = */ { \ ++ /* .size = */ (size__), \ ++ /* .flags = */ (enum fuse_buf_flags) 0, \ ++ /* .mem = */ NULL, \ ++ /* .fd = */ -1, \ ++ /* .pos = */ 0, \ ++ } } \ ++ } ) ++ ++/** ++ * Get total size of data in a fuse buffer vector ++ * ++ * @param bufv buffer vector ++ * @return size of data ++ */ ++size_t fuse_buf_size(const struct fuse_bufvec *bufv); ++ ++/** ++ * Copy data from one buffer vector to another ++ * ++ * @param dst destination buffer vector ++ * @param src source buffer vector ++ * @param flags flags controlling the copy ++ * @return actual number of bytes copied or -errno on error ++ */ ++ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, ++ enum fuse_buf_copy_flags flags); ++ ++/* ----------------------------------------------------------- * ++ * Signal handling * ++ * ----------------------------------------------------------- */ ++ ++/** ++ * Exit session on HUP, TERM and INT signals and ignore PIPE signal ++ * ++ * Stores session in a global variable. May only be called once per ++ * process until fuse_remove_signal_handlers() is called. ++ * ++ * Once either of the POSIX signals arrives, the signal handler calls ++ * fuse_session_exit(). ++ * ++ * @param se the session to exit ++ * @return 0 on success, -1 on failure ++ * ++ * See also: ++ * fuse_remove_signal_handlers() ++ */ ++int fuse_set_signal_handlers(struct fuse_session *se); ++ ++/** ++ * Restore default signal handlers ++ * ++ * Resets global session. After this fuse_set_signal_handlers() may ++ * be called again. ++ * ++ * @param se the same session as given in fuse_set_signal_handlers() ++ * ++ * See also: ++ * fuse_set_signal_handlers() ++ */ ++void fuse_remove_signal_handlers(struct fuse_session *se); ++ ++/* ----------------------------------------------------------- * ++ * Compatibility stuff * ++ * ----------------------------------------------------------- */ ++ ++#if !defined(FUSE_USE_VERSION) || FUSE_USE_VERSION < 30 ++# error only API version 30 or greater is supported ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++ ++ ++/* ++ * This interface uses 64 bit off_t. ++ * ++ * On 32bit systems please add -D_FILE_OFFSET_BITS=64 to your compile flags! ++ */ ++ ++#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus ++_Static_assert(sizeof(off_t) == 8, "fuse: off_t must be 64bit"); ++#else ++struct _fuse_off_t_must_be_64bit_dummy_struct \ ++ { unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); }; ++#endif ++ ++#endif /* FUSE_COMMON_H_ */ +diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h +new file mode 100644 +index 0000000000..d38b630ac5 +--- /dev/null ++++ b/tools/virtiofsd/fuse_i.h +@@ -0,0 +1,139 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2007 Miklos Szeredi ++ ++ This program can be distributed under the terms of the GNU LGPLv2. ++ See the file COPYING.LIB ++*/ ++ ++#include "fuse.h" ++#include "fuse_lowlevel.h" ++ ++struct mount_opts; ++ ++struct fuse_req { ++ struct fuse_session *se; ++ uint64_t unique; ++ int ctr; ++ pthread_mutex_t lock; ++ struct fuse_ctx ctx; ++ struct fuse_chan *ch; ++ int interrupted; ++ unsigned int ioctl_64bit : 1; ++ union { ++ struct { ++ uint64_t unique; ++ } i; ++ struct { ++ fuse_interrupt_func_t func; ++ void *data; ++ } ni; ++ } u; ++ struct fuse_req *next; ++ struct fuse_req *prev; ++}; ++ ++struct fuse_notify_req { ++ uint64_t unique; ++ void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t, ++ const void *, const struct fuse_buf *); ++ struct fuse_notify_req *next; ++ struct fuse_notify_req *prev; ++}; ++ ++struct fuse_session { ++ char *mountpoint; ++ volatile int exited; ++ int fd; ++ struct mount_opts *mo; ++ int debug; ++ int deny_others; ++ struct fuse_lowlevel_ops op; ++ int got_init; ++ struct cuse_data *cuse_data; ++ void *userdata; ++ uid_t owner; ++ struct fuse_conn_info conn; ++ struct fuse_req list; ++ struct fuse_req interrupts; ++ pthread_mutex_t lock; ++ int got_destroy; ++ pthread_key_t pipe_key; ++ int broken_splice_nonblock; ++ uint64_t notify_ctr; ++ struct fuse_notify_req notify_list; ++ size_t bufsize; ++ int error; ++}; ++ ++struct fuse_chan { ++ pthread_mutex_t lock; ++ int ctr; ++ int fd; ++}; ++ ++/** ++ * Filesystem module ++ * ++ * Filesystem modules are registered with the FUSE_REGISTER_MODULE() ++ * macro. ++ * ++ */ ++struct fuse_module { ++ char *name; ++ fuse_module_factory_t factory; ++ struct fuse_module *next; ++ struct fusemod_so *so; ++ int ctr; ++}; ++ ++/* ----------------------------------------------------------- * ++ * Channel interface (when using -o clone_fd) * ++ * ----------------------------------------------------------- */ ++ ++/** ++ * Obtain counted reference to the channel ++ * ++ * @param ch the channel ++ * @return the channel ++ */ ++struct fuse_chan *fuse_chan_get(struct fuse_chan *ch); ++ ++/** ++ * Drop counted reference to a channel ++ * ++ * @param ch the channel ++ */ ++void fuse_chan_put(struct fuse_chan *ch); ++ ++struct mount_opts *parse_mount_opts(struct fuse_args *args); ++void destroy_mount_opts(struct mount_opts *mo); ++void fuse_mount_version(void); ++unsigned get_max_read(struct mount_opts *o); ++void fuse_kern_unmount(const char *mountpoint, int fd); ++int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo); ++ ++int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, ++ int count); ++void fuse_free_req(fuse_req_t req); ++ ++void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg); ++ ++int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg); ++ ++int fuse_session_receive_buf_int(struct fuse_session *se, struct fuse_buf *buf, ++ struct fuse_chan *ch); ++void fuse_session_process_buf_int(struct fuse_session *se, ++ const struct fuse_buf *buf, struct fuse_chan *ch); ++ ++struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op, ++ size_t op_size, void *private_data); ++int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config); ++int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config); ++ ++#define FUSE_MAX_MAX_PAGES 256 ++#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32 ++ ++/* room needed in buffer to accommodate header */ ++#define FUSE_BUFFER_HEADER_SIZE 0x1000 ++ +diff --git a/tools/virtiofsd/fuse_log.h b/tools/virtiofsd/fuse_log.h +new file mode 100644 +index 0000000000..5e112e0f53 +--- /dev/null ++++ b/tools/virtiofsd/fuse_log.h +@@ -0,0 +1,82 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2019 Red Hat, Inc. ++ ++ This program can be distributed under the terms of the GNU LGPLv2. ++ See the file COPYING.LIB. ++*/ ++ ++#ifndef FUSE_LOG_H_ ++#define FUSE_LOG_H_ ++ ++/** @file ++ * ++ * This file defines the logging interface of FUSE ++ */ ++ ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * Log severity level ++ * ++ * These levels correspond to syslog(2) log levels since they are widely used. ++ */ ++enum fuse_log_level { ++ FUSE_LOG_EMERG, ++ FUSE_LOG_ALERT, ++ FUSE_LOG_CRIT, ++ FUSE_LOG_ERR, ++ FUSE_LOG_WARNING, ++ FUSE_LOG_NOTICE, ++ FUSE_LOG_INFO, ++ FUSE_LOG_DEBUG ++}; ++ ++/** ++ * Log message handler function. ++ * ++ * This function must be thread-safe. It may be called from any libfuse ++ * function, including fuse_parse_cmdline() and other functions invoked before ++ * a FUSE filesystem is created. ++ * ++ * Install a custom log message handler function using fuse_set_log_func(). ++ * ++ * @param level log severity level ++ * @param fmt sprintf-style format string including newline ++ * @param ap format string arguments ++ */ ++typedef void (*fuse_log_func_t)(enum fuse_log_level level, ++ const char *fmt, va_list ap); ++ ++/** ++ * Install a custom log handler function. ++ * ++ * Log messages are emitted by libfuse functions to report errors and debug ++ * information. Messages are printed to stderr by default but this can be ++ * overridden by installing a custom log message handler function. ++ * ++ * The log message handler function is global and affects all FUSE filesystems ++ * created within this process. ++ * ++ * @param func a custom log message handler function or NULL to revert to ++ * the default ++ */ ++void fuse_set_log_func(fuse_log_func_t func); ++ ++/** ++ * Emit a log message ++ * ++ * @param level severity level (FUSE_LOG_ERR, FUSE_LOG_DEBUG, etc) ++ * @param fmt sprintf-style format string including newline ++ */ ++void fuse_log(enum fuse_log_level level, const char *fmt, ...); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* FUSE_LOG_H_ */ +diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h +new file mode 100644 +index 0000000000..18c6363f07 +--- /dev/null ++++ b/tools/virtiofsd/fuse_lowlevel.h +@@ -0,0 +1,2089 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2007 Miklos Szeredi ++ ++ This program can be distributed under the terms of the GNU LGPLv2. ++ See the file COPYING.LIB. ++*/ ++ ++#ifndef FUSE_LOWLEVEL_H_ ++#define FUSE_LOWLEVEL_H_ ++ ++/** @file ++ * ++ * Low level API ++ * ++ * IMPORTANT: you should define FUSE_USE_VERSION before including this ++ * header. To use the newest API define it to 31 (recommended for any ++ * new application). ++ */ ++ ++#ifndef FUSE_USE_VERSION ++#error FUSE_USE_VERSION not defined ++#endif ++ ++#include "fuse_common.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/* ----------------------------------------------------------- * ++ * Miscellaneous definitions * ++ * ----------------------------------------------------------- */ ++ ++/** The node ID of the root inode */ ++#define FUSE_ROOT_ID 1 ++ ++/** Inode number type */ ++typedef uint64_t fuse_ino_t; ++ ++/** Request pointer type */ ++typedef struct fuse_req *fuse_req_t; ++ ++/** ++ * Session ++ * ++ * This provides hooks for processing requests, and exiting ++ */ ++struct fuse_session; ++ ++/** Directory entry parameters supplied to fuse_reply_entry() */ ++struct fuse_entry_param { ++ /** Unique inode number ++ * ++ * In lookup, zero means negative entry (from version 2.5) ++ * Returning ENOENT also means negative entry, but by setting zero ++ * ino the kernel may cache negative entries for entry_timeout ++ * seconds. ++ */ ++ fuse_ino_t ino; ++ ++ /** Generation number for this entry. ++ * ++ * If the file system will be exported over NFS, the ++ * ino/generation pairs need to be unique over the file ++ * system's lifetime (rather than just the mount time). So if ++ * the file system reuses an inode after it has been deleted, ++ * it must assign a new, previously unused generation number ++ * to the inode at the same time. ++ * ++ */ ++ uint64_t generation; ++ ++ /** Inode attributes. ++ * ++ * Even if attr_timeout == 0, attr must be correct. For example, ++ * for open(), FUSE uses attr.st_size from lookup() to determine ++ * how many bytes to request. If this value is not correct, ++ * incorrect data will be returned. ++ */ ++ struct stat attr; ++ ++ /** Validity timeout (in seconds) for inode attributes. If ++ attributes only change as a result of requests that come ++ through the kernel, this should be set to a very large ++ value. */ ++ double attr_timeout; ++ ++ /** Validity timeout (in seconds) for the name. If directory ++ entries are changed/deleted only as a result of requests ++ that come through the kernel, this should be set to a very ++ large value. */ ++ double entry_timeout; ++}; ++ ++/** ++ * Additional context associated with requests. ++ * ++ * Note that the reported client uid, gid and pid may be zero in some ++ * situations. For example, if the FUSE file system is running in a ++ * PID or user namespace but then accessed from outside the namespace, ++ * there is no valid uid/pid/gid that could be reported. ++ */ ++struct fuse_ctx { ++ /** User ID of the calling process */ ++ uid_t uid; ++ ++ /** Group ID of the calling process */ ++ gid_t gid; ++ ++ /** Thread ID of the calling process */ ++ pid_t pid; ++ ++ /** Umask of the calling process */ ++ mode_t umask; ++}; ++ ++struct fuse_forget_data { ++ fuse_ino_t ino; ++ uint64_t nlookup; ++}; ++ ++/* 'to_set' flags in setattr */ ++#define FUSE_SET_ATTR_MODE (1 << 0) ++#define FUSE_SET_ATTR_UID (1 << 1) ++#define FUSE_SET_ATTR_GID (1 << 2) ++#define FUSE_SET_ATTR_SIZE (1 << 3) ++#define FUSE_SET_ATTR_ATIME (1 << 4) ++#define FUSE_SET_ATTR_MTIME (1 << 5) ++#define FUSE_SET_ATTR_ATIME_NOW (1 << 7) ++#define FUSE_SET_ATTR_MTIME_NOW (1 << 8) ++#define FUSE_SET_ATTR_CTIME (1 << 10) ++ ++/* ----------------------------------------------------------- * ++ * Request methods and replies * ++ * ----------------------------------------------------------- */ ++ ++/** ++ * Low level filesystem operations ++ * ++ * Most of the methods (with the exception of init and destroy) ++ * receive a request handle (fuse_req_t) as their first argument. ++ * This handle must be passed to one of the specified reply functions. ++ * ++ * This may be done inside the method invocation, or after the call ++ * has returned. The request handle is valid until one of the reply ++ * functions is called. ++ * ++ * Other pointer arguments (name, fuse_file_info, etc) are not valid ++ * after the call has returned, so if they are needed later, their ++ * contents have to be copied. ++ * ++ * In general, all methods are expected to perform any necessary ++ * permission checking. However, a filesystem may delegate this task ++ * to the kernel by passing the `default_permissions` mount option to ++ * `fuse_session_new()`. In this case, methods will only be called if ++ * the kernel's permission check has succeeded. ++ * ++ * The filesystem sometimes needs to handle a return value of -ENOENT ++ * from the reply function, which means, that the request was ++ * interrupted, and the reply discarded. For example if ++ * fuse_reply_open() return -ENOENT means, that the release method for ++ * this file will not be called. ++ */ ++struct fuse_lowlevel_ops { ++ /** ++ * Initialize filesystem ++ * ++ * This function is called when libfuse establishes ++ * communication with the FUSE kernel module. The file system ++ * should use this module to inspect and/or modify the ++ * connection parameters provided in the `conn` structure. ++ * ++ * Note that some parameters may be overwritten by options ++ * passed to fuse_session_new() which take precedence over the ++ * values set in this handler. ++ * ++ * There's no reply to this function ++ * ++ * @param userdata the user data passed to fuse_session_new() ++ */ ++ void (*init) (void *userdata, struct fuse_conn_info *conn); ++ ++ /** ++ * Clean up filesystem. ++ * ++ * Called on filesystem exit. When this method is called, the ++ * connection to the kernel may be gone already, so that eg. calls ++ * to fuse_lowlevel_notify_* will fail. ++ * ++ * There's no reply to this function ++ * ++ * @param userdata the user data passed to fuse_session_new() ++ */ ++ void (*destroy) (void *userdata); ++ ++ /** ++ * Look up a directory entry by name and get its attributes. ++ * ++ * Valid replies: ++ * fuse_reply_entry ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param parent inode number of the parent directory ++ * @param name the name to look up ++ */ ++ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name); ++ ++ /** ++ * Forget about an inode ++ * ++ * This function is called when the kernel removes an inode ++ * from its internal caches. ++ * ++ * The inode's lookup count increases by one for every call to ++ * fuse_reply_entry and fuse_reply_create. The nlookup parameter ++ * indicates by how much the lookup count should be decreased. ++ * ++ * Inodes with a non-zero lookup count may receive request from ++ * the kernel even after calls to unlink, rmdir or (when ++ * overwriting an existing file) rename. Filesystems must handle ++ * such requests properly and it is recommended to defer removal ++ * of the inode until the lookup count reaches zero. Calls to ++ * unlink, rmdir or rename will be followed closely by forget ++ * unless the file or directory is open, in which case the ++ * kernel issues forget only after the release or releasedir ++ * calls. ++ * ++ * Note that if a file system will be exported over NFS the ++ * inodes lifetime must extend even beyond forget. See the ++ * generation field in struct fuse_entry_param above. ++ * ++ * On unmount the lookup count for all inodes implicitly drops ++ * to zero. It is not guaranteed that the file system will ++ * receive corresponding forget messages for the affected ++ * inodes. ++ * ++ * Valid replies: ++ * fuse_reply_none ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param nlookup the number of lookups to forget ++ */ ++ void (*forget) (fuse_req_t req, fuse_ino_t ino, uint64_t nlookup); ++ ++ /** ++ * Get file attributes. ++ * ++ * If writeback caching is enabled, the kernel may have a ++ * better idea of a file's length than the FUSE file system ++ * (eg if there has been a write that extended the file size, ++ * but that has not yet been passed to the filesystem.n ++ * ++ * In this case, the st_size value provided by the file system ++ * will be ignored. ++ * ++ * Valid replies: ++ * fuse_reply_attr ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi for future use, currently always NULL ++ */ ++ void (*getattr) (fuse_req_t req, fuse_ino_t ino, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Set file attributes ++ * ++ * In the 'attr' argument only members indicated by the 'to_set' ++ * bitmask contain valid values. Other members contain undefined ++ * values. ++ * ++ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is ++ * expected to reset the setuid and setgid bits if the file ++ * size or owner is being changed. ++ * ++ * If the setattr was invoked from the ftruncate() system call ++ * under Linux kernel versions 2.6.15 or later, the fi->fh will ++ * contain the value set by the open method or will be undefined ++ * if the open method didn't set any value. Otherwise (not ++ * ftruncate call, or kernel version earlier than 2.6.15) the fi ++ * parameter will be NULL. ++ * ++ * Valid replies: ++ * fuse_reply_attr ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param attr the attributes ++ * @param to_set bit mask of attributes which should be set ++ * @param fi file information, or NULL ++ */ ++ void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr, ++ int to_set, struct fuse_file_info *fi); ++ ++ /** ++ * Read symbolic link ++ * ++ * Valid replies: ++ * fuse_reply_readlink ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ */ ++ void (*readlink) (fuse_req_t req, fuse_ino_t ino); ++ ++ /** ++ * Create file node ++ * ++ * Create a regular file, character device, block device, fifo or ++ * socket node. ++ * ++ * Valid replies: ++ * fuse_reply_entry ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param parent inode number of the parent directory ++ * @param name to create ++ * @param mode file type and mode with which to create the new file ++ * @param rdev the device number (only valid if created file is a device) ++ */ ++ void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name, ++ mode_t mode, dev_t rdev); ++ ++ /** ++ * Create a directory ++ * ++ * Valid replies: ++ * fuse_reply_entry ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param parent inode number of the parent directory ++ * @param name to create ++ * @param mode with which to create the new file ++ */ ++ void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name, ++ mode_t mode); ++ ++ /** ++ * Remove a file ++ * ++ * If the file's inode's lookup count is non-zero, the file ++ * system is expected to postpone any removal of the inode ++ * until the lookup count reaches zero (see description of the ++ * forget function). ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param parent inode number of the parent directory ++ * @param name to remove ++ */ ++ void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name); ++ ++ /** ++ * Remove a directory ++ * ++ * If the directory's inode's lookup count is non-zero, the ++ * file system is expected to postpone any removal of the ++ * inode until the lookup count reaches zero (see description ++ * of the forget function). ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param parent inode number of the parent directory ++ * @param name to remove ++ */ ++ void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name); ++ ++ /** ++ * Create a symbolic link ++ * ++ * Valid replies: ++ * fuse_reply_entry ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param link the contents of the symbolic link ++ * @param parent inode number of the parent directory ++ * @param name to create ++ */ ++ void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent, ++ const char *name); ++ ++ /** Rename a file ++ * ++ * If the target exists it should be atomically replaced. If ++ * the target's inode's lookup count is non-zero, the file ++ * system is expected to postpone any removal of the inode ++ * until the lookup count reaches zero (see description of the ++ * forget function). ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure with error code EINVAL, i.e. all ++ * future bmap requests will fail with EINVAL without being ++ * send to the filesystem process. ++ * ++ * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If ++ * RENAME_NOREPLACE is specified, the filesystem must not ++ * overwrite *newname* if it exists and return an error ++ * instead. If `RENAME_EXCHANGE` is specified, the filesystem ++ * must atomically exchange the two files, i.e. both must ++ * exist and neither may be deleted. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param parent inode number of the old parent directory ++ * @param name old name ++ * @param newparent inode number of the new parent directory ++ * @param newname new name ++ */ ++ void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name, ++ fuse_ino_t newparent, const char *newname, ++ unsigned int flags); ++ ++ /** ++ * Create a hard link ++ * ++ * Valid replies: ++ * fuse_reply_entry ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the old inode number ++ * @param newparent inode number of the new parent directory ++ * @param newname new name to create ++ */ ++ void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, ++ const char *newname); ++ ++ /** ++ * Open a file ++ * ++ * Open flags are available in fi->flags. The following rules ++ * apply. ++ * ++ * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be ++ * filtered out / handled by the kernel. ++ * ++ * - Access modes (O_RDONLY, O_WRONLY, O_RDWR) should be used ++ * by the filesystem to check if the operation is ++ * permitted. If the ``-o default_permissions`` mount ++ * option is given, this check is already done by the ++ * kernel before calling open() and may thus be omitted by ++ * the filesystem. ++ * ++ * - When writeback caching is enabled, the kernel may send ++ * read requests even for files opened with O_WRONLY. The ++ * filesystem should be prepared to handle this. ++ * ++ * - When writeback caching is disabled, the filesystem is ++ * expected to properly handle the O_APPEND flag and ensure ++ * that each write is appending to the end of the file. ++ * ++ * - When writeback caching is enabled, the kernel will ++ * handle O_APPEND. However, unless all changes to the file ++ * come through the kernel this will not work reliably. The ++ * filesystem should thus either ignore the O_APPEND flag ++ * (and let the kernel handle it), or return an error ++ * (indicating that reliably O_APPEND is not available). ++ * ++ * Filesystem may store an arbitrary file handle (pointer, ++ * index, etc) in fi->fh, and use this in other all other file ++ * operations (read, write, flush, release, fsync). ++ * ++ * Filesystem may also implement stateless file I/O and not store ++ * anything in fi->fh. ++ * ++ * There are also some flags (direct_io, keep_cache) which the ++ * filesystem may set in fi, to change the way the file is opened. ++ * See fuse_file_info structure in for more details. ++ * ++ * If this request is answered with an error code of ENOSYS ++ * and FUSE_CAP_NO_OPEN_SUPPORT is set in ++ * `fuse_conn_info.capable`, this is treated as success and ++ * future calls to open and release will also succeed without being ++ * sent to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_open ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ */ ++ void (*open) (fuse_req_t req, fuse_ino_t ino, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Read data ++ * ++ * Read should send exactly the number of bytes requested except ++ * on EOF or error, otherwise the rest of the data will be ++ * substituted with zeroes. An exception to this is when the file ++ * has been opened in 'direct_io' mode, in which case the return ++ * value of the read system call will reflect the return value of ++ * this operation. ++ * ++ * fi->fh will contain the value set by the open method, or will ++ * be undefined if the open method didn't set any value. ++ * ++ * Valid replies: ++ * fuse_reply_buf ++ * fuse_reply_iov ++ * fuse_reply_data ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param size number of bytes to read ++ * @param off offset to read from ++ * @param fi file information ++ */ ++ void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Write data ++ * ++ * Write should return exactly the number of bytes requested ++ * except on error. An exception to this is when the file has ++ * been opened in 'direct_io' mode, in which case the return value ++ * of the write system call will reflect the return value of this ++ * operation. ++ * ++ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is ++ * expected to reset the setuid and setgid bits. ++ * ++ * fi->fh will contain the value set by the open method, or will ++ * be undefined if the open method didn't set any value. ++ * ++ * Valid replies: ++ * fuse_reply_write ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param buf data to write ++ * @param size number of bytes to write ++ * @param off offset to write to ++ * @param fi file information ++ */ ++ void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf, ++ size_t size, off_t off, struct fuse_file_info *fi); ++ ++ /** ++ * Flush method ++ * ++ * This is called on each close() of the opened file. ++ * ++ * Since file descriptors can be duplicated (dup, dup2, fork), for ++ * one open call there may be many flush calls. ++ * ++ * Filesystems shouldn't assume that flush will always be called ++ * after some writes, or that if will be called at all. ++ * ++ * fi->fh will contain the value set by the open method, or will ++ * be undefined if the open method didn't set any value. ++ * ++ * NOTE: the name of the method is misleading, since (unlike ++ * fsync) the filesystem is not forced to flush pending writes. ++ * One reason to flush data is if the filesystem wants to return ++ * write errors during close. However, such use is non-portable ++ * because POSIX does not require [close] to wait for delayed I/O to ++ * complete. ++ * ++ * If the filesystem supports file locking operations (setlk, ++ * getlk) it should remove all locks belonging to 'fi->owner'. ++ * ++ * If this request is answered with an error code of ENOSYS, ++ * this is treated as success and future calls to flush() will ++ * succeed automatically without being send to the filesystem ++ * process. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ * ++ * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html ++ */ ++ void (*flush) (fuse_req_t req, fuse_ino_t ino, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Release an open file ++ * ++ * Release is called when there are no more references to an open ++ * file: all file descriptors are closed and all memory mappings ++ * are unmapped. ++ * ++ * For every open call there will be exactly one release call (unless ++ * the filesystem is force-unmounted). ++ * ++ * The filesystem may reply with an error, but error values are ++ * not returned to close() or munmap() which triggered the ++ * release. ++ * ++ * fi->fh will contain the value set by the open method, or will ++ * be undefined if the open method didn't set any value. ++ * fi->flags will contain the same flags as for open. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ */ ++ void (*release) (fuse_req_t req, fuse_ino_t ino, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Synchronize file contents ++ * ++ * If the datasync parameter is non-zero, then only the user data ++ * should be flushed, not the meta data. ++ * ++ * If this request is answered with an error code of ENOSYS, ++ * this is treated as success and future calls to fsync() will ++ * succeed automatically without being send to the filesystem ++ * process. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param datasync flag indicating if only data should be flushed ++ * @param fi file information ++ */ ++ void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Open a directory ++ * ++ * Filesystem may store an arbitrary file handle (pointer, index, ++ * etc) in fi->fh, and use this in other all other directory ++ * stream operations (readdir, releasedir, fsyncdir). ++ * ++ * If this request is answered with an error code of ENOSYS and ++ * FUSE_CAP_NO_OPENDIR_SUPPORT is set in `fuse_conn_info.capable`, ++ * this is treated as success and future calls to opendir and ++ * releasedir will also succeed without being sent to the filesystem ++ * process. In addition, the kernel will cache readdir results ++ * as if opendir returned FOPEN_KEEP_CACHE | FOPEN_CACHE_DIR. ++ * ++ * Valid replies: ++ * fuse_reply_open ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ */ ++ void (*opendir) (fuse_req_t req, fuse_ino_t ino, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Read directory ++ * ++ * Send a buffer filled using fuse_add_direntry(), with size not ++ * exceeding the requested size. Send an empty buffer on end of ++ * stream. ++ * ++ * fi->fh will contain the value set by the opendir method, or ++ * will be undefined if the opendir method didn't set any value. ++ * ++ * Returning a directory entry from readdir() does not affect ++ * its lookup count. ++ * ++ * If off_t is non-zero, then it will correspond to one of the off_t ++ * values that was previously returned by readdir() for the same ++ * directory handle. In this case, readdir() should skip over entries ++ * coming before the position defined by the off_t value. If entries ++ * are added or removed while the directory handle is open, they filesystem ++ * may still include the entries that have been removed, and may not ++ * report the entries that have been created. However, addition or ++ * removal of entries must never cause readdir() to skip over unrelated ++ * entries or to report them more than once. This means ++ * that off_t can not be a simple index that enumerates the entries ++ * that have been returned but must contain sufficient information to ++ * uniquely determine the next directory entry to return even when the ++ * set of entries is changing. ++ * ++ * The function does not have to report the '.' and '..' ++ * entries, but is allowed to do so. Note that, if readdir does ++ * not return '.' or '..', they will not be implicitly returned, ++ * and this behavior is observable by the caller. ++ * ++ * Valid replies: ++ * fuse_reply_buf ++ * fuse_reply_data ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param size maximum number of bytes to send ++ * @param off offset to continue reading the directory stream ++ * @param fi file information ++ */ ++ void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Release an open directory ++ * ++ * For every opendir call there will be exactly one releasedir ++ * call (unless the filesystem is force-unmounted). ++ * ++ * fi->fh will contain the value set by the opendir method, or ++ * will be undefined if the opendir method didn't set any value. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ */ ++ void (*releasedir) (fuse_req_t req, fuse_ino_t ino, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Synchronize directory contents ++ * ++ * If the datasync parameter is non-zero, then only the directory ++ * contents should be flushed, not the meta data. ++ * ++ * fi->fh will contain the value set by the opendir method, or ++ * will be undefined if the opendir method didn't set any value. ++ * ++ * If this request is answered with an error code of ENOSYS, ++ * this is treated as success and future calls to fsyncdir() will ++ * succeed automatically without being send to the filesystem ++ * process. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param datasync flag indicating if only data should be flushed ++ * @param fi file information ++ */ ++ void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Get file system statistics ++ * ++ * Valid replies: ++ * fuse_reply_statfs ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number, zero means "undefined" ++ */ ++ void (*statfs) (fuse_req_t req, fuse_ino_t ino); ++ ++ /** ++ * Set an extended attribute ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all ++ * future setxattr() requests will fail with EOPNOTSUPP without being ++ * send to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ */ ++ void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, ++ const char *value, size_t size, int flags); ++ ++ /** ++ * Get an extended attribute ++ * ++ * If size is zero, the size of the value should be sent with ++ * fuse_reply_xattr. ++ * ++ * If the size is non-zero, and the value fits in the buffer, the ++ * value should be sent with fuse_reply_buf. ++ * ++ * If the size is too small for the value, the ERANGE error should ++ * be sent. ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all ++ * future getxattr() requests will fail with EOPNOTSUPP without being ++ * send to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_buf ++ * fuse_reply_data ++ * fuse_reply_xattr ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param name of the extended attribute ++ * @param size maximum size of the value to send ++ */ ++ void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, ++ size_t size); ++ ++ /** ++ * List extended attribute names ++ * ++ * If size is zero, the total size of the attribute list should be ++ * sent with fuse_reply_xattr. ++ * ++ * If the size is non-zero, and the null character separated ++ * attribute list fits in the buffer, the list should be sent with ++ * fuse_reply_buf. ++ * ++ * If the size is too small for the list, the ERANGE error should ++ * be sent. ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all ++ * future listxattr() requests will fail with EOPNOTSUPP without being ++ * send to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_buf ++ * fuse_reply_data ++ * fuse_reply_xattr ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param size maximum size of the list to send ++ */ ++ void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size); ++ ++ /** ++ * Remove an extended attribute ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all ++ * future removexattr() requests will fail with EOPNOTSUPP without being ++ * send to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param name of the extended attribute ++ */ ++ void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name); ++ ++ /** ++ * Check file access permissions ++ * ++ * This will be called for the access() and chdir() system ++ * calls. If the 'default_permissions' mount option is given, ++ * this method is not called. ++ * ++ * This method is not called under Linux kernel versions 2.4.x ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent success, i.e. this and all future access() ++ * requests will succeed without being send to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param mask requested access mode ++ */ ++ void (*access) (fuse_req_t req, fuse_ino_t ino, int mask); ++ ++ /** ++ * Create and open a file ++ * ++ * If the file does not exist, first create it with the specified ++ * mode, and then open it. ++ * ++ * See the description of the open handler for more ++ * information. ++ * ++ * If this method is not implemented or under Linux kernel ++ * versions earlier than 2.6.15, the mknod() and open() methods ++ * will be called instead. ++ * ++ * If this request is answered with an error code of ENOSYS, the handler ++ * is treated as not implemented (i.e., for this and future requests the ++ * mknod() and open() handlers will be called instead). ++ * ++ * Valid replies: ++ * fuse_reply_create ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param parent inode number of the parent directory ++ * @param name to create ++ * @param mode file type and mode with which to create the new file ++ * @param fi file information ++ */ ++ void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name, ++ mode_t mode, struct fuse_file_info *fi); ++ ++ /** ++ * Test for a POSIX file lock ++ * ++ * Valid replies: ++ * fuse_reply_lock ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ * @param lock the region/type to test ++ */ ++ void (*getlk) (fuse_req_t req, fuse_ino_t ino, ++ struct fuse_file_info *fi, struct flock *lock); ++ ++ /** ++ * Acquire, modify or release a POSIX file lock ++ * ++ * For POSIX threads (NPTL) there's a 1-1 relation between pid and ++ * owner, but otherwise this is not always the case. For checking ++ * lock ownership, 'fi->owner' must be used. The l_pid field in ++ * 'struct flock' should only be used to fill in this field in ++ * getlk(). ++ * ++ * Note: if the locking methods are not implemented, the kernel ++ * will still allow file locking to work locally. Hence these are ++ * only interesting for network filesystems and similar. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ * @param lock the region/type to set ++ * @param sleep locking operation may sleep ++ */ ++ void (*setlk) (fuse_req_t req, fuse_ino_t ino, ++ struct fuse_file_info *fi, ++ struct flock *lock, int sleep); ++ ++ /** ++ * Map block index within file to block index within device ++ * ++ * Note: This makes sense only for block device backed filesystems ++ * mounted with the 'blkdev' option ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure, i.e. all future bmap() requests will ++ * fail with the same error code without being send to the filesystem ++ * process. ++ * ++ * Valid replies: ++ * fuse_reply_bmap ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param blocksize unit of block index ++ * @param idx block index within file ++ */ ++ void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize, ++ uint64_t idx); ++ ++ /** ++ * Ioctl ++ * ++ * Note: For unrestricted ioctls (not allowed for FUSE ++ * servers), data in and out areas can be discovered by giving ++ * iovs and setting FUSE_IOCTL_RETRY in *flags*. For ++ * restricted ioctls, kernel prepares in/out data area ++ * according to the information encoded in cmd. ++ * ++ * Valid replies: ++ * fuse_reply_ioctl_retry ++ * fuse_reply_ioctl ++ * fuse_reply_ioctl_iov ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param cmd ioctl command ++ * @param arg ioctl argument ++ * @param fi file information ++ * @param flags for FUSE_IOCTL_* flags ++ * @param in_buf data fetched from the caller ++ * @param in_bufsz number of fetched bytes ++ * @param out_bufsz maximum size of output data ++ * ++ * Note : the unsigned long request submitted by the application ++ * is truncated to 32 bits. ++ */ ++ void (*ioctl) (fuse_req_t req, fuse_ino_t ino, unsigned int cmd, ++ void *arg, struct fuse_file_info *fi, unsigned flags, ++ const void *in_buf, size_t in_bufsz, size_t out_bufsz); ++ ++ /** ++ * Poll for IO readiness ++ * ++ * Note: If ph is non-NULL, the client should notify ++ * when IO readiness events occur by calling ++ * fuse_lowlevel_notify_poll() with the specified ph. ++ * ++ * Regardless of the number of times poll with a non-NULL ph ++ * is received, single notification is enough to clear all. ++ * Notifying more times incurs overhead but doesn't harm ++ * correctness. ++ * ++ * The callee is responsible for destroying ph with ++ * fuse_pollhandle_destroy() when no longer in use. ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as success (with a kernel-defined default poll-mask) and ++ * future calls to pull() will succeed the same way without being send ++ * to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_poll ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ * @param ph poll handle to be used for notification ++ */ ++ void (*poll) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, ++ struct fuse_pollhandle *ph); ++ ++ /** ++ * Write data made available in a buffer ++ * ++ * This is a more generic version of the ->write() method. If ++ * FUSE_CAP_SPLICE_READ is set in fuse_conn_info.want and the ++ * kernel supports splicing from the fuse device, then the ++ * data will be made available in pipe for supporting zero ++ * copy data transfer. ++ * ++ * buf->count is guaranteed to be one (and thus buf->idx is ++ * always zero). The write_buf handler must ensure that ++ * bufv->off is correctly updated (reflecting the number of ++ * bytes read from bufv->buf[0]). ++ * ++ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is ++ * expected to reset the setuid and setgid bits. ++ * ++ * Valid replies: ++ * fuse_reply_write ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param bufv buffer containing the data ++ * @param off offset to write to ++ * @param fi file information ++ */ ++ void (*write_buf) (fuse_req_t req, fuse_ino_t ino, ++ struct fuse_bufvec *bufv, off_t off, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Callback function for the retrieve request ++ * ++ * Valid replies: ++ * fuse_reply_none ++ * ++ * @param req request handle ++ * @param cookie user data supplied to fuse_lowlevel_notify_retrieve() ++ * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve() ++ * @param offset the offset supplied to fuse_lowlevel_notify_retrieve() ++ * @param bufv the buffer containing the returned data ++ */ ++ void (*retrieve_reply) (fuse_req_t req, void *cookie, fuse_ino_t ino, ++ off_t offset, struct fuse_bufvec *bufv); ++ ++ /** ++ * Forget about multiple inodes ++ * ++ * See description of the forget function for more ++ * information. ++ * ++ * Valid replies: ++ * fuse_reply_none ++ * ++ * @param req request handle ++ */ ++ void (*forget_multi) (fuse_req_t req, size_t count, ++ struct fuse_forget_data *forgets); ++ ++ /** ++ * Acquire, modify or release a BSD file lock ++ * ++ * Note: if the locking methods are not implemented, the kernel ++ * will still allow file locking to work locally. Hence these are ++ * only interesting for network filesystems and similar. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ * @param op the locking operation, see flock(2) ++ */ ++ void (*flock) (fuse_req_t req, fuse_ino_t ino, ++ struct fuse_file_info *fi, int op); ++ ++ /** ++ * Allocate requested space. If this function returns success then ++ * subsequent writes to the specified range shall not fail due to the lack ++ * of free space on the file system storage media. ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all ++ * future fallocate() requests will fail with EOPNOTSUPP without being ++ * send to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param offset starting point for allocated region ++ * @param length size of allocated region ++ * @param mode determines the operation to be performed on the given range, ++ * see fallocate(2) ++ */ ++ void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode, ++ off_t offset, off_t length, struct fuse_file_info *fi); ++ ++ /** ++ * Read directory with attributes ++ * ++ * Send a buffer filled using fuse_add_direntry_plus(), with size not ++ * exceeding the requested size. Send an empty buffer on end of ++ * stream. ++ * ++ * fi->fh will contain the value set by the opendir method, or ++ * will be undefined if the opendir method didn't set any value. ++ * ++ * In contrast to readdir() (which does not affect the lookup counts), ++ * the lookup count of every entry returned by readdirplus(), except "." ++ * and "..", is incremented by one. ++ * ++ * Valid replies: ++ * fuse_reply_buf ++ * fuse_reply_data ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param size maximum number of bytes to send ++ * @param off offset to continue reading the directory stream ++ * @param fi file information ++ */ ++ void (*readdirplus) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Copy a range of data from one file to another ++ * ++ * Performs an optimized copy between two file descriptors without the ++ * additional cost of transferring data through the FUSE kernel module ++ * to user space (glibc) and then back into the FUSE filesystem again. ++ * ++ * In case this method is not implemented, glibc falls back to reading ++ * data from the source and writing to the destination. Effectively ++ * doing an inefficient copy of the data. ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all ++ * future copy_file_range() requests will fail with EOPNOTSUPP without ++ * being send to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_write ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino_in the inode number or the source file ++ * @param off_in starting point from were the data should be read ++ * @param fi_in file information of the source file ++ * @param ino_out the inode number or the destination file ++ * @param off_out starting point where the data should be written ++ * @param fi_out file information of the destination file ++ * @param len maximum size of the data to copy ++ * @param flags passed along with the copy_file_range() syscall ++ */ ++ void (*copy_file_range) (fuse_req_t req, fuse_ino_t ino_in, ++ off_t off_in, struct fuse_file_info *fi_in, ++ fuse_ino_t ino_out, off_t off_out, ++ struct fuse_file_info *fi_out, size_t len, ++ int flags); ++ ++ /** ++ * Find next data or hole after the specified offset ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure, i.e. all future lseek() requests will ++ * fail with the same error code without being send to the filesystem ++ * process. ++ * ++ * Valid replies: ++ * fuse_reply_lseek ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param off offset to start search from ++ * @param whence either SEEK_DATA or SEEK_HOLE ++ * @param fi file information ++ */ ++ void (*lseek) (fuse_req_t req, fuse_ino_t ino, off_t off, int whence, ++ struct fuse_file_info *fi); ++}; ++ ++/** ++ * Reply with an error code or success. ++ * ++ * Possible requests: ++ * all except forget ++ * ++ * Whereever possible, error codes should be chosen from the list of ++ * documented error conditions in the corresponding system calls ++ * manpage. ++ * ++ * An error code of ENOSYS is sometimes treated specially. This is ++ * indicated in the documentation of the affected handler functions. ++ * ++ * The following requests may be answered with a zero error code: ++ * unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr, ++ * removexattr, setlk. ++ * ++ * @param req request handle ++ * @param err the positive error value, or zero for success ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_err(fuse_req_t req, int err); ++ ++/** ++ * Don't send reply ++ * ++ * Possible requests: ++ * forget ++ * forget_multi ++ * retrieve_reply ++ * ++ * @param req request handle ++ */ ++void fuse_reply_none(fuse_req_t req); ++ ++/** ++ * Reply with a directory entry ++ * ++ * Possible requests: ++ * lookup, mknod, mkdir, symlink, link ++ * ++ * Side effects: ++ * increments the lookup count on success ++ * ++ * @param req request handle ++ * @param e the entry parameters ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e); ++ ++/** ++ * Reply with a directory entry and open parameters ++ * ++ * currently the following members of 'fi' are used: ++ * fh, direct_io, keep_cache ++ * ++ * Possible requests: ++ * create ++ * ++ * Side effects: ++ * increments the lookup count on success ++ * ++ * @param req request handle ++ * @param e the entry parameters ++ * @param fi file information ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, ++ const struct fuse_file_info *fi); ++ ++/** ++ * Reply with attributes ++ * ++ * Possible requests: ++ * getattr, setattr ++ * ++ * @param req request handle ++ * @param attr the attributes ++ * @param attr_timeout validity timeout (in seconds) for the attributes ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_attr(fuse_req_t req, const struct stat *attr, ++ double attr_timeout); ++ ++/** ++ * Reply with the contents of a symbolic link ++ * ++ * Possible requests: ++ * readlink ++ * ++ * @param req request handle ++ * @param link symbolic link contents ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_readlink(fuse_req_t req, const char *link); ++ ++/** ++ * Reply with open parameters ++ * ++ * currently the following members of 'fi' are used: ++ * fh, direct_io, keep_cache ++ * ++ * Possible requests: ++ * open, opendir ++ * ++ * @param req request handle ++ * @param fi file information ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi); ++ ++/** ++ * Reply with number of bytes written ++ * ++ * Possible requests: ++ * write ++ * ++ * @param req request handle ++ * @param count the number of bytes written ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_write(fuse_req_t req, size_t count); ++ ++/** ++ * Reply with data ++ * ++ * Possible requests: ++ * read, readdir, getxattr, listxattr ++ * ++ * @param req request handle ++ * @param buf buffer containing data ++ * @param size the size of data in bytes ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size); ++ ++/** ++ * Reply with data copied/moved from buffer(s) ++ * ++ * Zero copy data transfer ("splicing") will be used under ++ * the following circumstances: ++ * ++ * 1. FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.want, and ++ * 2. the kernel supports splicing from the fuse device ++ * (FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.capable), and ++ * 3. *flags* does not contain FUSE_BUF_NO_SPLICE ++ * 4. The amount of data that is provided in file-descriptor backed ++ * buffers (i.e., buffers for which bufv[n].flags == FUSE_BUF_FD) ++ * is at least twice the page size. ++ * ++ * In order for SPLICE_F_MOVE to be used, the following additional ++ * conditions have to be fulfilled: ++ * ++ * 1. FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.want, and ++ * 2. the kernel supports it (i.e, FUSE_CAP_SPLICE_MOVE is set in ++ fuse_conn_info.capable), and ++ * 3. *flags* contains FUSE_BUF_SPLICE_MOVE ++ * ++ * Note that, if splice is used, the data is actually spliced twice: ++ * once into a temporary pipe (to prepend header data), and then again ++ * into the kernel. If some of the provided buffers are memory-backed, ++ * the data in them is copied in step one and spliced in step two. ++ * ++ * The FUSE_BUF_SPLICE_FORCE_SPLICE and FUSE_BUF_SPLICE_NONBLOCK flags ++ * are silently ignored. ++ * ++ * Possible requests: ++ * read, readdir, getxattr, listxattr ++ * ++ * Side effects: ++ * when used to return data from a readdirplus() (but not readdir()) ++ * call, increments the lookup count of each returned entry by one ++ * on success. ++ * ++ * @param req request handle ++ * @param bufv buffer vector ++ * @param flags flags controlling the copy ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, ++ enum fuse_buf_copy_flags flags); ++ ++/** ++ * Reply with data vector ++ * ++ * Possible requests: ++ * read, readdir, getxattr, listxattr ++ * ++ * @param req request handle ++ * @param iov the vector containing the data ++ * @param count the size of vector ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count); ++ ++/** ++ * Reply with filesystem statistics ++ * ++ * Possible requests: ++ * statfs ++ * ++ * @param req request handle ++ * @param stbuf filesystem statistics ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf); ++ ++/** ++ * Reply with needed buffer size ++ * ++ * Possible requests: ++ * getxattr, listxattr ++ * ++ * @param req request handle ++ * @param count the buffer size needed in bytes ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_xattr(fuse_req_t req, size_t count); ++ ++/** ++ * Reply with file lock information ++ * ++ * Possible requests: ++ * getlk ++ * ++ * @param req request handle ++ * @param lock the lock information ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_lock(fuse_req_t req, const struct flock *lock); ++ ++/** ++ * Reply with block index ++ * ++ * Possible requests: ++ * bmap ++ * ++ * @param req request handle ++ * @param idx block index within device ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_bmap(fuse_req_t req, uint64_t idx); ++ ++/* ----------------------------------------------------------- * ++ * Filling a buffer in readdir * ++ * ----------------------------------------------------------- */ ++ ++/** ++ * Add a directory entry to the buffer ++ * ++ * Buffer needs to be large enough to hold the entry. If it's not, ++ * then the entry is not filled in but the size of the entry is still ++ * returned. The caller can check this by comparing the bufsize ++ * parameter with the returned entry size. If the entry size is ++ * larger than the buffer size, the operation failed. ++ * ++ * From the 'stbuf' argument the st_ino field and bits 12-15 of the ++ * st_mode field are used. The other fields are ignored. ++ * ++ * *off* should be any non-zero value that the filesystem can use to ++ * identify the current point in the directory stream. It does not ++ * need to be the actual physical position. A value of zero is ++ * reserved to mean "from the beginning", and should therefore never ++ * be used (the first call to fuse_add_direntry should be passed the ++ * offset of the second directory entry). ++ * ++ * @param req request handle ++ * @param buf the point where the new entry will be added to the buffer ++ * @param bufsize remaining size of the buffer ++ * @param name the name of the entry ++ * @param stbuf the file attributes ++ * @param off the offset of the next entry ++ * @return the space needed for the entry ++ */ ++size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, ++ const char *name, const struct stat *stbuf, ++ off_t off); ++ ++/** ++ * Add a directory entry to the buffer with the attributes ++ * ++ * See documentation of `fuse_add_direntry()` for more details. ++ * ++ * @param req request handle ++ * @param buf the point where the new entry will be added to the buffer ++ * @param bufsize remaining size of the buffer ++ * @param name the name of the entry ++ * @param e the directory entry ++ * @param off the offset of the next entry ++ * @return the space needed for the entry ++ */ ++size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, ++ const char *name, ++ const struct fuse_entry_param *e, off_t off); ++ ++/** ++ * Reply to ask for data fetch and output buffer preparation. ioctl ++ * will be retried with the specified input data fetched and output ++ * buffer prepared. ++ * ++ * Possible requests: ++ * ioctl ++ * ++ * @param req request handle ++ * @param in_iov iovec specifying data to fetch from the caller ++ * @param in_count number of entries in in_iov ++ * @param out_iov iovec specifying addresses to write output to ++ * @param out_count number of entries in out_iov ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_ioctl_retry(fuse_req_t req, ++ const struct iovec *in_iov, size_t in_count, ++ const struct iovec *out_iov, size_t out_count); ++ ++/** ++ * Reply to finish ioctl ++ * ++ * Possible requests: ++ * ioctl ++ * ++ * @param req request handle ++ * @param result result to be passed to the caller ++ * @param buf buffer containing output data ++ * @param size length of output data ++ */ ++int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size); ++ ++/** ++ * Reply to finish ioctl with iov buffer ++ * ++ * Possible requests: ++ * ioctl ++ * ++ * @param req request handle ++ * @param result result to be passed to the caller ++ * @param iov the vector containing the data ++ * @param count the size of vector ++ */ ++int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, ++ int count); ++ ++/** ++ * Reply with poll result event mask ++ * ++ * @param req request handle ++ * @param revents poll result event mask ++ */ ++int fuse_reply_poll(fuse_req_t req, unsigned revents); ++ ++/** ++ * Reply with offset ++ * ++ * Possible requests: ++ * lseek ++ * ++ * @param req request handle ++ * @param off offset of next data or hole ++ * @return zero for success, -errno for failure to send reply ++ */ ++int fuse_reply_lseek(fuse_req_t req, off_t off); ++ ++/* ----------------------------------------------------------- * ++ * Notification * ++ * ----------------------------------------------------------- */ ++ ++/** ++ * Notify IO readiness event ++ * ++ * For more information, please read comment for poll operation. ++ * ++ * @param ph poll handle to notify IO readiness event for ++ */ ++int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph); ++ ++/** ++ * Notify to invalidate cache for an inode. ++ * ++ * Added in FUSE protocol version 7.12. If the kernel does not support ++ * this (or a newer) version, the function will return -ENOSYS and do ++ * nothing. ++ * ++ * If the filesystem has writeback caching enabled, invalidating an ++ * inode will first trigger a writeback of all dirty pages. The call ++ * will block until all writeback requests have completed and the ++ * inode has been invalidated. It will, however, not wait for ++ * completion of pending writeback requests that have been issued ++ * before. ++ * ++ * If there are no dirty pages, this function will never block. ++ * ++ * @param se the session object ++ * @param ino the inode number ++ * @param off the offset in the inode where to start invalidating ++ * or negative to invalidate attributes only ++ * @param len the amount of cache to invalidate or 0 for all ++ * @return zero for success, -errno for failure ++ */ ++int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, ++ off_t off, off_t len); ++ ++/** ++ * Notify to invalidate parent attributes and the dentry matching ++ * parent/name ++ * ++ * To avoid a deadlock this function must not be called in the ++ * execution path of a related filesytem operation or within any code ++ * that could hold a lock that could be needed to execute such an ++ * operation. As of kernel 4.18, a "related operation" is a lookup(), ++ * symlink(), mknod(), mkdir(), unlink(), rename(), link() or create() ++ * request for the parent, and a setattr(), unlink(), rmdir(), ++ * rename(), setxattr(), removexattr(), readdir() or readdirplus() ++ * request for the inode itself. ++ * ++ * When called correctly, this function will never block. ++ * ++ * Added in FUSE protocol version 7.12. If the kernel does not support ++ * this (or a newer) version, the function will return -ENOSYS and do ++ * nothing. ++ * ++ * @param se the session object ++ * @param parent inode number ++ * @param name file name ++ * @param namelen strlen() of file name ++ * @return zero for success, -errno for failure ++ */ ++int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, ++ const char *name, size_t namelen); ++ ++/** ++ * This function behaves like fuse_lowlevel_notify_inval_entry() with ++ * the following additional effect (at least as of Linux kernel 4.8): ++ * ++ * If the provided *child* inode matches the inode that is currently ++ * associated with the cached dentry, and if there are any inotify ++ * watches registered for the dentry, then the watchers are informed ++ * that the dentry has been deleted. ++ * ++ * To avoid a deadlock this function must not be called while ++ * executing a related filesytem operation or while holding a lock ++ * that could be needed to execute such an operation (see the ++ * description of fuse_lowlevel_notify_inval_entry() for more ++ * details). ++ * ++ * When called correctly, this function will never block. ++ * ++ * Added in FUSE protocol version 7.18. If the kernel does not support ++ * this (or a newer) version, the function will return -ENOSYS and do ++ * nothing. ++ * ++ * @param se the session object ++ * @param parent inode number ++ * @param child inode number ++ * @param name file name ++ * @param namelen strlen() of file name ++ * @return zero for success, -errno for failure ++ */ ++int fuse_lowlevel_notify_delete(struct fuse_session *se, ++ fuse_ino_t parent, fuse_ino_t child, ++ const char *name, size_t namelen); ++ ++/** ++ * Store data to the kernel buffers ++ * ++ * Synchronously store data in the kernel buffers belonging to the ++ * given inode. The stored data is marked up-to-date (no read will be ++ * performed against it, unless it's invalidated or evicted from the ++ * cache). ++ * ++ * If the stored data overflows the current file size, then the size ++ * is extended, similarly to a write(2) on the filesystem. ++ * ++ * If this function returns an error, then the store wasn't fully ++ * completed, but it may have been partially completed. ++ * ++ * Added in FUSE protocol version 7.15. If the kernel does not support ++ * this (or a newer) version, the function will return -ENOSYS and do ++ * nothing. ++ * ++ * @param se the session object ++ * @param ino the inode number ++ * @param offset the starting offset into the file to store to ++ * @param bufv buffer vector ++ * @param flags flags controlling the copy ++ * @return zero for success, -errno for failure ++ */ ++int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, ++ off_t offset, struct fuse_bufvec *bufv, ++ enum fuse_buf_copy_flags flags); ++/** ++ * Retrieve data from the kernel buffers ++ * ++ * Retrieve data in the kernel buffers belonging to the given inode. ++ * If successful then the retrieve_reply() method will be called with ++ * the returned data. ++ * ++ * Only present pages are returned in the retrieve reply. Retrieving ++ * stops when it finds a non-present page and only data prior to that ++ * is returned. ++ * ++ * If this function returns an error, then the retrieve will not be ++ * completed and no reply will be sent. ++ * ++ * This function doesn't change the dirty state of pages in the kernel ++ * buffer. For dirty pages the write() method will be called ++ * regardless of having been retrieved previously. ++ * ++ * Added in FUSE protocol version 7.15. If the kernel does not support ++ * this (or a newer) version, the function will return -ENOSYS and do ++ * nothing. ++ * ++ * @param se the session object ++ * @param ino the inode number ++ * @param size the number of bytes to retrieve ++ * @param offset the starting offset into the file to retrieve from ++ * @param cookie user data to supply to the reply callback ++ * @return zero for success, -errno for failure ++ */ ++int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, ++ size_t size, off_t offset, void *cookie); ++ ++ ++/* ----------------------------------------------------------- * ++ * Utility functions * ++ * ----------------------------------------------------------- */ ++ ++/** ++ * Get the userdata from the request ++ * ++ * @param req request handle ++ * @return the user data passed to fuse_session_new() ++ */ ++void *fuse_req_userdata(fuse_req_t req); ++ ++/** ++ * Get the context from the request ++ * ++ * The pointer returned by this function will only be valid for the ++ * request's lifetime ++ * ++ * @param req request handle ++ * @return the context structure ++ */ ++const struct fuse_ctx *fuse_req_ctx(fuse_req_t req); ++ ++/** ++ * Get the current supplementary group IDs for the specified request ++ * ++ * Similar to the getgroups(2) system call, except the return value is ++ * always the total number of group IDs, even if it is larger than the ++ * specified size. ++ * ++ * The current fuse kernel module in linux (as of 2.6.30) doesn't pass ++ * the group list to userspace, hence this function needs to parse ++ * "/proc/$TID/task/$TID/status" to get the group IDs. ++ * ++ * This feature may not be supported on all operating systems. In ++ * such a case this function will return -ENOSYS. ++ * ++ * @param req request handle ++ * @param size size of given array ++ * @param list array of group IDs to be filled in ++ * @return the total number of supplementary group IDs or -errno on failure ++ */ ++int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]); ++ ++/** ++ * Callback function for an interrupt ++ * ++ * @param req interrupted request ++ * @param data user data ++ */ ++typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data); ++ ++/** ++ * Register/unregister callback for an interrupt ++ * ++ * If an interrupt has already happened, then the callback function is ++ * called from within this function, hence it's not possible for ++ * interrupts to be lost. ++ * ++ * @param req request handle ++ * @param func the callback function or NULL for unregister ++ * @param data user data passed to the callback function ++ */ ++void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, ++ void *data); ++ ++/** ++ * Check if a request has already been interrupted ++ * ++ * @param req request handle ++ * @return 1 if the request has been interrupted, 0 otherwise ++ */ ++int fuse_req_interrupted(fuse_req_t req); ++ ++ ++/* ----------------------------------------------------------- * ++ * Inquiry functions * ++ * ----------------------------------------------------------- */ ++ ++/** ++ * Print low-level version information to stdout. ++ */ ++void fuse_lowlevel_version(void); ++ ++/** ++ * Print available low-level options to stdout. This is not an ++ * exhaustive list, but includes only those options that may be of ++ * interest to an end-user of a file system. ++ */ ++void fuse_lowlevel_help(void); ++ ++/** ++ * Print available options for `fuse_parse_cmdline()`. ++ */ ++void fuse_cmdline_help(void); ++ ++/* ----------------------------------------------------------- * ++ * Filesystem setup & teardown * ++ * ----------------------------------------------------------- */ ++ ++struct fuse_cmdline_opts { ++ int singlethread; ++ int foreground; ++ int debug; ++ int nodefault_subtype; ++ char *mountpoint; ++ int show_version; ++ int show_help; ++ int clone_fd; ++ unsigned int max_idle_threads; ++}; ++ ++/** ++ * Utility function to parse common options for simple file systems ++ * using the low-level API. A help text that describes the available ++ * options can be printed with `fuse_cmdline_help`. A single ++ * non-option argument is treated as the mountpoint. Multiple ++ * non-option arguments will result in an error. ++ * ++ * If neither -o subtype= or -o fsname= options are given, a new ++ * subtype option will be added and set to the basename of the program ++ * (the fsname will remain unset, and then defaults to "fuse"). ++ * ++ * Known options will be removed from *args*, unknown options will ++ * remain. ++ * ++ * @param args argument vector (input+output) ++ * @param opts output argument for parsed options ++ * @return 0 on success, -1 on failure ++ */ ++int fuse_parse_cmdline(struct fuse_args *args, ++ struct fuse_cmdline_opts *opts); ++ ++/** ++ * Create a low level session. ++ * ++ * Returns a session structure suitable for passing to ++ * fuse_session_mount() and fuse_session_loop(). ++ * ++ * This function accepts most file-system independent mount options ++ * (like context, nodev, ro - see mount(8)), as well as the general ++ * fuse mount options listed in mount.fuse(8) (e.g. -o allow_root and ++ * -o default_permissions, but not ``-o use_ino``). Instead of `-o ++ * debug`, debugging may also enabled with `-d` or `--debug`. ++ * ++ * If not all options are known, an error message is written to stderr ++ * and the function returns NULL. ++ * ++ * Option parsing skips argv[0], which is assumed to contain the ++ * program name. To prevent accidentally passing an option in ++ * argv[0], this element must always be present (even if no options ++ * are specified). It may be set to the empty string ('\0') if no ++ * reasonable value can be provided. ++ * ++ * @param args argument vector ++ * @param op the (low-level) filesystem operations ++ * @param op_size sizeof(struct fuse_lowlevel_ops) ++ * @param userdata user data ++ * ++ * @return the fuse session on success, NULL on failure ++ **/ ++struct fuse_session *fuse_session_new(struct fuse_args *args, ++ const struct fuse_lowlevel_ops *op, ++ size_t op_size, void *userdata); ++ ++/** ++ * Mount a FUSE file system. ++ * ++ * @param mountpoint the mount point path ++ * @param se session object ++ * ++ * @return 0 on success, -1 on failure. ++ **/ ++int fuse_session_mount(struct fuse_session *se, const char *mountpoint); ++ ++/** ++ * Enter a single threaded, blocking event loop. ++ * ++ * When the event loop terminates because the connection to the FUSE ++ * kernel module has been closed, this function returns zero. This ++ * happens when the filesystem is unmounted regularly (by the ++ * filesystem owner or root running the umount(8) or fusermount(1) ++ * command), or if connection is explicitly severed by writing ``1`` ++ * to the``abort`` file in ``/sys/fs/fuse/connections/NNN``. The only ++ * way to distinguish between these two conditions is to check if the ++ * filesystem is still mounted after the session loop returns. ++ * ++ * When some error occurs during request processing, the function ++ * returns a negated errno(3) value. ++ * ++ * If the loop has been terminated because of a signal handler ++ * installed by fuse_set_signal_handlers(), this function returns the ++ * (positive) signal value that triggered the exit. ++ * ++ * @param se the session ++ * @return 0, -errno, or a signal value ++ */ ++int fuse_session_loop(struct fuse_session *se); ++ ++/** ++ * Enter a multi-threaded event loop. ++ * ++ * For a description of the return value and the conditions when the ++ * event loop exits, refer to the documentation of ++ * fuse_session_loop(). ++ * ++ * @param se the session ++ * @param config session loop configuration ++ * @return see fuse_session_loop() ++ */ ++#if FUSE_USE_VERSION < 32 ++int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd); ++#define fuse_session_loop_mt(se, clone_fd) fuse_session_loop_mt_31(se, clone_fd) ++#else ++int fuse_session_loop_mt(struct fuse_session *se, struct fuse_loop_config *config); ++#endif ++ ++/** ++ * Flag a session as terminated. ++ * ++ * This function is invoked by the POSIX signal handlers, when ++ * registered using fuse_set_signal_handlers(). It will cause any ++ * running event loops to terminate on the next opportunity. ++ * ++ * @param se the session ++ */ ++void fuse_session_exit(struct fuse_session *se); ++ ++/** ++ * Reset the terminated flag of a session ++ * ++ * @param se the session ++ */ ++void fuse_session_reset(struct fuse_session *se); ++ ++/** ++ * Query the terminated flag of a session ++ * ++ * @param se the session ++ * @return 1 if exited, 0 if not exited ++ */ ++int fuse_session_exited(struct fuse_session *se); ++ ++/** ++ * Ensure that file system is unmounted. ++ * ++ * In regular operation, the file system is typically unmounted by the ++ * user calling umount(8) or fusermount(1), which then terminates the ++ * FUSE session loop. However, the session loop may also terminate as ++ * a result of an explicit call to fuse_session_exit() (e.g. by a ++ * signal handler installed by fuse_set_signal_handler()). In this ++ * case the filesystem remains mounted, but any attempt to access it ++ * will block (while the filesystem process is still running) or give ++ * an ESHUTDOWN error (after the filesystem process has terminated). ++ * ++ * If the communication channel with the FUSE kernel module is still ++ * open (i.e., if the session loop was terminated by an explicit call ++ * to fuse_session_exit()), this function will close it and unmount ++ * the filesystem. If the communication channel has been closed by the ++ * kernel, this method will do (almost) nothing. ++ * ++ * NOTE: The above semantics mean that if the connection to the kernel ++ * is terminated via the ``/sys/fs/fuse/connections/NNN/abort`` file, ++ * this method will *not* unmount the filesystem. ++ * ++ * @param se the session ++ */ ++void fuse_session_unmount(struct fuse_session *se); ++ ++/** ++ * Destroy a session ++ * ++ * @param se the session ++ */ ++void fuse_session_destroy(struct fuse_session *se); ++ ++/* ----------------------------------------------------------- * ++ * Custom event loop support * ++ * ----------------------------------------------------------- */ ++ ++/** ++ * Return file descriptor for communication with kernel. ++ * ++ * The file selector can be used to integrate FUSE with a custom event ++ * loop. Whenever data is available for reading on the provided fd, ++ * the event loop should call `fuse_session_receive_buf` followed by ++ * `fuse_session_process_buf` to process the request. ++ * ++ * The returned file descriptor is valid until `fuse_session_unmount` ++ * is called. ++ * ++ * @param se the session ++ * @return a file descriptor ++ */ ++int fuse_session_fd(struct fuse_session *se); ++ ++/** ++ * Process a raw request supplied in a generic buffer ++ * ++ * The fuse_buf may contain a memory buffer or a pipe file descriptor. ++ * ++ * @param se the session ++ * @param buf the fuse_buf containing the request ++ */ ++void fuse_session_process_buf(struct fuse_session *se, ++ const struct fuse_buf *buf); ++ ++/** ++ * Read a raw request from the kernel into the supplied buffer. ++ * ++ * Depending on file system options, system capabilities, and request ++ * size the request is either read into a memory buffer or spliced ++ * into a temporary pipe. ++ * ++ * @param se the session ++ * @param buf the fuse_buf to store the request in ++ * @return the actual size of the raw request, or -errno on error ++ */ ++int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* FUSE_LOWLEVEL_H_ */ +diff --git a/tools/virtiofsd/fuse_misc.h b/tools/virtiofsd/fuse_misc.h +new file mode 100644 +index 0000000000..2f6663ed7d +--- /dev/null ++++ b/tools/virtiofsd/fuse_misc.h +@@ -0,0 +1,59 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2007 Miklos Szeredi ++ ++ This program can be distributed under the terms of the GNU LGPLv2. ++ See the file COPYING.LIB ++*/ ++ ++#include ++ ++/* ++ Versioned symbols cannot be used in some cases because it ++ - confuse the dynamic linker in uClibc ++ - not supported on MacOSX (in MachO binary format) ++*/ ++#if (!defined(__UCLIBC__) && !defined(__APPLE__)) ++#define FUSE_SYMVER(x) __asm__(x) ++#else ++#define FUSE_SYMVER(x) ++#endif ++ ++#ifndef USE_UCLIBC ++#define fuse_mutex_init(mut) pthread_mutex_init(mut, NULL) ++#else ++/* Is this hack still needed? */ ++static inline void fuse_mutex_init(pthread_mutex_t *mut) ++{ ++ pthread_mutexattr_t attr; ++ pthread_mutexattr_init(&attr); ++ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); ++ pthread_mutex_init(mut, &attr); ++ pthread_mutexattr_destroy(&attr); ++} ++#endif ++ ++#ifdef HAVE_STRUCT_STAT_ST_ATIM ++/* Linux */ ++#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec) ++#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec) ++#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec) ++#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val) ++#define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctim.tv_nsec = (val) ++#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val) ++#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC) ++/* FreeBSD */ ++#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec) ++#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec) ++#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec) ++#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val) ++#define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctimespec.tv_nsec = (val) ++#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val) ++#else ++#define ST_ATIM_NSEC(stbuf) 0 ++#define ST_CTIM_NSEC(stbuf) 0 ++#define ST_MTIM_NSEC(stbuf) 0 ++#define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0) ++#define ST_CTIM_NSEC_SET(stbuf, val) do { } while (0) ++#define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0) ++#endif +diff --git a/tools/virtiofsd/fuse_opt.h b/tools/virtiofsd/fuse_opt.h +new file mode 100644 +index 0000000000..d8573e74fd +--- /dev/null ++++ b/tools/virtiofsd/fuse_opt.h +@@ -0,0 +1,271 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2007 Miklos Szeredi ++ ++ This program can be distributed under the terms of the GNU LGPLv2. ++ See the file COPYING.LIB. ++*/ ++ ++#ifndef FUSE_OPT_H_ ++#define FUSE_OPT_H_ ++ ++/** @file ++ * ++ * This file defines the option parsing interface of FUSE ++ */ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * Option description ++ * ++ * This structure describes a single option, and action associated ++ * with it, in case it matches. ++ * ++ * More than one such match may occur, in which case the action for ++ * each match is executed. ++ * ++ * There are three possible actions in case of a match: ++ * ++ * i) An integer (int or unsigned) variable determined by 'offset' is ++ * set to 'value' ++ * ++ * ii) The processing function is called, with 'value' as the key ++ * ++ * iii) An integer (any) or string (char *) variable determined by ++ * 'offset' is set to the value of an option parameter ++ * ++ * 'offset' should normally be either set to ++ * ++ * - 'offsetof(struct foo, member)' actions i) and iii) ++ * ++ * - -1 action ii) ++ * ++ * The 'offsetof()' macro is defined in the header. ++ * ++ * The template determines which options match, and also have an ++ * effect on the action. Normally the action is either i) or ii), but ++ * if a format is present in the template, then action iii) is ++ * performed. ++ * ++ * The types of templates are: ++ * ++ * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only ++ * themselves. Invalid values are "--" and anything beginning ++ * with "-o" ++ * ++ * 2) "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or ++ * the relevant option in a comma separated option list ++ * ++ * 3) "bar=", "--foo=", etc. These are variations of 1) and 2) ++ * which have a parameter ++ * ++ * 4) "bar=%s", "--foo=%lu", etc. Same matching as above but perform ++ * action iii). ++ * ++ * 5) "-x ", etc. Matches either "-xparam" or "-x param" as ++ * two separate arguments ++ * ++ * 6) "-x %s", etc. Combination of 4) and 5) ++ * ++ * If the format is "%s", memory is allocated for the string unlike with ++ * scanf(). The previous value (if non-NULL) stored at the this location is ++ * freed. ++ */ ++struct fuse_opt { ++ /** Matching template and optional parameter formatting */ ++ const char *templ; ++ ++ /** ++ * Offset of variable within 'data' parameter of fuse_opt_parse() ++ * or -1 ++ */ ++ unsigned long offset; ++ ++ /** ++ * Value to set the variable to, or to be passed as 'key' to the ++ * processing function. Ignored if template has a format ++ */ ++ int value; ++}; ++ ++/** ++ * Key option. In case of a match, the processing function will be ++ * called with the specified key. ++ */ ++#define FUSE_OPT_KEY(templ, key) { templ, -1U, key } ++ ++/** ++ * Last option. An array of 'struct fuse_opt' must end with a NULL ++ * template value ++ */ ++#define FUSE_OPT_END { NULL, 0, 0 } ++ ++/** ++ * Argument list ++ */ ++struct fuse_args { ++ /** Argument count */ ++ int argc; ++ ++ /** Argument vector. NULL terminated */ ++ char **argv; ++ ++ /** Is 'argv' allocated? */ ++ int allocated; ++}; ++ ++/** ++ * Initializer for 'struct fuse_args' ++ */ ++#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 } ++ ++/** ++ * Key value passed to the processing function if an option did not ++ * match any template ++ */ ++#define FUSE_OPT_KEY_OPT -1 ++ ++/** ++ * Key value passed to the processing function for all non-options ++ * ++ * Non-options are the arguments beginning with a character other than ++ * '-' or all arguments after the special '--' option ++ */ ++#define FUSE_OPT_KEY_NONOPT -2 ++ ++/** ++ * Special key value for options to keep ++ * ++ * Argument is not passed to processing function, but behave as if the ++ * processing function returned 1 ++ */ ++#define FUSE_OPT_KEY_KEEP -3 ++ ++/** ++ * Special key value for options to discard ++ * ++ * Argument is not passed to processing function, but behave as if the ++ * processing function returned zero ++ */ ++#define FUSE_OPT_KEY_DISCARD -4 ++ ++/** ++ * Processing function ++ * ++ * This function is called if ++ * - option did not match any 'struct fuse_opt' ++ * - argument is a non-option ++ * - option did match and offset was set to -1 ++ * ++ * The 'arg' parameter will always contain the whole argument or ++ * option including the parameter if exists. A two-argument option ++ * ("-x foo") is always converted to single argument option of the ++ * form "-xfoo" before this function is called. ++ * ++ * Options of the form '-ofoo' are passed to this function without the ++ * '-o' prefix. ++ * ++ * The return value of this function determines whether this argument ++ * is to be inserted into the output argument vector, or discarded. ++ * ++ * @param data is the user data passed to the fuse_opt_parse() function ++ * @param arg is the whole argument or option ++ * @param key determines why the processing function was called ++ * @param outargs the current output argument list ++ * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept ++ */ ++typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, ++ struct fuse_args *outargs); ++ ++/** ++ * Option parsing function ++ * ++ * If 'args' was returned from a previous call to fuse_opt_parse() or ++ * it was constructed from ++ * ++ * A NULL 'args' is equivalent to an empty argument vector ++ * ++ * A NULL 'opts' is equivalent to an 'opts' array containing a single ++ * end marker ++ * ++ * A NULL 'proc' is equivalent to a processing function always ++ * returning '1' ++ * ++ * @param args is the input and output argument list ++ * @param data is the user data ++ * @param opts is the option description array ++ * @param proc is the processing function ++ * @return -1 on error, 0 on success ++ */ ++int fuse_opt_parse(struct fuse_args *args, void *data, ++ const struct fuse_opt opts[], fuse_opt_proc_t proc); ++ ++/** ++ * Add an option to a comma separated option list ++ * ++ * @param opts is a pointer to an option list, may point to a NULL value ++ * @param opt is the option to add ++ * @return -1 on allocation error, 0 on success ++ */ ++int fuse_opt_add_opt(char **opts, const char *opt); ++ ++/** ++ * Add an option, escaping commas, to a comma separated option list ++ * ++ * @param opts is a pointer to an option list, may point to a NULL value ++ * @param opt is the option to add ++ * @return -1 on allocation error, 0 on success ++ */ ++int fuse_opt_add_opt_escaped(char **opts, const char *opt); ++ ++/** ++ * Add an argument to a NULL terminated argument vector ++ * ++ * @param args is the structure containing the current argument list ++ * @param arg is the new argument to add ++ * @return -1 on allocation error, 0 on success ++ */ ++int fuse_opt_add_arg(struct fuse_args *args, const char *arg); ++ ++/** ++ * Add an argument at the specified position in a NULL terminated ++ * argument vector ++ * ++ * Adds the argument to the N-th position. This is useful for adding ++ * options at the beginning of the array which must not come after the ++ * special '--' option. ++ * ++ * @param args is the structure containing the current argument list ++ * @param pos is the position at which to add the argument ++ * @param arg is the new argument to add ++ * @return -1 on allocation error, 0 on success ++ */ ++int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg); ++ ++/** ++ * Free the contents of argument list ++ * ++ * The structure itself is not freed ++ * ++ * @param args is the structure containing the argument list ++ */ ++void fuse_opt_free_args(struct fuse_args *args); ++ ++ ++/** ++ * Check if an option matches ++ * ++ * @param opts is the option description array ++ * @param opt is the option to match ++ * @return 1 if a match is found, 0 if not ++ */ ++int fuse_opt_match(const struct fuse_opt opts[], const char *opt); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* FUSE_OPT_H_ */ +diff --git a/tools/virtiofsd/passthrough_helpers.h b/tools/virtiofsd/passthrough_helpers.h +new file mode 100644 +index 0000000000..6b77c33600 +--- /dev/null ++++ b/tools/virtiofsd/passthrough_helpers.h +@@ -0,0 +1,76 @@ ++/* ++ * FUSE: Filesystem in Userspace ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE ++ */ ++ ++/* ++ * Creates files on the underlying file system in response to a FUSE_MKNOD ++ * operation ++ */ ++static int mknod_wrapper(int dirfd, const char *path, const char *link, ++ int mode, dev_t rdev) ++{ ++ int res; ++ ++ if (S_ISREG(mode)) { ++ res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode); ++ if (res >= 0) ++ res = close(res); ++ } else if (S_ISDIR(mode)) { ++ res = mkdirat(dirfd, path, mode); ++ } else if (S_ISLNK(mode) && link != NULL) { ++ res = symlinkat(link, dirfd, path); ++ } else if (S_ISFIFO(mode)) { ++ res = mkfifoat(dirfd, path, mode); ++#ifdef __FreeBSD__ ++ } else if (S_ISSOCK(mode)) { ++ struct sockaddr_un su; ++ int fd; ++ ++ if (strlen(path) >= sizeof(su.sun_path)) { ++ errno = ENAMETOOLONG; ++ return -1; ++ } ++ fd = socket(AF_UNIX, SOCK_STREAM, 0); ++ if (fd >= 0) { ++ /* ++ * We must bind the socket to the underlying file ++ * system to create the socket file, even though ++ * we'll never listen on this socket. ++ */ ++ su.sun_family = AF_UNIX; ++ strncpy(su.sun_path, path, sizeof(su.sun_path)); ++ res = bindat(dirfd, fd, (struct sockaddr*)&su, ++ sizeof(su)); ++ if (res == 0) ++ close(fd); ++ } else { ++ res = -1; ++ } ++#endif ++ } else { ++ res = mknodat(dirfd, path, mode, rdev); ++ } ++ ++ return res; ++} diff --git a/0011-virtiofsd-Pull-in-kernel-s-fuse.h.patch b/0011-virtiofsd-Pull-in-kernel-s-fuse.h.patch new file mode 100644 index 0000000..fbf4e25 --- /dev/null +++ b/0011-virtiofsd-Pull-in-kernel-s-fuse.h.patch @@ -0,0 +1,929 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:00:40 +0000 +Subject: [PATCH] virtiofsd: Pull in kernel's fuse.h +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Update scripts/update-linux-headers.sh to add fuse.h and +use it to pull in fuse.h from the kernel; from v5.5-rc1 + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit a62a9e192bc5f0aa0bc076b51db5a069add87c78) +--- + include/standard-headers/linux/fuse.h | 891 ++++++++++++++++++++++++++ + scripts/update-linux-headers.sh | 1 + + 2 files changed, 892 insertions(+) + create mode 100644 include/standard-headers/linux/fuse.h + +diff --git a/include/standard-headers/linux/fuse.h b/include/standard-headers/linux/fuse.h +new file mode 100644 +index 0000000000..f4df0a40f6 +--- /dev/null ++++ b/include/standard-headers/linux/fuse.h +@@ -0,0 +1,891 @@ ++/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */ ++/* ++ This file defines the kernel interface of FUSE ++ Copyright (C) 2001-2008 Miklos Szeredi ++ ++ This program can be distributed under the terms of the GNU GPL. ++ See the file COPYING. ++ ++ This -- and only this -- header file may also be distributed under ++ the terms of the BSD Licence as follows: ++ ++ Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved. ++ ++ Redistribution and use in source and binary forms, with or without ++ modification, are permitted provided that the following conditions ++ are met: ++ 1. Redistributions of source code must retain the above copyright ++ notice, this list of conditions and the following disclaimer. ++ 2. Redistributions in binary form must reproduce the above copyright ++ notice, this list of conditions and the following disclaimer in the ++ documentation and/or other materials provided with the distribution. ++ ++ THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND ++ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE ++ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ SUCH DAMAGE. ++*/ ++ ++/* ++ * This file defines the kernel interface of FUSE ++ * ++ * Protocol changelog: ++ * ++ * 7.1: ++ * - add the following messages: ++ * FUSE_SETATTR, FUSE_SYMLINK, FUSE_MKNOD, FUSE_MKDIR, FUSE_UNLINK, ++ * FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, FUSE_OPEN, FUSE_READ, FUSE_WRITE, ++ * FUSE_RELEASE, FUSE_FSYNC, FUSE_FLUSH, FUSE_SETXATTR, FUSE_GETXATTR, ++ * FUSE_LISTXATTR, FUSE_REMOVEXATTR, FUSE_OPENDIR, FUSE_READDIR, ++ * FUSE_RELEASEDIR ++ * - add padding to messages to accommodate 32-bit servers on 64-bit kernels ++ * ++ * 7.2: ++ * - add FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE flags ++ * - add FUSE_FSYNCDIR message ++ * ++ * 7.3: ++ * - add FUSE_ACCESS message ++ * - add FUSE_CREATE message ++ * - add filehandle to fuse_setattr_in ++ * ++ * 7.4: ++ * - add frsize to fuse_kstatfs ++ * - clean up request size limit checking ++ * ++ * 7.5: ++ * - add flags and max_write to fuse_init_out ++ * ++ * 7.6: ++ * - add max_readahead to fuse_init_in and fuse_init_out ++ * ++ * 7.7: ++ * - add FUSE_INTERRUPT message ++ * - add POSIX file lock support ++ * ++ * 7.8: ++ * - add lock_owner and flags fields to fuse_release_in ++ * - add FUSE_BMAP message ++ * - add FUSE_DESTROY message ++ * ++ * 7.9: ++ * - new fuse_getattr_in input argument of GETATTR ++ * - add lk_flags in fuse_lk_in ++ * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in ++ * - add blksize field to fuse_attr ++ * - add file flags field to fuse_read_in and fuse_write_in ++ * - Add ATIME_NOW and MTIME_NOW flags to fuse_setattr_in ++ * ++ * 7.10 ++ * - add nonseekable open flag ++ * ++ * 7.11 ++ * - add IOCTL message ++ * - add unsolicited notification support ++ * - add POLL message and NOTIFY_POLL notification ++ * ++ * 7.12 ++ * - add umask flag to input argument of create, mknod and mkdir ++ * - add notification messages for invalidation of inodes and ++ * directory entries ++ * ++ * 7.13 ++ * - make max number of background requests and congestion threshold ++ * tunables ++ * ++ * 7.14 ++ * - add splice support to fuse device ++ * ++ * 7.15 ++ * - add store notify ++ * - add retrieve notify ++ * ++ * 7.16 ++ * - add BATCH_FORGET request ++ * - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct ++ * fuse_ioctl_iovec' instead of ambiguous 'struct iovec' ++ * - add FUSE_IOCTL_32BIT flag ++ * ++ * 7.17 ++ * - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK ++ * ++ * 7.18 ++ * - add FUSE_IOCTL_DIR flag ++ * - add FUSE_NOTIFY_DELETE ++ * ++ * 7.19 ++ * - add FUSE_FALLOCATE ++ * ++ * 7.20 ++ * - add FUSE_AUTO_INVAL_DATA ++ * ++ * 7.21 ++ * - add FUSE_READDIRPLUS ++ * - send the requested events in POLL request ++ * ++ * 7.22 ++ * - add FUSE_ASYNC_DIO ++ * ++ * 7.23 ++ * - add FUSE_WRITEBACK_CACHE ++ * - add time_gran to fuse_init_out ++ * - add reserved space to fuse_init_out ++ * - add FATTR_CTIME ++ * - add ctime and ctimensec to fuse_setattr_in ++ * - add FUSE_RENAME2 request ++ * - add FUSE_NO_OPEN_SUPPORT flag ++ * ++ * 7.24 ++ * - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support ++ * ++ * 7.25 ++ * - add FUSE_PARALLEL_DIROPS ++ * ++ * 7.26 ++ * - add FUSE_HANDLE_KILLPRIV ++ * - add FUSE_POSIX_ACL ++ * ++ * 7.27 ++ * - add FUSE_ABORT_ERROR ++ * ++ * 7.28 ++ * - add FUSE_COPY_FILE_RANGE ++ * - add FOPEN_CACHE_DIR ++ * - add FUSE_MAX_PAGES, add max_pages to init_out ++ * - add FUSE_CACHE_SYMLINKS ++ * ++ * 7.29 ++ * - add FUSE_NO_OPENDIR_SUPPORT flag ++ * ++ * 7.30 ++ * - add FUSE_EXPLICIT_INVAL_DATA ++ * - add FUSE_IOCTL_COMPAT_X32 ++ * ++ * 7.31 ++ * - add FUSE_WRITE_KILL_PRIV flag ++ * - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING ++ * - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag ++ */ ++ ++#ifndef _LINUX_FUSE_H ++#define _LINUX_FUSE_H ++ ++#include ++ ++/* ++ * Version negotiation: ++ * ++ * Both the kernel and userspace send the version they support in the ++ * INIT request and reply respectively. ++ * ++ * If the major versions match then both shall use the smallest ++ * of the two minor versions for communication. ++ * ++ * If the kernel supports a larger major version, then userspace shall ++ * reply with the major version it supports, ignore the rest of the ++ * INIT message and expect a new INIT message from the kernel with a ++ * matching major version. ++ * ++ * If the library supports a larger major version, then it shall fall ++ * back to the major protocol version sent by the kernel for ++ * communication and reply with that major version (and an arbitrary ++ * supported minor version). ++ */ ++ ++/** Version number of this interface */ ++#define FUSE_KERNEL_VERSION 7 ++ ++/** Minor version number of this interface */ ++#define FUSE_KERNEL_MINOR_VERSION 31 ++ ++/** The node ID of the root inode */ ++#define FUSE_ROOT_ID 1 ++ ++/* Make sure all structures are padded to 64bit boundary, so 32bit ++ userspace works under 64bit kernels */ ++ ++struct fuse_attr { ++ uint64_t ino; ++ uint64_t size; ++ uint64_t blocks; ++ uint64_t atime; ++ uint64_t mtime; ++ uint64_t ctime; ++ uint32_t atimensec; ++ uint32_t mtimensec; ++ uint32_t ctimensec; ++ uint32_t mode; ++ uint32_t nlink; ++ uint32_t uid; ++ uint32_t gid; ++ uint32_t rdev; ++ uint32_t blksize; ++ uint32_t padding; ++}; ++ ++struct fuse_kstatfs { ++ uint64_t blocks; ++ uint64_t bfree; ++ uint64_t bavail; ++ uint64_t files; ++ uint64_t ffree; ++ uint32_t bsize; ++ uint32_t namelen; ++ uint32_t frsize; ++ uint32_t padding; ++ uint32_t spare[6]; ++}; ++ ++struct fuse_file_lock { ++ uint64_t start; ++ uint64_t end; ++ uint32_t type; ++ uint32_t pid; /* tgid */ ++}; ++ ++/** ++ * Bitmasks for fuse_setattr_in.valid ++ */ ++#define FATTR_MODE (1 << 0) ++#define FATTR_UID (1 << 1) ++#define FATTR_GID (1 << 2) ++#define FATTR_SIZE (1 << 3) ++#define FATTR_ATIME (1 << 4) ++#define FATTR_MTIME (1 << 5) ++#define FATTR_FH (1 << 6) ++#define FATTR_ATIME_NOW (1 << 7) ++#define FATTR_MTIME_NOW (1 << 8) ++#define FATTR_LOCKOWNER (1 << 9) ++#define FATTR_CTIME (1 << 10) ++ ++/** ++ * Flags returned by the OPEN request ++ * ++ * FOPEN_DIRECT_IO: bypass page cache for this open file ++ * FOPEN_KEEP_CACHE: don't invalidate the data cache on open ++ * FOPEN_NONSEEKABLE: the file is not seekable ++ * FOPEN_CACHE_DIR: allow caching this directory ++ * FOPEN_STREAM: the file is stream-like (no file position at all) ++ */ ++#define FOPEN_DIRECT_IO (1 << 0) ++#define FOPEN_KEEP_CACHE (1 << 1) ++#define FOPEN_NONSEEKABLE (1 << 2) ++#define FOPEN_CACHE_DIR (1 << 3) ++#define FOPEN_STREAM (1 << 4) ++ ++/** ++ * INIT request/reply flags ++ * ++ * FUSE_ASYNC_READ: asynchronous read requests ++ * FUSE_POSIX_LOCKS: remote locking for POSIX file locks ++ * FUSE_FILE_OPS: kernel sends file handle for fstat, etc... (not yet supported) ++ * FUSE_ATOMIC_O_TRUNC: handles the O_TRUNC open flag in the filesystem ++ * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".." ++ * FUSE_BIG_WRITES: filesystem can handle write size larger than 4kB ++ * FUSE_DONT_MASK: don't apply umask to file mode on create operations ++ * FUSE_SPLICE_WRITE: kernel supports splice write on the device ++ * FUSE_SPLICE_MOVE: kernel supports splice move on the device ++ * FUSE_SPLICE_READ: kernel supports splice read on the device ++ * FUSE_FLOCK_LOCKS: remote locking for BSD style file locks ++ * FUSE_HAS_IOCTL_DIR: kernel supports ioctl on directories ++ * FUSE_AUTO_INVAL_DATA: automatically invalidate cached pages ++ * FUSE_DO_READDIRPLUS: do READDIRPLUS (READDIR+LOOKUP in one) ++ * FUSE_READDIRPLUS_AUTO: adaptive readdirplus ++ * FUSE_ASYNC_DIO: asynchronous direct I/O submission ++ * FUSE_WRITEBACK_CACHE: use writeback cache for buffered writes ++ * FUSE_NO_OPEN_SUPPORT: kernel supports zero-message opens ++ * FUSE_PARALLEL_DIROPS: allow parallel lookups and readdir ++ * FUSE_HANDLE_KILLPRIV: fs handles killing suid/sgid/cap on write/chown/trunc ++ * FUSE_POSIX_ACL: filesystem supports posix acls ++ * FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED ++ * FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages ++ * FUSE_CACHE_SYMLINKS: cache READLINK responses ++ * FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir ++ * FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request ++ * FUSE_MAP_ALIGNMENT: map_alignment field is valid ++ */ ++#define FUSE_ASYNC_READ (1 << 0) ++#define FUSE_POSIX_LOCKS (1 << 1) ++#define FUSE_FILE_OPS (1 << 2) ++#define FUSE_ATOMIC_O_TRUNC (1 << 3) ++#define FUSE_EXPORT_SUPPORT (1 << 4) ++#define FUSE_BIG_WRITES (1 << 5) ++#define FUSE_DONT_MASK (1 << 6) ++#define FUSE_SPLICE_WRITE (1 << 7) ++#define FUSE_SPLICE_MOVE (1 << 8) ++#define FUSE_SPLICE_READ (1 << 9) ++#define FUSE_FLOCK_LOCKS (1 << 10) ++#define FUSE_HAS_IOCTL_DIR (1 << 11) ++#define FUSE_AUTO_INVAL_DATA (1 << 12) ++#define FUSE_DO_READDIRPLUS (1 << 13) ++#define FUSE_READDIRPLUS_AUTO (1 << 14) ++#define FUSE_ASYNC_DIO (1 << 15) ++#define FUSE_WRITEBACK_CACHE (1 << 16) ++#define FUSE_NO_OPEN_SUPPORT (1 << 17) ++#define FUSE_PARALLEL_DIROPS (1 << 18) ++#define FUSE_HANDLE_KILLPRIV (1 << 19) ++#define FUSE_POSIX_ACL (1 << 20) ++#define FUSE_ABORT_ERROR (1 << 21) ++#define FUSE_MAX_PAGES (1 << 22) ++#define FUSE_CACHE_SYMLINKS (1 << 23) ++#define FUSE_NO_OPENDIR_SUPPORT (1 << 24) ++#define FUSE_EXPLICIT_INVAL_DATA (1 << 25) ++#define FUSE_MAP_ALIGNMENT (1 << 26) ++ ++/** ++ * CUSE INIT request/reply flags ++ * ++ * CUSE_UNRESTRICTED_IOCTL: use unrestricted ioctl ++ */ ++#define CUSE_UNRESTRICTED_IOCTL (1 << 0) ++ ++/** ++ * Release flags ++ */ ++#define FUSE_RELEASE_FLUSH (1 << 0) ++#define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1) ++ ++/** ++ * Getattr flags ++ */ ++#define FUSE_GETATTR_FH (1 << 0) ++ ++/** ++ * Lock flags ++ */ ++#define FUSE_LK_FLOCK (1 << 0) ++ ++/** ++ * WRITE flags ++ * ++ * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed ++ * FUSE_WRITE_LOCKOWNER: lock_owner field is valid ++ * FUSE_WRITE_KILL_PRIV: kill suid and sgid bits ++ */ ++#define FUSE_WRITE_CACHE (1 << 0) ++#define FUSE_WRITE_LOCKOWNER (1 << 1) ++#define FUSE_WRITE_KILL_PRIV (1 << 2) ++ ++/** ++ * Read flags ++ */ ++#define FUSE_READ_LOCKOWNER (1 << 1) ++ ++/** ++ * Ioctl flags ++ * ++ * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine ++ * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed ++ * FUSE_IOCTL_RETRY: retry with new iovecs ++ * FUSE_IOCTL_32BIT: 32bit ioctl ++ * FUSE_IOCTL_DIR: is a directory ++ * FUSE_IOCTL_COMPAT_X32: x32 compat ioctl on 64bit machine (64bit time_t) ++ * ++ * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs ++ */ ++#define FUSE_IOCTL_COMPAT (1 << 0) ++#define FUSE_IOCTL_UNRESTRICTED (1 << 1) ++#define FUSE_IOCTL_RETRY (1 << 2) ++#define FUSE_IOCTL_32BIT (1 << 3) ++#define FUSE_IOCTL_DIR (1 << 4) ++#define FUSE_IOCTL_COMPAT_X32 (1 << 5) ++ ++#define FUSE_IOCTL_MAX_IOV 256 ++ ++/** ++ * Poll flags ++ * ++ * FUSE_POLL_SCHEDULE_NOTIFY: request poll notify ++ */ ++#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0) ++ ++/** ++ * Fsync flags ++ * ++ * FUSE_FSYNC_FDATASYNC: Sync data only, not metadata ++ */ ++#define FUSE_FSYNC_FDATASYNC (1 << 0) ++ ++enum fuse_opcode { ++ FUSE_LOOKUP = 1, ++ FUSE_FORGET = 2, /* no reply */ ++ FUSE_GETATTR = 3, ++ FUSE_SETATTR = 4, ++ FUSE_READLINK = 5, ++ FUSE_SYMLINK = 6, ++ FUSE_MKNOD = 8, ++ FUSE_MKDIR = 9, ++ FUSE_UNLINK = 10, ++ FUSE_RMDIR = 11, ++ FUSE_RENAME = 12, ++ FUSE_LINK = 13, ++ FUSE_OPEN = 14, ++ FUSE_READ = 15, ++ FUSE_WRITE = 16, ++ FUSE_STATFS = 17, ++ FUSE_RELEASE = 18, ++ FUSE_FSYNC = 20, ++ FUSE_SETXATTR = 21, ++ FUSE_GETXATTR = 22, ++ FUSE_LISTXATTR = 23, ++ FUSE_REMOVEXATTR = 24, ++ FUSE_FLUSH = 25, ++ FUSE_INIT = 26, ++ FUSE_OPENDIR = 27, ++ FUSE_READDIR = 28, ++ FUSE_RELEASEDIR = 29, ++ FUSE_FSYNCDIR = 30, ++ FUSE_GETLK = 31, ++ FUSE_SETLK = 32, ++ FUSE_SETLKW = 33, ++ FUSE_ACCESS = 34, ++ FUSE_CREATE = 35, ++ FUSE_INTERRUPT = 36, ++ FUSE_BMAP = 37, ++ FUSE_DESTROY = 38, ++ FUSE_IOCTL = 39, ++ FUSE_POLL = 40, ++ FUSE_NOTIFY_REPLY = 41, ++ FUSE_BATCH_FORGET = 42, ++ FUSE_FALLOCATE = 43, ++ FUSE_READDIRPLUS = 44, ++ FUSE_RENAME2 = 45, ++ FUSE_LSEEK = 46, ++ FUSE_COPY_FILE_RANGE = 47, ++ FUSE_SETUPMAPPING = 48, ++ FUSE_REMOVEMAPPING = 49, ++ ++ /* CUSE specific operations */ ++ CUSE_INIT = 4096, ++ ++ /* Reserved opcodes: helpful to detect structure endian-ness */ ++ CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */ ++ FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */ ++}; ++ ++enum fuse_notify_code { ++ FUSE_NOTIFY_POLL = 1, ++ FUSE_NOTIFY_INVAL_INODE = 2, ++ FUSE_NOTIFY_INVAL_ENTRY = 3, ++ FUSE_NOTIFY_STORE = 4, ++ FUSE_NOTIFY_RETRIEVE = 5, ++ FUSE_NOTIFY_DELETE = 6, ++ FUSE_NOTIFY_CODE_MAX, ++}; ++ ++/* The read buffer is required to be at least 8k, but may be much larger */ ++#define FUSE_MIN_READ_BUFFER 8192 ++ ++#define FUSE_COMPAT_ENTRY_OUT_SIZE 120 ++ ++struct fuse_entry_out { ++ uint64_t nodeid; /* Inode ID */ ++ uint64_t generation; /* Inode generation: nodeid:gen must ++ be unique for the fs's lifetime */ ++ uint64_t entry_valid; /* Cache timeout for the name */ ++ uint64_t attr_valid; /* Cache timeout for the attributes */ ++ uint32_t entry_valid_nsec; ++ uint32_t attr_valid_nsec; ++ struct fuse_attr attr; ++}; ++ ++struct fuse_forget_in { ++ uint64_t nlookup; ++}; ++ ++struct fuse_forget_one { ++ uint64_t nodeid; ++ uint64_t nlookup; ++}; ++ ++struct fuse_batch_forget_in { ++ uint32_t count; ++ uint32_t dummy; ++}; ++ ++struct fuse_getattr_in { ++ uint32_t getattr_flags; ++ uint32_t dummy; ++ uint64_t fh; ++}; ++ ++#define FUSE_COMPAT_ATTR_OUT_SIZE 96 ++ ++struct fuse_attr_out { ++ uint64_t attr_valid; /* Cache timeout for the attributes */ ++ uint32_t attr_valid_nsec; ++ uint32_t dummy; ++ struct fuse_attr attr; ++}; ++ ++#define FUSE_COMPAT_MKNOD_IN_SIZE 8 ++ ++struct fuse_mknod_in { ++ uint32_t mode; ++ uint32_t rdev; ++ uint32_t umask; ++ uint32_t padding; ++}; ++ ++struct fuse_mkdir_in { ++ uint32_t mode; ++ uint32_t umask; ++}; ++ ++struct fuse_rename_in { ++ uint64_t newdir; ++}; ++ ++struct fuse_rename2_in { ++ uint64_t newdir; ++ uint32_t flags; ++ uint32_t padding; ++}; ++ ++struct fuse_link_in { ++ uint64_t oldnodeid; ++}; ++ ++struct fuse_setattr_in { ++ uint32_t valid; ++ uint32_t padding; ++ uint64_t fh; ++ uint64_t size; ++ uint64_t lock_owner; ++ uint64_t atime; ++ uint64_t mtime; ++ uint64_t ctime; ++ uint32_t atimensec; ++ uint32_t mtimensec; ++ uint32_t ctimensec; ++ uint32_t mode; ++ uint32_t unused4; ++ uint32_t uid; ++ uint32_t gid; ++ uint32_t unused5; ++}; ++ ++struct fuse_open_in { ++ uint32_t flags; ++ uint32_t unused; ++}; ++ ++struct fuse_create_in { ++ uint32_t flags; ++ uint32_t mode; ++ uint32_t umask; ++ uint32_t padding; ++}; ++ ++struct fuse_open_out { ++ uint64_t fh; ++ uint32_t open_flags; ++ uint32_t padding; ++}; ++ ++struct fuse_release_in { ++ uint64_t fh; ++ uint32_t flags; ++ uint32_t release_flags; ++ uint64_t lock_owner; ++}; ++ ++struct fuse_flush_in { ++ uint64_t fh; ++ uint32_t unused; ++ uint32_t padding; ++ uint64_t lock_owner; ++}; ++ ++struct fuse_read_in { ++ uint64_t fh; ++ uint64_t offset; ++ uint32_t size; ++ uint32_t read_flags; ++ uint64_t lock_owner; ++ uint32_t flags; ++ uint32_t padding; ++}; ++ ++#define FUSE_COMPAT_WRITE_IN_SIZE 24 ++ ++struct fuse_write_in { ++ uint64_t fh; ++ uint64_t offset; ++ uint32_t size; ++ uint32_t write_flags; ++ uint64_t lock_owner; ++ uint32_t flags; ++ uint32_t padding; ++}; ++ ++struct fuse_write_out { ++ uint32_t size; ++ uint32_t padding; ++}; ++ ++#define FUSE_COMPAT_STATFS_SIZE 48 ++ ++struct fuse_statfs_out { ++ struct fuse_kstatfs st; ++}; ++ ++struct fuse_fsync_in { ++ uint64_t fh; ++ uint32_t fsync_flags; ++ uint32_t padding; ++}; ++ ++struct fuse_setxattr_in { ++ uint32_t size; ++ uint32_t flags; ++}; ++ ++struct fuse_getxattr_in { ++ uint32_t size; ++ uint32_t padding; ++}; ++ ++struct fuse_getxattr_out { ++ uint32_t size; ++ uint32_t padding; ++}; ++ ++struct fuse_lk_in { ++ uint64_t fh; ++ uint64_t owner; ++ struct fuse_file_lock lk; ++ uint32_t lk_flags; ++ uint32_t padding; ++}; ++ ++struct fuse_lk_out { ++ struct fuse_file_lock lk; ++}; ++ ++struct fuse_access_in { ++ uint32_t mask; ++ uint32_t padding; ++}; ++ ++struct fuse_init_in { ++ uint32_t major; ++ uint32_t minor; ++ uint32_t max_readahead; ++ uint32_t flags; ++}; ++ ++#define FUSE_COMPAT_INIT_OUT_SIZE 8 ++#define FUSE_COMPAT_22_INIT_OUT_SIZE 24 ++ ++struct fuse_init_out { ++ uint32_t major; ++ uint32_t minor; ++ uint32_t max_readahead; ++ uint32_t flags; ++ uint16_t max_background; ++ uint16_t congestion_threshold; ++ uint32_t max_write; ++ uint32_t time_gran; ++ uint16_t max_pages; ++ uint16_t map_alignment; ++ uint32_t unused[8]; ++}; ++ ++#define CUSE_INIT_INFO_MAX 4096 ++ ++struct cuse_init_in { ++ uint32_t major; ++ uint32_t minor; ++ uint32_t unused; ++ uint32_t flags; ++}; ++ ++struct cuse_init_out { ++ uint32_t major; ++ uint32_t minor; ++ uint32_t unused; ++ uint32_t flags; ++ uint32_t max_read; ++ uint32_t max_write; ++ uint32_t dev_major; /* chardev major */ ++ uint32_t dev_minor; /* chardev minor */ ++ uint32_t spare[10]; ++}; ++ ++struct fuse_interrupt_in { ++ uint64_t unique; ++}; ++ ++struct fuse_bmap_in { ++ uint64_t block; ++ uint32_t blocksize; ++ uint32_t padding; ++}; ++ ++struct fuse_bmap_out { ++ uint64_t block; ++}; ++ ++struct fuse_ioctl_in { ++ uint64_t fh; ++ uint32_t flags; ++ uint32_t cmd; ++ uint64_t arg; ++ uint32_t in_size; ++ uint32_t out_size; ++}; ++ ++struct fuse_ioctl_iovec { ++ uint64_t base; ++ uint64_t len; ++}; ++ ++struct fuse_ioctl_out { ++ int32_t result; ++ uint32_t flags; ++ uint32_t in_iovs; ++ uint32_t out_iovs; ++}; ++ ++struct fuse_poll_in { ++ uint64_t fh; ++ uint64_t kh; ++ uint32_t flags; ++ uint32_t events; ++}; ++ ++struct fuse_poll_out { ++ uint32_t revents; ++ uint32_t padding; ++}; ++ ++struct fuse_notify_poll_wakeup_out { ++ uint64_t kh; ++}; ++ ++struct fuse_fallocate_in { ++ uint64_t fh; ++ uint64_t offset; ++ uint64_t length; ++ uint32_t mode; ++ uint32_t padding; ++}; ++ ++struct fuse_in_header { ++ uint32_t len; ++ uint32_t opcode; ++ uint64_t unique; ++ uint64_t nodeid; ++ uint32_t uid; ++ uint32_t gid; ++ uint32_t pid; ++ uint32_t padding; ++}; ++ ++struct fuse_out_header { ++ uint32_t len; ++ int32_t error; ++ uint64_t unique; ++}; ++ ++struct fuse_dirent { ++ uint64_t ino; ++ uint64_t off; ++ uint32_t namelen; ++ uint32_t type; ++ char name[]; ++}; ++ ++#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) ++#define FUSE_DIRENT_ALIGN(x) \ ++ (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1)) ++#define FUSE_DIRENT_SIZE(d) \ ++ FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) ++ ++struct fuse_direntplus { ++ struct fuse_entry_out entry_out; ++ struct fuse_dirent dirent; ++}; ++ ++#define FUSE_NAME_OFFSET_DIRENTPLUS \ ++ offsetof(struct fuse_direntplus, dirent.name) ++#define FUSE_DIRENTPLUS_SIZE(d) \ ++ FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen) ++ ++struct fuse_notify_inval_inode_out { ++ uint64_t ino; ++ int64_t off; ++ int64_t len; ++}; ++ ++struct fuse_notify_inval_entry_out { ++ uint64_t parent; ++ uint32_t namelen; ++ uint32_t padding; ++}; ++ ++struct fuse_notify_delete_out { ++ uint64_t parent; ++ uint64_t child; ++ uint32_t namelen; ++ uint32_t padding; ++}; ++ ++struct fuse_notify_store_out { ++ uint64_t nodeid; ++ uint64_t offset; ++ uint32_t size; ++ uint32_t padding; ++}; ++ ++struct fuse_notify_retrieve_out { ++ uint64_t notify_unique; ++ uint64_t nodeid; ++ uint64_t offset; ++ uint32_t size; ++ uint32_t padding; ++}; ++ ++/* Matches the size of fuse_write_in */ ++struct fuse_notify_retrieve_in { ++ uint64_t dummy1; ++ uint64_t offset; ++ uint32_t size; ++ uint32_t dummy2; ++ uint64_t dummy3; ++ uint64_t dummy4; ++}; ++ ++/* Device ioctls: */ ++#define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t) ++ ++struct fuse_lseek_in { ++ uint64_t fh; ++ uint64_t offset; ++ uint32_t whence; ++ uint32_t padding; ++}; ++ ++struct fuse_lseek_out { ++ uint64_t offset; ++}; ++ ++struct fuse_copy_file_range_in { ++ uint64_t fh_in; ++ uint64_t off_in; ++ uint64_t nodeid_out; ++ uint64_t fh_out; ++ uint64_t off_out; ++ uint64_t len; ++ uint64_t flags; ++}; ++ ++#endif /* _LINUX_FUSE_H */ +diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh +index f76d77363b..29c27f4681 100755 +--- a/scripts/update-linux-headers.sh ++++ b/scripts/update-linux-headers.sh +@@ -186,6 +186,7 @@ rm -rf "$output/include/standard-headers/linux" + mkdir -p "$output/include/standard-headers/linux" + for i in "$tmpdir"/include/linux/*virtio*.h \ + "$tmpdir/include/linux/qemu_fw_cfg.h" \ ++ "$tmpdir/include/linux/fuse.h" \ + "$tmpdir/include/linux/input.h" \ + "$tmpdir/include/linux/input-event-codes.h" \ + "$tmpdir/include/linux/pci_regs.h" \ diff --git a/0012-virtiofsd-Add-auxiliary-.c-s.patch b/0012-virtiofsd-Add-auxiliary-.c-s.patch new file mode 100644 index 0000000..645e280 --- /dev/null +++ b/0012-virtiofsd-Add-auxiliary-.c-s.patch @@ -0,0 +1,1371 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:00:41 +0000 +Subject: [PATCH] virtiofsd: Add auxiliary .c's +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add most of the non-main .c files we need from upstream fuse-3.8.0 + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit ffcf8d9f8649c6e56b1193bbbc9c9f7388920043) +--- + tools/virtiofsd/buffer.c | 321 ++++++++++++++++++++++++ + tools/virtiofsd/fuse_log.c | 40 +++ + tools/virtiofsd/fuse_opt.c | 423 +++++++++++++++++++++++++++++++ + tools/virtiofsd/fuse_signals.c | 91 +++++++ + tools/virtiofsd/helper.c | 440 +++++++++++++++++++++++++++++++++ + 5 files changed, 1315 insertions(+) + create mode 100644 tools/virtiofsd/buffer.c + create mode 100644 tools/virtiofsd/fuse_log.c + create mode 100644 tools/virtiofsd/fuse_opt.c + create mode 100644 tools/virtiofsd/fuse_signals.c + create mode 100644 tools/virtiofsd/helper.c + +diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c +new file mode 100644 +index 0000000000..5ab9b87455 +--- /dev/null ++++ b/tools/virtiofsd/buffer.c +@@ -0,0 +1,321 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2010 Miklos Szeredi ++ ++ Functions for dealing with `struct fuse_buf` and `struct ++ fuse_bufvec`. ++ ++ This program can be distributed under the terms of the GNU LGPLv2. ++ See the file COPYING.LIB ++*/ ++ ++#define _GNU_SOURCE ++ ++#include "config.h" ++#include "fuse_i.h" ++#include "fuse_lowlevel.h" ++#include ++#include ++#include ++#include ++ ++size_t fuse_buf_size(const struct fuse_bufvec *bufv) ++{ ++ size_t i; ++ size_t size = 0; ++ ++ for (i = 0; i < bufv->count; i++) { ++ if (bufv->buf[i].size == SIZE_MAX) ++ size = SIZE_MAX; ++ else ++ size += bufv->buf[i].size; ++ } ++ ++ return size; ++} ++ ++static size_t min_size(size_t s1, size_t s2) ++{ ++ return s1 < s2 ? s1 : s2; ++} ++ ++static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off, ++ const struct fuse_buf *src, size_t src_off, ++ size_t len) ++{ ++ ssize_t res = 0; ++ size_t copied = 0; ++ ++ while (len) { ++ if (dst->flags & FUSE_BUF_FD_SEEK) { ++ res = pwrite(dst->fd, (char *)src->mem + src_off, len, ++ dst->pos + dst_off); ++ } else { ++ res = write(dst->fd, (char *)src->mem + src_off, len); ++ } ++ if (res == -1) { ++ if (!copied) ++ return -errno; ++ break; ++ } ++ if (res == 0) ++ break; ++ ++ copied += res; ++ if (!(dst->flags & FUSE_BUF_FD_RETRY)) ++ break; ++ ++ src_off += res; ++ dst_off += res; ++ len -= res; ++ } ++ ++ return copied; ++} ++ ++static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off, ++ const struct fuse_buf *src, size_t src_off, ++ size_t len) ++{ ++ ssize_t res = 0; ++ size_t copied = 0; ++ ++ while (len) { ++ if (src->flags & FUSE_BUF_FD_SEEK) { ++ res = pread(src->fd, (char *)dst->mem + dst_off, len, ++ src->pos + src_off); ++ } else { ++ res = read(src->fd, (char *)dst->mem + dst_off, len); ++ } ++ if (res == -1) { ++ if (!copied) ++ return -errno; ++ break; ++ } ++ if (res == 0) ++ break; ++ ++ copied += res; ++ if (!(src->flags & FUSE_BUF_FD_RETRY)) ++ break; ++ ++ dst_off += res; ++ src_off += res; ++ len -= res; ++ } ++ ++ return copied; ++} ++ ++static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off, ++ const struct fuse_buf *src, size_t src_off, ++ size_t len) ++{ ++ char buf[4096]; ++ struct fuse_buf tmp = { ++ .size = sizeof(buf), ++ .flags = 0, ++ }; ++ ssize_t res; ++ size_t copied = 0; ++ ++ tmp.mem = buf; ++ ++ while (len) { ++ size_t this_len = min_size(tmp.size, len); ++ size_t read_len; ++ ++ res = fuse_buf_read(&tmp, 0, src, src_off, this_len); ++ if (res < 0) { ++ if (!copied) ++ return res; ++ break; ++ } ++ if (res == 0) ++ break; ++ ++ read_len = res; ++ res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len); ++ if (res < 0) { ++ if (!copied) ++ return res; ++ break; ++ } ++ if (res == 0) ++ break; ++ ++ copied += res; ++ ++ if (res < this_len) ++ break; ++ ++ dst_off += res; ++ src_off += res; ++ len -= res; ++ } ++ ++ return copied; ++} ++ ++#ifdef HAVE_SPLICE ++static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off, ++ const struct fuse_buf *src, size_t src_off, ++ size_t len, enum fuse_buf_copy_flags flags) ++{ ++ int splice_flags = 0; ++ off_t *srcpos = NULL; ++ off_t *dstpos = NULL; ++ off_t srcpos_val; ++ off_t dstpos_val; ++ ssize_t res; ++ size_t copied = 0; ++ ++ if (flags & FUSE_BUF_SPLICE_MOVE) ++ splice_flags |= SPLICE_F_MOVE; ++ if (flags & FUSE_BUF_SPLICE_NONBLOCK) ++ splice_flags |= SPLICE_F_NONBLOCK; ++ ++ if (src->flags & FUSE_BUF_FD_SEEK) { ++ srcpos_val = src->pos + src_off; ++ srcpos = &srcpos_val; ++ } ++ if (dst->flags & FUSE_BUF_FD_SEEK) { ++ dstpos_val = dst->pos + dst_off; ++ dstpos = &dstpos_val; ++ } ++ ++ while (len) { ++ res = splice(src->fd, srcpos, dst->fd, dstpos, len, ++ splice_flags); ++ if (res == -1) { ++ if (copied) ++ break; ++ ++ if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE)) ++ return -errno; ++ ++ /* Maybe splice is not supported for this combination */ ++ return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, ++ len); ++ } ++ if (res == 0) ++ break; ++ ++ copied += res; ++ if (!(src->flags & FUSE_BUF_FD_RETRY) && ++ !(dst->flags & FUSE_BUF_FD_RETRY)) { ++ break; ++ } ++ ++ len -= res; ++ } ++ ++ return copied; ++} ++#else ++static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off, ++ const struct fuse_buf *src, size_t src_off, ++ size_t len, enum fuse_buf_copy_flags flags) ++{ ++ (void) flags; ++ ++ return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); ++} ++#endif ++ ++ ++static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off, ++ const struct fuse_buf *src, size_t src_off, ++ size_t len, enum fuse_buf_copy_flags flags) ++{ ++ int src_is_fd = src->flags & FUSE_BUF_IS_FD; ++ int dst_is_fd = dst->flags & FUSE_BUF_IS_FD; ++ ++ if (!src_is_fd && !dst_is_fd) { ++ char *dstmem = (char *)dst->mem + dst_off; ++ char *srcmem = (char *)src->mem + src_off; ++ ++ if (dstmem != srcmem) { ++ if (dstmem + len <= srcmem || srcmem + len <= dstmem) ++ memcpy(dstmem, srcmem, len); ++ else ++ memmove(dstmem, srcmem, len); ++ } ++ ++ return len; ++ } else if (!src_is_fd) { ++ return fuse_buf_write(dst, dst_off, src, src_off, len); ++ } else if (!dst_is_fd) { ++ return fuse_buf_read(dst, dst_off, src, src_off, len); ++ } else if (flags & FUSE_BUF_NO_SPLICE) { ++ return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); ++ } else { ++ return fuse_buf_splice(dst, dst_off, src, src_off, len, flags); ++ } ++} ++ ++static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv) ++{ ++ if (bufv->idx < bufv->count) ++ return &bufv->buf[bufv->idx]; ++ else ++ return NULL; ++} ++ ++static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len) ++{ ++ const struct fuse_buf *buf = fuse_bufvec_current(bufv); ++ ++ bufv->off += len; ++ assert(bufv->off <= buf->size); ++ if (bufv->off == buf->size) { ++ assert(bufv->idx < bufv->count); ++ bufv->idx++; ++ if (bufv->idx == bufv->count) ++ return 0; ++ bufv->off = 0; ++ } ++ return 1; ++} ++ ++ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv, ++ enum fuse_buf_copy_flags flags) ++{ ++ size_t copied = 0; ++ ++ if (dstv == srcv) ++ return fuse_buf_size(dstv); ++ ++ for (;;) { ++ const struct fuse_buf *src = fuse_bufvec_current(srcv); ++ const struct fuse_buf *dst = fuse_bufvec_current(dstv); ++ size_t src_len; ++ size_t dst_len; ++ size_t len; ++ ssize_t res; ++ ++ if (src == NULL || dst == NULL) ++ break; ++ ++ src_len = src->size - srcv->off; ++ dst_len = dst->size - dstv->off; ++ len = min_size(src_len, dst_len); ++ ++ res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags); ++ if (res < 0) { ++ if (!copied) ++ return res; ++ break; ++ } ++ copied += res; ++ ++ if (!fuse_bufvec_advance(srcv, res) || ++ !fuse_bufvec_advance(dstv, res)) ++ break; ++ ++ if (res < len) ++ break; ++ } ++ ++ return copied; ++} +diff --git a/tools/virtiofsd/fuse_log.c b/tools/virtiofsd/fuse_log.c +new file mode 100644 +index 0000000000..0d268ab014 +--- /dev/null ++++ b/tools/virtiofsd/fuse_log.c +@@ -0,0 +1,40 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2019 Red Hat, Inc. ++ ++ Logging API. ++ ++ This program can be distributed under the terms of the GNU LGPLv2. ++ See the file COPYING.LIB ++*/ ++ ++#include "fuse_log.h" ++ ++#include ++#include ++ ++static void default_log_func( ++ __attribute__(( unused )) enum fuse_log_level level, ++ const char *fmt, va_list ap) ++{ ++ vfprintf(stderr, fmt, ap); ++} ++ ++static fuse_log_func_t log_func = default_log_func; ++ ++void fuse_set_log_func(fuse_log_func_t func) ++{ ++ if (!func) ++ func = default_log_func; ++ ++ log_func = func; ++} ++ ++void fuse_log(enum fuse_log_level level, const char *fmt, ...) ++{ ++ va_list ap; ++ ++ va_start(ap, fmt); ++ log_func(level, fmt, ap); ++ va_end(ap); ++} +diff --git a/tools/virtiofsd/fuse_opt.c b/tools/virtiofsd/fuse_opt.c +new file mode 100644 +index 0000000000..93066b926e +--- /dev/null ++++ b/tools/virtiofsd/fuse_opt.c +@@ -0,0 +1,423 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2007 Miklos Szeredi ++ ++ Implementation of option parsing routines (dealing with `struct ++ fuse_args`). ++ ++ This program can be distributed under the terms of the GNU LGPLv2. ++ See the file COPYING.LIB ++*/ ++ ++#include "config.h" ++#include "fuse_i.h" ++#include "fuse_opt.h" ++#include "fuse_misc.h" ++ ++#include ++#include ++#include ++#include ++ ++struct fuse_opt_context { ++ void *data; ++ const struct fuse_opt *opt; ++ fuse_opt_proc_t proc; ++ int argctr; ++ int argc; ++ char **argv; ++ struct fuse_args outargs; ++ char *opts; ++ int nonopt; ++}; ++ ++void fuse_opt_free_args(struct fuse_args *args) ++{ ++ if (args) { ++ if (args->argv && args->allocated) { ++ int i; ++ for (i = 0; i < args->argc; i++) ++ free(args->argv[i]); ++ free(args->argv); ++ } ++ args->argc = 0; ++ args->argv = NULL; ++ args->allocated = 0; ++ } ++} ++ ++static int alloc_failed(void) ++{ ++ fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); ++ return -1; ++} ++ ++int fuse_opt_add_arg(struct fuse_args *args, const char *arg) ++{ ++ char **newargv; ++ char *newarg; ++ ++ assert(!args->argv || args->allocated); ++ ++ newarg = strdup(arg); ++ if (!newarg) ++ return alloc_failed(); ++ ++ newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); ++ if (!newargv) { ++ free(newarg); ++ return alloc_failed(); ++ } ++ ++ args->argv = newargv; ++ args->allocated = 1; ++ args->argv[args->argc++] = newarg; ++ args->argv[args->argc] = NULL; ++ return 0; ++} ++ ++static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos, ++ const char *arg) ++{ ++ assert(pos <= args->argc); ++ if (fuse_opt_add_arg(args, arg) == -1) ++ return -1; ++ ++ if (pos != args->argc - 1) { ++ char *newarg = args->argv[args->argc - 1]; ++ memmove(&args->argv[pos + 1], &args->argv[pos], ++ sizeof(char *) * (args->argc - pos - 1)); ++ args->argv[pos] = newarg; ++ } ++ return 0; ++} ++ ++int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) ++{ ++ return fuse_opt_insert_arg_common(args, pos, arg); ++} ++ ++static int next_arg(struct fuse_opt_context *ctx, const char *opt) ++{ ++ if (ctx->argctr + 1 >= ctx->argc) { ++ fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt); ++ return -1; ++ } ++ ctx->argctr++; ++ return 0; ++} ++ ++static int add_arg(struct fuse_opt_context *ctx, const char *arg) ++{ ++ return fuse_opt_add_arg(&ctx->outargs, arg); ++} ++ ++static int add_opt_common(char **opts, const char *opt, int esc) ++{ ++ unsigned oldlen = *opts ? strlen(*opts) : 0; ++ char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1); ++ ++ if (!d) ++ return alloc_failed(); ++ ++ *opts = d; ++ if (oldlen) { ++ d += oldlen; ++ *d++ = ','; ++ } ++ ++ for (; *opt; opt++) { ++ if (esc && (*opt == ',' || *opt == '\\')) ++ *d++ = '\\'; ++ *d++ = *opt; ++ } ++ *d = '\0'; ++ ++ return 0; ++} ++ ++int fuse_opt_add_opt(char **opts, const char *opt) ++{ ++ return add_opt_common(opts, opt, 0); ++} ++ ++int fuse_opt_add_opt_escaped(char **opts, const char *opt) ++{ ++ return add_opt_common(opts, opt, 1); ++} ++ ++static int add_opt(struct fuse_opt_context *ctx, const char *opt) ++{ ++ return add_opt_common(&ctx->opts, opt, 1); ++} ++ ++static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key, ++ int iso) ++{ ++ if (key == FUSE_OPT_KEY_DISCARD) ++ return 0; ++ ++ if (key != FUSE_OPT_KEY_KEEP && ctx->proc) { ++ int res = ctx->proc(ctx->data, arg, key, &ctx->outargs); ++ if (res == -1 || !res) ++ return res; ++ } ++ if (iso) ++ return add_opt(ctx, arg); ++ else ++ return add_arg(ctx, arg); ++} ++ ++static int match_template(const char *t, const char *arg, unsigned *sepp) ++{ ++ int arglen = strlen(arg); ++ const char *sep = strchr(t, '='); ++ sep = sep ? sep : strchr(t, ' '); ++ if (sep && (!sep[1] || sep[1] == '%')) { ++ int tlen = sep - t; ++ if (sep[0] == '=') ++ tlen ++; ++ if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { ++ *sepp = sep - t; ++ return 1; ++ } ++ } ++ if (strcmp(t, arg) == 0) { ++ *sepp = 0; ++ return 1; ++ } ++ return 0; ++} ++ ++static const struct fuse_opt *find_opt(const struct fuse_opt *opt, ++ const char *arg, unsigned *sepp) ++{ ++ for (; opt && opt->templ; opt++) ++ if (match_template(opt->templ, arg, sepp)) ++ return opt; ++ return NULL; ++} ++ ++int fuse_opt_match(const struct fuse_opt *opts, const char *opt) ++{ ++ unsigned dummy; ++ return find_opt(opts, opt, &dummy) ? 1 : 0; ++} ++ ++static int process_opt_param(void *var, const char *format, const char *param, ++ const char *arg) ++{ ++ assert(format[0] == '%'); ++ if (format[1] == 's') { ++ char **s = var; ++ char *copy = strdup(param); ++ if (!copy) ++ return alloc_failed(); ++ ++ free(*s); ++ *s = copy; ++ } else { ++ if (sscanf(param, format, var) != 1) { ++ fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n", arg); ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++static int process_opt(struct fuse_opt_context *ctx, ++ const struct fuse_opt *opt, unsigned sep, ++ const char *arg, int iso) ++{ ++ if (opt->offset == -1U) { ++ if (call_proc(ctx, arg, opt->value, iso) == -1) ++ return -1; ++ } else { ++ void *var = (char *)ctx->data + opt->offset; ++ if (sep && opt->templ[sep + 1]) { ++ const char *param = arg + sep; ++ if (opt->templ[sep] == '=') ++ param ++; ++ if (process_opt_param(var, opt->templ + sep + 1, ++ param, arg) == -1) ++ return -1; ++ } else ++ *(int *)var = opt->value; ++ } ++ return 0; ++} ++ ++static int process_opt_sep_arg(struct fuse_opt_context *ctx, ++ const struct fuse_opt *opt, unsigned sep, ++ const char *arg, int iso) ++{ ++ int res; ++ char *newarg; ++ char *param; ++ ++ if (next_arg(ctx, arg) == -1) ++ return -1; ++ ++ param = ctx->argv[ctx->argctr]; ++ newarg = malloc(sep + strlen(param) + 1); ++ if (!newarg) ++ return alloc_failed(); ++ ++ memcpy(newarg, arg, sep); ++ strcpy(newarg + sep, param); ++ res = process_opt(ctx, opt, sep, newarg, iso); ++ free(newarg); ++ ++ return res; ++} ++ ++static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso) ++{ ++ unsigned sep; ++ const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); ++ if (opt) { ++ for (; opt; opt = find_opt(opt + 1, arg, &sep)) { ++ int res; ++ if (sep && opt->templ[sep] == ' ' && !arg[sep]) ++ res = process_opt_sep_arg(ctx, opt, sep, arg, ++ iso); ++ else ++ res = process_opt(ctx, opt, sep, arg, iso); ++ if (res == -1) ++ return -1; ++ } ++ return 0; ++ } else ++ return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); ++} ++ ++static int process_real_option_group(struct fuse_opt_context *ctx, char *opts) ++{ ++ char *s = opts; ++ char *d = s; ++ int end = 0; ++ ++ while (!end) { ++ if (*s == '\0') ++ end = 1; ++ if (*s == ',' || end) { ++ int res; ++ ++ *d = '\0'; ++ res = process_gopt(ctx, opts, 1); ++ if (res == -1) ++ return -1; ++ d = opts; ++ } else { ++ if (s[0] == '\\' && s[1] != '\0') { ++ s++; ++ if (s[0] >= '0' && s[0] <= '3' && ++ s[1] >= '0' && s[1] <= '7' && ++ s[2] >= '0' && s[2] <= '7') { ++ *d++ = (s[0] - '0') * 0100 + ++ (s[1] - '0') * 0010 + ++ (s[2] - '0'); ++ s += 2; ++ } else { ++ *d++ = *s; ++ } ++ } else { ++ *d++ = *s; ++ } ++ } ++ s++; ++ } ++ ++ return 0; ++} ++ ++static int process_option_group(struct fuse_opt_context *ctx, const char *opts) ++{ ++ int res; ++ char *copy = strdup(opts); ++ ++ if (!copy) { ++ fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); ++ return -1; ++ } ++ res = process_real_option_group(ctx, copy); ++ free(copy); ++ return res; ++} ++ ++static int process_one(struct fuse_opt_context *ctx, const char *arg) ++{ ++ if (ctx->nonopt || arg[0] != '-') ++ return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); ++ else if (arg[1] == 'o') { ++ if (arg[2]) ++ return process_option_group(ctx, arg + 2); ++ else { ++ if (next_arg(ctx, arg) == -1) ++ return -1; ++ ++ return process_option_group(ctx, ++ ctx->argv[ctx->argctr]); ++ } ++ } else if (arg[1] == '-' && !arg[2]) { ++ if (add_arg(ctx, arg) == -1) ++ return -1; ++ ctx->nonopt = ctx->outargs.argc; ++ return 0; ++ } else ++ return process_gopt(ctx, arg, 0); ++} ++ ++static int opt_parse(struct fuse_opt_context *ctx) ++{ ++ if (ctx->argc) { ++ if (add_arg(ctx, ctx->argv[0]) == -1) ++ return -1; ++ } ++ ++ for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) ++ if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) ++ return -1; ++ ++ if (ctx->opts) { ++ if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 || ++ fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) ++ return -1; ++ } ++ ++ /* If option separator ("--") is the last argument, remove it */ ++ if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc && ++ strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) { ++ free(ctx->outargs.argv[ctx->outargs.argc - 1]); ++ ctx->outargs.argv[--ctx->outargs.argc] = NULL; ++ } ++ ++ return 0; ++} ++ ++int fuse_opt_parse(struct fuse_args *args, void *data, ++ const struct fuse_opt opts[], fuse_opt_proc_t proc) ++{ ++ int res; ++ struct fuse_opt_context ctx = { ++ .data = data, ++ .opt = opts, ++ .proc = proc, ++ }; ++ ++ if (!args || !args->argv || !args->argc) ++ return 0; ++ ++ ctx.argc = args->argc; ++ ctx.argv = args->argv; ++ ++ res = opt_parse(&ctx); ++ if (res != -1) { ++ struct fuse_args tmp = *args; ++ *args = ctx.outargs; ++ ctx.outargs = tmp; ++ } ++ free(ctx.opts); ++ fuse_opt_free_args(&ctx.outargs); ++ return res; ++} +diff --git a/tools/virtiofsd/fuse_signals.c b/tools/virtiofsd/fuse_signals.c +new file mode 100644 +index 0000000000..4271947bd4 +--- /dev/null ++++ b/tools/virtiofsd/fuse_signals.c +@@ -0,0 +1,91 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2007 Miklos Szeredi ++ ++ Utility functions for setting signal handlers. ++ ++ This program can be distributed under the terms of the GNU LGPLv2. ++ See the file COPYING.LIB ++*/ ++ ++#include "config.h" ++#include "fuse_lowlevel.h" ++#include "fuse_i.h" ++ ++#include ++#include ++#include ++#include ++ ++static struct fuse_session *fuse_instance; ++ ++static void exit_handler(int sig) ++{ ++ if (fuse_instance) { ++ fuse_session_exit(fuse_instance); ++ if(sig <= 0) { ++ fuse_log(FUSE_LOG_ERR, "assertion error: signal value <= 0\n"); ++ abort(); ++ } ++ fuse_instance->error = sig; ++ } ++} ++ ++static void do_nothing(int sig) ++{ ++ (void) sig; ++} ++ ++static int set_one_signal_handler(int sig, void (*handler)(int), int remove) ++{ ++ struct sigaction sa; ++ struct sigaction old_sa; ++ ++ memset(&sa, 0, sizeof(struct sigaction)); ++ sa.sa_handler = remove ? SIG_DFL : handler; ++ sigemptyset(&(sa.sa_mask)); ++ sa.sa_flags = 0; ++ ++ if (sigaction(sig, NULL, &old_sa) == -1) { ++ perror("fuse: cannot get old signal handler"); ++ return -1; ++ } ++ ++ if (old_sa.sa_handler == (remove ? handler : SIG_DFL) && ++ sigaction(sig, &sa, NULL) == -1) { ++ perror("fuse: cannot set signal handler"); ++ return -1; ++ } ++ return 0; ++} ++ ++int fuse_set_signal_handlers(struct fuse_session *se) ++{ ++ /* If we used SIG_IGN instead of the do_nothing function, ++ then we would be unable to tell if we set SIG_IGN (and ++ thus should reset to SIG_DFL in fuse_remove_signal_handlers) ++ or if it was already set to SIG_IGN (and should be left ++ untouched. */ ++ if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 || ++ set_one_signal_handler(SIGINT, exit_handler, 0) == -1 || ++ set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 || ++ set_one_signal_handler(SIGPIPE, do_nothing, 0) == -1) ++ return -1; ++ ++ fuse_instance = se; ++ return 0; ++} ++ ++void fuse_remove_signal_handlers(struct fuse_session *se) ++{ ++ if (fuse_instance != se) ++ fuse_log(FUSE_LOG_ERR, ++ "fuse: fuse_remove_signal_handlers: unknown session\n"); ++ else ++ fuse_instance = NULL; ++ ++ set_one_signal_handler(SIGHUP, exit_handler, 1); ++ set_one_signal_handler(SIGINT, exit_handler, 1); ++ set_one_signal_handler(SIGTERM, exit_handler, 1); ++ set_one_signal_handler(SIGPIPE, do_nothing, 1); ++} +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +new file mode 100644 +index 0000000000..64ff7ad6d5 +--- /dev/null ++++ b/tools/virtiofsd/helper.c +@@ -0,0 +1,440 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2007 Miklos Szeredi ++ ++ Helper functions to create (simple) standalone programs. With the ++ aid of these functions it should be possible to create full FUSE ++ file system by implementing nothing but the request handlers. ++ ++ This program can be distributed under the terms of the GNU LGPLv2. ++ See the file COPYING.LIB. ++*/ ++ ++#include "config.h" ++#include "fuse_i.h" ++#include "fuse_misc.h" ++#include "fuse_opt.h" ++#include "fuse_lowlevel.h" ++#include "mount_util.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define FUSE_HELPER_OPT(t, p) \ ++ { t, offsetof(struct fuse_cmdline_opts, p), 1 } ++ ++static const struct fuse_opt fuse_helper_opts[] = { ++ FUSE_HELPER_OPT("-h", show_help), ++ FUSE_HELPER_OPT("--help", show_help), ++ FUSE_HELPER_OPT("-V", show_version), ++ FUSE_HELPER_OPT("--version", show_version), ++ FUSE_HELPER_OPT("-d", debug), ++ FUSE_HELPER_OPT("debug", debug), ++ FUSE_HELPER_OPT("-d", foreground), ++ FUSE_HELPER_OPT("debug", foreground), ++ FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), ++ FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), ++ FUSE_HELPER_OPT("-f", foreground), ++ FUSE_HELPER_OPT("-s", singlethread), ++ FUSE_HELPER_OPT("fsname=", nodefault_subtype), ++ FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), ++#ifndef __FreeBSD__ ++ FUSE_HELPER_OPT("subtype=", nodefault_subtype), ++ FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), ++#endif ++ FUSE_HELPER_OPT("clone_fd", clone_fd), ++ FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads), ++ FUSE_OPT_END ++}; ++ ++struct fuse_conn_info_opts { ++ int atomic_o_trunc; ++ int no_remote_posix_lock; ++ int no_remote_flock; ++ int splice_write; ++ int splice_move; ++ int splice_read; ++ int no_splice_write; ++ int no_splice_move; ++ int no_splice_read; ++ int auto_inval_data; ++ int no_auto_inval_data; ++ int no_readdirplus; ++ int no_readdirplus_auto; ++ int async_dio; ++ int no_async_dio; ++ int writeback_cache; ++ int no_writeback_cache; ++ int async_read; ++ int sync_read; ++ unsigned max_write; ++ unsigned max_readahead; ++ unsigned max_background; ++ unsigned congestion_threshold; ++ unsigned time_gran; ++ int set_max_write; ++ int set_max_readahead; ++ int set_max_background; ++ int set_congestion_threshold; ++ int set_time_gran; ++}; ++ ++#define CONN_OPTION(t, p, v) \ ++ { t, offsetof(struct fuse_conn_info_opts, p), v } ++static const struct fuse_opt conn_info_opt_spec[] = { ++ CONN_OPTION("max_write=%u", max_write, 0), ++ CONN_OPTION("max_write=", set_max_write, 1), ++ CONN_OPTION("max_readahead=%u", max_readahead, 0), ++ CONN_OPTION("max_readahead=", set_max_readahead, 1), ++ CONN_OPTION("max_background=%u", max_background, 0), ++ CONN_OPTION("max_background=", set_max_background, 1), ++ CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0), ++ CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1), ++ CONN_OPTION("sync_read", sync_read, 1), ++ CONN_OPTION("async_read", async_read, 1), ++ CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1), ++ CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1), ++ CONN_OPTION("no_remote_lock", no_remote_flock, 1), ++ CONN_OPTION("no_remote_flock", no_remote_flock, 1), ++ CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1), ++ CONN_OPTION("splice_write", splice_write, 1), ++ CONN_OPTION("no_splice_write", no_splice_write, 1), ++ CONN_OPTION("splice_move", splice_move, 1), ++ CONN_OPTION("no_splice_move", no_splice_move, 1), ++ CONN_OPTION("splice_read", splice_read, 1), ++ CONN_OPTION("no_splice_read", no_splice_read, 1), ++ CONN_OPTION("auto_inval_data", auto_inval_data, 1), ++ CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1), ++ CONN_OPTION("readdirplus=no", no_readdirplus, 1), ++ CONN_OPTION("readdirplus=yes", no_readdirplus, 0), ++ CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1), ++ CONN_OPTION("readdirplus=auto", no_readdirplus, 0), ++ CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0), ++ CONN_OPTION("async_dio", async_dio, 1), ++ CONN_OPTION("no_async_dio", no_async_dio, 1), ++ CONN_OPTION("writeback_cache", writeback_cache, 1), ++ CONN_OPTION("no_writeback_cache", no_writeback_cache, 1), ++ CONN_OPTION("time_gran=%u", time_gran, 0), ++ CONN_OPTION("time_gran=", set_time_gran, 1), ++ FUSE_OPT_END ++}; ++ ++ ++void fuse_cmdline_help(void) ++{ ++ printf(" -h --help print help\n" ++ " -V --version print version\n" ++ " -d -o debug enable debug output (implies -f)\n" ++ " -f foreground operation\n" ++ " -s disable multi-threaded operation\n" ++ " -o clone_fd use separate fuse device fd for each thread\n" ++ " (may improve performance)\n" ++ " -o max_idle_threads the maximum number of idle worker threads\n" ++ " allowed (default: 10)\n"); ++} ++ ++static int fuse_helper_opt_proc(void *data, const char *arg, int key, ++ struct fuse_args *outargs) ++{ ++ (void) outargs; ++ struct fuse_cmdline_opts *opts = data; ++ ++ switch (key) { ++ case FUSE_OPT_KEY_NONOPT: ++ if (!opts->mountpoint) { ++ if (fuse_mnt_parse_fuse_fd(arg) != -1) { ++ return fuse_opt_add_opt(&opts->mountpoint, arg); ++ } ++ ++ char mountpoint[PATH_MAX] = ""; ++ if (realpath(arg, mountpoint) == NULL) { ++ fuse_log(FUSE_LOG_ERR, ++ "fuse: bad mount point `%s': %s\n", ++ arg, strerror(errno)); ++ return -1; ++ } ++ return fuse_opt_add_opt(&opts->mountpoint, mountpoint); ++ } else { ++ fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg); ++ return -1; ++ } ++ ++ default: ++ /* Pass through unknown options */ ++ return 1; ++ } ++} ++ ++/* Under FreeBSD, there is no subtype option so this ++ function actually sets the fsname */ ++static int add_default_subtype(const char *progname, struct fuse_args *args) ++{ ++ int res; ++ char *subtype_opt; ++ ++ const char *basename = strrchr(progname, '/'); ++ if (basename == NULL) ++ basename = progname; ++ else if (basename[1] != '\0') ++ basename++; ++ ++ subtype_opt = (char *) malloc(strlen(basename) + 64); ++ if (subtype_opt == NULL) { ++ fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); ++ return -1; ++ } ++#ifdef __FreeBSD__ ++ sprintf(subtype_opt, "-ofsname=%s", basename); ++#else ++ sprintf(subtype_opt, "-osubtype=%s", basename); ++#endif ++ res = fuse_opt_add_arg(args, subtype_opt); ++ free(subtype_opt); ++ return res; ++} ++ ++int fuse_parse_cmdline(struct fuse_args *args, ++ struct fuse_cmdline_opts *opts) ++{ ++ memset(opts, 0, sizeof(struct fuse_cmdline_opts)); ++ ++ opts->max_idle_threads = 10; ++ ++ if (fuse_opt_parse(args, opts, fuse_helper_opts, ++ fuse_helper_opt_proc) == -1) ++ return -1; ++ ++ /* *Linux*: if neither -o subtype nor -o fsname are specified, ++ set subtype to program's basename. ++ *FreeBSD*: if fsname is not specified, set to program's ++ basename. */ ++ if (!opts->nodefault_subtype) ++ if (add_default_subtype(args->argv[0], args) == -1) ++ return -1; ++ ++ return 0; ++} ++ ++ ++int fuse_daemonize(int foreground) ++{ ++ if (!foreground) { ++ int nullfd; ++ int waiter[2]; ++ char completed; ++ ++ if (pipe(waiter)) { ++ perror("fuse_daemonize: pipe"); ++ return -1; ++ } ++ ++ /* ++ * demonize current process by forking it and killing the ++ * parent. This makes current process as a child of 'init'. ++ */ ++ switch(fork()) { ++ case -1: ++ perror("fuse_daemonize: fork"); ++ return -1; ++ case 0: ++ break; ++ default: ++ (void) read(waiter[0], &completed, sizeof(completed)); ++ _exit(0); ++ } ++ ++ if (setsid() == -1) { ++ perror("fuse_daemonize: setsid"); ++ return -1; ++ } ++ ++ (void) chdir("/"); ++ ++ nullfd = open("/dev/null", O_RDWR, 0); ++ if (nullfd != -1) { ++ (void) dup2(nullfd, 0); ++ (void) dup2(nullfd, 1); ++ (void) dup2(nullfd, 2); ++ if (nullfd > 2) ++ close(nullfd); ++ } ++ ++ /* Propagate completion of daemon initialization */ ++ completed = 1; ++ (void) write(waiter[1], &completed, sizeof(completed)); ++ close(waiter[0]); ++ close(waiter[1]); ++ } else { ++ (void) chdir("/"); ++ } ++ return 0; ++} ++ ++int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, ++ size_t op_size, void *user_data) ++{ ++ struct fuse_args args = FUSE_ARGS_INIT(argc, argv); ++ struct fuse *fuse; ++ struct fuse_cmdline_opts opts; ++ int res; ++ ++ if (fuse_parse_cmdline(&args, &opts) != 0) ++ return 1; ++ ++ if (opts.show_version) { ++ printf("FUSE library version %s\n", PACKAGE_VERSION); ++ fuse_lowlevel_version(); ++ res = 0; ++ goto out1; ++ } ++ ++ if (opts.show_help) { ++ if(args.argv[0][0] != '\0') ++ printf("usage: %s [options] \n\n", ++ args.argv[0]); ++ printf("FUSE options:\n"); ++ fuse_cmdline_help(); ++ fuse_lib_help(&args); ++ res = 0; ++ goto out1; ++ } ++ ++ if (!opts.show_help && ++ !opts.mountpoint) { ++ fuse_log(FUSE_LOG_ERR, "error: no mountpoint specified\n"); ++ res = 2; ++ goto out1; ++ } ++ ++ ++ fuse = fuse_new_31(&args, op, op_size, user_data); ++ if (fuse == NULL) { ++ res = 3; ++ goto out1; ++ } ++ ++ if (fuse_mount(fuse,opts.mountpoint) != 0) { ++ res = 4; ++ goto out2; ++ } ++ ++ if (fuse_daemonize(opts.foreground) != 0) { ++ res = 5; ++ goto out3; ++ } ++ ++ struct fuse_session *se = fuse_get_session(fuse); ++ if (fuse_set_signal_handlers(se) != 0) { ++ res = 6; ++ goto out3; ++ } ++ ++ if (opts.singlethread) ++ res = fuse_loop(fuse); ++ else { ++ struct fuse_loop_config loop_config; ++ loop_config.clone_fd = opts.clone_fd; ++ loop_config.max_idle_threads = opts.max_idle_threads; ++ res = fuse_loop_mt_32(fuse, &loop_config); ++ } ++ if (res) ++ res = 7; ++ ++ fuse_remove_signal_handlers(se); ++out3: ++ fuse_unmount(fuse); ++out2: ++ fuse_destroy(fuse); ++out1: ++ free(opts.mountpoint); ++ fuse_opt_free_args(&args); ++ return res; ++} ++ ++ ++void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, ++ struct fuse_conn_info *conn) ++{ ++ if(opts->set_max_write) ++ conn->max_write = opts->max_write; ++ if(opts->set_max_background) ++ conn->max_background = opts->max_background; ++ if(opts->set_congestion_threshold) ++ conn->congestion_threshold = opts->congestion_threshold; ++ if(opts->set_time_gran) ++ conn->time_gran = opts->time_gran; ++ if(opts->set_max_readahead) ++ conn->max_readahead = opts->max_readahead; ++ ++#define LL_ENABLE(cond,cap) \ ++ if (cond) conn->want |= (cap) ++#define LL_DISABLE(cond,cap) \ ++ if (cond) conn->want &= ~(cap) ++ ++ LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ); ++ LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ); ++ ++ LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE); ++ LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE); ++ ++ LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE); ++ LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE); ++ ++ LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); ++ LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); ++ ++ LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS); ++ LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO); ++ ++ LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO); ++ LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO); ++ ++ LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE); ++ LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE); ++ ++ LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ); ++ LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ); ++ ++ LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS); ++ LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS); ++} ++ ++struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args) ++{ ++ struct fuse_conn_info_opts *opts; ++ ++ opts = calloc(1, sizeof(struct fuse_conn_info_opts)); ++ if(opts == NULL) { ++ fuse_log(FUSE_LOG_ERR, "calloc failed\n"); ++ return NULL; ++ } ++ if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) { ++ free(opts); ++ return NULL; ++ } ++ return opts; ++} ++ ++int fuse_open_channel(const char *mountpoint, const char* options) ++{ ++ struct mount_opts *opts = NULL; ++ int fd = -1; ++ const char *argv[] = { "", "-o", options }; ++ int argc = sizeof(argv) / sizeof(argv[0]); ++ struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv); ++ ++ opts = parse_mount_opts(&args); ++ if (opts == NULL) ++ return -1; ++ ++ fd = fuse_kern_mount(mountpoint, opts); ++ destroy_mount_opts(opts); ++ ++ return fd; ++} diff --git a/0013-virtiofsd-Add-fuse_lowlevel.c.patch b/0013-virtiofsd-Add-fuse_lowlevel.c.patch new file mode 100644 index 0000000..c4a16e4 --- /dev/null +++ b/0013-virtiofsd-Add-fuse_lowlevel.c.patch @@ -0,0 +1,3156 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:00:42 +0000 +Subject: [PATCH] virtiofsd: Add fuse_lowlevel.c +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +fuse_lowlevel is one of the largest files from the library +and does most of the work. Add it separately to keep the diff +sizes small. +Again this is from upstream fuse-3.8.0 + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 2de121f01e37e2fe98a4362f4abf7c0848697f76) +--- + tools/virtiofsd/fuse_lowlevel.c | 3129 +++++++++++++++++++++++++++++++ + 1 file changed, 3129 insertions(+) + create mode 100644 tools/virtiofsd/fuse_lowlevel.c + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +new file mode 100644 +index 0000000000..f2d7038e34 +--- /dev/null ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -0,0 +1,3129 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2007 Miklos Szeredi ++ ++ Implementation of (most of) the low-level FUSE API. The session loop ++ functions are implemented in separate files. ++ ++ This program can be distributed under the terms of the GNU LGPLv2. ++ See the file COPYING.LIB ++*/ ++ ++#define _GNU_SOURCE ++ ++#include "config.h" ++#include "fuse_i.h" ++#include "fuse_kernel.h" ++#include "fuse_opt.h" ++#include "fuse_misc.h" ++#include "mount_util.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#ifndef F_LINUX_SPECIFIC_BASE ++#define F_LINUX_SPECIFIC_BASE 1024 ++#endif ++#ifndef F_SETPIPE_SZ ++#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) ++#endif ++ ++ ++#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) ++#define OFFSET_MAX 0x7fffffffffffffffLL ++ ++#define container_of(ptr, type, member) ({ \ ++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ ++ (type *)( (char *)__mptr - offsetof(type,member) );}) ++ ++struct fuse_pollhandle { ++ uint64_t kh; ++ struct fuse_session *se; ++}; ++ ++static size_t pagesize; ++ ++static __attribute__((constructor)) void fuse_ll_init_pagesize(void) ++{ ++ pagesize = getpagesize(); ++} ++ ++static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr) ++{ ++ attr->ino = stbuf->st_ino; ++ attr->mode = stbuf->st_mode; ++ attr->nlink = stbuf->st_nlink; ++ attr->uid = stbuf->st_uid; ++ attr->gid = stbuf->st_gid; ++ attr->rdev = stbuf->st_rdev; ++ attr->size = stbuf->st_size; ++ attr->blksize = stbuf->st_blksize; ++ attr->blocks = stbuf->st_blocks; ++ attr->atime = stbuf->st_atime; ++ attr->mtime = stbuf->st_mtime; ++ attr->ctime = stbuf->st_ctime; ++ attr->atimensec = ST_ATIM_NSEC(stbuf); ++ attr->mtimensec = ST_MTIM_NSEC(stbuf); ++ attr->ctimensec = ST_CTIM_NSEC(stbuf); ++} ++ ++static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf) ++{ ++ stbuf->st_mode = attr->mode; ++ stbuf->st_uid = attr->uid; ++ stbuf->st_gid = attr->gid; ++ stbuf->st_size = attr->size; ++ stbuf->st_atime = attr->atime; ++ stbuf->st_mtime = attr->mtime; ++ stbuf->st_ctime = attr->ctime; ++ ST_ATIM_NSEC_SET(stbuf, attr->atimensec); ++ ST_MTIM_NSEC_SET(stbuf, attr->mtimensec); ++ ST_CTIM_NSEC_SET(stbuf, attr->ctimensec); ++} ++ ++static size_t iov_length(const struct iovec *iov, size_t count) ++{ ++ size_t seg; ++ size_t ret = 0; ++ ++ for (seg = 0; seg < count; seg++) ++ ret += iov[seg].iov_len; ++ return ret; ++} ++ ++static void list_init_req(struct fuse_req *req) ++{ ++ req->next = req; ++ req->prev = req; ++} ++ ++static void list_del_req(struct fuse_req *req) ++{ ++ struct fuse_req *prev = req->prev; ++ struct fuse_req *next = req->next; ++ prev->next = next; ++ next->prev = prev; ++} ++ ++static void list_add_req(struct fuse_req *req, struct fuse_req *next) ++{ ++ struct fuse_req *prev = next->prev; ++ req->next = next; ++ req->prev = prev; ++ prev->next = req; ++ next->prev = req; ++} ++ ++static void destroy_req(fuse_req_t req) ++{ ++ pthread_mutex_destroy(&req->lock); ++ free(req); ++} ++ ++void fuse_free_req(fuse_req_t req) ++{ ++ int ctr; ++ struct fuse_session *se = req->se; ++ ++ pthread_mutex_lock(&se->lock); ++ req->u.ni.func = NULL; ++ req->u.ni.data = NULL; ++ list_del_req(req); ++ ctr = --req->ctr; ++ fuse_chan_put(req->ch); ++ req->ch = NULL; ++ pthread_mutex_unlock(&se->lock); ++ if (!ctr) ++ destroy_req(req); ++} ++ ++static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se) ++{ ++ struct fuse_req *req; ++ ++ req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req)); ++ if (req == NULL) { ++ fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n"); ++ } else { ++ req->se = se; ++ req->ctr = 1; ++ list_init_req(req); ++ fuse_mutex_init(&req->lock); ++ } ++ ++ return req; ++} ++ ++/* Send data. If *ch* is NULL, send via session master fd */ ++static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, ++ struct iovec *iov, int count) ++{ ++ struct fuse_out_header *out = iov[0].iov_base; ++ ++ out->len = iov_length(iov, count); ++ if (se->debug) { ++ if (out->unique == 0) { ++ fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n", ++ out->error, out->len); ++ } else if (out->error) { ++ fuse_log(FUSE_LOG_DEBUG, ++ " unique: %llu, error: %i (%s), outsize: %i\n", ++ (unsigned long long) out->unique, out->error, ++ strerror(-out->error), out->len); ++ } else { ++ fuse_log(FUSE_LOG_DEBUG, ++ " unique: %llu, success, outsize: %i\n", ++ (unsigned long long) out->unique, out->len); ++ } ++ } ++ ++ ssize_t res = writev(ch ? ch->fd : se->fd, ++ iov, count); ++ int err = errno; ++ ++ if (res == -1) { ++ assert(se != NULL); ++ ++ /* ENOENT means the operation was interrupted */ ++ if (!fuse_session_exited(se) && err != ENOENT) ++ perror("fuse: writing device"); ++ return -err; ++ } ++ ++ return 0; ++} ++ ++ ++int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, ++ int count) ++{ ++ struct fuse_out_header out; ++ ++ if (error <= -1000 || error > 0) { ++ fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error); ++ error = -ERANGE; ++ } ++ ++ out.unique = req->unique; ++ out.error = error; ++ ++ iov[0].iov_base = &out; ++ iov[0].iov_len = sizeof(struct fuse_out_header); ++ ++ return fuse_send_msg(req->se, req->ch, iov, count); ++} ++ ++static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov, ++ int count) ++{ ++ int res; ++ ++ res = fuse_send_reply_iov_nofree(req, error, iov, count); ++ fuse_free_req(req); ++ return res; ++} ++ ++static int send_reply(fuse_req_t req, int error, const void *arg, ++ size_t argsize) ++{ ++ struct iovec iov[2]; ++ int count = 1; ++ if (argsize) { ++ iov[1].iov_base = (void *) arg; ++ iov[1].iov_len = argsize; ++ count++; ++ } ++ return send_reply_iov(req, error, iov, count); ++} ++ ++int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count) ++{ ++ int res; ++ struct iovec *padded_iov; ++ ++ padded_iov = malloc((count + 1) * sizeof(struct iovec)); ++ if (padded_iov == NULL) ++ return fuse_reply_err(req, ENOMEM); ++ ++ memcpy(padded_iov + 1, iov, count * sizeof(struct iovec)); ++ count++; ++ ++ res = send_reply_iov(req, 0, padded_iov, count); ++ free(padded_iov); ++ ++ return res; ++} ++ ++ ++/* `buf` is allowed to be empty so that the proper size may be ++ allocated by the caller */ ++size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, ++ const char *name, const struct stat *stbuf, off_t off) ++{ ++ (void)req; ++ size_t namelen; ++ size_t entlen; ++ size_t entlen_padded; ++ struct fuse_dirent *dirent; ++ ++ namelen = strlen(name); ++ entlen = FUSE_NAME_OFFSET + namelen; ++ entlen_padded = FUSE_DIRENT_ALIGN(entlen); ++ ++ if ((buf == NULL) || (entlen_padded > bufsize)) ++ return entlen_padded; ++ ++ dirent = (struct fuse_dirent*) buf; ++ dirent->ino = stbuf->st_ino; ++ dirent->off = off; ++ dirent->namelen = namelen; ++ dirent->type = (stbuf->st_mode & S_IFMT) >> 12; ++ memcpy(dirent->name, name, namelen); ++ memset(dirent->name + namelen, 0, entlen_padded - entlen); ++ ++ return entlen_padded; ++} ++ ++static void convert_statfs(const struct statvfs *stbuf, ++ struct fuse_kstatfs *kstatfs) ++{ ++ kstatfs->bsize = stbuf->f_bsize; ++ kstatfs->frsize = stbuf->f_frsize; ++ kstatfs->blocks = stbuf->f_blocks; ++ kstatfs->bfree = stbuf->f_bfree; ++ kstatfs->bavail = stbuf->f_bavail; ++ kstatfs->files = stbuf->f_files; ++ kstatfs->ffree = stbuf->f_ffree; ++ kstatfs->namelen = stbuf->f_namemax; ++} ++ ++static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize) ++{ ++ return send_reply(req, 0, arg, argsize); ++} ++ ++int fuse_reply_err(fuse_req_t req, int err) ++{ ++ return send_reply(req, -err, NULL, 0); ++} ++ ++void fuse_reply_none(fuse_req_t req) ++{ ++ fuse_free_req(req); ++} ++ ++static unsigned long calc_timeout_sec(double t) ++{ ++ if (t > (double) ULONG_MAX) ++ return ULONG_MAX; ++ else if (t < 0.0) ++ return 0; ++ else ++ return (unsigned long) t; ++} ++ ++static unsigned int calc_timeout_nsec(double t) ++{ ++ double f = t - (double) calc_timeout_sec(t); ++ if (f < 0.0) ++ return 0; ++ else if (f >= 0.999999999) ++ return 999999999; ++ else ++ return (unsigned int) (f * 1.0e9); ++} ++ ++static void fill_entry(struct fuse_entry_out *arg, ++ const struct fuse_entry_param *e) ++{ ++ arg->nodeid = e->ino; ++ arg->generation = e->generation; ++ arg->entry_valid = calc_timeout_sec(e->entry_timeout); ++ arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout); ++ arg->attr_valid = calc_timeout_sec(e->attr_timeout); ++ arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout); ++ convert_stat(&e->attr, &arg->attr); ++} ++ ++/* `buf` is allowed to be empty so that the proper size may be ++ allocated by the caller */ ++size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, ++ const char *name, ++ const struct fuse_entry_param *e, off_t off) ++{ ++ (void)req; ++ size_t namelen; ++ size_t entlen; ++ size_t entlen_padded; ++ ++ namelen = strlen(name); ++ entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen; ++ entlen_padded = FUSE_DIRENT_ALIGN(entlen); ++ if ((buf == NULL) || (entlen_padded > bufsize)) ++ return entlen_padded; ++ ++ struct fuse_direntplus *dp = (struct fuse_direntplus *) buf; ++ memset(&dp->entry_out, 0, sizeof(dp->entry_out)); ++ fill_entry(&dp->entry_out, e); ++ ++ struct fuse_dirent *dirent = &dp->dirent; ++ dirent->ino = e->attr.st_ino; ++ dirent->off = off; ++ dirent->namelen = namelen; ++ dirent->type = (e->attr.st_mode & S_IFMT) >> 12; ++ memcpy(dirent->name, name, namelen); ++ memset(dirent->name + namelen, 0, entlen_padded - entlen); ++ ++ return entlen_padded; ++} ++ ++static void fill_open(struct fuse_open_out *arg, ++ const struct fuse_file_info *f) ++{ ++ arg->fh = f->fh; ++ if (f->direct_io) ++ arg->open_flags |= FOPEN_DIRECT_IO; ++ if (f->keep_cache) ++ arg->open_flags |= FOPEN_KEEP_CACHE; ++ if (f->cache_readdir) ++ arg->open_flags |= FOPEN_CACHE_DIR; ++ if (f->nonseekable) ++ arg->open_flags |= FOPEN_NONSEEKABLE; ++} ++ ++int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e) ++{ ++ struct fuse_entry_out arg; ++ size_t size = req->se->conn.proto_minor < 9 ? ++ FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg); ++ ++ /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant ++ negative entry */ ++ if (!e->ino && req->se->conn.proto_minor < 4) ++ return fuse_reply_err(req, ENOENT); ++ ++ memset(&arg, 0, sizeof(arg)); ++ fill_entry(&arg, e); ++ return send_reply_ok(req, &arg, size); ++} ++ ++int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, ++ const struct fuse_file_info *f) ++{ ++ char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)]; ++ size_t entrysize = req->se->conn.proto_minor < 9 ? ++ FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out); ++ struct fuse_entry_out *earg = (struct fuse_entry_out *) buf; ++ struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize); ++ ++ memset(buf, 0, sizeof(buf)); ++ fill_entry(earg, e); ++ fill_open(oarg, f); ++ return send_reply_ok(req, buf, ++ entrysize + sizeof(struct fuse_open_out)); ++} ++ ++int fuse_reply_attr(fuse_req_t req, const struct stat *attr, ++ double attr_timeout) ++{ ++ struct fuse_attr_out arg; ++ size_t size = req->se->conn.proto_minor < 9 ? ++ FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg); ++ ++ memset(&arg, 0, sizeof(arg)); ++ arg.attr_valid = calc_timeout_sec(attr_timeout); ++ arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout); ++ convert_stat(attr, &arg.attr); ++ ++ return send_reply_ok(req, &arg, size); ++} ++ ++int fuse_reply_readlink(fuse_req_t req, const char *linkname) ++{ ++ return send_reply_ok(req, linkname, strlen(linkname)); ++} ++ ++int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f) ++{ ++ struct fuse_open_out arg; ++ ++ memset(&arg, 0, sizeof(arg)); ++ fill_open(&arg, f); ++ return send_reply_ok(req, &arg, sizeof(arg)); ++} ++ ++int fuse_reply_write(fuse_req_t req, size_t count) ++{ ++ struct fuse_write_out arg; ++ ++ memset(&arg, 0, sizeof(arg)); ++ arg.size = count; ++ ++ return send_reply_ok(req, &arg, sizeof(arg)); ++} ++ ++int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size) ++{ ++ return send_reply_ok(req, buf, size); ++} ++ ++static int fuse_send_data_iov_fallback(struct fuse_session *se, ++ struct fuse_chan *ch, ++ struct iovec *iov, int iov_count, ++ struct fuse_bufvec *buf, ++ size_t len) ++{ ++ struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len); ++ void *mbuf; ++ int res; ++ ++ /* Optimize common case */ ++ if (buf->count == 1 && buf->idx == 0 && buf->off == 0 && ++ !(buf->buf[0].flags & FUSE_BUF_IS_FD)) { ++ /* FIXME: also avoid memory copy if there are multiple buffers ++ but none of them contain an fd */ ++ ++ iov[iov_count].iov_base = buf->buf[0].mem; ++ iov[iov_count].iov_len = len; ++ iov_count++; ++ return fuse_send_msg(se, ch, iov, iov_count); ++ } ++ ++ res = posix_memalign(&mbuf, pagesize, len); ++ if (res != 0) ++ return res; ++ ++ mem_buf.buf[0].mem = mbuf; ++ res = fuse_buf_copy(&mem_buf, buf, 0); ++ if (res < 0) { ++ free(mbuf); ++ return -res; ++ } ++ len = res; ++ ++ iov[iov_count].iov_base = mbuf; ++ iov[iov_count].iov_len = len; ++ iov_count++; ++ res = fuse_send_msg(se, ch, iov, iov_count); ++ free(mbuf); ++ ++ return res; ++} ++ ++struct fuse_ll_pipe { ++ size_t size; ++ int can_grow; ++ int pipe[2]; ++}; ++ ++static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp) ++{ ++ close(llp->pipe[0]); ++ close(llp->pipe[1]); ++ free(llp); ++} ++ ++#ifdef HAVE_SPLICE ++#if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC) ++static int fuse_pipe(int fds[2]) ++{ ++ int rv = pipe(fds); ++ ++ if (rv == -1) ++ return rv; ++ ++ if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 || ++ fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1 || ++ fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || ++ fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { ++ close(fds[0]); ++ close(fds[1]); ++ rv = -1; ++ } ++ return rv; ++} ++#else ++static int fuse_pipe(int fds[2]) ++{ ++ return pipe2(fds, O_CLOEXEC | O_NONBLOCK); ++} ++#endif ++ ++static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se) ++{ ++ struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key); ++ if (llp == NULL) { ++ int res; ++ ++ llp = malloc(sizeof(struct fuse_ll_pipe)); ++ if (llp == NULL) ++ return NULL; ++ ++ res = fuse_pipe(llp->pipe); ++ if (res == -1) { ++ free(llp); ++ return NULL; ++ } ++ ++ /* ++ *the default size is 16 pages on linux ++ */ ++ llp->size = pagesize * 16; ++ llp->can_grow = 1; ++ ++ pthread_setspecific(se->pipe_key, llp); ++ } ++ ++ return llp; ++} ++#endif ++ ++static void fuse_ll_clear_pipe(struct fuse_session *se) ++{ ++ struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key); ++ if (llp) { ++ pthread_setspecific(se->pipe_key, NULL); ++ fuse_ll_pipe_free(llp); ++ } ++} ++ ++#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE) ++static int read_back(int fd, char *buf, size_t len) ++{ ++ int res; ++ ++ res = read(fd, buf, len); ++ if (res == -1) { ++ fuse_log(FUSE_LOG_ERR, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno)); ++ return -EIO; ++ } ++ if (res != len) { ++ fuse_log(FUSE_LOG_ERR, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len); ++ return -EIO; ++ } ++ return 0; ++} ++ ++static int grow_pipe_to_max(int pipefd) ++{ ++ int max; ++ int res; ++ int maxfd; ++ char buf[32]; ++ ++ maxfd = open("/proc/sys/fs/pipe-max-size", O_RDONLY); ++ if (maxfd < 0) ++ return -errno; ++ ++ res = read(maxfd, buf, sizeof(buf) - 1); ++ if (res < 0) { ++ int saved_errno; ++ ++ saved_errno = errno; ++ close(maxfd); ++ return -saved_errno; ++ } ++ close(maxfd); ++ buf[res] = '\0'; ++ ++ max = atoi(buf); ++ res = fcntl(pipefd, F_SETPIPE_SZ, max); ++ if (res < 0) ++ return -errno; ++ return max; ++} ++ ++static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, ++ struct iovec *iov, int iov_count, ++ struct fuse_bufvec *buf, unsigned int flags) ++{ ++ int res; ++ size_t len = fuse_buf_size(buf); ++ struct fuse_out_header *out = iov[0].iov_base; ++ struct fuse_ll_pipe *llp; ++ int splice_flags; ++ size_t pipesize; ++ size_t total_fd_size; ++ size_t idx; ++ size_t headerlen; ++ struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len); ++ ++ if (se->broken_splice_nonblock) ++ goto fallback; ++ ++ if (flags & FUSE_BUF_NO_SPLICE) ++ goto fallback; ++ ++ total_fd_size = 0; ++ for (idx = buf->idx; idx < buf->count; idx++) { ++ if (buf->buf[idx].flags & FUSE_BUF_IS_FD) { ++ total_fd_size = buf->buf[idx].size; ++ if (idx == buf->idx) ++ total_fd_size -= buf->off; ++ } ++ } ++ if (total_fd_size < 2 * pagesize) ++ goto fallback; ++ ++ if (se->conn.proto_minor < 14 || ++ !(se->conn.want & FUSE_CAP_SPLICE_WRITE)) ++ goto fallback; ++ ++ llp = fuse_ll_get_pipe(se); ++ if (llp == NULL) ++ goto fallback; ++ ++ ++ headerlen = iov_length(iov, iov_count); ++ ++ out->len = headerlen + len; ++ ++ /* ++ * Heuristic for the required pipe size, does not work if the ++ * source contains less than page size fragments ++ */ ++ pipesize = pagesize * (iov_count + buf->count + 1) + out->len; ++ ++ if (llp->size < pipesize) { ++ if (llp->can_grow) { ++ res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize); ++ if (res == -1) { ++ res = grow_pipe_to_max(llp->pipe[0]); ++ if (res > 0) ++ llp->size = res; ++ llp->can_grow = 0; ++ goto fallback; ++ } ++ llp->size = res; ++ } ++ if (llp->size < pipesize) ++ goto fallback; ++ } ++ ++ ++ res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK); ++ if (res == -1) ++ goto fallback; ++ ++ if (res != headerlen) { ++ res = -EIO; ++ fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res, ++ headerlen); ++ goto clear_pipe; ++ } ++ ++ pipe_buf.buf[0].flags = FUSE_BUF_IS_FD; ++ pipe_buf.buf[0].fd = llp->pipe[1]; ++ ++ res = fuse_buf_copy(&pipe_buf, buf, ++ FUSE_BUF_FORCE_SPLICE | FUSE_BUF_SPLICE_NONBLOCK); ++ if (res < 0) { ++ if (res == -EAGAIN || res == -EINVAL) { ++ /* ++ * Should only get EAGAIN on kernels with ++ * broken SPLICE_F_NONBLOCK support (<= ++ * 2.6.35) where this error or a short read is ++ * returned even if the pipe itself is not ++ * full ++ * ++ * EINVAL might mean that splice can't handle ++ * this combination of input and output. ++ */ ++ if (res == -EAGAIN) ++ se->broken_splice_nonblock = 1; ++ ++ pthread_setspecific(se->pipe_key, NULL); ++ fuse_ll_pipe_free(llp); ++ goto fallback; ++ } ++ res = -res; ++ goto clear_pipe; ++ } ++ ++ if (res != 0 && res < len) { ++ struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len); ++ void *mbuf; ++ size_t now_len = res; ++ /* ++ * For regular files a short count is either ++ * 1) due to EOF, or ++ * 2) because of broken SPLICE_F_NONBLOCK (see above) ++ * ++ * For other inputs it's possible that we overflowed ++ * the pipe because of small buffer fragments. ++ */ ++ ++ res = posix_memalign(&mbuf, pagesize, len); ++ if (res != 0) ++ goto clear_pipe; ++ ++ mem_buf.buf[0].mem = mbuf; ++ mem_buf.off = now_len; ++ res = fuse_buf_copy(&mem_buf, buf, 0); ++ if (res > 0) { ++ char *tmpbuf; ++ size_t extra_len = res; ++ /* ++ * Trickiest case: got more data. Need to get ++ * back the data from the pipe and then fall ++ * back to regular write. ++ */ ++ tmpbuf = malloc(headerlen); ++ if (tmpbuf == NULL) { ++ free(mbuf); ++ res = ENOMEM; ++ goto clear_pipe; ++ } ++ res = read_back(llp->pipe[0], tmpbuf, headerlen); ++ free(tmpbuf); ++ if (res != 0) { ++ free(mbuf); ++ goto clear_pipe; ++ } ++ res = read_back(llp->pipe[0], mbuf, now_len); ++ if (res != 0) { ++ free(mbuf); ++ goto clear_pipe; ++ } ++ len = now_len + extra_len; ++ iov[iov_count].iov_base = mbuf; ++ iov[iov_count].iov_len = len; ++ iov_count++; ++ res = fuse_send_msg(se, ch, iov, iov_count); ++ free(mbuf); ++ return res; ++ } ++ free(mbuf); ++ res = now_len; ++ } ++ len = res; ++ out->len = headerlen + len; ++ ++ if (se->debug) { ++ fuse_log(FUSE_LOG_DEBUG, ++ " unique: %llu, success, outsize: %i (splice)\n", ++ (unsigned long long) out->unique, out->len); ++ } ++ ++ splice_flags = 0; ++ if ((flags & FUSE_BUF_SPLICE_MOVE) && ++ (se->conn.want & FUSE_CAP_SPLICE_MOVE)) ++ splice_flags |= SPLICE_F_MOVE; ++ ++ res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, ++ NULL, out->len, splice_flags); ++ if (res == -1) { ++ res = -errno; ++ perror("fuse: splice from pipe"); ++ goto clear_pipe; ++ } ++ if (res != out->len) { ++ res = -EIO; ++ fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n", ++ res, out->len); ++ goto clear_pipe; ++ } ++ return 0; ++ ++clear_pipe: ++ fuse_ll_clear_pipe(se); ++ return res; ++ ++fallback: ++ return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); ++} ++#else ++static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, ++ struct iovec *iov, int iov_count, ++ struct fuse_bufvec *buf, unsigned int flags) ++{ ++ size_t len = fuse_buf_size(buf); ++ (void) flags; ++ ++ return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); ++} ++#endif ++ ++int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, ++ enum fuse_buf_copy_flags flags) ++{ ++ struct iovec iov[2]; ++ struct fuse_out_header out; ++ int res; ++ ++ iov[0].iov_base = &out; ++ iov[0].iov_len = sizeof(struct fuse_out_header); ++ ++ out.unique = req->unique; ++ out.error = 0; ++ ++ res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags); ++ if (res <= 0) { ++ fuse_free_req(req); ++ return res; ++ } else { ++ return fuse_reply_err(req, res); ++ } ++} ++ ++int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf) ++{ ++ struct fuse_statfs_out arg; ++ size_t size = req->se->conn.proto_minor < 4 ? ++ FUSE_COMPAT_STATFS_SIZE : sizeof(arg); ++ ++ memset(&arg, 0, sizeof(arg)); ++ convert_statfs(stbuf, &arg.st); ++ ++ return send_reply_ok(req, &arg, size); ++} ++ ++int fuse_reply_xattr(fuse_req_t req, size_t count) ++{ ++ struct fuse_getxattr_out arg; ++ ++ memset(&arg, 0, sizeof(arg)); ++ arg.size = count; ++ ++ return send_reply_ok(req, &arg, sizeof(arg)); ++} ++ ++int fuse_reply_lock(fuse_req_t req, const struct flock *lock) ++{ ++ struct fuse_lk_out arg; ++ ++ memset(&arg, 0, sizeof(arg)); ++ arg.lk.type = lock->l_type; ++ if (lock->l_type != F_UNLCK) { ++ arg.lk.start = lock->l_start; ++ if (lock->l_len == 0) ++ arg.lk.end = OFFSET_MAX; ++ else ++ arg.lk.end = lock->l_start + lock->l_len - 1; ++ } ++ arg.lk.pid = lock->l_pid; ++ return send_reply_ok(req, &arg, sizeof(arg)); ++} ++ ++int fuse_reply_bmap(fuse_req_t req, uint64_t idx) ++{ ++ struct fuse_bmap_out arg; ++ ++ memset(&arg, 0, sizeof(arg)); ++ arg.block = idx; ++ ++ return send_reply_ok(req, &arg, sizeof(arg)); ++} ++ ++static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov, ++ size_t count) ++{ ++ struct fuse_ioctl_iovec *fiov; ++ size_t i; ++ ++ fiov = malloc(sizeof(fiov[0]) * count); ++ if (!fiov) ++ return NULL; ++ ++ for (i = 0; i < count; i++) { ++ fiov[i].base = (uintptr_t) iov[i].iov_base; ++ fiov[i].len = iov[i].iov_len; ++ } ++ ++ return fiov; ++} ++ ++int fuse_reply_ioctl_retry(fuse_req_t req, ++ const struct iovec *in_iov, size_t in_count, ++ const struct iovec *out_iov, size_t out_count) ++{ ++ struct fuse_ioctl_out arg; ++ struct fuse_ioctl_iovec *in_fiov = NULL; ++ struct fuse_ioctl_iovec *out_fiov = NULL; ++ struct iovec iov[4]; ++ size_t count = 1; ++ int res; ++ ++ memset(&arg, 0, sizeof(arg)); ++ arg.flags |= FUSE_IOCTL_RETRY; ++ arg.in_iovs = in_count; ++ arg.out_iovs = out_count; ++ iov[count].iov_base = &arg; ++ iov[count].iov_len = sizeof(arg); ++ count++; ++ ++ if (req->se->conn.proto_minor < 16) { ++ if (in_count) { ++ iov[count].iov_base = (void *)in_iov; ++ iov[count].iov_len = sizeof(in_iov[0]) * in_count; ++ count++; ++ } ++ ++ if (out_count) { ++ iov[count].iov_base = (void *)out_iov; ++ iov[count].iov_len = sizeof(out_iov[0]) * out_count; ++ count++; ++ } ++ } else { ++ /* Can't handle non-compat 64bit ioctls on 32bit */ ++ if (sizeof(void *) == 4 && req->ioctl_64bit) { ++ res = fuse_reply_err(req, EINVAL); ++ goto out; ++ } ++ ++ if (in_count) { ++ in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count); ++ if (!in_fiov) ++ goto enomem; ++ ++ iov[count].iov_base = (void *)in_fiov; ++ iov[count].iov_len = sizeof(in_fiov[0]) * in_count; ++ count++; ++ } ++ if (out_count) { ++ out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count); ++ if (!out_fiov) ++ goto enomem; ++ ++ iov[count].iov_base = (void *)out_fiov; ++ iov[count].iov_len = sizeof(out_fiov[0]) * out_count; ++ count++; ++ } ++ } ++ ++ res = send_reply_iov(req, 0, iov, count); ++out: ++ free(in_fiov); ++ free(out_fiov); ++ ++ return res; ++ ++enomem: ++ res = fuse_reply_err(req, ENOMEM); ++ goto out; ++} ++ ++int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size) ++{ ++ struct fuse_ioctl_out arg; ++ struct iovec iov[3]; ++ size_t count = 1; ++ ++ memset(&arg, 0, sizeof(arg)); ++ arg.result = result; ++ iov[count].iov_base = &arg; ++ iov[count].iov_len = sizeof(arg); ++ count++; ++ ++ if (size) { ++ iov[count].iov_base = (char *) buf; ++ iov[count].iov_len = size; ++ count++; ++ } ++ ++ return send_reply_iov(req, 0, iov, count); ++} ++ ++int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, ++ int count) ++{ ++ struct iovec *padded_iov; ++ struct fuse_ioctl_out arg; ++ int res; ++ ++ padded_iov = malloc((count + 2) * sizeof(struct iovec)); ++ if (padded_iov == NULL) ++ return fuse_reply_err(req, ENOMEM); ++ ++ memset(&arg, 0, sizeof(arg)); ++ arg.result = result; ++ padded_iov[1].iov_base = &arg; ++ padded_iov[1].iov_len = sizeof(arg); ++ ++ memcpy(&padded_iov[2], iov, count * sizeof(struct iovec)); ++ ++ res = send_reply_iov(req, 0, padded_iov, count + 2); ++ free(padded_iov); ++ ++ return res; ++} ++ ++int fuse_reply_poll(fuse_req_t req, unsigned revents) ++{ ++ struct fuse_poll_out arg; ++ ++ memset(&arg, 0, sizeof(arg)); ++ arg.revents = revents; ++ ++ return send_reply_ok(req, &arg, sizeof(arg)); ++} ++ ++int fuse_reply_lseek(fuse_req_t req, off_t off) ++{ ++ struct fuse_lseek_out arg; ++ ++ memset(&arg, 0, sizeof(arg)); ++ arg.offset = off; ++ ++ return send_reply_ok(req, &arg, sizeof(arg)); ++} ++ ++static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ char *name = (char *) inarg; ++ ++ if (req->se->op.lookup) ++ req->se->op.lookup(req, nodeid, name); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg; ++ ++ if (req->se->op.forget) ++ req->se->op.forget(req, nodeid, arg->nlookup); ++ else ++ fuse_reply_none(req); ++} ++ ++static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid, ++ const void *inarg) ++{ ++ struct fuse_batch_forget_in *arg = (void *) inarg; ++ struct fuse_forget_one *param = (void *) PARAM(arg); ++ unsigned int i; ++ ++ (void) nodeid; ++ ++ if (req->se->op.forget_multi) { ++ req->se->op.forget_multi(req, arg->count, ++ (struct fuse_forget_data *) param); ++ } else if (req->se->op.forget) { ++ for (i = 0; i < arg->count; i++) { ++ struct fuse_forget_one *forget = ¶m[i]; ++ struct fuse_req *dummy_req; ++ ++ dummy_req = fuse_ll_alloc_req(req->se); ++ if (dummy_req == NULL) ++ break; ++ ++ dummy_req->unique = req->unique; ++ dummy_req->ctx = req->ctx; ++ dummy_req->ch = NULL; ++ ++ req->se->op.forget(dummy_req, forget->nodeid, ++ forget->nlookup); ++ } ++ fuse_reply_none(req); ++ } else { ++ fuse_reply_none(req); ++ } ++} ++ ++static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_file_info *fip = NULL; ++ struct fuse_file_info fi; ++ ++ if (req->se->conn.proto_minor >= 9) { ++ struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg; ++ ++ if (arg->getattr_flags & FUSE_GETATTR_FH) { ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ fip = &fi; ++ } ++ } ++ ++ if (req->se->op.getattr) ++ req->se->op.getattr(req, nodeid, fip); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg; ++ ++ if (req->se->op.setattr) { ++ struct fuse_file_info *fi = NULL; ++ struct fuse_file_info fi_store; ++ struct stat stbuf; ++ memset(&stbuf, 0, sizeof(stbuf)); ++ convert_attr(arg, &stbuf); ++ if (arg->valid & FATTR_FH) { ++ arg->valid &= ~FATTR_FH; ++ memset(&fi_store, 0, sizeof(fi_store)); ++ fi = &fi_store; ++ fi->fh = arg->fh; ++ } ++ arg->valid &= ++ FUSE_SET_ATTR_MODE | ++ FUSE_SET_ATTR_UID | ++ FUSE_SET_ATTR_GID | ++ FUSE_SET_ATTR_SIZE | ++ FUSE_SET_ATTR_ATIME | ++ FUSE_SET_ATTR_MTIME | ++ FUSE_SET_ATTR_ATIME_NOW | ++ FUSE_SET_ATTR_MTIME_NOW | ++ FUSE_SET_ATTR_CTIME; ++ ++ req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi); ++ } else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_access_in *arg = (struct fuse_access_in *) inarg; ++ ++ if (req->se->op.access) ++ req->se->op.access(req, nodeid, arg->mask); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ (void) inarg; ++ ++ if (req->se->op.readlink) ++ req->se->op.readlink(req, nodeid); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg; ++ char *name = PARAM(arg); ++ ++ if (req->se->conn.proto_minor >= 12) ++ req->ctx.umask = arg->umask; ++ else ++ name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE; ++ ++ if (req->se->op.mknod) ++ req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg; ++ ++ if (req->se->conn.proto_minor >= 12) ++ req->ctx.umask = arg->umask; ++ ++ if (req->se->op.mkdir) ++ req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ char *name = (char *) inarg; ++ ++ if (req->se->op.unlink) ++ req->se->op.unlink(req, nodeid, name); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ char *name = (char *) inarg; ++ ++ if (req->se->op.rmdir) ++ req->se->op.rmdir(req, nodeid, name); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ char *name = (char *) inarg; ++ char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1; ++ ++ if (req->se->op.symlink) ++ req->se->op.symlink(req, linkname, nodeid, name); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg; ++ char *oldname = PARAM(arg); ++ char *newname = oldname + strlen(oldname) + 1; ++ ++ if (req->se->op.rename) ++ req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, ++ 0); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_rename2_in *arg = (struct fuse_rename2_in *) inarg; ++ char *oldname = PARAM(arg); ++ char *newname = oldname + strlen(oldname) + 1; ++ ++ if (req->se->op.rename) ++ req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, ++ arg->flags); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_link_in *arg = (struct fuse_link_in *) inarg; ++ ++ if (req->se->op.link) ++ req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg)); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_create_in *arg = (struct fuse_create_in *) inarg; ++ ++ if (req->se->op.create) { ++ struct fuse_file_info fi; ++ char *name = PARAM(arg); ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.flags = arg->flags; ++ ++ if (req->se->conn.proto_minor >= 12) ++ req->ctx.umask = arg->umask; ++ else ++ name = (char *) inarg + sizeof(struct fuse_open_in); ++ ++ req->se->op.create(req, nodeid, name, arg->mode, &fi); ++ } else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_open_in *arg = (struct fuse_open_in *) inarg; ++ struct fuse_file_info fi; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.flags = arg->flags; ++ ++ if (req->se->op.open) ++ req->se->op.open(req, nodeid, &fi); ++ else ++ fuse_reply_open(req, &fi); ++} ++ ++static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_read_in *arg = (struct fuse_read_in *) inarg; ++ ++ if (req->se->op.read) { ++ struct fuse_file_info fi; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ if (req->se->conn.proto_minor >= 9) { ++ fi.lock_owner = arg->lock_owner; ++ fi.flags = arg->flags; ++ } ++ req->se->op.read(req, nodeid, arg->size, arg->offset, &fi); ++ } else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_write_in *arg = (struct fuse_write_in *) inarg; ++ struct fuse_file_info fi; ++ char *param; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0; ++ ++ if (req->se->conn.proto_minor < 9) { ++ param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE; ++ } else { ++ fi.lock_owner = arg->lock_owner; ++ fi.flags = arg->flags; ++ param = PARAM(arg); ++ } ++ ++ if (req->se->op.write) ++ req->se->op.write(req, nodeid, param, arg->size, ++ arg->offset, &fi); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, ++ const struct fuse_buf *ibuf) ++{ ++ struct fuse_session *se = req->se; ++ struct fuse_bufvec bufv = { ++ .buf[0] = *ibuf, ++ .count = 1, ++ }; ++ struct fuse_write_in *arg = (struct fuse_write_in *) inarg; ++ struct fuse_file_info fi; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ fi.writepage = arg->write_flags & FUSE_WRITE_CACHE; ++ ++ if (se->conn.proto_minor < 9) { ++ bufv.buf[0].mem = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE; ++ bufv.buf[0].size -= sizeof(struct fuse_in_header) + ++ FUSE_COMPAT_WRITE_IN_SIZE; ++ assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD)); ++ } else { ++ fi.lock_owner = arg->lock_owner; ++ fi.flags = arg->flags; ++ if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) ++ bufv.buf[0].mem = PARAM(arg); ++ ++ bufv.buf[0].size -= sizeof(struct fuse_in_header) + ++ sizeof(struct fuse_write_in); ++ } ++ if (bufv.buf[0].size < arg->size) { ++ fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n"); ++ fuse_reply_err(req, EIO); ++ goto out; ++ } ++ bufv.buf[0].size = arg->size; ++ ++ se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi); ++ ++out: ++ /* Need to reset the pipe if ->write_buf() didn't consume all data */ ++ if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count) ++ fuse_ll_clear_pipe(se); ++} ++ ++static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg; ++ struct fuse_file_info fi; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ fi.flush = 1; ++ if (req->se->conn.proto_minor >= 7) ++ fi.lock_owner = arg->lock_owner; ++ ++ if (req->se->op.flush) ++ req->se->op.flush(req, nodeid, &fi); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_release_in *arg = (struct fuse_release_in *) inarg; ++ struct fuse_file_info fi; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.flags = arg->flags; ++ fi.fh = arg->fh; ++ if (req->se->conn.proto_minor >= 8) { ++ fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; ++ fi.lock_owner = arg->lock_owner; ++ } ++ if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) { ++ fi.flock_release = 1; ++ fi.lock_owner = arg->lock_owner; ++ } ++ ++ if (req->se->op.release) ++ req->se->op.release(req, nodeid, &fi); ++ else ++ fuse_reply_err(req, 0); ++} ++ ++static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; ++ struct fuse_file_info fi; ++ int datasync = arg->fsync_flags & 1; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ ++ if (req->se->op.fsync) ++ req->se->op.fsync(req, nodeid, datasync, &fi); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_open_in *arg = (struct fuse_open_in *) inarg; ++ struct fuse_file_info fi; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.flags = arg->flags; ++ ++ if (req->se->op.opendir) ++ req->se->op.opendir(req, nodeid, &fi); ++ else ++ fuse_reply_open(req, &fi); ++} ++ ++static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_read_in *arg = (struct fuse_read_in *) inarg; ++ struct fuse_file_info fi; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ ++ if (req->se->op.readdir) ++ req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_read_in *arg = (struct fuse_read_in *) inarg; ++ struct fuse_file_info fi; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ ++ if (req->se->op.readdirplus) ++ req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_release_in *arg = (struct fuse_release_in *) inarg; ++ struct fuse_file_info fi; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.flags = arg->flags; ++ fi.fh = arg->fh; ++ ++ if (req->se->op.releasedir) ++ req->se->op.releasedir(req, nodeid, &fi); ++ else ++ fuse_reply_err(req, 0); ++} ++ ++static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; ++ struct fuse_file_info fi; ++ int datasync = arg->fsync_flags & 1; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ ++ if (req->se->op.fsyncdir) ++ req->se->op.fsyncdir(req, nodeid, datasync, &fi); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ (void) nodeid; ++ (void) inarg; ++ ++ if (req->se->op.statfs) ++ req->se->op.statfs(req, nodeid); ++ else { ++ struct statvfs buf = { ++ .f_namemax = 255, ++ .f_bsize = 512, ++ }; ++ fuse_reply_statfs(req, &buf); ++ } ++} ++ ++static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg; ++ char *name = PARAM(arg); ++ char *value = name + strlen(name) + 1; ++ ++ if (req->se->op.setxattr) ++ req->se->op.setxattr(req, nodeid, name, value, arg->size, ++ arg->flags); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; ++ ++ if (req->se->op.getxattr) ++ req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; ++ ++ if (req->se->op.listxattr) ++ req->se->op.listxattr(req, nodeid, arg->size); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ char *name = (char *) inarg; ++ ++ if (req->se->op.removexattr) ++ req->se->op.removexattr(req, nodeid, name); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void convert_fuse_file_lock(struct fuse_file_lock *fl, ++ struct flock *flock) ++{ ++ memset(flock, 0, sizeof(struct flock)); ++ flock->l_type = fl->type; ++ flock->l_whence = SEEK_SET; ++ flock->l_start = fl->start; ++ if (fl->end == OFFSET_MAX) ++ flock->l_len = 0; ++ else ++ flock->l_len = fl->end - fl->start + 1; ++ flock->l_pid = fl->pid; ++} ++ ++static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg; ++ struct fuse_file_info fi; ++ struct flock flock; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ fi.lock_owner = arg->owner; ++ ++ convert_fuse_file_lock(&arg->lk, &flock); ++ if (req->se->op.getlk) ++ req->se->op.getlk(req, nodeid, &fi, &flock); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, ++ const void *inarg, int sleep) ++{ ++ struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg; ++ struct fuse_file_info fi; ++ struct flock flock; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ fi.lock_owner = arg->owner; ++ ++ if (arg->lk_flags & FUSE_LK_FLOCK) { ++ int op = 0; ++ ++ switch (arg->lk.type) { ++ case F_RDLCK: ++ op = LOCK_SH; ++ break; ++ case F_WRLCK: ++ op = LOCK_EX; ++ break; ++ case F_UNLCK: ++ op = LOCK_UN; ++ break; ++ } ++ if (!sleep) ++ op |= LOCK_NB; ++ ++ if (req->se->op.flock) ++ req->se->op.flock(req, nodeid, &fi, op); ++ else ++ fuse_reply_err(req, ENOSYS); ++ } else { ++ convert_fuse_file_lock(&arg->lk, &flock); ++ if (req->se->op.setlk) ++ req->se->op.setlk(req, nodeid, &fi, &flock, sleep); ++ else ++ fuse_reply_err(req, ENOSYS); ++ } ++} ++ ++static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ do_setlk_common(req, nodeid, inarg, 0); ++} ++ ++static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ do_setlk_common(req, nodeid, inarg, 1); ++} ++ ++static int find_interrupted(struct fuse_session *se, struct fuse_req *req) ++{ ++ struct fuse_req *curr; ++ ++ for (curr = se->list.next; curr != &se->list; curr = curr->next) { ++ if (curr->unique == req->u.i.unique) { ++ fuse_interrupt_func_t func; ++ void *data; ++ ++ curr->ctr++; ++ pthread_mutex_unlock(&se->lock); ++ ++ /* Ugh, ugly locking */ ++ pthread_mutex_lock(&curr->lock); ++ pthread_mutex_lock(&se->lock); ++ curr->interrupted = 1; ++ func = curr->u.ni.func; ++ data = curr->u.ni.data; ++ pthread_mutex_unlock(&se->lock); ++ if (func) ++ func(curr, data); ++ pthread_mutex_unlock(&curr->lock); ++ ++ pthread_mutex_lock(&se->lock); ++ curr->ctr--; ++ if (!curr->ctr) ++ destroy_req(curr); ++ ++ return 1; ++ } ++ } ++ for (curr = se->interrupts.next; curr != &se->interrupts; ++ curr = curr->next) { ++ if (curr->u.i.unique == req->u.i.unique) ++ return 1; ++ } ++ return 0; ++} ++ ++static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg; ++ struct fuse_session *se = req->se; ++ ++ (void) nodeid; ++ if (se->debug) ++ fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n", ++ (unsigned long long) arg->unique); ++ ++ req->u.i.unique = arg->unique; ++ ++ pthread_mutex_lock(&se->lock); ++ if (find_interrupted(se, req)) ++ destroy_req(req); ++ else ++ list_add_req(req, &se->interrupts); ++ pthread_mutex_unlock(&se->lock); ++} ++ ++static struct fuse_req *check_interrupt(struct fuse_session *se, ++ struct fuse_req *req) ++{ ++ struct fuse_req *curr; ++ ++ for (curr = se->interrupts.next; curr != &se->interrupts; ++ curr = curr->next) { ++ if (curr->u.i.unique == req->unique) { ++ req->interrupted = 1; ++ list_del_req(curr); ++ free(curr); ++ return NULL; ++ } ++ } ++ curr = se->interrupts.next; ++ if (curr != &se->interrupts) { ++ list_del_req(curr); ++ list_init_req(curr); ++ return curr; ++ } else ++ return NULL; ++} ++ ++static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg; ++ ++ if (req->se->op.bmap) ++ req->se->op.bmap(req, nodeid, arg->blocksize, arg->block); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg; ++ unsigned int flags = arg->flags; ++ void *in_buf = arg->in_size ? PARAM(arg) : NULL; ++ struct fuse_file_info fi; ++ ++ if (flags & FUSE_IOCTL_DIR && ++ !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) { ++ fuse_reply_err(req, ENOTTY); ++ return; ++ } ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ ++ if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 && ++ !(flags & FUSE_IOCTL_32BIT)) { ++ req->ioctl_64bit = 1; ++ } ++ ++ if (req->se->op.ioctl) ++ req->se->op.ioctl(req, nodeid, arg->cmd, ++ (void *)(uintptr_t)arg->arg, &fi, flags, ++ in_buf, arg->in_size, arg->out_size); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++void fuse_pollhandle_destroy(struct fuse_pollhandle *ph) ++{ ++ free(ph); ++} ++ ++static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg; ++ struct fuse_file_info fi; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ fi.poll_events = arg->events; ++ ++ if (req->se->op.poll) { ++ struct fuse_pollhandle *ph = NULL; ++ ++ if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) { ++ ph = malloc(sizeof(struct fuse_pollhandle)); ++ if (ph == NULL) { ++ fuse_reply_err(req, ENOMEM); ++ return; ++ } ++ ph->kh = arg->kh; ++ ph->se = req->se; ++ } ++ ++ req->se->op.poll(req, nodeid, &fi, ph); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } ++} ++ ++static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg; ++ struct fuse_file_info fi; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ ++ if (req->se->op.fallocate) ++ req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void *inarg) ++{ ++ struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in *) inarg; ++ struct fuse_file_info fi_in, fi_out; ++ ++ memset(&fi_in, 0, sizeof(fi_in)); ++ fi_in.fh = arg->fh_in; ++ ++ memset(&fi_out, 0, sizeof(fi_out)); ++ fi_out.fh = arg->fh_out; ++ ++ ++ if (req->se->op.copy_file_range) ++ req->se->op.copy_file_range(req, nodeid_in, arg->off_in, ++ &fi_in, arg->nodeid_out, ++ arg->off_out, &fi_out, arg->len, ++ arg->flags); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_lseek_in *arg = (struct fuse_lseek_in *) inarg; ++ struct fuse_file_info fi; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ ++ if (req->se->op.lseek) ++ req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi); ++ else ++ fuse_reply_err(req, ENOSYS); ++} ++ ++static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_init_in *arg = (struct fuse_init_in *) inarg; ++ struct fuse_init_out outarg; ++ struct fuse_session *se = req->se; ++ size_t bufsize = se->bufsize; ++ size_t outargsize = sizeof(outarg); ++ ++ (void) nodeid; ++ if (se->debug) { ++ fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor); ++ if (arg->major == 7 && arg->minor >= 6) { ++ fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags); ++ fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n", ++ arg->max_readahead); ++ } ++ } ++ se->conn.proto_major = arg->major; ++ se->conn.proto_minor = arg->minor; ++ se->conn.capable = 0; ++ se->conn.want = 0; ++ ++ memset(&outarg, 0, sizeof(outarg)); ++ outarg.major = FUSE_KERNEL_VERSION; ++ outarg.minor = FUSE_KERNEL_MINOR_VERSION; ++ ++ if (arg->major < 7) { ++ fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n", ++ arg->major, arg->minor); ++ fuse_reply_err(req, EPROTO); ++ return; ++ } ++ ++ if (arg->major > 7) { ++ /* Wait for a second INIT request with a 7.X version */ ++ send_reply_ok(req, &outarg, sizeof(outarg)); ++ return; ++ } ++ ++ if (arg->minor >= 6) { ++ if (arg->max_readahead < se->conn.max_readahead) ++ se->conn.max_readahead = arg->max_readahead; ++ if (arg->flags & FUSE_ASYNC_READ) ++ se->conn.capable |= FUSE_CAP_ASYNC_READ; ++ if (arg->flags & FUSE_POSIX_LOCKS) ++ se->conn.capable |= FUSE_CAP_POSIX_LOCKS; ++ if (arg->flags & FUSE_ATOMIC_O_TRUNC) ++ se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC; ++ if (arg->flags & FUSE_EXPORT_SUPPORT) ++ se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT; ++ if (arg->flags & FUSE_DONT_MASK) ++ se->conn.capable |= FUSE_CAP_DONT_MASK; ++ if (arg->flags & FUSE_FLOCK_LOCKS) ++ se->conn.capable |= FUSE_CAP_FLOCK_LOCKS; ++ if (arg->flags & FUSE_AUTO_INVAL_DATA) ++ se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA; ++ if (arg->flags & FUSE_DO_READDIRPLUS) ++ se->conn.capable |= FUSE_CAP_READDIRPLUS; ++ if (arg->flags & FUSE_READDIRPLUS_AUTO) ++ se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO; ++ if (arg->flags & FUSE_ASYNC_DIO) ++ se->conn.capable |= FUSE_CAP_ASYNC_DIO; ++ if (arg->flags & FUSE_WRITEBACK_CACHE) ++ se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE; ++ if (arg->flags & FUSE_NO_OPEN_SUPPORT) ++ se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT; ++ if (arg->flags & FUSE_PARALLEL_DIROPS) ++ se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS; ++ if (arg->flags & FUSE_POSIX_ACL) ++ se->conn.capable |= FUSE_CAP_POSIX_ACL; ++ if (arg->flags & FUSE_HANDLE_KILLPRIV) ++ se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV; ++ if (arg->flags & FUSE_NO_OPENDIR_SUPPORT) ++ se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT; ++ if (!(arg->flags & FUSE_MAX_PAGES)) { ++ size_t max_bufsize = ++ FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() ++ + FUSE_BUFFER_HEADER_SIZE; ++ if (bufsize > max_bufsize) { ++ bufsize = max_bufsize; ++ } ++ } ++ } else { ++ se->conn.max_readahead = 0; ++ } ++ ++ if (se->conn.proto_minor >= 14) { ++#ifdef HAVE_SPLICE ++#ifdef HAVE_VMSPLICE ++ se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; ++#endif ++ se->conn.capable |= FUSE_CAP_SPLICE_READ; ++#endif ++ } ++ if (se->conn.proto_minor >= 18) ++ se->conn.capable |= FUSE_CAP_IOCTL_DIR; ++ ++ /* Default settings for modern filesystems. ++ * ++ * Most of these capabilities were disabled by default in ++ * libfuse2 for backwards compatibility reasons. In libfuse3, ++ * we can finally enable them by default (as long as they're ++ * supported by the kernel). ++ */ ++#define LL_SET_DEFAULT(cond, cap) \ ++ if ((cond) && (se->conn.capable & (cap))) \ ++ se->conn.want |= (cap) ++ LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ); ++ LL_SET_DEFAULT(1, FUSE_CAP_PARALLEL_DIROPS); ++ LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA); ++ LL_SET_DEFAULT(1, FUSE_CAP_HANDLE_KILLPRIV); ++ LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO); ++ LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR); ++ LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC); ++ LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ); ++ LL_SET_DEFAULT(se->op.getlk && se->op.setlk, ++ FUSE_CAP_POSIX_LOCKS); ++ LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS); ++ LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS); ++ LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir, ++ FUSE_CAP_READDIRPLUS_AUTO); ++ se->conn.time_gran = 1; ++ ++ if (bufsize < FUSE_MIN_READ_BUFFER) { ++ fuse_log(FUSE_LOG_ERR, "fuse: warning: buffer size too small: %zu\n", ++ bufsize); ++ bufsize = FUSE_MIN_READ_BUFFER; ++ } ++ se->bufsize = bufsize; ++ ++ if (se->conn.max_write > bufsize - FUSE_BUFFER_HEADER_SIZE) ++ se->conn.max_write = bufsize - FUSE_BUFFER_HEADER_SIZE; ++ ++ se->got_init = 1; ++ if (se->op.init) ++ se->op.init(se->userdata, &se->conn); ++ ++ if (se->conn.want & (~se->conn.capable)) { ++ fuse_log(FUSE_LOG_ERR, "fuse: error: filesystem requested capabilities " ++ "0x%x that are not supported by kernel, aborting.\n", ++ se->conn.want & (~se->conn.capable)); ++ fuse_reply_err(req, EPROTO); ++ se->error = -EPROTO; ++ fuse_session_exit(se); ++ return; ++ } ++ ++ unsigned max_read_mo = get_max_read(se->mo); ++ if (se->conn.max_read != max_read_mo) { ++ fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() " ++ "requested different maximum read size (%u vs %u)\n", ++ se->conn.max_read, max_read_mo); ++ fuse_reply_err(req, EPROTO); ++ se->error = -EPROTO; ++ fuse_session_exit(se); ++ return; ++ } ++ ++ if (se->conn.max_write < bufsize - FUSE_BUFFER_HEADER_SIZE) { ++ se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE; ++ } ++ if (arg->flags & FUSE_MAX_PAGES) { ++ outarg.flags |= FUSE_MAX_PAGES; ++ outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1; ++ } ++ ++ /* Always enable big writes, this is superseded ++ by the max_write option */ ++ outarg.flags |= FUSE_BIG_WRITES; ++ ++ if (se->conn.want & FUSE_CAP_ASYNC_READ) ++ outarg.flags |= FUSE_ASYNC_READ; ++ if (se->conn.want & FUSE_CAP_POSIX_LOCKS) ++ outarg.flags |= FUSE_POSIX_LOCKS; ++ if (se->conn.want & FUSE_CAP_ATOMIC_O_TRUNC) ++ outarg.flags |= FUSE_ATOMIC_O_TRUNC; ++ if (se->conn.want & FUSE_CAP_EXPORT_SUPPORT) ++ outarg.flags |= FUSE_EXPORT_SUPPORT; ++ if (se->conn.want & FUSE_CAP_DONT_MASK) ++ outarg.flags |= FUSE_DONT_MASK; ++ if (se->conn.want & FUSE_CAP_FLOCK_LOCKS) ++ outarg.flags |= FUSE_FLOCK_LOCKS; ++ if (se->conn.want & FUSE_CAP_AUTO_INVAL_DATA) ++ outarg.flags |= FUSE_AUTO_INVAL_DATA; ++ if (se->conn.want & FUSE_CAP_READDIRPLUS) ++ outarg.flags |= FUSE_DO_READDIRPLUS; ++ if (se->conn.want & FUSE_CAP_READDIRPLUS_AUTO) ++ outarg.flags |= FUSE_READDIRPLUS_AUTO; ++ if (se->conn.want & FUSE_CAP_ASYNC_DIO) ++ outarg.flags |= FUSE_ASYNC_DIO; ++ if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE) ++ outarg.flags |= FUSE_WRITEBACK_CACHE; ++ if (se->conn.want & FUSE_CAP_POSIX_ACL) ++ outarg.flags |= FUSE_POSIX_ACL; ++ outarg.max_readahead = se->conn.max_readahead; ++ outarg.max_write = se->conn.max_write; ++ if (se->conn.proto_minor >= 13) { ++ if (se->conn.max_background >= (1 << 16)) ++ se->conn.max_background = (1 << 16) - 1; ++ if (se->conn.congestion_threshold > se->conn.max_background) ++ se->conn.congestion_threshold = se->conn.max_background; ++ if (!se->conn.congestion_threshold) { ++ se->conn.congestion_threshold = ++ se->conn.max_background * 3 / 4; ++ } ++ ++ outarg.max_background = se->conn.max_background; ++ outarg.congestion_threshold = se->conn.congestion_threshold; ++ } ++ if (se->conn.proto_minor >= 23) ++ outarg.time_gran = se->conn.time_gran; ++ ++ if (se->debug) { ++ fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor); ++ fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); ++ fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n", ++ outarg.max_readahead); ++ fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write); ++ fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n", ++ outarg.max_background); ++ fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n", ++ outarg.congestion_threshold); ++ fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", ++ outarg.time_gran); ++ } ++ if (arg->minor < 5) ++ outargsize = FUSE_COMPAT_INIT_OUT_SIZE; ++ else if (arg->minor < 23) ++ outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE; ++ ++ send_reply_ok(req, &outarg, outargsize); ++} ++ ++static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++{ ++ struct fuse_session *se = req->se; ++ ++ (void) nodeid; ++ (void) inarg; ++ ++ se->got_destroy = 1; ++ if (se->op.destroy) ++ se->op.destroy(se->userdata); ++ ++ send_reply_ok(req, NULL, 0); ++} ++ ++static void list_del_nreq(struct fuse_notify_req *nreq) ++{ ++ struct fuse_notify_req *prev = nreq->prev; ++ struct fuse_notify_req *next = nreq->next; ++ prev->next = next; ++ next->prev = prev; ++} ++ ++static void list_add_nreq(struct fuse_notify_req *nreq, ++ struct fuse_notify_req *next) ++{ ++ struct fuse_notify_req *prev = next->prev; ++ nreq->next = next; ++ nreq->prev = prev; ++ prev->next = nreq; ++ next->prev = nreq; ++} ++ ++static void list_init_nreq(struct fuse_notify_req *nreq) ++{ ++ nreq->next = nreq; ++ nreq->prev = nreq; ++} ++ ++static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid, ++ const void *inarg, const struct fuse_buf *buf) ++{ ++ struct fuse_session *se = req->se; ++ struct fuse_notify_req *nreq; ++ struct fuse_notify_req *head; ++ ++ pthread_mutex_lock(&se->lock); ++ head = &se->notify_list; ++ for (nreq = head->next; nreq != head; nreq = nreq->next) { ++ if (nreq->unique == req->unique) { ++ list_del_nreq(nreq); ++ break; ++ } ++ } ++ pthread_mutex_unlock(&se->lock); ++ ++ if (nreq != head) ++ nreq->reply(nreq, req, nodeid, inarg, buf); ++} ++ ++static int send_notify_iov(struct fuse_session *se, int notify_code, ++ struct iovec *iov, int count) ++{ ++ struct fuse_out_header out; ++ ++ if (!se->got_init) ++ return -ENOTCONN; ++ ++ out.unique = 0; ++ out.error = notify_code; ++ iov[0].iov_base = &out; ++ iov[0].iov_len = sizeof(struct fuse_out_header); ++ ++ return fuse_send_msg(se, NULL, iov, count); ++} ++ ++int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph) ++{ ++ if (ph != NULL) { ++ struct fuse_notify_poll_wakeup_out outarg; ++ struct iovec iov[2]; ++ ++ outarg.kh = ph->kh; ++ ++ iov[1].iov_base = &outarg; ++ iov[1].iov_len = sizeof(outarg); ++ ++ return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2); ++ } else { ++ return 0; ++ } ++} ++ ++int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, ++ off_t off, off_t len) ++{ ++ struct fuse_notify_inval_inode_out outarg; ++ struct iovec iov[2]; ++ ++ if (!se) ++ return -EINVAL; ++ ++ if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) ++ return -ENOSYS; ++ ++ outarg.ino = ino; ++ outarg.off = off; ++ outarg.len = len; ++ ++ iov[1].iov_base = &outarg; ++ iov[1].iov_len = sizeof(outarg); ++ ++ return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2); ++} ++ ++int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, ++ const char *name, size_t namelen) ++{ ++ struct fuse_notify_inval_entry_out outarg; ++ struct iovec iov[3]; ++ ++ if (!se) ++ return -EINVAL; ++ ++ if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) ++ return -ENOSYS; ++ ++ outarg.parent = parent; ++ outarg.namelen = namelen; ++ outarg.padding = 0; ++ ++ iov[1].iov_base = &outarg; ++ iov[1].iov_len = sizeof(outarg); ++ iov[2].iov_base = (void *)name; ++ iov[2].iov_len = namelen + 1; ++ ++ return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3); ++} ++ ++int fuse_lowlevel_notify_delete(struct fuse_session *se, ++ fuse_ino_t parent, fuse_ino_t child, ++ const char *name, size_t namelen) ++{ ++ struct fuse_notify_delete_out outarg; ++ struct iovec iov[3]; ++ ++ if (!se) ++ return -EINVAL; ++ ++ if (se->conn.proto_major < 6 || se->conn.proto_minor < 18) ++ return -ENOSYS; ++ ++ outarg.parent = parent; ++ outarg.child = child; ++ outarg.namelen = namelen; ++ outarg.padding = 0; ++ ++ iov[1].iov_base = &outarg; ++ iov[1].iov_len = sizeof(outarg); ++ iov[2].iov_base = (void *)name; ++ iov[2].iov_len = namelen + 1; ++ ++ return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3); ++} ++ ++int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, ++ off_t offset, struct fuse_bufvec *bufv, ++ enum fuse_buf_copy_flags flags) ++{ ++ struct fuse_out_header out; ++ struct fuse_notify_store_out outarg; ++ struct iovec iov[3]; ++ size_t size = fuse_buf_size(bufv); ++ int res; ++ ++ if (!se) ++ return -EINVAL; ++ ++ if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) ++ return -ENOSYS; ++ ++ out.unique = 0; ++ out.error = FUSE_NOTIFY_STORE; ++ ++ outarg.nodeid = ino; ++ outarg.offset = offset; ++ outarg.size = size; ++ outarg.padding = 0; ++ ++ iov[0].iov_base = &out; ++ iov[0].iov_len = sizeof(out); ++ iov[1].iov_base = &outarg; ++ iov[1].iov_len = sizeof(outarg); ++ ++ res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags); ++ if (res > 0) ++ res = -res; ++ ++ return res; ++} ++ ++struct fuse_retrieve_req { ++ struct fuse_notify_req nreq; ++ void *cookie; ++}; ++ ++static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq, ++ fuse_req_t req, fuse_ino_t ino, ++ const void *inarg, ++ const struct fuse_buf *ibuf) ++{ ++ struct fuse_session *se = req->se; ++ struct fuse_retrieve_req *rreq = ++ container_of(nreq, struct fuse_retrieve_req, nreq); ++ const struct fuse_notify_retrieve_in *arg = inarg; ++ struct fuse_bufvec bufv = { ++ .buf[0] = *ibuf, ++ .count = 1, ++ }; ++ ++ if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) ++ bufv.buf[0].mem = PARAM(arg); ++ ++ bufv.buf[0].size -= sizeof(struct fuse_in_header) + ++ sizeof(struct fuse_notify_retrieve_in); ++ ++ if (bufv.buf[0].size < arg->size) { ++ fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n"); ++ fuse_reply_none(req); ++ goto out; ++ } ++ bufv.buf[0].size = arg->size; ++ ++ if (se->op.retrieve_reply) { ++ se->op.retrieve_reply(req, rreq->cookie, ino, ++ arg->offset, &bufv); ++ } else { ++ fuse_reply_none(req); ++ } ++out: ++ free(rreq); ++ if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count) ++ fuse_ll_clear_pipe(se); ++} ++ ++int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, ++ size_t size, off_t offset, void *cookie) ++{ ++ struct fuse_notify_retrieve_out outarg; ++ struct iovec iov[2]; ++ struct fuse_retrieve_req *rreq; ++ int err; ++ ++ if (!se) ++ return -EINVAL; ++ ++ if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) ++ return -ENOSYS; ++ ++ rreq = malloc(sizeof(*rreq)); ++ if (rreq == NULL) ++ return -ENOMEM; ++ ++ pthread_mutex_lock(&se->lock); ++ rreq->cookie = cookie; ++ rreq->nreq.unique = se->notify_ctr++; ++ rreq->nreq.reply = fuse_ll_retrieve_reply; ++ list_add_nreq(&rreq->nreq, &se->notify_list); ++ pthread_mutex_unlock(&se->lock); ++ ++ outarg.notify_unique = rreq->nreq.unique; ++ outarg.nodeid = ino; ++ outarg.offset = offset; ++ outarg.size = size; ++ outarg.padding = 0; ++ ++ iov[1].iov_base = &outarg; ++ iov[1].iov_len = sizeof(outarg); ++ ++ err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2); ++ if (err) { ++ pthread_mutex_lock(&se->lock); ++ list_del_nreq(&rreq->nreq); ++ pthread_mutex_unlock(&se->lock); ++ free(rreq); ++ } ++ ++ return err; ++} ++ ++void *fuse_req_userdata(fuse_req_t req) ++{ ++ return req->se->userdata; ++} ++ ++const struct fuse_ctx *fuse_req_ctx(fuse_req_t req) ++{ ++ return &req->ctx; ++} ++ ++void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, ++ void *data) ++{ ++ pthread_mutex_lock(&req->lock); ++ pthread_mutex_lock(&req->se->lock); ++ req->u.ni.func = func; ++ req->u.ni.data = data; ++ pthread_mutex_unlock(&req->se->lock); ++ if (req->interrupted && func) ++ func(req, data); ++ pthread_mutex_unlock(&req->lock); ++} ++ ++int fuse_req_interrupted(fuse_req_t req) ++{ ++ int interrupted; ++ ++ pthread_mutex_lock(&req->se->lock); ++ interrupted = req->interrupted; ++ pthread_mutex_unlock(&req->se->lock); ++ ++ return interrupted; ++} ++ ++static struct { ++ void (*func)(fuse_req_t, fuse_ino_t, const void *); ++ const char *name; ++} fuse_ll_ops[] = { ++ [FUSE_LOOKUP] = { do_lookup, "LOOKUP" }, ++ [FUSE_FORGET] = { do_forget, "FORGET" }, ++ [FUSE_GETATTR] = { do_getattr, "GETATTR" }, ++ [FUSE_SETATTR] = { do_setattr, "SETATTR" }, ++ [FUSE_READLINK] = { do_readlink, "READLINK" }, ++ [FUSE_SYMLINK] = { do_symlink, "SYMLINK" }, ++ [FUSE_MKNOD] = { do_mknod, "MKNOD" }, ++ [FUSE_MKDIR] = { do_mkdir, "MKDIR" }, ++ [FUSE_UNLINK] = { do_unlink, "UNLINK" }, ++ [FUSE_RMDIR] = { do_rmdir, "RMDIR" }, ++ [FUSE_RENAME] = { do_rename, "RENAME" }, ++ [FUSE_LINK] = { do_link, "LINK" }, ++ [FUSE_OPEN] = { do_open, "OPEN" }, ++ [FUSE_READ] = { do_read, "READ" }, ++ [FUSE_WRITE] = { do_write, "WRITE" }, ++ [FUSE_STATFS] = { do_statfs, "STATFS" }, ++ [FUSE_RELEASE] = { do_release, "RELEASE" }, ++ [FUSE_FSYNC] = { do_fsync, "FSYNC" }, ++ [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" }, ++ [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" }, ++ [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" }, ++ [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" }, ++ [FUSE_FLUSH] = { do_flush, "FLUSH" }, ++ [FUSE_INIT] = { do_init, "INIT" }, ++ [FUSE_OPENDIR] = { do_opendir, "OPENDIR" }, ++ [FUSE_READDIR] = { do_readdir, "READDIR" }, ++ [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" }, ++ [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" }, ++ [FUSE_GETLK] = { do_getlk, "GETLK" }, ++ [FUSE_SETLK] = { do_setlk, "SETLK" }, ++ [FUSE_SETLKW] = { do_setlkw, "SETLKW" }, ++ [FUSE_ACCESS] = { do_access, "ACCESS" }, ++ [FUSE_CREATE] = { do_create, "CREATE" }, ++ [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" }, ++ [FUSE_BMAP] = { do_bmap, "BMAP" }, ++ [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, ++ [FUSE_POLL] = { do_poll, "POLL" }, ++ [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" }, ++ [FUSE_DESTROY] = { do_destroy, "DESTROY" }, ++ [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" }, ++ [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" }, ++ [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"}, ++ [FUSE_RENAME2] = { do_rename2, "RENAME2" }, ++ [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" }, ++ [FUSE_LSEEK] = { do_lseek, "LSEEK" }, ++ [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" }, ++}; ++ ++#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) ++ ++static const char *opname(enum fuse_opcode opcode) ++{ ++ if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name) ++ return "???"; ++ else ++ return fuse_ll_ops[opcode].name; ++} ++ ++static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst, ++ struct fuse_bufvec *src) ++{ ++ ssize_t res = fuse_buf_copy(dst, src, 0); ++ if (res < 0) { ++ fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res)); ++ return res; ++ } ++ if ((size_t)res < fuse_buf_size(dst)) { ++ fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n"); ++ return -1; ++ } ++ return 0; ++} ++ ++void fuse_session_process_buf(struct fuse_session *se, ++ const struct fuse_buf *buf) ++{ ++ fuse_session_process_buf_int(se, buf, NULL); ++} ++ ++void fuse_session_process_buf_int(struct fuse_session *se, ++ const struct fuse_buf *buf, struct fuse_chan *ch) ++{ ++ const size_t write_header_size = sizeof(struct fuse_in_header) + ++ sizeof(struct fuse_write_in); ++ struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 }; ++ struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size); ++ struct fuse_in_header *in; ++ const void *inarg; ++ struct fuse_req *req; ++ void *mbuf = NULL; ++ int err; ++ int res; ++ ++ if (buf->flags & FUSE_BUF_IS_FD) { ++ if (buf->size < tmpbuf.buf[0].size) ++ tmpbuf.buf[0].size = buf->size; ++ ++ mbuf = malloc(tmpbuf.buf[0].size); ++ if (mbuf == NULL) { ++ fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n"); ++ goto clear_pipe; ++ } ++ tmpbuf.buf[0].mem = mbuf; ++ ++ res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv); ++ if (res < 0) ++ goto clear_pipe; ++ ++ in = mbuf; ++ } else { ++ in = buf->mem; ++ } ++ ++ if (se->debug) { ++ fuse_log(FUSE_LOG_DEBUG, ++ "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n", ++ (unsigned long long) in->unique, ++ opname((enum fuse_opcode) in->opcode), in->opcode, ++ (unsigned long long) in->nodeid, buf->size, in->pid); ++ } ++ ++ req = fuse_ll_alloc_req(se); ++ if (req == NULL) { ++ struct fuse_out_header out = { ++ .unique = in->unique, ++ .error = -ENOMEM, ++ }; ++ struct iovec iov = { ++ .iov_base = &out, ++ .iov_len = sizeof(struct fuse_out_header), ++ }; ++ ++ fuse_send_msg(se, ch, &iov, 1); ++ goto clear_pipe; ++ } ++ ++ req->unique = in->unique; ++ req->ctx.uid = in->uid; ++ req->ctx.gid = in->gid; ++ req->ctx.pid = in->pid; ++ req->ch = ch ? fuse_chan_get(ch) : NULL; ++ ++ err = EIO; ++ if (!se->got_init) { ++ enum fuse_opcode expected; ++ ++ expected = se->cuse_data ? CUSE_INIT : FUSE_INIT; ++ if (in->opcode != expected) ++ goto reply_err; ++ } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT) ++ goto reply_err; ++ ++ err = EACCES; ++ /* Implement -o allow_root */ ++ if (se->deny_others && in->uid != se->owner && in->uid != 0 && ++ in->opcode != FUSE_INIT && in->opcode != FUSE_READ && ++ in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC && ++ in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR && ++ in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR && ++ in->opcode != FUSE_NOTIFY_REPLY && ++ in->opcode != FUSE_READDIRPLUS) ++ goto reply_err; ++ ++ err = ENOSYS; ++ if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) ++ goto reply_err; ++ if (in->opcode != FUSE_INTERRUPT) { ++ struct fuse_req *intr; ++ pthread_mutex_lock(&se->lock); ++ intr = check_interrupt(se, req); ++ list_add_req(req, &se->list); ++ pthread_mutex_unlock(&se->lock); ++ if (intr) ++ fuse_reply_err(intr, EAGAIN); ++ } ++ ++ if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size && ++ (in->opcode != FUSE_WRITE || !se->op.write_buf) && ++ in->opcode != FUSE_NOTIFY_REPLY) { ++ void *newmbuf; ++ ++ err = ENOMEM; ++ newmbuf = realloc(mbuf, buf->size); ++ if (newmbuf == NULL) ++ goto reply_err; ++ mbuf = newmbuf; ++ ++ tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size); ++ tmpbuf.buf[0].mem = (char *)mbuf + write_header_size; ++ ++ res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv); ++ err = -res; ++ if (res < 0) ++ goto reply_err; ++ ++ in = mbuf; ++ } ++ ++ inarg = (void *) &in[1]; ++ if (in->opcode == FUSE_WRITE && se->op.write_buf) ++ do_write_buf(req, in->nodeid, inarg, buf); ++ else if (in->opcode == FUSE_NOTIFY_REPLY) ++ do_notify_reply(req, in->nodeid, inarg, buf); ++ else ++ fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); ++ ++out_free: ++ free(mbuf); ++ return; ++ ++reply_err: ++ fuse_reply_err(req, err); ++clear_pipe: ++ if (buf->flags & FUSE_BUF_IS_FD) ++ fuse_ll_clear_pipe(se); ++ goto out_free; ++} ++ ++#define LL_OPTION(n,o,v) \ ++ { n, offsetof(struct fuse_session, o), v } ++ ++static const struct fuse_opt fuse_ll_opts[] = { ++ LL_OPTION("debug", debug, 1), ++ LL_OPTION("-d", debug, 1), ++ LL_OPTION("--debug", debug, 1), ++ LL_OPTION("allow_root", deny_others, 1), ++ FUSE_OPT_END ++}; ++ ++void fuse_lowlevel_version(void) ++{ ++ printf("using FUSE kernel interface version %i.%i\n", ++ FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); ++ fuse_mount_version(); ++} ++ ++void fuse_lowlevel_help(void) ++{ ++ /* These are not all options, but the ones that are ++ potentially of interest to an end-user */ ++ printf( ++" -o allow_other allow access by all users\n" ++" -o allow_root allow access by root\n" ++" -o auto_unmount auto unmount on process termination\n"); ++} ++ ++void fuse_session_destroy(struct fuse_session *se) ++{ ++ struct fuse_ll_pipe *llp; ++ ++ if (se->got_init && !se->got_destroy) { ++ if (se->op.destroy) ++ se->op.destroy(se->userdata); ++ } ++ llp = pthread_getspecific(se->pipe_key); ++ if (llp != NULL) ++ fuse_ll_pipe_free(llp); ++ pthread_key_delete(se->pipe_key); ++ pthread_mutex_destroy(&se->lock); ++ free(se->cuse_data); ++ if (se->fd != -1) ++ close(se->fd); ++ destroy_mount_opts(se->mo); ++ free(se); ++} ++ ++ ++static void fuse_ll_pipe_destructor(void *data) ++{ ++ struct fuse_ll_pipe *llp = data; ++ fuse_ll_pipe_free(llp); ++} ++ ++int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf) ++{ ++ return fuse_session_receive_buf_int(se, buf, NULL); ++} ++ ++int fuse_session_receive_buf_int(struct fuse_session *se, struct fuse_buf *buf, ++ struct fuse_chan *ch) ++{ ++ int err; ++ ssize_t res; ++#ifdef HAVE_SPLICE ++ size_t bufsize = se->bufsize; ++ struct fuse_ll_pipe *llp; ++ struct fuse_buf tmpbuf; ++ ++ if (se->conn.proto_minor < 14 || !(se->conn.want & FUSE_CAP_SPLICE_READ)) ++ goto fallback; ++ ++ llp = fuse_ll_get_pipe(se); ++ if (llp == NULL) ++ goto fallback; ++ ++ if (llp->size < bufsize) { ++ if (llp->can_grow) { ++ res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize); ++ if (res == -1) { ++ llp->can_grow = 0; ++ res = grow_pipe_to_max(llp->pipe[0]); ++ if (res > 0) ++ llp->size = res; ++ goto fallback; ++ } ++ llp->size = res; ++ } ++ if (llp->size < bufsize) ++ goto fallback; ++ } ++ ++ res = splice(ch ? ch->fd : se->fd, ++ NULL, llp->pipe[1], NULL, bufsize, 0); ++ err = errno; ++ ++ if (fuse_session_exited(se)) ++ return 0; ++ ++ if (res == -1) { ++ if (err == ENODEV) { ++ /* Filesystem was unmounted, or connection was aborted ++ via /sys/fs/fuse/connections */ ++ fuse_session_exit(se); ++ return 0; ++ } ++ if (err != EINTR && err != EAGAIN) ++ perror("fuse: splice from device"); ++ return -err; ++ } ++ ++ if (res < sizeof(struct fuse_in_header)) { ++ fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n"); ++ return -EIO; ++ } ++ ++ tmpbuf = (struct fuse_buf) { ++ .size = res, ++ .flags = FUSE_BUF_IS_FD, ++ .fd = llp->pipe[0], ++ }; ++ ++ /* ++ * Don't bother with zero copy for small requests. ++ * fuse_loop_mt() needs to check for FORGET so this more than ++ * just an optimization. ++ */ ++ if (res < sizeof(struct fuse_in_header) + ++ sizeof(struct fuse_write_in) + pagesize) { ++ struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 }; ++ struct fuse_bufvec dst = { .count = 1 }; ++ ++ if (!buf->mem) { ++ buf->mem = malloc(se->bufsize); ++ if (!buf->mem) { ++ fuse_log(FUSE_LOG_ERR, ++ "fuse: failed to allocate read buffer\n"); ++ return -ENOMEM; ++ } ++ } ++ buf->size = se->bufsize; ++ buf->flags = 0; ++ dst.buf[0] = *buf; ++ ++ res = fuse_buf_copy(&dst, &src, 0); ++ if (res < 0) { ++ fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", ++ strerror(-res)); ++ fuse_ll_clear_pipe(se); ++ return res; ++ } ++ if (res < tmpbuf.size) { ++ fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n"); ++ fuse_ll_clear_pipe(se); ++ return -EIO; ++ } ++ assert(res == tmpbuf.size); ++ ++ } else { ++ /* Don't overwrite buf->mem, as that would cause a leak */ ++ buf->fd = tmpbuf.fd; ++ buf->flags = tmpbuf.flags; ++ } ++ buf->size = tmpbuf.size; ++ ++ return res; ++ ++fallback: ++#endif ++ if (!buf->mem) { ++ buf->mem = malloc(se->bufsize); ++ if (!buf->mem) { ++ fuse_log(FUSE_LOG_ERR, ++ "fuse: failed to allocate read buffer\n"); ++ return -ENOMEM; ++ } ++ } ++ ++restart: ++ res = read(ch ? ch->fd : se->fd, buf->mem, se->bufsize); ++ err = errno; ++ ++ if (fuse_session_exited(se)) ++ return 0; ++ if (res == -1) { ++ /* ENOENT means the operation was interrupted, it's safe ++ to restart */ ++ if (err == ENOENT) ++ goto restart; ++ ++ if (err == ENODEV) { ++ /* Filesystem was unmounted, or connection was aborted ++ via /sys/fs/fuse/connections */ ++ fuse_session_exit(se); ++ return 0; ++ } ++ /* Errors occurring during normal operation: EINTR (read ++ interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem ++ umounted) */ ++ if (err != EINTR && err != EAGAIN) ++ perror("fuse: reading device"); ++ return -err; ++ } ++ if ((size_t) res < sizeof(struct fuse_in_header)) { ++ fuse_log(FUSE_LOG_ERR, "short read on fuse device\n"); ++ return -EIO; ++ } ++ ++ buf->size = res; ++ ++ return res; ++} ++ ++struct fuse_session *fuse_session_new(struct fuse_args *args, ++ const struct fuse_lowlevel_ops *op, ++ size_t op_size, void *userdata) ++{ ++ int err; ++ struct fuse_session *se; ++ struct mount_opts *mo; ++ ++ if (sizeof(struct fuse_lowlevel_ops) < op_size) { ++ fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n"); ++ op_size = sizeof(struct fuse_lowlevel_ops); ++ } ++ ++ if (args->argc == 0) { ++ fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n"); ++ return NULL; ++ } ++ ++ se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session)); ++ if (se == NULL) { ++ fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n"); ++ goto out1; ++ } ++ se->fd = -1; ++ se->conn.max_write = UINT_MAX; ++ se->conn.max_readahead = UINT_MAX; ++ ++ /* Parse options */ ++ if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1) ++ goto out2; ++ if(se->deny_others) { ++ /* Allowing access only by root is done by instructing ++ * kernel to allow access by everyone, and then restricting ++ * access to root and mountpoint owner in libfuse. ++ */ ++ // We may be adding the option a second time, but ++ // that doesn't hurt. ++ if(fuse_opt_add_arg(args, "-oallow_other") == -1) ++ goto out2; ++ } ++ mo = parse_mount_opts(args); ++ if (mo == NULL) ++ goto out3; ++ ++ if(args->argc == 1 && ++ args->argv[0][0] == '-') { ++ fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but " ++ "will be ignored\n"); ++ } else if (args->argc != 1) { ++ int i; ++ fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `"); ++ for(i = 1; i < args->argc-1; i++) ++ fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]); ++ fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]); ++ goto out4; ++ } ++ ++ if (se->debug) ++ fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSION); ++ ++ se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() + ++ FUSE_BUFFER_HEADER_SIZE; ++ ++ list_init_req(&se->list); ++ list_init_req(&se->interrupts); ++ list_init_nreq(&se->notify_list); ++ se->notify_ctr = 1; ++ fuse_mutex_init(&se->lock); ++ ++ err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor); ++ if (err) { ++ fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n", ++ strerror(err)); ++ goto out5; ++ } ++ ++ memcpy(&se->op, op, op_size); ++ se->owner = getuid(); ++ se->userdata = userdata; ++ ++ se->mo = mo; ++ return se; ++ ++out5: ++ pthread_mutex_destroy(&se->lock); ++out4: ++ fuse_opt_free_args(args); ++out3: ++ free(mo); ++out2: ++ free(se); ++out1: ++ return NULL; ++} ++ ++int fuse_session_mount(struct fuse_session *se, const char *mountpoint) ++{ ++ int fd; ++ ++ /* ++ * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos ++ * would ensue. ++ */ ++ do { ++ fd = open("/dev/null", O_RDWR); ++ if (fd > 2) ++ close(fd); ++ } while (fd >= 0 && fd <= 2); ++ ++ /* ++ * To allow FUSE daemons to run without privileges, the caller may open ++ * /dev/fuse before launching the file system and pass on the file ++ * descriptor by specifying /dev/fd/N as the mount point. Note that the ++ * parent process takes care of performing the mount in this case. ++ */ ++ fd = fuse_mnt_parse_fuse_fd(mountpoint); ++ if (fd != -1) { ++ if (fcntl(fd, F_GETFD) == -1) { ++ fuse_log(FUSE_LOG_ERR, ++ "fuse: Invalid file descriptor /dev/fd/%u\n", ++ fd); ++ return -1; ++ } ++ se->fd = fd; ++ return 0; ++ } ++ ++ /* Open channel */ ++ fd = fuse_kern_mount(mountpoint, se->mo); ++ if (fd == -1) ++ return -1; ++ se->fd = fd; ++ ++ /* Save mountpoint */ ++ se->mountpoint = strdup(mountpoint); ++ if (se->mountpoint == NULL) ++ goto error_out; ++ ++ return 0; ++ ++error_out: ++ fuse_kern_unmount(mountpoint, fd); ++ return -1; ++} ++ ++int fuse_session_fd(struct fuse_session *se) ++{ ++ return se->fd; ++} ++ ++void fuse_session_unmount(struct fuse_session *se) ++{ ++ if (se->mountpoint != NULL) { ++ fuse_kern_unmount(se->mountpoint, se->fd); ++ free(se->mountpoint); ++ se->mountpoint = NULL; ++ } ++} ++ ++#ifdef linux ++int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) ++{ ++ char *buf; ++ size_t bufsize = 1024; ++ char path[128]; ++ int ret; ++ int fd; ++ unsigned long pid = req->ctx.pid; ++ char *s; ++ ++ sprintf(path, "/proc/%lu/task/%lu/status", pid, pid); ++ ++retry: ++ buf = malloc(bufsize); ++ if (buf == NULL) ++ return -ENOMEM; ++ ++ ret = -EIO; ++ fd = open(path, O_RDONLY); ++ if (fd == -1) ++ goto out_free; ++ ++ ret = read(fd, buf, bufsize); ++ close(fd); ++ if (ret < 0) { ++ ret = -EIO; ++ goto out_free; ++ } ++ ++ if ((size_t)ret == bufsize) { ++ free(buf); ++ bufsize *= 4; ++ goto retry; ++ } ++ ++ ret = -EIO; ++ s = strstr(buf, "\nGroups:"); ++ if (s == NULL) ++ goto out_free; ++ ++ s += 8; ++ ret = 0; ++ while (1) { ++ char *end; ++ unsigned long val = strtoul(s, &end, 0); ++ if (end == s) ++ break; ++ ++ s = end; ++ if (ret < size) ++ list[ret] = val; ++ ret++; ++ } ++ ++out_free: ++ free(buf); ++ return ret; ++} ++#else /* linux */ ++/* ++ * This is currently not implemented on other than Linux... ++ */ ++int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) ++{ ++ (void) req; (void) size; (void) list; ++ return -ENOSYS; ++} ++#endif ++ ++void fuse_session_exit(struct fuse_session *se) ++{ ++ se->exited = 1; ++} ++ ++void fuse_session_reset(struct fuse_session *se) ++{ ++ se->exited = 0; ++ se->error = 0; ++} ++ ++int fuse_session_exited(struct fuse_session *se) ++{ ++ return se->exited; ++} diff --git a/0014-virtiofsd-Add-passthrough_ll.patch b/0014-virtiofsd-Add-passthrough_ll.patch new file mode 100644 index 0000000..3e1f0b0 --- /dev/null +++ b/0014-virtiofsd-Add-passthrough_ll.patch @@ -0,0 +1,1370 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:00:43 +0000 +Subject: [PATCH] virtiofsd: Add passthrough_ll +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +passthrough_ll is one of the examples in the upstream fuse project +and is the main part of our daemon here. It passes through requests +from fuse to the underlying filesystem, using syscalls as directly +as possible. + +>From libfuse fuse-3.8.0 + +Signed-off-by: Dr. David Alan Gilbert + Fixed up 'GPL' to 'GPLv2' as per Dan's comments and consistent + with the 'LICENSE' file in libfuse; patch sent to libfuse to fix + it upstream. +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 7c6b66027241f41720240fc6ee1021cdbd975b2e) +--- + tools/virtiofsd/passthrough_ll.c | 1338 ++++++++++++++++++++++++++++++ + 1 file changed, 1338 insertions(+) + create mode 100644 tools/virtiofsd/passthrough_ll.c + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +new file mode 100644 +index 0000000000..e1a605691a +--- /dev/null ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -0,0 +1,1338 @@ ++/* ++ FUSE: Filesystem in Userspace ++ Copyright (C) 2001-2007 Miklos Szeredi ++ ++ This program can be distributed under the terms of the GNU GPLv2. ++ See the file COPYING. ++*/ ++ ++/** @file ++ * ++ * This file system mirrors the existing file system hierarchy of the ++ * system, starting at the root file system. This is implemented by ++ * just "passing through" all requests to the corresponding user-space ++ * libc functions. In contrast to passthrough.c and passthrough_fh.c, ++ * this implementation uses the low-level API. Its performance should ++ * be the least bad among the three, but many operations are not ++ * implemented. In particular, it is not possible to remove files (or ++ * directories) because the code necessary to defer actual removal ++ * until the file is not opened anymore would make the example much ++ * more complicated. ++ * ++ * When writeback caching is enabled (-o writeback mount option), it ++ * is only possible to write to files for which the mounting user has ++ * read permissions. This is because the writeback cache requires the ++ * kernel to be able to issue read requests for all files (which the ++ * passthrough filesystem cannot satisfy if it can't read the file in ++ * the underlying filesystem). ++ * ++ * Compile with: ++ * ++ * gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o passthrough_ll ++ * ++ * ## Source code ## ++ * \include passthrough_ll.c ++ */ ++ ++#define _GNU_SOURCE ++#define FUSE_USE_VERSION 31 ++ ++#include "config.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "passthrough_helpers.h" ++ ++/* We are re-using pointers to our `struct lo_inode` and `struct ++ lo_dirp` elements as inodes. This means that we must be able to ++ store uintptr_t values in a fuse_ino_t variable. The following ++ incantation checks this condition at compile time. */ ++#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus ++_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t), ++ "fuse_ino_t too small to hold uintptr_t values!"); ++#else ++struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \ ++ { unsigned _uintptr_to_must_hold_fuse_ino_t: ++ ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); }; ++#endif ++ ++struct lo_inode { ++ struct lo_inode *next; /* protected by lo->mutex */ ++ struct lo_inode *prev; /* protected by lo->mutex */ ++ int fd; ++ bool is_symlink; ++ ino_t ino; ++ dev_t dev; ++ uint64_t refcount; /* protected by lo->mutex */ ++}; ++ ++enum { ++ CACHE_NEVER, ++ CACHE_NORMAL, ++ CACHE_ALWAYS, ++}; ++ ++struct lo_data { ++ pthread_mutex_t mutex; ++ int debug; ++ int writeback; ++ int flock; ++ int xattr; ++ const char *source; ++ double timeout; ++ int cache; ++ int timeout_set; ++ struct lo_inode root; /* protected by lo->mutex */ ++}; ++ ++static const struct fuse_opt lo_opts[] = { ++ { "writeback", ++ offsetof(struct lo_data, writeback), 1 }, ++ { "no_writeback", ++ offsetof(struct lo_data, writeback), 0 }, ++ { "source=%s", ++ offsetof(struct lo_data, source), 0 }, ++ { "flock", ++ offsetof(struct lo_data, flock), 1 }, ++ { "no_flock", ++ offsetof(struct lo_data, flock), 0 }, ++ { "xattr", ++ offsetof(struct lo_data, xattr), 1 }, ++ { "no_xattr", ++ offsetof(struct lo_data, xattr), 0 }, ++ { "timeout=%lf", ++ offsetof(struct lo_data, timeout), 0 }, ++ { "timeout=", ++ offsetof(struct lo_data, timeout_set), 1 }, ++ { "cache=never", ++ offsetof(struct lo_data, cache), CACHE_NEVER }, ++ { "cache=auto", ++ offsetof(struct lo_data, cache), CACHE_NORMAL }, ++ { "cache=always", ++ offsetof(struct lo_data, cache), CACHE_ALWAYS }, ++ ++ FUSE_OPT_END ++}; ++ ++static struct lo_data *lo_data(fuse_req_t req) ++{ ++ return (struct lo_data *) fuse_req_userdata(req); ++} ++ ++static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) ++{ ++ if (ino == FUSE_ROOT_ID) ++ return &lo_data(req)->root; ++ else ++ return (struct lo_inode *) (uintptr_t) ino; ++} ++ ++static int lo_fd(fuse_req_t req, fuse_ino_t ino) ++{ ++ return lo_inode(req, ino)->fd; ++} ++ ++static bool lo_debug(fuse_req_t req) ++{ ++ return lo_data(req)->debug != 0; ++} ++ ++static void lo_init(void *userdata, ++ struct fuse_conn_info *conn) ++{ ++ struct lo_data *lo = (struct lo_data*) userdata; ++ ++ if(conn->capable & FUSE_CAP_EXPORT_SUPPORT) ++ conn->want |= FUSE_CAP_EXPORT_SUPPORT; ++ ++ if (lo->writeback && ++ conn->capable & FUSE_CAP_WRITEBACK_CACHE) { ++ if (lo->debug) ++ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); ++ conn->want |= FUSE_CAP_WRITEBACK_CACHE; ++ } ++ if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { ++ if (lo->debug) ++ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); ++ conn->want |= FUSE_CAP_FLOCK_LOCKS; ++ } ++} ++ ++static void lo_getattr(fuse_req_t req, fuse_ino_t ino, ++ struct fuse_file_info *fi) ++{ ++ int res; ++ struct stat buf; ++ struct lo_data *lo = lo_data(req); ++ ++ (void) fi; ++ ++ res = fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); ++ if (res == -1) ++ return (void) fuse_reply_err(req, errno); ++ ++ fuse_reply_attr(req, &buf, lo->timeout); ++} ++ ++static int utimensat_empty_nofollow(struct lo_inode *inode, ++ const struct timespec *tv) ++{ ++ int res; ++ char procname[64]; ++ ++ if (inode->is_symlink) { ++ res = utimensat(inode->fd, "", tv, ++ AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); ++ if (res == -1 && errno == EINVAL) { ++ /* Sorry, no race free way to set times on symlink. */ ++ errno = EPERM; ++ } ++ return res; ++ } ++ sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ ++ return utimensat(AT_FDCWD, procname, tv, 0); ++} ++ ++static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, ++ int valid, struct fuse_file_info *fi) ++{ ++ int saverr; ++ char procname[64]; ++ struct lo_inode *inode = lo_inode(req, ino); ++ int ifd = inode->fd; ++ int res; ++ ++ if (valid & FUSE_SET_ATTR_MODE) { ++ if (fi) { ++ res = fchmod(fi->fh, attr->st_mode); ++ } else { ++ sprintf(procname, "/proc/self/fd/%i", ifd); ++ res = chmod(procname, attr->st_mode); ++ } ++ if (res == -1) ++ goto out_err; ++ } ++ if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) { ++ uid_t uid = (valid & FUSE_SET_ATTR_UID) ? ++ attr->st_uid : (uid_t) -1; ++ gid_t gid = (valid & FUSE_SET_ATTR_GID) ? ++ attr->st_gid : (gid_t) -1; ++ ++ res = fchownat(ifd, "", uid, gid, ++ AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); ++ if (res == -1) ++ goto out_err; ++ } ++ if (valid & FUSE_SET_ATTR_SIZE) { ++ if (fi) { ++ res = ftruncate(fi->fh, attr->st_size); ++ } else { ++ sprintf(procname, "/proc/self/fd/%i", ifd); ++ res = truncate(procname, attr->st_size); ++ } ++ if (res == -1) ++ goto out_err; ++ } ++ if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { ++ struct timespec tv[2]; ++ ++ tv[0].tv_sec = 0; ++ tv[1].tv_sec = 0; ++ tv[0].tv_nsec = UTIME_OMIT; ++ tv[1].tv_nsec = UTIME_OMIT; ++ ++ if (valid & FUSE_SET_ATTR_ATIME_NOW) ++ tv[0].tv_nsec = UTIME_NOW; ++ else if (valid & FUSE_SET_ATTR_ATIME) ++ tv[0] = attr->st_atim; ++ ++ if (valid & FUSE_SET_ATTR_MTIME_NOW) ++ tv[1].tv_nsec = UTIME_NOW; ++ else if (valid & FUSE_SET_ATTR_MTIME) ++ tv[1] = attr->st_mtim; ++ ++ if (fi) ++ res = futimens(fi->fh, tv); ++ else ++ res = utimensat_empty_nofollow(inode, tv); ++ if (res == -1) ++ goto out_err; ++ } ++ ++ return lo_getattr(req, ino, fi); ++ ++out_err: ++ saverr = errno; ++ fuse_reply_err(req, saverr); ++} ++ ++static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st) ++{ ++ struct lo_inode *p; ++ struct lo_inode *ret = NULL; ++ ++ pthread_mutex_lock(&lo->mutex); ++ for (p = lo->root.next; p != &lo->root; p = p->next) { ++ if (p->ino == st->st_ino && p->dev == st->st_dev) { ++ assert(p->refcount > 0); ++ ret = p; ++ ret->refcount++; ++ break; ++ } ++ } ++ pthread_mutex_unlock(&lo->mutex); ++ return ret; ++} ++ ++static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, ++ struct fuse_entry_param *e) ++{ ++ int newfd; ++ int res; ++ int saverr; ++ struct lo_data *lo = lo_data(req); ++ struct lo_inode *inode; ++ ++ memset(e, 0, sizeof(*e)); ++ e->attr_timeout = lo->timeout; ++ e->entry_timeout = lo->timeout; ++ ++ newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW); ++ if (newfd == -1) ++ goto out_err; ++ ++ res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); ++ if (res == -1) ++ goto out_err; ++ ++ inode = lo_find(lo_data(req), &e->attr); ++ if (inode) { ++ close(newfd); ++ newfd = -1; ++ } else { ++ struct lo_inode *prev, *next; ++ ++ saverr = ENOMEM; ++ inode = calloc(1, sizeof(struct lo_inode)); ++ if (!inode) ++ goto out_err; ++ ++ inode->is_symlink = S_ISLNK(e->attr.st_mode); ++ inode->refcount = 1; ++ inode->fd = newfd; ++ inode->ino = e->attr.st_ino; ++ inode->dev = e->attr.st_dev; ++ ++ pthread_mutex_lock(&lo->mutex); ++ prev = &lo->root; ++ next = prev->next; ++ next->prev = inode; ++ inode->next = next; ++ inode->prev = prev; ++ prev->next = inode; ++ pthread_mutex_unlock(&lo->mutex); ++ } ++ e->ino = (uintptr_t) inode; ++ ++ if (lo_debug(req)) ++ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", ++ (unsigned long long) parent, name, (unsigned long long) e->ino); ++ ++ return 0; ++ ++out_err: ++ saverr = errno; ++ if (newfd != -1) ++ close(newfd); ++ return saverr; ++} ++ ++static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) ++{ ++ struct fuse_entry_param e; ++ int err; ++ ++ if (lo_debug(req)) ++ fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", ++ parent, name); ++ ++ err = lo_do_lookup(req, parent, name, &e); ++ if (err) ++ fuse_reply_err(req, err); ++ else ++ fuse_reply_entry(req, &e); ++} ++ ++static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, ++ const char *name, mode_t mode, dev_t rdev, ++ const char *link) ++{ ++ int res; ++ int saverr; ++ struct lo_inode *dir = lo_inode(req, parent); ++ struct fuse_entry_param e; ++ ++ saverr = ENOMEM; ++ ++ res = mknod_wrapper(dir->fd, name, link, mode, rdev); ++ ++ saverr = errno; ++ if (res == -1) ++ goto out; ++ ++ saverr = lo_do_lookup(req, parent, name, &e); ++ if (saverr) ++ goto out; ++ ++ if (lo_debug(req)) ++ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", ++ (unsigned long long) parent, name, (unsigned long long) e.ino); ++ ++ fuse_reply_entry(req, &e); ++ return; ++ ++out: ++ fuse_reply_err(req, saverr); ++} ++ ++static void lo_mknod(fuse_req_t req, fuse_ino_t parent, ++ const char *name, mode_t mode, dev_t rdev) ++{ ++ lo_mknod_symlink(req, parent, name, mode, rdev, NULL); ++} ++ ++static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, ++ mode_t mode) ++{ ++ lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL); ++} ++ ++static void lo_symlink(fuse_req_t req, const char *link, ++ fuse_ino_t parent, const char *name) ++{ ++ lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link); ++} ++ ++static int linkat_empty_nofollow(struct lo_inode *inode, int dfd, ++ const char *name) ++{ ++ int res; ++ char procname[64]; ++ ++ if (inode->is_symlink) { ++ res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH); ++ if (res == -1 && (errno == ENOENT || errno == EINVAL)) { ++ /* Sorry, no race free way to hard-link a symlink. */ ++ errno = EPERM; ++ } ++ return res; ++ } ++ ++ sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ ++ return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW); ++} ++ ++static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, ++ const char *name) ++{ ++ int res; ++ struct lo_data *lo = lo_data(req); ++ struct lo_inode *inode = lo_inode(req, ino); ++ struct fuse_entry_param e; ++ int saverr; ++ ++ memset(&e, 0, sizeof(struct fuse_entry_param)); ++ e.attr_timeout = lo->timeout; ++ e.entry_timeout = lo->timeout; ++ ++ res = linkat_empty_nofollow(inode, lo_fd(req, parent), name); ++ if (res == -1) ++ goto out_err; ++ ++ res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); ++ if (res == -1) ++ goto out_err; ++ ++ pthread_mutex_lock(&lo->mutex); ++ inode->refcount++; ++ pthread_mutex_unlock(&lo->mutex); ++ e.ino = (uintptr_t) inode; ++ ++ if (lo_debug(req)) ++ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", ++ (unsigned long long) parent, name, ++ (unsigned long long) e.ino); ++ ++ fuse_reply_entry(req, &e); ++ return; ++ ++out_err: ++ saverr = errno; ++ fuse_reply_err(req, saverr); ++} ++ ++static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) ++{ ++ int res; ++ ++ res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); ++ ++ fuse_reply_err(req, res == -1 ? errno : 0); ++} ++ ++static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, ++ fuse_ino_t newparent, const char *newname, ++ unsigned int flags) ++{ ++ int res; ++ ++ if (flags) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ ++ res = renameat(lo_fd(req, parent), name, ++ lo_fd(req, newparent), newname); ++ ++ fuse_reply_err(req, res == -1 ? errno : 0); ++} ++ ++static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) ++{ ++ int res; ++ ++ res = unlinkat(lo_fd(req, parent), name, 0); ++ ++ fuse_reply_err(req, res == -1 ? errno : 0); ++} ++ ++static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) ++{ ++ if (!inode) ++ return; ++ ++ pthread_mutex_lock(&lo->mutex); ++ assert(inode->refcount >= n); ++ inode->refcount -= n; ++ if (!inode->refcount) { ++ struct lo_inode *prev, *next; ++ ++ prev = inode->prev; ++ next = inode->next; ++ next->prev = prev; ++ prev->next = next; ++ ++ pthread_mutex_unlock(&lo->mutex); ++ close(inode->fd); ++ free(inode); ++ ++ } else { ++ pthread_mutex_unlock(&lo->mutex); ++ } ++} ++ ++static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) ++{ ++ struct lo_data *lo = lo_data(req); ++ struct lo_inode *inode = lo_inode(req, ino); ++ ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", ++ (unsigned long long) ino, ++ (unsigned long long) inode->refcount, ++ (unsigned long long) nlookup); ++ } ++ ++ unref_inode(lo, inode, nlookup); ++} ++ ++static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) ++{ ++ lo_forget_one(req, ino, nlookup); ++ fuse_reply_none(req); ++} ++ ++static void lo_forget_multi(fuse_req_t req, size_t count, ++ struct fuse_forget_data *forgets) ++{ ++ int i; ++ ++ for (i = 0; i < count; i++) ++ lo_forget_one(req, forgets[i].ino, forgets[i].nlookup); ++ fuse_reply_none(req); ++} ++ ++static void lo_readlink(fuse_req_t req, fuse_ino_t ino) ++{ ++ char buf[PATH_MAX + 1]; ++ int res; ++ ++ res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf)); ++ if (res == -1) ++ return (void) fuse_reply_err(req, errno); ++ ++ if (res == sizeof(buf)) ++ return (void) fuse_reply_err(req, ENAMETOOLONG); ++ ++ buf[res] = '\0'; ++ ++ fuse_reply_readlink(req, buf); ++} ++ ++struct lo_dirp { ++ DIR *dp; ++ struct dirent *entry; ++ off_t offset; ++}; ++ ++static struct lo_dirp *lo_dirp(struct fuse_file_info *fi) ++{ ++ return (struct lo_dirp *) (uintptr_t) fi->fh; ++} ++ ++static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) ++{ ++ int error = ENOMEM; ++ struct lo_data *lo = lo_data(req); ++ struct lo_dirp *d; ++ int fd; ++ ++ d = calloc(1, sizeof(struct lo_dirp)); ++ if (d == NULL) ++ goto out_err; ++ ++ fd = openat(lo_fd(req, ino), ".", O_RDONLY); ++ if (fd == -1) ++ goto out_errno; ++ ++ d->dp = fdopendir(fd); ++ if (d->dp == NULL) ++ goto out_errno; ++ ++ d->offset = 0; ++ d->entry = NULL; ++ ++ fi->fh = (uintptr_t) d; ++ if (lo->cache == CACHE_ALWAYS) ++ fi->keep_cache = 1; ++ fuse_reply_open(req, fi); ++ return; ++ ++out_errno: ++ error = errno; ++out_err: ++ if (d) { ++ if (fd != -1) ++ close(fd); ++ free(d); ++ } ++ fuse_reply_err(req, error); ++} ++ ++static int is_dot_or_dotdot(const char *name) ++{ ++ return name[0] == '.' && (name[1] == '\0' || ++ (name[1] == '.' && name[2] == '\0')); ++} ++ ++static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, ++ off_t offset, struct fuse_file_info *fi, int plus) ++{ ++ struct lo_dirp *d = lo_dirp(fi); ++ char *buf; ++ char *p; ++ size_t rem = size; ++ int err; ++ ++ (void) ino; ++ ++ buf = calloc(1, size); ++ if (!buf) { ++ err = ENOMEM; ++ goto error; ++ } ++ p = buf; ++ ++ if (offset != d->offset) { ++ seekdir(d->dp, offset); ++ d->entry = NULL; ++ d->offset = offset; ++ } ++ while (1) { ++ size_t entsize; ++ off_t nextoff; ++ const char *name; ++ ++ if (!d->entry) { ++ errno = 0; ++ d->entry = readdir(d->dp); ++ if (!d->entry) { ++ if (errno) { // Error ++ err = errno; ++ goto error; ++ } else { // End of stream ++ break; ++ } ++ } ++ } ++ nextoff = d->entry->d_off; ++ name = d->entry->d_name; ++ fuse_ino_t entry_ino = 0; ++ if (plus) { ++ struct fuse_entry_param e; ++ if (is_dot_or_dotdot(name)) { ++ e = (struct fuse_entry_param) { ++ .attr.st_ino = d->entry->d_ino, ++ .attr.st_mode = d->entry->d_type << 12, ++ }; ++ } else { ++ err = lo_do_lookup(req, ino, name, &e); ++ if (err) ++ goto error; ++ entry_ino = e.ino; ++ } ++ ++ entsize = fuse_add_direntry_plus(req, p, rem, name, ++ &e, nextoff); ++ } else { ++ struct stat st = { ++ .st_ino = d->entry->d_ino, ++ .st_mode = d->entry->d_type << 12, ++ }; ++ entsize = fuse_add_direntry(req, p, rem, name, ++ &st, nextoff); ++ } ++ if (entsize > rem) { ++ if (entry_ino != 0) ++ lo_forget_one(req, entry_ino, 1); ++ break; ++ } ++ ++ p += entsize; ++ rem -= entsize; ++ ++ d->entry = NULL; ++ d->offset = nextoff; ++ } ++ ++ err = 0; ++error: ++ // If there's an error, we can only signal it if we haven't stored ++ // any entries yet - otherwise we'd end up with wrong lookup ++ // counts for the entries that are already in the buffer. So we ++ // return what we've collected until that point. ++ if (err && rem == size) ++ fuse_reply_err(req, err); ++ else ++ fuse_reply_buf(req, buf, size - rem); ++ free(buf); ++} ++ ++static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, ++ off_t offset, struct fuse_file_info *fi) ++{ ++ lo_do_readdir(req, ino, size, offset, fi, 0); ++} ++ ++static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, ++ off_t offset, struct fuse_file_info *fi) ++{ ++ lo_do_readdir(req, ino, size, offset, fi, 1); ++} ++ ++static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) ++{ ++ struct lo_dirp *d = lo_dirp(fi); ++ (void) ino; ++ closedir(d->dp); ++ free(d); ++ fuse_reply_err(req, 0); ++} ++ ++static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, ++ mode_t mode, struct fuse_file_info *fi) ++{ ++ int fd; ++ struct lo_data *lo = lo_data(req); ++ struct fuse_entry_param e; ++ int err; ++ ++ if (lo_debug(req)) ++ fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", ++ parent, name); ++ ++ fd = openat(lo_fd(req, parent), name, ++ (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode); ++ if (fd == -1) ++ return (void) fuse_reply_err(req, errno); ++ ++ fi->fh = fd; ++ if (lo->cache == CACHE_NEVER) ++ fi->direct_io = 1; ++ else if (lo->cache == CACHE_ALWAYS) ++ fi->keep_cache = 1; ++ ++ err = lo_do_lookup(req, parent, name, &e); ++ if (err) ++ fuse_reply_err(req, err); ++ else ++ fuse_reply_create(req, &e, fi); ++} ++ ++static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, ++ struct fuse_file_info *fi) ++{ ++ int res; ++ int fd = dirfd(lo_dirp(fi)->dp); ++ (void) ino; ++ if (datasync) ++ res = fdatasync(fd); ++ else ++ res = fsync(fd); ++ fuse_reply_err(req, res == -1 ? errno : 0); ++} ++ ++static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) ++{ ++ int fd; ++ char buf[64]; ++ struct lo_data *lo = lo_data(req); ++ ++ if (lo_debug(req)) ++ fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ++ ino, fi->flags); ++ ++ /* With writeback cache, kernel may send read requests even ++ when userspace opened write-only */ ++ if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) { ++ fi->flags &= ~O_ACCMODE; ++ fi->flags |= O_RDWR; ++ } ++ ++ /* With writeback cache, O_APPEND is handled by the kernel. ++ This breaks atomicity (since the file may change in the ++ underlying filesystem, so that the kernel's idea of the ++ end of the file isn't accurate anymore). In this example, ++ we just accept that. A more rigorous filesystem may want ++ to return an error here */ ++ if (lo->writeback && (fi->flags & O_APPEND)) ++ fi->flags &= ~O_APPEND; ++ ++ sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino)); ++ fd = open(buf, fi->flags & ~O_NOFOLLOW); ++ if (fd == -1) ++ return (void) fuse_reply_err(req, errno); ++ ++ fi->fh = fd; ++ if (lo->cache == CACHE_NEVER) ++ fi->direct_io = 1; ++ else if (lo->cache == CACHE_ALWAYS) ++ fi->keep_cache = 1; ++ fuse_reply_open(req, fi); ++} ++ ++static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) ++{ ++ (void) ino; ++ ++ close(fi->fh); ++ fuse_reply_err(req, 0); ++} ++ ++static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) ++{ ++ int res; ++ (void) ino; ++ res = close(dup(fi->fh)); ++ fuse_reply_err(req, res == -1 ? errno : 0); ++} ++ ++static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, ++ struct fuse_file_info *fi) ++{ ++ int res; ++ (void) ino; ++ if (datasync) ++ res = fdatasync(fi->fh); ++ else ++ res = fsync(fi->fh); ++ fuse_reply_err(req, res == -1 ? errno : 0); ++} ++ ++static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, ++ off_t offset, struct fuse_file_info *fi) ++{ ++ struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); ++ ++ if (lo_debug(req)) ++ fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, " ++ "off=%lu)\n", ino, size, (unsigned long) offset); ++ ++ buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; ++ buf.buf[0].fd = fi->fh; ++ buf.buf[0].pos = offset; ++ ++ fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE); ++} ++ ++static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, ++ struct fuse_bufvec *in_buf, off_t off, ++ struct fuse_file_info *fi) ++{ ++ (void) ino; ++ ssize_t res; ++ struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); ++ ++ out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; ++ out_buf.buf[0].fd = fi->fh; ++ out_buf.buf[0].pos = off; ++ ++ if (lo_debug(req)) ++ fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n", ++ ino, out_buf.buf[0].size, (unsigned long) off); ++ ++ res = fuse_buf_copy(&out_buf, in_buf, 0); ++ if(res < 0) ++ fuse_reply_err(req, -res); ++ else ++ fuse_reply_write(req, (size_t) res); ++} ++ ++static void lo_statfs(fuse_req_t req, fuse_ino_t ino) ++{ ++ int res; ++ struct statvfs stbuf; ++ ++ res = fstatvfs(lo_fd(req, ino), &stbuf); ++ if (res == -1) ++ fuse_reply_err(req, errno); ++ else ++ fuse_reply_statfs(req, &stbuf); ++} ++ ++static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, ++ off_t offset, off_t length, struct fuse_file_info *fi) ++{ ++ int err = EOPNOTSUPP; ++ (void) ino; ++ ++#ifdef HAVE_FALLOCATE ++ err = fallocate(fi->fh, mode, offset, length); ++ if (err < 0) ++ err = errno; ++ ++#elif defined(HAVE_POSIX_FALLOCATE) ++ if (mode) { ++ fuse_reply_err(req, EOPNOTSUPP); ++ return; ++ } ++ ++ err = posix_fallocate(fi->fh, offset, length); ++#endif ++ ++ fuse_reply_err(req, err); ++} ++ ++static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, ++ int op) ++{ ++ int res; ++ (void) ino; ++ ++ res = flock(fi->fh, op); ++ ++ fuse_reply_err(req, res == -1 ? errno : 0); ++} ++ ++static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, ++ size_t size) ++{ ++ char *value = NULL; ++ char procname[64]; ++ struct lo_inode *inode = lo_inode(req, ino); ++ ssize_t ret; ++ int saverr; ++ ++ saverr = ENOSYS; ++ if (!lo_data(req)->xattr) ++ goto out; ++ ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", ++ ino, name, size); ++ } ++ ++ if (inode->is_symlink) { ++ /* Sorry, no race free way to getxattr on symlink. */ ++ saverr = EPERM; ++ goto out; ++ } ++ ++ sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ ++ if (size) { ++ value = malloc(size); ++ if (!value) ++ goto out_err; ++ ++ ret = getxattr(procname, name, value, size); ++ if (ret == -1) ++ goto out_err; ++ saverr = 0; ++ if (ret == 0) ++ goto out; ++ ++ fuse_reply_buf(req, value, ret); ++ } else { ++ ret = getxattr(procname, name, NULL, 0); ++ if (ret == -1) ++ goto out_err; ++ ++ fuse_reply_xattr(req, ret); ++ } ++out_free: ++ free(value); ++ return; ++ ++out_err: ++ saverr = errno; ++out: ++ fuse_reply_err(req, saverr); ++ goto out_free; ++} ++ ++static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) ++{ ++ char *value = NULL; ++ char procname[64]; ++ struct lo_inode *inode = lo_inode(req, ino); ++ ssize_t ret; ++ int saverr; ++ ++ saverr = ENOSYS; ++ if (!lo_data(req)->xattr) ++ goto out; ++ ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n", ++ ino, size); ++ } ++ ++ if (inode->is_symlink) { ++ /* Sorry, no race free way to listxattr on symlink. */ ++ saverr = EPERM; ++ goto out; ++ } ++ ++ sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ ++ if (size) { ++ value = malloc(size); ++ if (!value) ++ goto out_err; ++ ++ ret = listxattr(procname, value, size); ++ if (ret == -1) ++ goto out_err; ++ saverr = 0; ++ if (ret == 0) ++ goto out; ++ ++ fuse_reply_buf(req, value, ret); ++ } else { ++ ret = listxattr(procname, NULL, 0); ++ if (ret == -1) ++ goto out_err; ++ ++ fuse_reply_xattr(req, ret); ++ } ++out_free: ++ free(value); ++ return; ++ ++out_err: ++ saverr = errno; ++out: ++ fuse_reply_err(req, saverr); ++ goto out_free; ++} ++ ++static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, ++ const char *value, size_t size, int flags) ++{ ++ char procname[64]; ++ struct lo_inode *inode = lo_inode(req, ino); ++ ssize_t ret; ++ int saverr; ++ ++ saverr = ENOSYS; ++ if (!lo_data(req)->xattr) ++ goto out; ++ ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n", ++ ino, name, value, size); ++ } ++ ++ if (inode->is_symlink) { ++ /* Sorry, no race free way to setxattr on symlink. */ ++ saverr = EPERM; ++ goto out; ++ } ++ ++ sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ ++ ret = setxattr(procname, name, value, size, flags); ++ saverr = ret == -1 ? errno : 0; ++ ++out: ++ fuse_reply_err(req, saverr); ++} ++ ++static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) ++{ ++ char procname[64]; ++ struct lo_inode *inode = lo_inode(req, ino); ++ ssize_t ret; ++ int saverr; ++ ++ saverr = ENOSYS; ++ if (!lo_data(req)->xattr) ++ goto out; ++ ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", ++ ino, name); ++ } ++ ++ if (inode->is_symlink) { ++ /* Sorry, no race free way to setxattr on symlink. */ ++ saverr = EPERM; ++ goto out; ++ } ++ ++ sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ ++ ret = removexattr(procname, name); ++ saverr = ret == -1 ? errno : 0; ++ ++out: ++ fuse_reply_err(req, saverr); ++} ++ ++#ifdef HAVE_COPY_FILE_RANGE ++static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, ++ struct fuse_file_info *fi_in, ++ fuse_ino_t ino_out, off_t off_out, ++ struct fuse_file_info *fi_out, size_t len, ++ int flags) ++{ ++ ssize_t res; ++ ++ if (lo_debug(req)) ++ fuse_log(FUSE_LOG_DEBUG, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, " ++ "off=%lu, ino=%" PRIu64 "/fd=%lu, " ++ "off=%lu, size=%zd, flags=0x%x)\n", ++ ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out, ++ len, flags); ++ ++ res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len, ++ flags); ++ if (res < 0) ++ fuse_reply_err(req, -errno); ++ else ++ fuse_reply_write(req, res); ++} ++#endif ++ ++static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, ++ struct fuse_file_info *fi) ++{ ++ off_t res; ++ ++ (void)ino; ++ res = lseek(fi->fh, off, whence); ++ if (res != -1) ++ fuse_reply_lseek(req, res); ++ else ++ fuse_reply_err(req, errno); ++} ++ ++static struct fuse_lowlevel_ops lo_oper = { ++ .init = lo_init, ++ .lookup = lo_lookup, ++ .mkdir = lo_mkdir, ++ .mknod = lo_mknod, ++ .symlink = lo_symlink, ++ .link = lo_link, ++ .unlink = lo_unlink, ++ .rmdir = lo_rmdir, ++ .rename = lo_rename, ++ .forget = lo_forget, ++ .forget_multi = lo_forget_multi, ++ .getattr = lo_getattr, ++ .setattr = lo_setattr, ++ .readlink = lo_readlink, ++ .opendir = lo_opendir, ++ .readdir = lo_readdir, ++ .readdirplus = lo_readdirplus, ++ .releasedir = lo_releasedir, ++ .fsyncdir = lo_fsyncdir, ++ .create = lo_create, ++ .open = lo_open, ++ .release = lo_release, ++ .flush = lo_flush, ++ .fsync = lo_fsync, ++ .read = lo_read, ++ .write_buf = lo_write_buf, ++ .statfs = lo_statfs, ++ .fallocate = lo_fallocate, ++ .flock = lo_flock, ++ .getxattr = lo_getxattr, ++ .listxattr = lo_listxattr, ++ .setxattr = lo_setxattr, ++ .removexattr = lo_removexattr, ++#ifdef HAVE_COPY_FILE_RANGE ++ .copy_file_range = lo_copy_file_range, ++#endif ++ .lseek = lo_lseek, ++}; ++ ++int main(int argc, char *argv[]) ++{ ++ struct fuse_args args = FUSE_ARGS_INIT(argc, argv); ++ struct fuse_session *se; ++ struct fuse_cmdline_opts opts; ++ struct lo_data lo = { .debug = 0, ++ .writeback = 0 }; ++ int ret = -1; ++ ++ /* Don't mask creation mode, kernel already did that */ ++ umask(0); ++ ++ pthread_mutex_init(&lo.mutex, NULL); ++ lo.root.next = lo.root.prev = &lo.root; ++ lo.root.fd = -1; ++ lo.cache = CACHE_NORMAL; ++ ++ if (fuse_parse_cmdline(&args, &opts) != 0) ++ return 1; ++ if (opts.show_help) { ++ printf("usage: %s [options] \n\n", argv[0]); ++ fuse_cmdline_help(); ++ fuse_lowlevel_help(); ++ ret = 0; ++ goto err_out1; ++ } else if (opts.show_version) { ++ printf("FUSE library version %s\n", fuse_pkgversion()); ++ fuse_lowlevel_version(); ++ ret = 0; ++ goto err_out1; ++ } ++ ++ if(opts.mountpoint == NULL) { ++ printf("usage: %s [options] \n", argv[0]); ++ printf(" %s --help\n", argv[0]); ++ ret = 1; ++ goto err_out1; ++ } ++ ++ if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1) ++ return 1; ++ ++ lo.debug = opts.debug; ++ lo.root.refcount = 2; ++ if (lo.source) { ++ struct stat stat; ++ int res; ++ ++ res = lstat(lo.source, &stat); ++ if (res == -1) { ++ fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n", ++ lo.source); ++ exit(1); ++ } ++ if (!S_ISDIR(stat.st_mode)) { ++ fuse_log(FUSE_LOG_ERR, "source is not a directory\n"); ++ exit(1); ++ } ++ ++ } else { ++ lo.source = "/"; ++ } ++ lo.root.is_symlink = false; ++ if (!lo.timeout_set) { ++ switch (lo.cache) { ++ case CACHE_NEVER: ++ lo.timeout = 0.0; ++ break; ++ ++ case CACHE_NORMAL: ++ lo.timeout = 1.0; ++ break; ++ ++ case CACHE_ALWAYS: ++ lo.timeout = 86400.0; ++ break; ++ } ++ } else if (lo.timeout < 0) { ++ fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n", ++ lo.timeout); ++ exit(1); ++ } ++ ++ lo.root.fd = open(lo.source, O_PATH); ++ if (lo.root.fd == -1) { ++ fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", ++ lo.source); ++ exit(1); ++ } ++ ++ se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); ++ if (se == NULL) ++ goto err_out1; ++ ++ if (fuse_set_signal_handlers(se) != 0) ++ goto err_out2; ++ ++ if (fuse_session_mount(se, opts.mountpoint) != 0) ++ goto err_out3; ++ ++ fuse_daemonize(opts.foreground); ++ ++ /* Block until ctrl+c or fusermount -u */ ++ if (opts.singlethread) ++ ret = fuse_session_loop(se); ++ else ++ ret = fuse_session_loop_mt(se, opts.clone_fd); ++ ++ fuse_session_unmount(se); ++err_out3: ++ fuse_remove_signal_handlers(se); ++err_out2: ++ fuse_session_destroy(se); ++err_out1: ++ free(opts.mountpoint); ++ fuse_opt_free_args(&args); ++ ++ if (lo.root.fd >= 0) ++ close(lo.root.fd); ++ ++ return ret ? 1 : 0; ++} diff --git a/0015-virtiofsd-Trim-down-imported-files.patch b/0015-virtiofsd-Trim-down-imported-files.patch new file mode 100644 index 0000000..98ee88d --- /dev/null +++ b/0015-virtiofsd-Trim-down-imported-files.patch @@ -0,0 +1,1565 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:00:44 +0000 +Subject: [PATCH] virtiofsd: Trim down imported files +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There's a lot of the original fuse code we don't need; trim them down. + +Signed-off-by: Dr. David Alan Gilbert +with additional trimming by: +Signed-off-by: Misono Tomohiro +Reviewed-by: Daniel P. Berrangé +Reviewed-by: Xiao Yang +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit a3e23f325439a290c504d6bbc48c2e742149ecab) +--- + tools/virtiofsd/buffer.c | 71 +-- + tools/virtiofsd/fuse.h | 46 -- + tools/virtiofsd/fuse_common.h | 32 -- + tools/virtiofsd/fuse_i.h | 41 -- + tools/virtiofsd/fuse_log.h | 8 - + tools/virtiofsd/fuse_lowlevel.c | 675 +------------------------- + tools/virtiofsd/fuse_lowlevel.h | 28 -- + tools/virtiofsd/fuse_opt.h | 8 - + tools/virtiofsd/helper.c | 143 ------ + tools/virtiofsd/passthrough_helpers.h | 26 - + tools/virtiofsd/passthrough_ll.c | 1 - + 11 files changed, 8 insertions(+), 1071 deletions(-) + +diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c +index 5ab9b87455..aefb7dbf15 100644 +--- a/tools/virtiofsd/buffer.c ++++ b/tools/virtiofsd/buffer.c +@@ -157,73 +157,6 @@ static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off, + return copied; + } + +-#ifdef HAVE_SPLICE +-static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off, +- const struct fuse_buf *src, size_t src_off, +- size_t len, enum fuse_buf_copy_flags flags) +-{ +- int splice_flags = 0; +- off_t *srcpos = NULL; +- off_t *dstpos = NULL; +- off_t srcpos_val; +- off_t dstpos_val; +- ssize_t res; +- size_t copied = 0; +- +- if (flags & FUSE_BUF_SPLICE_MOVE) +- splice_flags |= SPLICE_F_MOVE; +- if (flags & FUSE_BUF_SPLICE_NONBLOCK) +- splice_flags |= SPLICE_F_NONBLOCK; +- +- if (src->flags & FUSE_BUF_FD_SEEK) { +- srcpos_val = src->pos + src_off; +- srcpos = &srcpos_val; +- } +- if (dst->flags & FUSE_BUF_FD_SEEK) { +- dstpos_val = dst->pos + dst_off; +- dstpos = &dstpos_val; +- } +- +- while (len) { +- res = splice(src->fd, srcpos, dst->fd, dstpos, len, +- splice_flags); +- if (res == -1) { +- if (copied) +- break; +- +- if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE)) +- return -errno; +- +- /* Maybe splice is not supported for this combination */ +- return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, +- len); +- } +- if (res == 0) +- break; +- +- copied += res; +- if (!(src->flags & FUSE_BUF_FD_RETRY) && +- !(dst->flags & FUSE_BUF_FD_RETRY)) { +- break; +- } +- +- len -= res; +- } +- +- return copied; +-} +-#else +-static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off, +- const struct fuse_buf *src, size_t src_off, +- size_t len, enum fuse_buf_copy_flags flags) +-{ +- (void) flags; +- +- return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); +-} +-#endif +- +- + static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off, + const struct fuse_buf *src, size_t src_off, + size_t len, enum fuse_buf_copy_flags flags) +@@ -247,10 +180,8 @@ static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off, + return fuse_buf_write(dst, dst_off, src, src_off, len); + } else if (!dst_is_fd) { + return fuse_buf_read(dst, dst_off, src, src_off, len); +- } else if (flags & FUSE_BUF_NO_SPLICE) { +- return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); + } else { +- return fuse_buf_splice(dst, dst_off, src, src_off, len, flags); ++ return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); + } + } + +diff --git a/tools/virtiofsd/fuse.h b/tools/virtiofsd/fuse.h +index 883f6e59fb..3202fba6bb 100644 +--- a/tools/virtiofsd/fuse.h ++++ b/tools/virtiofsd/fuse.h +@@ -25,10 +25,6 @@ + #include + #include + +-#ifdef __cplusplus +-extern "C" { +-#endif +- + /* ----------------------------------------------------------- * + * Basic FUSE API * + * ----------------------------------------------------------- */ +@@ -978,44 +974,6 @@ int fuse_loop(struct fuse *f); + */ + void fuse_exit(struct fuse *f); + +-/** +- * FUSE event loop with multiple threads +- * +- * Requests from the kernel are processed, and the appropriate +- * operations are called. Request are processed in parallel by +- * distributing them between multiple threads. +- * +- * For a description of the return value and the conditions when the +- * event loop exits, refer to the documentation of +- * fuse_session_loop(). +- * +- * Note: using fuse_loop() instead of fuse_loop_mt() means you are running in +- * single-threaded mode, and that you will not have to worry about reentrancy, +- * though you will have to worry about recursive lookups. In single-threaded +- * mode, FUSE will wait for one callback to return before calling another. +- * +- * Enabling multiple threads, by using fuse_loop_mt(), will cause FUSE to make +- * multiple simultaneous calls into the various callback functions given by your +- * fuse_operations record. +- * +- * If you are using multiple threads, you can enjoy all the parallel execution +- * and interactive response benefits of threads, and you get to enjoy all the +- * benefits of race conditions and locking bugs, too. Ensure that any code used +- * in the callback function of fuse_operations is also thread-safe. +- * +- * @param f the FUSE handle +- * @param config loop configuration +- * @return see fuse_session_loop() +- * +- * See also: fuse_loop() +- */ +-#if FUSE_USE_VERSION < 32 +-int fuse_loop_mt_31(struct fuse *f, int clone_fd); +-#define fuse_loop_mt(f, clone_fd) fuse_loop_mt_31(f, clone_fd) +-#else +-int fuse_loop_mt(struct fuse *f, struct fuse_loop_config *config); +-#endif +- + /** + * Get the current context + * +@@ -1268,8 +1226,4 @@ struct fuse_session *fuse_get_session(struct fuse *f); + */ + int fuse_open_channel(const char *mountpoint, const char *options); + +-#ifdef __cplusplus +-} +-#endif +- + #endif /* FUSE_H_ */ +diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h +index 2d686b2ac4..bf8f8cc865 100644 +--- a/tools/virtiofsd/fuse_common.h ++++ b/tools/virtiofsd/fuse_common.h +@@ -28,10 +28,6 @@ + #define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) + #define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) + +-#ifdef __cplusplus +-extern "C" { +-#endif +- + /** + * Information about an open file. + * +@@ -100,30 +96,6 @@ struct fuse_file_info { + uint32_t poll_events; + }; + +-/** +- * Configuration parameters passed to fuse_session_loop_mt() and +- * fuse_loop_mt(). +- */ +-struct fuse_loop_config { +- /** +- * whether to use separate device fds for each thread +- * (may increase performance) +- */ +- int clone_fd; +- +- /** +- * The maximum number of available worker threads before they +- * start to get deleted when they become idle. If not +- * specified, the default is 10. +- * +- * Adjusting this has performance implications; a very small number +- * of threads in the pool will cause a lot of thread creation and +- * deletion overhead and performance may suffer. When set to 0, a new +- * thread will be created to service every operation. +- */ +- unsigned int max_idle_threads; +-}; +- + /************************************************************************** + * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' * + **************************************************************************/ +@@ -802,10 +774,6 @@ void fuse_remove_signal_handlers(struct fuse_session *se); + # error only API version 30 or greater is supported + #endif + +-#ifdef __cplusplus +-} +-#endif +- + + /* + * This interface uses 64 bit off_t. +diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h +index d38b630ac5..b39522e3ca 100644 +--- a/tools/virtiofsd/fuse_i.h ++++ b/tools/virtiofsd/fuse_i.h +@@ -9,8 +9,6 @@ + #include "fuse.h" + #include "fuse_lowlevel.h" + +-struct mount_opts; +- + struct fuse_req { + struct fuse_session *se; + uint64_t unique; +@@ -45,7 +43,6 @@ struct fuse_session { + char *mountpoint; + volatile int exited; + int fd; +- struct mount_opts *mo; + int debug; + int deny_others; + struct fuse_lowlevel_ops op; +@@ -58,7 +55,6 @@ struct fuse_session { + struct fuse_req interrupts; + pthread_mutex_t lock; + int got_destroy; +- pthread_key_t pipe_key; + int broken_splice_nonblock; + uint64_t notify_ctr; + struct fuse_notify_req notify_list; +@@ -87,53 +83,16 @@ struct fuse_module { + int ctr; + }; + +-/* ----------------------------------------------------------- * +- * Channel interface (when using -o clone_fd) * +- * ----------------------------------------------------------- */ +- +-/** +- * Obtain counted reference to the channel +- * +- * @param ch the channel +- * @return the channel +- */ +-struct fuse_chan *fuse_chan_get(struct fuse_chan *ch); +- +-/** +- * Drop counted reference to a channel +- * +- * @param ch the channel +- */ +-void fuse_chan_put(struct fuse_chan *ch); +- +-struct mount_opts *parse_mount_opts(struct fuse_args *args); +-void destroy_mount_opts(struct mount_opts *mo); +-void fuse_mount_version(void); +-unsigned get_max_read(struct mount_opts *o); +-void fuse_kern_unmount(const char *mountpoint, int fd); +-int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo); +- + int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, + int count); + void fuse_free_req(fuse_req_t req); + +-void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg); +- +-int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg); +- +-int fuse_session_receive_buf_int(struct fuse_session *se, struct fuse_buf *buf, +- struct fuse_chan *ch); + void fuse_session_process_buf_int(struct fuse_session *se, + const struct fuse_buf *buf, struct fuse_chan *ch); + +-struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op, +- size_t op_size, void *private_data); +-int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config); +-int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config); + + #define FUSE_MAX_MAX_PAGES 256 + #define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32 + + /* room needed in buffer to accommodate header */ + #define FUSE_BUFFER_HEADER_SIZE 0x1000 +- +diff --git a/tools/virtiofsd/fuse_log.h b/tools/virtiofsd/fuse_log.h +index 5e112e0f53..0af700da6b 100644 +--- a/tools/virtiofsd/fuse_log.h ++++ b/tools/virtiofsd/fuse_log.h +@@ -16,10 +16,6 @@ + + #include + +-#ifdef __cplusplus +-extern "C" { +-#endif +- + /** + * Log severity level + * +@@ -75,8 +71,4 @@ void fuse_set_log_func(fuse_log_func_t func); + */ + void fuse_log(enum fuse_log_level level, const char *fmt, ...); + +-#ifdef __cplusplus +-} +-#endif +- + #endif /* FUSE_LOG_H_ */ +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index f2d7038e34..e6fa247924 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -16,7 +16,6 @@ + #include "fuse_kernel.h" + #include "fuse_opt.h" + #include "fuse_misc.h" +-#include "mount_util.h" + + #include + #include +@@ -28,12 +27,6 @@ + #include + #include + +-#ifndef F_LINUX_SPECIFIC_BASE +-#define F_LINUX_SPECIFIC_BASE 1024 +-#endif +-#ifndef F_SETPIPE_SZ +-#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) +-#endif + + + #define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) +@@ -137,7 +130,6 @@ void fuse_free_req(fuse_req_t req) + req->u.ni.data = NULL; + list_del_req(req); + ctr = --req->ctr; +- fuse_chan_put(req->ch); + req->ch = NULL; + pthread_mutex_unlock(&se->lock); + if (!ctr) +@@ -184,19 +176,7 @@ static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, + } + } + +- ssize_t res = writev(ch ? ch->fd : se->fd, +- iov, count); +- int err = errno; +- +- if (res == -1) { +- assert(se != NULL); +- +- /* ENOENT means the operation was interrupted */ +- if (!fuse_session_exited(se) && err != ENOENT) +- perror("fuse: writing device"); +- return -err; +- } +- ++ abort(); /* virtio should have taken it before here */ + return 0; + } + +@@ -480,10 +460,6 @@ static int fuse_send_data_iov_fallback(struct fuse_session *se, + struct fuse_bufvec *buf, + size_t len) + { +- struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len); +- void *mbuf; +- int res; +- + /* Optimize common case */ + if (buf->count == 1 && buf->idx == 0 && buf->off == 0 && + !(buf->buf[0].flags & FUSE_BUF_IS_FD)) { +@@ -496,350 +472,10 @@ static int fuse_send_data_iov_fallback(struct fuse_session *se, + return fuse_send_msg(se, ch, iov, iov_count); + } + +- res = posix_memalign(&mbuf, pagesize, len); +- if (res != 0) +- return res; +- +- mem_buf.buf[0].mem = mbuf; +- res = fuse_buf_copy(&mem_buf, buf, 0); +- if (res < 0) { +- free(mbuf); +- return -res; +- } +- len = res; +- +- iov[iov_count].iov_base = mbuf; +- iov[iov_count].iov_len = len; +- iov_count++; +- res = fuse_send_msg(se, ch, iov, iov_count); +- free(mbuf); +- +- return res; +-} +- +-struct fuse_ll_pipe { +- size_t size; +- int can_grow; +- int pipe[2]; +-}; +- +-static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp) +-{ +- close(llp->pipe[0]); +- close(llp->pipe[1]); +- free(llp); +-} +- +-#ifdef HAVE_SPLICE +-#if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC) +-static int fuse_pipe(int fds[2]) +-{ +- int rv = pipe(fds); +- +- if (rv == -1) +- return rv; +- +- if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 || +- fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1 || +- fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || +- fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { +- close(fds[0]); +- close(fds[1]); +- rv = -1; +- } +- return rv; +-} +-#else +-static int fuse_pipe(int fds[2]) +-{ +- return pipe2(fds, O_CLOEXEC | O_NONBLOCK); +-} +-#endif +- +-static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se) +-{ +- struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key); +- if (llp == NULL) { +- int res; +- +- llp = malloc(sizeof(struct fuse_ll_pipe)); +- if (llp == NULL) +- return NULL; +- +- res = fuse_pipe(llp->pipe); +- if (res == -1) { +- free(llp); +- return NULL; +- } +- +- /* +- *the default size is 16 pages on linux +- */ +- llp->size = pagesize * 16; +- llp->can_grow = 1; +- +- pthread_setspecific(se->pipe_key, llp); +- } +- +- return llp; +-} +-#endif +- +-static void fuse_ll_clear_pipe(struct fuse_session *se) +-{ +- struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key); +- if (llp) { +- pthread_setspecific(se->pipe_key, NULL); +- fuse_ll_pipe_free(llp); +- } +-} +- +-#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE) +-static int read_back(int fd, char *buf, size_t len) +-{ +- int res; +- +- res = read(fd, buf, len); +- if (res == -1) { +- fuse_log(FUSE_LOG_ERR, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno)); +- return -EIO; +- } +- if (res != len) { +- fuse_log(FUSE_LOG_ERR, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len); +- return -EIO; +- } ++ abort(); /* Will have taken vhost path */ + return 0; + } + +-static int grow_pipe_to_max(int pipefd) +-{ +- int max; +- int res; +- int maxfd; +- char buf[32]; +- +- maxfd = open("/proc/sys/fs/pipe-max-size", O_RDONLY); +- if (maxfd < 0) +- return -errno; +- +- res = read(maxfd, buf, sizeof(buf) - 1); +- if (res < 0) { +- int saved_errno; +- +- saved_errno = errno; +- close(maxfd); +- return -saved_errno; +- } +- close(maxfd); +- buf[res] = '\0'; +- +- max = atoi(buf); +- res = fcntl(pipefd, F_SETPIPE_SZ, max); +- if (res < 0) +- return -errno; +- return max; +-} +- +-static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, +- struct iovec *iov, int iov_count, +- struct fuse_bufvec *buf, unsigned int flags) +-{ +- int res; +- size_t len = fuse_buf_size(buf); +- struct fuse_out_header *out = iov[0].iov_base; +- struct fuse_ll_pipe *llp; +- int splice_flags; +- size_t pipesize; +- size_t total_fd_size; +- size_t idx; +- size_t headerlen; +- struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len); +- +- if (se->broken_splice_nonblock) +- goto fallback; +- +- if (flags & FUSE_BUF_NO_SPLICE) +- goto fallback; +- +- total_fd_size = 0; +- for (idx = buf->idx; idx < buf->count; idx++) { +- if (buf->buf[idx].flags & FUSE_BUF_IS_FD) { +- total_fd_size = buf->buf[idx].size; +- if (idx == buf->idx) +- total_fd_size -= buf->off; +- } +- } +- if (total_fd_size < 2 * pagesize) +- goto fallback; +- +- if (se->conn.proto_minor < 14 || +- !(se->conn.want & FUSE_CAP_SPLICE_WRITE)) +- goto fallback; +- +- llp = fuse_ll_get_pipe(se); +- if (llp == NULL) +- goto fallback; +- +- +- headerlen = iov_length(iov, iov_count); +- +- out->len = headerlen + len; +- +- /* +- * Heuristic for the required pipe size, does not work if the +- * source contains less than page size fragments +- */ +- pipesize = pagesize * (iov_count + buf->count + 1) + out->len; +- +- if (llp->size < pipesize) { +- if (llp->can_grow) { +- res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize); +- if (res == -1) { +- res = grow_pipe_to_max(llp->pipe[0]); +- if (res > 0) +- llp->size = res; +- llp->can_grow = 0; +- goto fallback; +- } +- llp->size = res; +- } +- if (llp->size < pipesize) +- goto fallback; +- } +- +- +- res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK); +- if (res == -1) +- goto fallback; +- +- if (res != headerlen) { +- res = -EIO; +- fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res, +- headerlen); +- goto clear_pipe; +- } +- +- pipe_buf.buf[0].flags = FUSE_BUF_IS_FD; +- pipe_buf.buf[0].fd = llp->pipe[1]; +- +- res = fuse_buf_copy(&pipe_buf, buf, +- FUSE_BUF_FORCE_SPLICE | FUSE_BUF_SPLICE_NONBLOCK); +- if (res < 0) { +- if (res == -EAGAIN || res == -EINVAL) { +- /* +- * Should only get EAGAIN on kernels with +- * broken SPLICE_F_NONBLOCK support (<= +- * 2.6.35) where this error or a short read is +- * returned even if the pipe itself is not +- * full +- * +- * EINVAL might mean that splice can't handle +- * this combination of input and output. +- */ +- if (res == -EAGAIN) +- se->broken_splice_nonblock = 1; +- +- pthread_setspecific(se->pipe_key, NULL); +- fuse_ll_pipe_free(llp); +- goto fallback; +- } +- res = -res; +- goto clear_pipe; +- } +- +- if (res != 0 && res < len) { +- struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len); +- void *mbuf; +- size_t now_len = res; +- /* +- * For regular files a short count is either +- * 1) due to EOF, or +- * 2) because of broken SPLICE_F_NONBLOCK (see above) +- * +- * For other inputs it's possible that we overflowed +- * the pipe because of small buffer fragments. +- */ +- +- res = posix_memalign(&mbuf, pagesize, len); +- if (res != 0) +- goto clear_pipe; +- +- mem_buf.buf[0].mem = mbuf; +- mem_buf.off = now_len; +- res = fuse_buf_copy(&mem_buf, buf, 0); +- if (res > 0) { +- char *tmpbuf; +- size_t extra_len = res; +- /* +- * Trickiest case: got more data. Need to get +- * back the data from the pipe and then fall +- * back to regular write. +- */ +- tmpbuf = malloc(headerlen); +- if (tmpbuf == NULL) { +- free(mbuf); +- res = ENOMEM; +- goto clear_pipe; +- } +- res = read_back(llp->pipe[0], tmpbuf, headerlen); +- free(tmpbuf); +- if (res != 0) { +- free(mbuf); +- goto clear_pipe; +- } +- res = read_back(llp->pipe[0], mbuf, now_len); +- if (res != 0) { +- free(mbuf); +- goto clear_pipe; +- } +- len = now_len + extra_len; +- iov[iov_count].iov_base = mbuf; +- iov[iov_count].iov_len = len; +- iov_count++; +- res = fuse_send_msg(se, ch, iov, iov_count); +- free(mbuf); +- return res; +- } +- free(mbuf); +- res = now_len; +- } +- len = res; +- out->len = headerlen + len; +- +- if (se->debug) { +- fuse_log(FUSE_LOG_DEBUG, +- " unique: %llu, success, outsize: %i (splice)\n", +- (unsigned long long) out->unique, out->len); +- } +- +- splice_flags = 0; +- if ((flags & FUSE_BUF_SPLICE_MOVE) && +- (se->conn.want & FUSE_CAP_SPLICE_MOVE)) +- splice_flags |= SPLICE_F_MOVE; +- +- res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, +- NULL, out->len, splice_flags); +- if (res == -1) { +- res = -errno; +- perror("fuse: splice from pipe"); +- goto clear_pipe; +- } +- if (res != out->len) { +- res = -EIO; +- fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n", +- res, out->len); +- goto clear_pipe; +- } +- return 0; +- +-clear_pipe: +- fuse_ll_clear_pipe(se); +- return res; +- +-fallback: +- return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); +-} +-#else + static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, + struct iovec *iov, int iov_count, + struct fuse_bufvec *buf, unsigned int flags) +@@ -849,7 +485,6 @@ static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, + + return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); + } +-#endif + + int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, + enum fuse_buf_copy_flags flags) +@@ -1408,16 +1043,11 @@ static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, + if (bufv.buf[0].size < arg->size) { + fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n"); + fuse_reply_err(req, EIO); +- goto out; ++ return; + } + bufv.buf[0].size = arg->size; + + se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi); +- +-out: +- /* Need to reset the pipe if ->write_buf() didn't consume all data */ +- if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count) +- fuse_ll_clear_pipe(se); + } + + static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +@@ -2038,17 +1668,6 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + return; + } + +- unsigned max_read_mo = get_max_read(se->mo); +- if (se->conn.max_read != max_read_mo) { +- fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() " +- "requested different maximum read size (%u vs %u)\n", +- se->conn.max_read, max_read_mo); +- fuse_reply_err(req, EPROTO); +- se->error = -EPROTO; +- fuse_session_exit(se); +- return; +- } +- + if (se->conn.max_write < bufsize - FUSE_BUFFER_HEADER_SIZE) { + se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE; + } +@@ -2364,8 +1983,6 @@ static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq, + } + out: + free(rreq); +- if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count) +- fuse_ll_clear_pipe(se); + } + + int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, +@@ -2496,7 +2113,6 @@ static struct { + [FUSE_RENAME2] = { do_rename2, "RENAME2" }, + [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" }, + [FUSE_LSEEK] = { do_lseek, "LSEEK" }, +- [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" }, + }; + + #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) +@@ -2509,21 +2125,6 @@ static const char *opname(enum fuse_opcode opcode) + return fuse_ll_ops[opcode].name; + } + +-static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst, +- struct fuse_bufvec *src) +-{ +- ssize_t res = fuse_buf_copy(dst, src, 0); +- if (res < 0) { +- fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res)); +- return res; +- } +- if ((size_t)res < fuse_buf_size(dst)) { +- fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n"); +- return -1; +- } +- return 0; +-} +- + void fuse_session_process_buf(struct fuse_session *se, + const struct fuse_buf *buf) + { +@@ -2533,36 +2134,12 @@ void fuse_session_process_buf(struct fuse_session *se, + void fuse_session_process_buf_int(struct fuse_session *se, + const struct fuse_buf *buf, struct fuse_chan *ch) + { +- const size_t write_header_size = sizeof(struct fuse_in_header) + +- sizeof(struct fuse_write_in); +- struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 }; +- struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size); + struct fuse_in_header *in; + const void *inarg; + struct fuse_req *req; +- void *mbuf = NULL; + int err; +- int res; +- +- if (buf->flags & FUSE_BUF_IS_FD) { +- if (buf->size < tmpbuf.buf[0].size) +- tmpbuf.buf[0].size = buf->size; + +- mbuf = malloc(tmpbuf.buf[0].size); +- if (mbuf == NULL) { +- fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n"); +- goto clear_pipe; +- } +- tmpbuf.buf[0].mem = mbuf; +- +- res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv); +- if (res < 0) +- goto clear_pipe; +- +- in = mbuf; +- } else { +- in = buf->mem; +- } ++ in = buf->mem; + + if (se->debug) { + fuse_log(FUSE_LOG_DEBUG, +@@ -2584,14 +2161,14 @@ void fuse_session_process_buf_int(struct fuse_session *se, + }; + + fuse_send_msg(se, ch, &iov, 1); +- goto clear_pipe; ++ return; + } + + req->unique = in->unique; + req->ctx.uid = in->uid; + req->ctx.gid = in->gid; + req->ctx.pid = in->pid; +- req->ch = ch ? fuse_chan_get(ch) : NULL; ++ req->ch = ch; + + err = EIO; + if (!se->got_init) { +@@ -2627,28 +2204,6 @@ void fuse_session_process_buf_int(struct fuse_session *se, + fuse_reply_err(intr, EAGAIN); + } + +- if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size && +- (in->opcode != FUSE_WRITE || !se->op.write_buf) && +- in->opcode != FUSE_NOTIFY_REPLY) { +- void *newmbuf; +- +- err = ENOMEM; +- newmbuf = realloc(mbuf, buf->size); +- if (newmbuf == NULL) +- goto reply_err; +- mbuf = newmbuf; +- +- tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size); +- tmpbuf.buf[0].mem = (char *)mbuf + write_header_size; +- +- res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv); +- err = -res; +- if (res < 0) +- goto reply_err; +- +- in = mbuf; +- } +- + inarg = (void *) &in[1]; + if (in->opcode == FUSE_WRITE && se->op.write_buf) + do_write_buf(req, in->nodeid, inarg, buf); +@@ -2657,16 +2212,10 @@ void fuse_session_process_buf_int(struct fuse_session *se, + else + fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); + +-out_free: +- free(mbuf); + return; + + reply_err: + fuse_reply_err(req, err); +-clear_pipe: +- if (buf->flags & FUSE_BUF_IS_FD) +- fuse_ll_clear_pipe(se); +- goto out_free; + } + + #define LL_OPTION(n,o,v) \ +@@ -2684,7 +2233,6 @@ void fuse_lowlevel_version(void) + { + printf("using FUSE kernel interface version %i.%i\n", + FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); +- fuse_mount_version(); + } + + void fuse_lowlevel_help(void) +@@ -2692,204 +2240,29 @@ void fuse_lowlevel_help(void) + /* These are not all options, but the ones that are + potentially of interest to an end-user */ + printf( +-" -o allow_other allow access by all users\n" + " -o allow_root allow access by root\n" +-" -o auto_unmount auto unmount on process termination\n"); ++); + } + + void fuse_session_destroy(struct fuse_session *se) + { +- struct fuse_ll_pipe *llp; +- + if (se->got_init && !se->got_destroy) { + if (se->op.destroy) + se->op.destroy(se->userdata); + } +- llp = pthread_getspecific(se->pipe_key); +- if (llp != NULL) +- fuse_ll_pipe_free(llp); +- pthread_key_delete(se->pipe_key); + pthread_mutex_destroy(&se->lock); + free(se->cuse_data); + if (se->fd != -1) + close(se->fd); +- destroy_mount_opts(se->mo); + free(se); + } + + +-static void fuse_ll_pipe_destructor(void *data) +-{ +- struct fuse_ll_pipe *llp = data; +- fuse_ll_pipe_free(llp); +-} +- +-int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf) +-{ +- return fuse_session_receive_buf_int(se, buf, NULL); +-} +- +-int fuse_session_receive_buf_int(struct fuse_session *se, struct fuse_buf *buf, +- struct fuse_chan *ch) +-{ +- int err; +- ssize_t res; +-#ifdef HAVE_SPLICE +- size_t bufsize = se->bufsize; +- struct fuse_ll_pipe *llp; +- struct fuse_buf tmpbuf; +- +- if (se->conn.proto_minor < 14 || !(se->conn.want & FUSE_CAP_SPLICE_READ)) +- goto fallback; +- +- llp = fuse_ll_get_pipe(se); +- if (llp == NULL) +- goto fallback; +- +- if (llp->size < bufsize) { +- if (llp->can_grow) { +- res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize); +- if (res == -1) { +- llp->can_grow = 0; +- res = grow_pipe_to_max(llp->pipe[0]); +- if (res > 0) +- llp->size = res; +- goto fallback; +- } +- llp->size = res; +- } +- if (llp->size < bufsize) +- goto fallback; +- } +- +- res = splice(ch ? ch->fd : se->fd, +- NULL, llp->pipe[1], NULL, bufsize, 0); +- err = errno; +- +- if (fuse_session_exited(se)) +- return 0; +- +- if (res == -1) { +- if (err == ENODEV) { +- /* Filesystem was unmounted, or connection was aborted +- via /sys/fs/fuse/connections */ +- fuse_session_exit(se); +- return 0; +- } +- if (err != EINTR && err != EAGAIN) +- perror("fuse: splice from device"); +- return -err; +- } +- +- if (res < sizeof(struct fuse_in_header)) { +- fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n"); +- return -EIO; +- } +- +- tmpbuf = (struct fuse_buf) { +- .size = res, +- .flags = FUSE_BUF_IS_FD, +- .fd = llp->pipe[0], +- }; +- +- /* +- * Don't bother with zero copy for small requests. +- * fuse_loop_mt() needs to check for FORGET so this more than +- * just an optimization. +- */ +- if (res < sizeof(struct fuse_in_header) + +- sizeof(struct fuse_write_in) + pagesize) { +- struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 }; +- struct fuse_bufvec dst = { .count = 1 }; +- +- if (!buf->mem) { +- buf->mem = malloc(se->bufsize); +- if (!buf->mem) { +- fuse_log(FUSE_LOG_ERR, +- "fuse: failed to allocate read buffer\n"); +- return -ENOMEM; +- } +- } +- buf->size = se->bufsize; +- buf->flags = 0; +- dst.buf[0] = *buf; +- +- res = fuse_buf_copy(&dst, &src, 0); +- if (res < 0) { +- fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", +- strerror(-res)); +- fuse_ll_clear_pipe(se); +- return res; +- } +- if (res < tmpbuf.size) { +- fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n"); +- fuse_ll_clear_pipe(se); +- return -EIO; +- } +- assert(res == tmpbuf.size); +- +- } else { +- /* Don't overwrite buf->mem, as that would cause a leak */ +- buf->fd = tmpbuf.fd; +- buf->flags = tmpbuf.flags; +- } +- buf->size = tmpbuf.size; +- +- return res; +- +-fallback: +-#endif +- if (!buf->mem) { +- buf->mem = malloc(se->bufsize); +- if (!buf->mem) { +- fuse_log(FUSE_LOG_ERR, +- "fuse: failed to allocate read buffer\n"); +- return -ENOMEM; +- } +- } +- +-restart: +- res = read(ch ? ch->fd : se->fd, buf->mem, se->bufsize); +- err = errno; +- +- if (fuse_session_exited(se)) +- return 0; +- if (res == -1) { +- /* ENOENT means the operation was interrupted, it's safe +- to restart */ +- if (err == ENOENT) +- goto restart; +- +- if (err == ENODEV) { +- /* Filesystem was unmounted, or connection was aborted +- via /sys/fs/fuse/connections */ +- fuse_session_exit(se); +- return 0; +- } +- /* Errors occurring during normal operation: EINTR (read +- interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem +- umounted) */ +- if (err != EINTR && err != EAGAIN) +- perror("fuse: reading device"); +- return -err; +- } +- if ((size_t) res < sizeof(struct fuse_in_header)) { +- fuse_log(FUSE_LOG_ERR, "short read on fuse device\n"); +- return -EIO; +- } +- +- buf->size = res; +- +- return res; +-} +- + struct fuse_session *fuse_session_new(struct fuse_args *args, + const struct fuse_lowlevel_ops *op, + size_t op_size, void *userdata) + { +- int err; + struct fuse_session *se; +- struct mount_opts *mo; + + if (sizeof(struct fuse_lowlevel_ops) < op_size) { + fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n"); +@@ -2913,20 +2286,6 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, + /* Parse options */ + if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1) + goto out2; +- if(se->deny_others) { +- /* Allowing access only by root is done by instructing +- * kernel to allow access by everyone, and then restricting +- * access to root and mountpoint owner in libfuse. +- */ +- // We may be adding the option a second time, but +- // that doesn't hurt. +- if(fuse_opt_add_arg(args, "-oallow_other") == -1) +- goto out2; +- } +- mo = parse_mount_opts(args); +- if (mo == NULL) +- goto out3; +- + if(args->argc == 1 && + args->argv[0][0] == '-') { + fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but " +@@ -2940,9 +2299,6 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, + goto out4; + } + +- if (se->debug) +- fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSION); +- + se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() + + FUSE_BUFFER_HEADER_SIZE; + +@@ -2952,26 +2308,14 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, + se->notify_ctr = 1; + fuse_mutex_init(&se->lock); + +- err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor); +- if (err) { +- fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n", +- strerror(err)); +- goto out5; +- } +- + memcpy(&se->op, op, op_size); + se->owner = getuid(); + se->userdata = userdata; + +- se->mo = mo; + return se; + +-out5: +- pthread_mutex_destroy(&se->lock); + out4: + fuse_opt_free_args(args); +-out3: +- free(mo); + out2: + free(se); + out1: +@@ -3035,11 +2379,6 @@ int fuse_session_fd(struct fuse_session *se) + + void fuse_session_unmount(struct fuse_session *se) + { +- if (se->mountpoint != NULL) { +- fuse_kern_unmount(se->mountpoint, se->fd); +- free(se->mountpoint); +- se->mountpoint = NULL; +- } + } + + #ifdef linux +diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h +index 18c6363f07..6b1adfcfd1 100644 +--- a/tools/virtiofsd/fuse_lowlevel.h ++++ b/tools/virtiofsd/fuse_lowlevel.h +@@ -31,10 +31,6 @@ + #include + #include + +-#ifdef __cplusplus +-extern "C" { +-#endif +- + /* ----------------------------------------------------------- * + * Miscellaneous definitions * + * ----------------------------------------------------------- */ +@@ -1863,14 +1859,12 @@ void fuse_cmdline_help(void); + * ----------------------------------------------------------- */ + + struct fuse_cmdline_opts { +- int singlethread; + int foreground; + int debug; + int nodefault_subtype; + char *mountpoint; + int show_version; + int show_help; +- int clone_fd; + unsigned int max_idle_threads; + }; + +@@ -1961,24 +1955,6 @@ int fuse_session_mount(struct fuse_session *se, const char *mountpoint); + */ + int fuse_session_loop(struct fuse_session *se); + +-/** +- * Enter a multi-threaded event loop. +- * +- * For a description of the return value and the conditions when the +- * event loop exits, refer to the documentation of +- * fuse_session_loop(). +- * +- * @param se the session +- * @param config session loop configuration +- * @return see fuse_session_loop() +- */ +-#if FUSE_USE_VERSION < 32 +-int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd); +-#define fuse_session_loop_mt(se, clone_fd) fuse_session_loop_mt_31(se, clone_fd) +-#else +-int fuse_session_loop_mt(struct fuse_session *se, struct fuse_loop_config *config); +-#endif +- + /** + * Flag a session as terminated. + * +@@ -2082,8 +2058,4 @@ void fuse_session_process_buf(struct fuse_session *se, + */ + int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf); + +-#ifdef __cplusplus +-} +-#endif +- + #endif /* FUSE_LOWLEVEL_H_ */ +diff --git a/tools/virtiofsd/fuse_opt.h b/tools/virtiofsd/fuse_opt.h +index d8573e74fd..69102555be 100644 +--- a/tools/virtiofsd/fuse_opt.h ++++ b/tools/virtiofsd/fuse_opt.h +@@ -14,10 +14,6 @@ + * This file defines the option parsing interface of FUSE + */ + +-#ifdef __cplusplus +-extern "C" { +-#endif +- + /** + * Option description + * +@@ -264,8 +260,4 @@ void fuse_opt_free_args(struct fuse_args *args); + */ + int fuse_opt_match(const struct fuse_opt opts[], const char *opt); + +-#ifdef __cplusplus +-} +-#endif +- + #endif /* FUSE_OPT_H_ */ +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index 64ff7ad6d5..5a2e64c6d0 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -41,14 +41,10 @@ static const struct fuse_opt fuse_helper_opts[] = { + FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), + FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), + FUSE_HELPER_OPT("-f", foreground), +- FUSE_HELPER_OPT("-s", singlethread), + FUSE_HELPER_OPT("fsname=", nodefault_subtype), + FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), +-#ifndef __FreeBSD__ + FUSE_HELPER_OPT("subtype=", nodefault_subtype), + FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), +-#endif +- FUSE_HELPER_OPT("clone_fd", clone_fd), + FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads), + FUSE_OPT_END + }; +@@ -132,9 +128,6 @@ void fuse_cmdline_help(void) + " -V --version print version\n" + " -d -o debug enable debug output (implies -f)\n" + " -f foreground operation\n" +- " -s disable multi-threaded operation\n" +- " -o clone_fd use separate fuse device fd for each thread\n" +- " (may improve performance)\n" + " -o max_idle_threads the maximum number of idle worker threads\n" + " allowed (default: 10)\n"); + } +@@ -171,34 +164,6 @@ static int fuse_helper_opt_proc(void *data, const char *arg, int key, + } + } + +-/* Under FreeBSD, there is no subtype option so this +- function actually sets the fsname */ +-static int add_default_subtype(const char *progname, struct fuse_args *args) +-{ +- int res; +- char *subtype_opt; +- +- const char *basename = strrchr(progname, '/'); +- if (basename == NULL) +- basename = progname; +- else if (basename[1] != '\0') +- basename++; +- +- subtype_opt = (char *) malloc(strlen(basename) + 64); +- if (subtype_opt == NULL) { +- fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); +- return -1; +- } +-#ifdef __FreeBSD__ +- sprintf(subtype_opt, "-ofsname=%s", basename); +-#else +- sprintf(subtype_opt, "-osubtype=%s", basename); +-#endif +- res = fuse_opt_add_arg(args, subtype_opt); +- free(subtype_opt); +- return res; +-} +- + int fuse_parse_cmdline(struct fuse_args *args, + struct fuse_cmdline_opts *opts) + { +@@ -210,14 +175,6 @@ int fuse_parse_cmdline(struct fuse_args *args, + fuse_helper_opt_proc) == -1) + return -1; + +- /* *Linux*: if neither -o subtype nor -o fsname are specified, +- set subtype to program's basename. +- *FreeBSD*: if fsname is not specified, set to program's +- basename. */ +- if (!opts->nodefault_subtype) +- if (add_default_subtype(args->argv[0], args) == -1) +- return -1; +- + return 0; + } + +@@ -276,88 +233,6 @@ int fuse_daemonize(int foreground) + return 0; + } + +-int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, +- size_t op_size, void *user_data) +-{ +- struct fuse_args args = FUSE_ARGS_INIT(argc, argv); +- struct fuse *fuse; +- struct fuse_cmdline_opts opts; +- int res; +- +- if (fuse_parse_cmdline(&args, &opts) != 0) +- return 1; +- +- if (opts.show_version) { +- printf("FUSE library version %s\n", PACKAGE_VERSION); +- fuse_lowlevel_version(); +- res = 0; +- goto out1; +- } +- +- if (opts.show_help) { +- if(args.argv[0][0] != '\0') +- printf("usage: %s [options] \n\n", +- args.argv[0]); +- printf("FUSE options:\n"); +- fuse_cmdline_help(); +- fuse_lib_help(&args); +- res = 0; +- goto out1; +- } +- +- if (!opts.show_help && +- !opts.mountpoint) { +- fuse_log(FUSE_LOG_ERR, "error: no mountpoint specified\n"); +- res = 2; +- goto out1; +- } +- +- +- fuse = fuse_new_31(&args, op, op_size, user_data); +- if (fuse == NULL) { +- res = 3; +- goto out1; +- } +- +- if (fuse_mount(fuse,opts.mountpoint) != 0) { +- res = 4; +- goto out2; +- } +- +- if (fuse_daemonize(opts.foreground) != 0) { +- res = 5; +- goto out3; +- } +- +- struct fuse_session *se = fuse_get_session(fuse); +- if (fuse_set_signal_handlers(se) != 0) { +- res = 6; +- goto out3; +- } +- +- if (opts.singlethread) +- res = fuse_loop(fuse); +- else { +- struct fuse_loop_config loop_config; +- loop_config.clone_fd = opts.clone_fd; +- loop_config.max_idle_threads = opts.max_idle_threads; +- res = fuse_loop_mt_32(fuse, &loop_config); +- } +- if (res) +- res = 7; +- +- fuse_remove_signal_handlers(se); +-out3: +- fuse_unmount(fuse); +-out2: +- fuse_destroy(fuse); +-out1: +- free(opts.mountpoint); +- fuse_opt_free_args(&args); +- return res; +-} +- +- + void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, + struct fuse_conn_info *conn) + { +@@ -420,21 +295,3 @@ struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args) + } + return opts; + } +- +-int fuse_open_channel(const char *mountpoint, const char* options) +-{ +- struct mount_opts *opts = NULL; +- int fd = -1; +- const char *argv[] = { "", "-o", options }; +- int argc = sizeof(argv) / sizeof(argv[0]); +- struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv); +- +- opts = parse_mount_opts(&args); +- if (opts == NULL) +- return -1; +- +- fd = fuse_kern_mount(mountpoint, opts); +- destroy_mount_opts(opts); +- +- return fd; +-} +diff --git a/tools/virtiofsd/passthrough_helpers.h b/tools/virtiofsd/passthrough_helpers.h +index 6b77c33600..7c5f561fbc 100644 +--- a/tools/virtiofsd/passthrough_helpers.h ++++ b/tools/virtiofsd/passthrough_helpers.h +@@ -42,32 +42,6 @@ static int mknod_wrapper(int dirfd, const char *path, const char *link, + res = symlinkat(link, dirfd, path); + } else if (S_ISFIFO(mode)) { + res = mkfifoat(dirfd, path, mode); +-#ifdef __FreeBSD__ +- } else if (S_ISSOCK(mode)) { +- struct sockaddr_un su; +- int fd; +- +- if (strlen(path) >= sizeof(su.sun_path)) { +- errno = ENAMETOOLONG; +- return -1; +- } +- fd = socket(AF_UNIX, SOCK_STREAM, 0); +- if (fd >= 0) { +- /* +- * We must bind the socket to the underlying file +- * system to create the socket file, even though +- * we'll never listen on this socket. +- */ +- su.sun_family = AF_UNIX; +- strncpy(su.sun_path, path, sizeof(su.sun_path)); +- res = bindat(dirfd, fd, (struct sockaddr*)&su, +- sizeof(su)); +- if (res == 0) +- close(fd); +- } else { +- res = -1; +- } +-#endif + } else { + res = mknodat(dirfd, path, mode, rdev); + } +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index e1a605691a..e5f7115bc1 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1240,7 +1240,6 @@ int main(int argc, char *argv[]) + ret = 0; + goto err_out1; + } else if (opts.show_version) { +- printf("FUSE library version %s\n", fuse_pkgversion()); + fuse_lowlevel_version(); + ret = 0; + goto err_out1; diff --git a/0016-virtiofsd-Format-imported-files-to-qemu-style.patch b/0016-virtiofsd-Format-imported-files-to-qemu-style.patch new file mode 100644 index 0000000..e050d14 --- /dev/null +++ b/0016-virtiofsd-Format-imported-files-to-qemu-style.patch @@ -0,0 +1,14727 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:00:45 +0000 +Subject: [PATCH] virtiofsd: Format imported files to qemu style +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Mostly using a set like: + +indent -nut -i 4 -nlp -br -cs -ce --no-space-after-function-call-names file +clang-format -style=file -i -- file +clang-tidy -fix-errors -checks=readability-braces-around-statements file +clang-format -style=file -i -- file + +With manual cleanups. + +The .clang-format used is below. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Reviewed by: Aleksandar Markovic + +Language: Cpp +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false # although we like it, it creates churn +AlignConsecutiveDeclarations: false +AlignEscapedNewlinesLeft: true +AlignOperands: true +AlignTrailingComments: false # churn +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None # AlwaysBreakAfterDefinitionReturnType is taken into account +AlwaysBreakBeforeMultilineStrings: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterStruct: false + AfterUnion: false + BeforeElse: false + IndentBraces: false +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeTernaryOperators: false +BreakStringLiterals: true +ColumnLimit: 80 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ForEachMacros: [ + 'CPU_FOREACH', + 'CPU_FOREACH_REVERSE', + 'CPU_FOREACH_SAFE', + 'IOMMU_NOTIFIER_FOREACH', + 'QLIST_FOREACH', + 'QLIST_FOREACH_ENTRY', + 'QLIST_FOREACH_RCU', + 'QLIST_FOREACH_SAFE', + 'QLIST_FOREACH_SAFE_RCU', + 'QSIMPLEQ_FOREACH', + 'QSIMPLEQ_FOREACH_SAFE', + 'QSLIST_FOREACH', + 'QSLIST_FOREACH_SAFE', + 'QTAILQ_FOREACH', + 'QTAILQ_FOREACH_REVERSE', + 'QTAILQ_FOREACH_SAFE', + 'QTAILQ_RAW_FOREACH', + 'RAMBLOCK_FOREACH' +] +IncludeCategories: + - Regex: '^"qemu/osdep.h' + Priority: -3 + - Regex: '^"(block|chardev|crypto|disas|exec|fpu|hw|io|libdecnumber|migration|monitor|net|qapi|qemu|qom|standard-headers|sysemu|ui)/' + Priority: -2 + - Regex: '^"(elf.h|qemu-common.h|glib-compat.h|qemu-io.h|trace-tcg.h)' + Priority: -1 + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '$' +IndentCaseLabels: false +IndentWidth: 4 +IndentWrappedFunctionNames: false +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '.*_BEGIN$' # only PREC_BEGIN ? +MacroBlockEnd: '.*_END$' +MaxEmptyLinesToKeep: 2 +PointerAlignment: Right +ReflowComments: true +SortIncludes: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeParens: ControlStatements +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInContainerLiterals: true +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +UseTab: Never +... + +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 7387863d033e8028aa09a815736617a7c4490827) +--- + tools/virtiofsd/buffer.c | 434 +-- + tools/virtiofsd/fuse.h | 1572 +++++------ + tools/virtiofsd/fuse_common.h | 730 ++--- + tools/virtiofsd/fuse_i.h | 121 +- + tools/virtiofsd/fuse_log.c | 38 +- + tools/virtiofsd/fuse_log.h | 32 +- + tools/virtiofsd/fuse_lowlevel.c | 3638 +++++++++++++------------ + tools/virtiofsd/fuse_lowlevel.h | 2392 ++++++++-------- + tools/virtiofsd/fuse_misc.h | 30 +- + tools/virtiofsd/fuse_opt.c | 659 ++--- + tools/virtiofsd/fuse_opt.h | 79 +- + tools/virtiofsd/fuse_signals.c | 118 +- + tools/virtiofsd/helper.c | 506 ++-- + tools/virtiofsd/passthrough_helpers.h | 33 +- + tools/virtiofsd/passthrough_ll.c | 2061 +++++++------- + 15 files changed, 6382 insertions(+), 6061 deletions(-) + +diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c +index aefb7dbf15..5df946c82c 100644 +--- a/tools/virtiofsd/buffer.c ++++ b/tools/virtiofsd/buffer.c +@@ -1,252 +1,272 @@ + /* +- FUSE: Filesystem in Userspace +- Copyright (C) 2010 Miklos Szeredi +- +- Functions for dealing with `struct fuse_buf` and `struct +- fuse_bufvec`. +- +- This program can be distributed under the terms of the GNU LGPLv2. +- See the file COPYING.LIB +-*/ ++ * FUSE: Filesystem in Userspace ++ * Copyright (C) 2010 Miklos Szeredi ++ * ++ * Functions for dealing with `struct fuse_buf` and `struct ++ * fuse_bufvec`. ++ * ++ * This program can be distributed under the terms of the GNU LGPLv2. ++ * See the file COPYING.LIB ++ */ + + #define _GNU_SOURCE + + #include "config.h" + #include "fuse_i.h" + #include "fuse_lowlevel.h" ++#include ++#include + #include + #include +-#include +-#include + + size_t fuse_buf_size(const struct fuse_bufvec *bufv) + { +- size_t i; +- size_t size = 0; +- +- for (i = 0; i < bufv->count; i++) { +- if (bufv->buf[i].size == SIZE_MAX) +- size = SIZE_MAX; +- else +- size += bufv->buf[i].size; +- } +- +- return size; ++ size_t i; ++ size_t size = 0; ++ ++ for (i = 0; i < bufv->count; i++) { ++ if (bufv->buf[i].size == SIZE_MAX) { ++ size = SIZE_MAX; ++ } else { ++ size += bufv->buf[i].size; ++ } ++ } ++ ++ return size; + } + + static size_t min_size(size_t s1, size_t s2) + { +- return s1 < s2 ? s1 : s2; ++ return s1 < s2 ? s1 : s2; + } + + static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off, +- const struct fuse_buf *src, size_t src_off, +- size_t len) ++ const struct fuse_buf *src, size_t src_off, ++ size_t len) + { +- ssize_t res = 0; +- size_t copied = 0; +- +- while (len) { +- if (dst->flags & FUSE_BUF_FD_SEEK) { +- res = pwrite(dst->fd, (char *)src->mem + src_off, len, +- dst->pos + dst_off); +- } else { +- res = write(dst->fd, (char *)src->mem + src_off, len); +- } +- if (res == -1) { +- if (!copied) +- return -errno; +- break; +- } +- if (res == 0) +- break; +- +- copied += res; +- if (!(dst->flags & FUSE_BUF_FD_RETRY)) +- break; +- +- src_off += res; +- dst_off += res; +- len -= res; +- } +- +- return copied; ++ ssize_t res = 0; ++ size_t copied = 0; ++ ++ while (len) { ++ if (dst->flags & FUSE_BUF_FD_SEEK) { ++ res = pwrite(dst->fd, (char *)src->mem + src_off, len, ++ dst->pos + dst_off); ++ } else { ++ res = write(dst->fd, (char *)src->mem + src_off, len); ++ } ++ if (res == -1) { ++ if (!copied) { ++ return -errno; ++ } ++ break; ++ } ++ if (res == 0) { ++ break; ++ } ++ ++ copied += res; ++ if (!(dst->flags & FUSE_BUF_FD_RETRY)) { ++ break; ++ } ++ ++ src_off += res; ++ dst_off += res; ++ len -= res; ++ } ++ ++ return copied; + } + + static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off, +- const struct fuse_buf *src, size_t src_off, +- size_t len) ++ const struct fuse_buf *src, size_t src_off, ++ size_t len) + { +- ssize_t res = 0; +- size_t copied = 0; +- +- while (len) { +- if (src->flags & FUSE_BUF_FD_SEEK) { +- res = pread(src->fd, (char *)dst->mem + dst_off, len, +- src->pos + src_off); +- } else { +- res = read(src->fd, (char *)dst->mem + dst_off, len); +- } +- if (res == -1) { +- if (!copied) +- return -errno; +- break; +- } +- if (res == 0) +- break; +- +- copied += res; +- if (!(src->flags & FUSE_BUF_FD_RETRY)) +- break; +- +- dst_off += res; +- src_off += res; +- len -= res; +- } +- +- return copied; ++ ssize_t res = 0; ++ size_t copied = 0; ++ ++ while (len) { ++ if (src->flags & FUSE_BUF_FD_SEEK) { ++ res = pread(src->fd, (char *)dst->mem + dst_off, len, ++ src->pos + src_off); ++ } else { ++ res = read(src->fd, (char *)dst->mem + dst_off, len); ++ } ++ if (res == -1) { ++ if (!copied) { ++ return -errno; ++ } ++ break; ++ } ++ if (res == 0) { ++ break; ++ } ++ ++ copied += res; ++ if (!(src->flags & FUSE_BUF_FD_RETRY)) { ++ break; ++ } ++ ++ dst_off += res; ++ src_off += res; ++ len -= res; ++ } ++ ++ return copied; + } + + static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off, +- const struct fuse_buf *src, size_t src_off, +- size_t len) ++ const struct fuse_buf *src, size_t src_off, ++ size_t len) + { +- char buf[4096]; +- struct fuse_buf tmp = { +- .size = sizeof(buf), +- .flags = 0, +- }; +- ssize_t res; +- size_t copied = 0; +- +- tmp.mem = buf; +- +- while (len) { +- size_t this_len = min_size(tmp.size, len); +- size_t read_len; +- +- res = fuse_buf_read(&tmp, 0, src, src_off, this_len); +- if (res < 0) { +- if (!copied) +- return res; +- break; +- } +- if (res == 0) +- break; +- +- read_len = res; +- res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len); +- if (res < 0) { +- if (!copied) +- return res; +- break; +- } +- if (res == 0) +- break; +- +- copied += res; +- +- if (res < this_len) +- break; +- +- dst_off += res; +- src_off += res; +- len -= res; +- } +- +- return copied; ++ char buf[4096]; ++ struct fuse_buf tmp = { ++ .size = sizeof(buf), ++ .flags = 0, ++ }; ++ ssize_t res; ++ size_t copied = 0; ++ ++ tmp.mem = buf; ++ ++ while (len) { ++ size_t this_len = min_size(tmp.size, len); ++ size_t read_len; ++ ++ res = fuse_buf_read(&tmp, 0, src, src_off, this_len); ++ if (res < 0) { ++ if (!copied) { ++ return res; ++ } ++ break; ++ } ++ if (res == 0) { ++ break; ++ } ++ ++ read_len = res; ++ res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len); ++ if (res < 0) { ++ if (!copied) { ++ return res; ++ } ++ break; ++ } ++ if (res == 0) { ++ break; ++ } ++ ++ copied += res; ++ ++ if (res < this_len) { ++ break; ++ } ++ ++ dst_off += res; ++ src_off += res; ++ len -= res; ++ } ++ ++ return copied; + } + + static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off, +- const struct fuse_buf *src, size_t src_off, +- size_t len, enum fuse_buf_copy_flags flags) ++ const struct fuse_buf *src, size_t src_off, ++ size_t len, enum fuse_buf_copy_flags flags) + { +- int src_is_fd = src->flags & FUSE_BUF_IS_FD; +- int dst_is_fd = dst->flags & FUSE_BUF_IS_FD; +- +- if (!src_is_fd && !dst_is_fd) { +- char *dstmem = (char *)dst->mem + dst_off; +- char *srcmem = (char *)src->mem + src_off; +- +- if (dstmem != srcmem) { +- if (dstmem + len <= srcmem || srcmem + len <= dstmem) +- memcpy(dstmem, srcmem, len); +- else +- memmove(dstmem, srcmem, len); +- } +- +- return len; +- } else if (!src_is_fd) { +- return fuse_buf_write(dst, dst_off, src, src_off, len); +- } else if (!dst_is_fd) { +- return fuse_buf_read(dst, dst_off, src, src_off, len); +- } else { +- return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); +- } ++ int src_is_fd = src->flags & FUSE_BUF_IS_FD; ++ int dst_is_fd = dst->flags & FUSE_BUF_IS_FD; ++ ++ if (!src_is_fd && !dst_is_fd) { ++ char *dstmem = (char *)dst->mem + dst_off; ++ char *srcmem = (char *)src->mem + src_off; ++ ++ if (dstmem != srcmem) { ++ if (dstmem + len <= srcmem || srcmem + len <= dstmem) { ++ memcpy(dstmem, srcmem, len); ++ } else { ++ memmove(dstmem, srcmem, len); ++ } ++ } ++ ++ return len; ++ } else if (!src_is_fd) { ++ return fuse_buf_write(dst, dst_off, src, src_off, len); ++ } else if (!dst_is_fd) { ++ return fuse_buf_read(dst, dst_off, src, src_off, len); ++ } else { ++ return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); ++ } + } + + static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv) + { +- if (bufv->idx < bufv->count) +- return &bufv->buf[bufv->idx]; +- else +- return NULL; ++ if (bufv->idx < bufv->count) { ++ return &bufv->buf[bufv->idx]; ++ } else { ++ return NULL; ++ } + } + + static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len) + { +- const struct fuse_buf *buf = fuse_bufvec_current(bufv); +- +- bufv->off += len; +- assert(bufv->off <= buf->size); +- if (bufv->off == buf->size) { +- assert(bufv->idx < bufv->count); +- bufv->idx++; +- if (bufv->idx == bufv->count) +- return 0; +- bufv->off = 0; +- } +- return 1; ++ const struct fuse_buf *buf = fuse_bufvec_current(bufv); ++ ++ bufv->off += len; ++ assert(bufv->off <= buf->size); ++ if (bufv->off == buf->size) { ++ assert(bufv->idx < bufv->count); ++ bufv->idx++; ++ if (bufv->idx == bufv->count) { ++ return 0; ++ } ++ bufv->off = 0; ++ } ++ return 1; + } + + ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv, +- enum fuse_buf_copy_flags flags) ++ enum fuse_buf_copy_flags flags) + { +- size_t copied = 0; +- +- if (dstv == srcv) +- return fuse_buf_size(dstv); +- +- for (;;) { +- const struct fuse_buf *src = fuse_bufvec_current(srcv); +- const struct fuse_buf *dst = fuse_bufvec_current(dstv); +- size_t src_len; +- size_t dst_len; +- size_t len; +- ssize_t res; +- +- if (src == NULL || dst == NULL) +- break; +- +- src_len = src->size - srcv->off; +- dst_len = dst->size - dstv->off; +- len = min_size(src_len, dst_len); +- +- res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags); +- if (res < 0) { +- if (!copied) +- return res; +- break; +- } +- copied += res; +- +- if (!fuse_bufvec_advance(srcv, res) || +- !fuse_bufvec_advance(dstv, res)) +- break; +- +- if (res < len) +- break; +- } +- +- return copied; ++ size_t copied = 0; ++ ++ if (dstv == srcv) { ++ return fuse_buf_size(dstv); ++ } ++ ++ for (;;) { ++ const struct fuse_buf *src = fuse_bufvec_current(srcv); ++ const struct fuse_buf *dst = fuse_bufvec_current(dstv); ++ size_t src_len; ++ size_t dst_len; ++ size_t len; ++ ssize_t res; ++ ++ if (src == NULL || dst == NULL) { ++ break; ++ } ++ ++ src_len = src->size - srcv->off; ++ dst_len = dst->size - dstv->off; ++ len = min_size(src_len, dst_len); ++ ++ res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags); ++ if (res < 0) { ++ if (!copied) { ++ return res; ++ } ++ break; ++ } ++ copied += res; ++ ++ if (!fuse_bufvec_advance(srcv, res) || ++ !fuse_bufvec_advance(dstv, res)) { ++ break; ++ } ++ ++ if (res < len) { ++ break; ++ } ++ } ++ ++ return copied; + } +diff --git a/tools/virtiofsd/fuse.h b/tools/virtiofsd/fuse.h +index 3202fba6bb..7a4c713559 100644 +--- a/tools/virtiofsd/fuse.h ++++ b/tools/virtiofsd/fuse.h +@@ -1,15 +1,15 @@ + /* +- FUSE: Filesystem in Userspace +- Copyright (C) 2001-2007 Miklos Szeredi +- +- This program can be distributed under the terms of the GNU LGPLv2. +- See the file COPYING.LIB. +-*/ ++ * FUSE: Filesystem in Userspace ++ * Copyright (C) 2001-2007 Miklos Szeredi ++ * ++ * This program can be distributed under the terms of the GNU LGPLv2. ++ * See the file COPYING.LIB. ++ */ + + #ifndef FUSE_H_ + #define FUSE_H_ + +-/** @file ++/* + * + * This file defines the library interface of FUSE + * +@@ -19,15 +19,15 @@ + #include "fuse_common.h" + + #include +-#include +-#include + #include + #include ++#include + #include ++#include + +-/* ----------------------------------------------------------- * +- * Basic FUSE API * +- * ----------------------------------------------------------- */ ++/* ++ * Basic FUSE API ++ */ + + /** Handle for a FUSE filesystem */ + struct fuse; +@@ -36,38 +36,39 @@ struct fuse; + * Readdir flags, passed to ->readdir() + */ + enum fuse_readdir_flags { +- /** +- * "Plus" mode. +- * +- * The kernel wants to prefill the inode cache during readdir. The +- * filesystem may honour this by filling in the attributes and setting +- * FUSE_FILL_DIR_FLAGS for the filler function. The filesystem may also +- * just ignore this flag completely. +- */ +- FUSE_READDIR_PLUS = (1 << 0), ++ /** ++ * "Plus" mode. ++ * ++ * The kernel wants to prefill the inode cache during readdir. The ++ * filesystem may honour this by filling in the attributes and setting ++ * FUSE_FILL_DIR_FLAGS for the filler function. The filesystem may also ++ * just ignore this flag completely. ++ */ ++ FUSE_READDIR_PLUS = (1 << 0), + }; + + enum fuse_fill_dir_flags { +- /** +- * "Plus" mode: all file attributes are valid +- * +- * The attributes are used by the kernel to prefill the inode cache +- * during a readdir. +- * +- * It is okay to set FUSE_FILL_DIR_PLUS if FUSE_READDIR_PLUS is not set +- * and vice versa. +- */ +- FUSE_FILL_DIR_PLUS = (1 << 1), ++ /** ++ * "Plus" mode: all file attributes are valid ++ * ++ * The attributes are used by the kernel to prefill the inode cache ++ * during a readdir. ++ * ++ * It is okay to set FUSE_FILL_DIR_PLUS if FUSE_READDIR_PLUS is not set ++ * and vice versa. ++ */ ++ FUSE_FILL_DIR_PLUS = (1 << 1), + }; + +-/** Function to add an entry in a readdir() operation ++/** ++ * Function to add an entry in a readdir() operation + * + * The *off* parameter can be any non-zero value that enables the + * filesystem to identify the current point in the directory + * stream. It does not need to be the actual physical position. A + * value of zero is reserved to indicate that seeking in directories + * is not supported. +- * ++ * + * @param buf the buffer passed to the readdir() operation + * @param name the file name of the directory entry + * @param stat file attributes, can be NULL +@@ -75,9 +76,9 @@ enum fuse_fill_dir_flags { + * @param flags fill flags + * @return 1 if buffer is full, zero otherwise + */ +-typedef int (*fuse_fill_dir_t) (void *buf, const char *name, +- const struct stat *stbuf, off_t off, +- enum fuse_fill_dir_flags flags); ++typedef int (*fuse_fill_dir_t)(void *buf, const char *name, ++ const struct stat *stbuf, off_t off, ++ enum fuse_fill_dir_flags flags); + /** + * Configuration of the high-level API + * +@@ -87,186 +88,186 @@ typedef int (*fuse_fill_dir_t) (void *buf, const char *name, + * file system implementation. + */ + struct fuse_config { +- /** +- * If `set_gid` is non-zero, the st_gid attribute of each file +- * is overwritten with the value of `gid`. +- */ +- int set_gid; +- unsigned int gid; +- +- /** +- * If `set_uid` is non-zero, the st_uid attribute of each file +- * is overwritten with the value of `uid`. +- */ +- int set_uid; +- unsigned int uid; +- +- /** +- * If `set_mode` is non-zero, the any permissions bits set in +- * `umask` are unset in the st_mode attribute of each file. +- */ +- int set_mode; +- unsigned int umask; +- +- /** +- * The timeout in seconds for which name lookups will be +- * cached. +- */ +- double entry_timeout; +- +- /** +- * The timeout in seconds for which a negative lookup will be +- * cached. This means, that if file did not exist (lookup +- * retuned ENOENT), the lookup will only be redone after the +- * timeout, and the file/directory will be assumed to not +- * exist until then. A value of zero means that negative +- * lookups are not cached. +- */ +- double negative_timeout; +- +- /** +- * The timeout in seconds for which file/directory attributes +- * (as returned by e.g. the `getattr` handler) are cached. +- */ +- double attr_timeout; +- +- /** +- * Allow requests to be interrupted +- */ +- int intr; +- +- /** +- * Specify which signal number to send to the filesystem when +- * a request is interrupted. The default is hardcoded to +- * USR1. +- */ +- int intr_signal; +- +- /** +- * Normally, FUSE assigns inodes to paths only for as long as +- * the kernel is aware of them. With this option inodes are +- * instead remembered for at least this many seconds. This +- * will require more memory, but may be necessary when using +- * applications that make use of inode numbers. +- * +- * A number of -1 means that inodes will be remembered for the +- * entire life-time of the file-system process. +- */ +- int remember; +- +- /** +- * The default behavior is that if an open file is deleted, +- * the file is renamed to a hidden file (.fuse_hiddenXXX), and +- * only removed when the file is finally released. This +- * relieves the filesystem implementation of having to deal +- * with this problem. This option disables the hiding +- * behavior, and files are removed immediately in an unlink +- * operation (or in a rename operation which overwrites an +- * existing file). +- * +- * It is recommended that you not use the hard_remove +- * option. When hard_remove is set, the following libc +- * functions fail on unlinked files (returning errno of +- * ENOENT): read(2), write(2), fsync(2), close(2), f*xattr(2), +- * ftruncate(2), fstat(2), fchmod(2), fchown(2) +- */ +- int hard_remove; +- +- /** +- * Honor the st_ino field in the functions getattr() and +- * fill_dir(). This value is used to fill in the st_ino field +- * in the stat(2), lstat(2), fstat(2) functions and the d_ino +- * field in the readdir(2) function. The filesystem does not +- * have to guarantee uniqueness, however some applications +- * rely on this value being unique for the whole filesystem. +- * +- * Note that this does *not* affect the inode that libfuse +- * and the kernel use internally (also called the "nodeid"). +- */ +- int use_ino; +- +- /** +- * If use_ino option is not given, still try to fill in the +- * d_ino field in readdir(2). If the name was previously +- * looked up, and is still in the cache, the inode number +- * found there will be used. Otherwise it will be set to -1. +- * If use_ino option is given, this option is ignored. +- */ +- int readdir_ino; +- +- /** +- * This option disables the use of page cache (file content cache) +- * in the kernel for this filesystem. This has several affects: +- * +- * 1. Each read(2) or write(2) system call will initiate one +- * or more read or write operations, data will not be +- * cached in the kernel. +- * +- * 2. The return value of the read() and write() system calls +- * will correspond to the return values of the read and +- * write operations. This is useful for example if the +- * file size is not known in advance (before reading it). +- * +- * Internally, enabling this option causes fuse to set the +- * `direct_io` field of `struct fuse_file_info` - overwriting +- * any value that was put there by the file system. +- */ +- int direct_io; +- +- /** +- * This option disables flushing the cache of the file +- * contents on every open(2). This should only be enabled on +- * filesystems where the file data is never changed +- * externally (not through the mounted FUSE filesystem). Thus +- * it is not suitable for network filesystems and other +- * intermediate filesystems. +- * +- * NOTE: if this option is not specified (and neither +- * direct_io) data is still cached after the open(2), so a +- * read(2) system call will not always initiate a read +- * operation. +- * +- * Internally, enabling this option causes fuse to set the +- * `keep_cache` field of `struct fuse_file_info` - overwriting +- * any value that was put there by the file system. +- */ +- int kernel_cache; +- +- /** +- * This option is an alternative to `kernel_cache`. Instead of +- * unconditionally keeping cached data, the cached data is +- * invalidated on open(2) if if the modification time or the +- * size of the file has changed since it was last opened. +- */ +- int auto_cache; +- +- /** +- * The timeout in seconds for which file attributes are cached +- * for the purpose of checking if auto_cache should flush the +- * file data on open. +- */ +- int ac_attr_timeout_set; +- double ac_attr_timeout; +- +- /** +- * If this option is given the file-system handlers for the +- * following operations will not receive path information: +- * read, write, flush, release, fsync, readdir, releasedir, +- * fsyncdir, lock, ioctl and poll. +- * +- * For the truncate, getattr, chmod, chown and utimens +- * operations the path will be provided only if the struct +- * fuse_file_info argument is NULL. +- */ +- int nullpath_ok; +- +- /** +- * The remaining options are used by libfuse internally and +- * should not be touched. +- */ +- int show_help; +- char *modules; +- int debug; ++ /** ++ * If `set_gid` is non-zero, the st_gid attribute of each file ++ * is overwritten with the value of `gid`. ++ */ ++ int set_gid; ++ unsigned int gid; ++ ++ /** ++ * If `set_uid` is non-zero, the st_uid attribute of each file ++ * is overwritten with the value of `uid`. ++ */ ++ int set_uid; ++ unsigned int uid; ++ ++ /** ++ * If `set_mode` is non-zero, the any permissions bits set in ++ * `umask` are unset in the st_mode attribute of each file. ++ */ ++ int set_mode; ++ unsigned int umask; ++ ++ /** ++ * The timeout in seconds for which name lookups will be ++ * cached. ++ */ ++ double entry_timeout; ++ ++ /** ++ * The timeout in seconds for which a negative lookup will be ++ * cached. This means, that if file did not exist (lookup ++ * retuned ENOENT), the lookup will only be redone after the ++ * timeout, and the file/directory will be assumed to not ++ * exist until then. A value of zero means that negative ++ * lookups are not cached. ++ */ ++ double negative_timeout; ++ ++ /** ++ * The timeout in seconds for which file/directory attributes ++ * (as returned by e.g. the `getattr` handler) are cached. ++ */ ++ double attr_timeout; ++ ++ /** ++ * Allow requests to be interrupted ++ */ ++ int intr; ++ ++ /** ++ * Specify which signal number to send to the filesystem when ++ * a request is interrupted. The default is hardcoded to ++ * USR1. ++ */ ++ int intr_signal; ++ ++ /** ++ * Normally, FUSE assigns inodes to paths only for as long as ++ * the kernel is aware of them. With this option inodes are ++ * instead remembered for at least this many seconds. This ++ * will require more memory, but may be necessary when using ++ * applications that make use of inode numbers. ++ * ++ * A number of -1 means that inodes will be remembered for the ++ * entire life-time of the file-system process. ++ */ ++ int remember; ++ ++ /** ++ * The default behavior is that if an open file is deleted, ++ * the file is renamed to a hidden file (.fuse_hiddenXXX), and ++ * only removed when the file is finally released. This ++ * relieves the filesystem implementation of having to deal ++ * with this problem. This option disables the hiding ++ * behavior, and files are removed immediately in an unlink ++ * operation (or in a rename operation which overwrites an ++ * existing file). ++ * ++ * It is recommended that you not use the hard_remove ++ * option. When hard_remove is set, the following libc ++ * functions fail on unlinked files (returning errno of ++ * ENOENT): read(2), write(2), fsync(2), close(2), f*xattr(2), ++ * ftruncate(2), fstat(2), fchmod(2), fchown(2) ++ */ ++ int hard_remove; ++ ++ /** ++ * Honor the st_ino field in the functions getattr() and ++ * fill_dir(). This value is used to fill in the st_ino field ++ * in the stat(2), lstat(2), fstat(2) functions and the d_ino ++ * field in the readdir(2) function. The filesystem does not ++ * have to guarantee uniqueness, however some applications ++ * rely on this value being unique for the whole filesystem. ++ * ++ * Note that this does *not* affect the inode that libfuse ++ * and the kernel use internally (also called the "nodeid"). ++ */ ++ int use_ino; ++ ++ /** ++ * If use_ino option is not given, still try to fill in the ++ * d_ino field in readdir(2). If the name was previously ++ * looked up, and is still in the cache, the inode number ++ * found there will be used. Otherwise it will be set to -1. ++ * If use_ino option is given, this option is ignored. ++ */ ++ int readdir_ino; ++ ++ /** ++ * This option disables the use of page cache (file content cache) ++ * in the kernel for this filesystem. This has several affects: ++ * ++ * 1. Each read(2) or write(2) system call will initiate one ++ * or more read or write operations, data will not be ++ * cached in the kernel. ++ * ++ * 2. The return value of the read() and write() system calls ++ * will correspond to the return values of the read and ++ * write operations. This is useful for example if the ++ * file size is not known in advance (before reading it). ++ * ++ * Internally, enabling this option causes fuse to set the ++ * `direct_io` field of `struct fuse_file_info` - overwriting ++ * any value that was put there by the file system. ++ */ ++ int direct_io; ++ ++ /** ++ * This option disables flushing the cache of the file ++ * contents on every open(2). This should only be enabled on ++ * filesystems where the file data is never changed ++ * externally (not through the mounted FUSE filesystem). Thus ++ * it is not suitable for network filesystems and other ++ * intermediate filesystems. ++ * ++ * NOTE: if this option is not specified (and neither ++ * direct_io) data is still cached after the open(2), so a ++ * read(2) system call will not always initiate a read ++ * operation. ++ * ++ * Internally, enabling this option causes fuse to set the ++ * `keep_cache` field of `struct fuse_file_info` - overwriting ++ * any value that was put there by the file system. ++ */ ++ int kernel_cache; ++ ++ /** ++ * This option is an alternative to `kernel_cache`. Instead of ++ * unconditionally keeping cached data, the cached data is ++ * invalidated on open(2) if if the modification time or the ++ * size of the file has changed since it was last opened. ++ */ ++ int auto_cache; ++ ++ /** ++ * The timeout in seconds for which file attributes are cached ++ * for the purpose of checking if auto_cache should flush the ++ * file data on open. ++ */ ++ int ac_attr_timeout_set; ++ double ac_attr_timeout; ++ ++ /** ++ * If this option is given the file-system handlers for the ++ * following operations will not receive path information: ++ * read, write, flush, release, fsync, readdir, releasedir, ++ * fsyncdir, lock, ioctl and poll. ++ * ++ * For the truncate, getattr, chmod, chown and utimens ++ * operations the path will be provided only if the struct ++ * fuse_file_info argument is NULL. ++ */ ++ int nullpath_ok; ++ ++ /** ++ * The remaining options are used by libfuse internally and ++ * should not be touched. ++ */ ++ int show_help; ++ char *modules; ++ int debug; + }; + + +@@ -293,515 +294,535 @@ struct fuse_config { + * Almost all operations take a path which can be of any length. + */ + struct fuse_operations { +- /** Get file attributes. +- * +- * Similar to stat(). The 'st_dev' and 'st_blksize' fields are +- * ignored. The 'st_ino' field is ignored except if the 'use_ino' +- * mount option is given. In that case it is passed to userspace, +- * but libfuse and the kernel will still assign a different +- * inode for internal use (called the "nodeid"). +- * +- * `fi` will always be NULL if the file is not currently open, but +- * may also be NULL if the file is open. +- */ +- int (*getattr) (const char *, struct stat *, struct fuse_file_info *fi); +- +- /** Read the target of a symbolic link +- * +- * The buffer should be filled with a null terminated string. The +- * buffer size argument includes the space for the terminating +- * null character. If the linkname is too long to fit in the +- * buffer, it should be truncated. The return value should be 0 +- * for success. +- */ +- int (*readlink) (const char *, char *, size_t); +- +- /** Create a file node +- * +- * This is called for creation of all non-directory, non-symlink +- * nodes. If the filesystem defines a create() method, then for +- * regular files that will be called instead. +- */ +- int (*mknod) (const char *, mode_t, dev_t); +- +- /** Create a directory +- * +- * Note that the mode argument may not have the type specification +- * bits set, i.e. S_ISDIR(mode) can be false. To obtain the +- * correct directory type bits use mode|S_IFDIR +- * */ +- int (*mkdir) (const char *, mode_t); +- +- /** Remove a file */ +- int (*unlink) (const char *); +- +- /** Remove a directory */ +- int (*rmdir) (const char *); +- +- /** Create a symbolic link */ +- int (*symlink) (const char *, const char *); +- +- /** Rename a file +- * +- * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If +- * RENAME_NOREPLACE is specified, the filesystem must not +- * overwrite *newname* if it exists and return an error +- * instead. If `RENAME_EXCHANGE` is specified, the filesystem +- * must atomically exchange the two files, i.e. both must +- * exist and neither may be deleted. +- */ +- int (*rename) (const char *, const char *, unsigned int flags); +- +- /** Create a hard link to a file */ +- int (*link) (const char *, const char *); +- +- /** Change the permission bits of a file +- * +- * `fi` will always be NULL if the file is not currenlty open, but +- * may also be NULL if the file is open. +- */ +- int (*chmod) (const char *, mode_t, struct fuse_file_info *fi); +- +- /** Change the owner and group of a file +- * +- * `fi` will always be NULL if the file is not currenlty open, but +- * may also be NULL if the file is open. +- * +- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is +- * expected to reset the setuid and setgid bits. +- */ +- int (*chown) (const char *, uid_t, gid_t, struct fuse_file_info *fi); +- +- /** Change the size of a file +- * +- * `fi` will always be NULL if the file is not currenlty open, but +- * may also be NULL if the file is open. +- * +- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is +- * expected to reset the setuid and setgid bits. +- */ +- int (*truncate) (const char *, off_t, struct fuse_file_info *fi); +- +- /** Open a file +- * +- * Open flags are available in fi->flags. The following rules +- * apply. +- * +- * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be +- * filtered out / handled by the kernel. +- * +- * - Access modes (O_RDONLY, O_WRONLY, O_RDWR, O_EXEC, O_SEARCH) +- * should be used by the filesystem to check if the operation is +- * permitted. If the ``-o default_permissions`` mount option is +- * given, this check is already done by the kernel before calling +- * open() and may thus be omitted by the filesystem. +- * +- * - When writeback caching is enabled, the kernel may send +- * read requests even for files opened with O_WRONLY. The +- * filesystem should be prepared to handle this. +- * +- * - When writeback caching is disabled, the filesystem is +- * expected to properly handle the O_APPEND flag and ensure +- * that each write is appending to the end of the file. +- * +- * - When writeback caching is enabled, the kernel will +- * handle O_APPEND. However, unless all changes to the file +- * come through the kernel this will not work reliably. The +- * filesystem should thus either ignore the O_APPEND flag +- * (and let the kernel handle it), or return an error +- * (indicating that reliably O_APPEND is not available). +- * +- * Filesystem may store an arbitrary file handle (pointer, +- * index, etc) in fi->fh, and use this in other all other file +- * operations (read, write, flush, release, fsync). +- * +- * Filesystem may also implement stateless file I/O and not store +- * anything in fi->fh. +- * +- * There are also some flags (direct_io, keep_cache) which the +- * filesystem may set in fi, to change the way the file is opened. +- * See fuse_file_info structure in for more details. +- * +- * If this request is answered with an error code of ENOSYS +- * and FUSE_CAP_NO_OPEN_SUPPORT is set in +- * `fuse_conn_info.capable`, this is treated as success and +- * future calls to open will also succeed without being send +- * to the filesystem process. +- * +- */ +- int (*open) (const char *, struct fuse_file_info *); +- +- /** Read data from an open file +- * +- * Read should return exactly the number of bytes requested except +- * on EOF or error, otherwise the rest of the data will be +- * substituted with zeroes. An exception to this is when the +- * 'direct_io' mount option is specified, in which case the return +- * value of the read system call will reflect the return value of +- * this operation. +- */ +- int (*read) (const char *, char *, size_t, off_t, +- struct fuse_file_info *); +- +- /** Write data to an open file +- * +- * Write should return exactly the number of bytes requested +- * except on error. An exception to this is when the 'direct_io' +- * mount option is specified (see read operation). +- * +- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is +- * expected to reset the setuid and setgid bits. +- */ +- int (*write) (const char *, const char *, size_t, off_t, +- struct fuse_file_info *); +- +- /** Get file system statistics +- * +- * The 'f_favail', 'f_fsid' and 'f_flag' fields are ignored +- */ +- int (*statfs) (const char *, struct statvfs *); +- +- /** Possibly flush cached data +- * +- * BIG NOTE: This is not equivalent to fsync(). It's not a +- * request to sync dirty data. +- * +- * Flush is called on each close() of a file descriptor, as opposed to +- * release which is called on the close of the last file descriptor for +- * a file. Under Linux, errors returned by flush() will be passed to +- * userspace as errors from close(), so flush() is a good place to write +- * back any cached dirty data. However, many applications ignore errors +- * on close(), and on non-Linux systems, close() may succeed even if flush() +- * returns an error. For these reasons, filesystems should not assume +- * that errors returned by flush will ever be noticed or even +- * delivered. +- * +- * NOTE: The flush() method may be called more than once for each +- * open(). This happens if more than one file descriptor refers to an +- * open file handle, e.g. due to dup(), dup2() or fork() calls. It is +- * not possible to determine if a flush is final, so each flush should +- * be treated equally. Multiple write-flush sequences are relatively +- * rare, so this shouldn't be a problem. +- * +- * Filesystems shouldn't assume that flush will be called at any +- * particular point. It may be called more times than expected, or not +- * at all. +- * +- * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html +- */ +- int (*flush) (const char *, struct fuse_file_info *); +- +- /** Release an open file +- * +- * Release is called when there are no more references to an open +- * file: all file descriptors are closed and all memory mappings +- * are unmapped. +- * +- * For every open() call there will be exactly one release() call +- * with the same flags and file handle. It is possible to +- * have a file opened more than once, in which case only the last +- * release will mean, that no more reads/writes will happen on the +- * file. The return value of release is ignored. +- */ +- int (*release) (const char *, struct fuse_file_info *); +- +- /** Synchronize file contents +- * +- * If the datasync parameter is non-zero, then only the user data +- * should be flushed, not the meta data. +- */ +- int (*fsync) (const char *, int, struct fuse_file_info *); +- +- /** Set extended attributes */ +- int (*setxattr) (const char *, const char *, const char *, size_t, int); +- +- /** Get extended attributes */ +- int (*getxattr) (const char *, const char *, char *, size_t); +- +- /** List extended attributes */ +- int (*listxattr) (const char *, char *, size_t); +- +- /** Remove extended attributes */ +- int (*removexattr) (const char *, const char *); +- +- /** Open directory +- * +- * Unless the 'default_permissions' mount option is given, +- * this method should check if opendir is permitted for this +- * directory. Optionally opendir may also return an arbitrary +- * filehandle in the fuse_file_info structure, which will be +- * passed to readdir, releasedir and fsyncdir. +- */ +- int (*opendir) (const char *, struct fuse_file_info *); +- +- /** Read directory +- * +- * The filesystem may choose between two modes of operation: +- * +- * 1) The readdir implementation ignores the offset parameter, and +- * passes zero to the filler function's offset. The filler +- * function will not return '1' (unless an error happens), so the +- * whole directory is read in a single readdir operation. +- * +- * 2) The readdir implementation keeps track of the offsets of the +- * directory entries. It uses the offset parameter and always +- * passes non-zero offset to the filler function. When the buffer +- * is full (or an error happens) the filler function will return +- * '1'. +- */ +- int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t, +- struct fuse_file_info *, enum fuse_readdir_flags); +- +- /** Release directory +- */ +- int (*releasedir) (const char *, struct fuse_file_info *); +- +- /** Synchronize directory contents +- * +- * If the datasync parameter is non-zero, then only the user data +- * should be flushed, not the meta data +- */ +- int (*fsyncdir) (const char *, int, struct fuse_file_info *); +- +- /** +- * Initialize filesystem +- * +- * The return value will passed in the `private_data` field of +- * `struct fuse_context` to all file operations, and as a +- * parameter to the destroy() method. It overrides the initial +- * value provided to fuse_main() / fuse_new(). +- */ +- void *(*init) (struct fuse_conn_info *conn, +- struct fuse_config *cfg); +- +- /** +- * Clean up filesystem +- * +- * Called on filesystem exit. +- */ +- void (*destroy) (void *private_data); +- +- /** +- * Check file access permissions +- * +- * This will be called for the access() system call. If the +- * 'default_permissions' mount option is given, this method is not +- * called. +- * +- * This method is not called under Linux kernel versions 2.4.x +- */ +- int (*access) (const char *, int); +- +- /** +- * Create and open a file +- * +- * If the file does not exist, first create it with the specified +- * mode, and then open it. +- * +- * If this method is not implemented or under Linux kernel +- * versions earlier than 2.6.15, the mknod() and open() methods +- * will be called instead. +- */ +- int (*create) (const char *, mode_t, struct fuse_file_info *); +- +- /** +- * Perform POSIX file locking operation +- * +- * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW. +- * +- * For the meaning of fields in 'struct flock' see the man page +- * for fcntl(2). The l_whence field will always be set to +- * SEEK_SET. +- * +- * For checking lock ownership, the 'fuse_file_info->owner' +- * argument must be used. +- * +- * For F_GETLK operation, the library will first check currently +- * held locks, and if a conflicting lock is found it will return +- * information without calling this method. This ensures, that +- * for local locks the l_pid field is correctly filled in. The +- * results may not be accurate in case of race conditions and in +- * the presence of hard links, but it's unlikely that an +- * application would rely on accurate GETLK results in these +- * cases. If a conflicting lock is not found, this method will be +- * called, and the filesystem may fill out l_pid by a meaningful +- * value, or it may leave this field zero. +- * +- * For F_SETLK and F_SETLKW the l_pid field will be set to the pid +- * of the process performing the locking operation. +- * +- * Note: if this method is not implemented, the kernel will still +- * allow file locking to work locally. Hence it is only +- * interesting for network filesystems and similar. +- */ +- int (*lock) (const char *, struct fuse_file_info *, int cmd, +- struct flock *); +- +- /** +- * Change the access and modification times of a file with +- * nanosecond resolution +- * +- * This supersedes the old utime() interface. New applications +- * should use this. +- * +- * `fi` will always be NULL if the file is not currenlty open, but +- * may also be NULL if the file is open. +- * +- * See the utimensat(2) man page for details. +- */ +- int (*utimens) (const char *, const struct timespec tv[2], +- struct fuse_file_info *fi); +- +- /** +- * Map block index within file to block index within device +- * +- * Note: This makes sense only for block device backed filesystems +- * mounted with the 'blkdev' option +- */ +- int (*bmap) (const char *, size_t blocksize, uint64_t *idx); +- +- /** +- * Ioctl +- * +- * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in +- * 64bit environment. The size and direction of data is +- * determined by _IOC_*() decoding of cmd. For _IOC_NONE, +- * data will be NULL, for _IOC_WRITE data is out area, for +- * _IOC_READ in area and if both are set in/out area. In all +- * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes. +- * +- * If flags has FUSE_IOCTL_DIR then the fuse_file_info refers to a +- * directory file handle. +- * +- * Note : the unsigned long request submitted by the application +- * is truncated to 32 bits. +- */ +- int (*ioctl) (const char *, unsigned int cmd, void *arg, +- struct fuse_file_info *, unsigned int flags, void *data); +- +- /** +- * Poll for IO readiness events +- * +- * Note: If ph is non-NULL, the client should notify +- * when IO readiness events occur by calling +- * fuse_notify_poll() with the specified ph. +- * +- * Regardless of the number of times poll with a non-NULL ph +- * is received, single notification is enough to clear all. +- * Notifying more times incurs overhead but doesn't harm +- * correctness. +- * +- * The callee is responsible for destroying ph with +- * fuse_pollhandle_destroy() when no longer in use. +- */ +- int (*poll) (const char *, struct fuse_file_info *, +- struct fuse_pollhandle *ph, unsigned *reventsp); +- +- /** Write contents of buffer to an open file +- * +- * Similar to the write() method, but data is supplied in a +- * generic buffer. Use fuse_buf_copy() to transfer data to +- * the destination. +- * +- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is +- * expected to reset the setuid and setgid bits. +- */ +- int (*write_buf) (const char *, struct fuse_bufvec *buf, off_t off, +- struct fuse_file_info *); +- +- /** Store data from an open file in a buffer +- * +- * Similar to the read() method, but data is stored and +- * returned in a generic buffer. +- * +- * No actual copying of data has to take place, the source +- * file descriptor may simply be stored in the buffer for +- * later data transfer. +- * +- * The buffer must be allocated dynamically and stored at the +- * location pointed to by bufp. If the buffer contains memory +- * regions, they too must be allocated using malloc(). The +- * allocated memory will be freed by the caller. +- */ +- int (*read_buf) (const char *, struct fuse_bufvec **bufp, +- size_t size, off_t off, struct fuse_file_info *); +- /** +- * Perform BSD file locking operation +- * +- * The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN +- * +- * Nonblocking requests will be indicated by ORing LOCK_NB to +- * the above operations +- * +- * For more information see the flock(2) manual page. +- * +- * Additionally fi->owner will be set to a value unique to +- * this open file. This same value will be supplied to +- * ->release() when the file is released. +- * +- * Note: if this method is not implemented, the kernel will still +- * allow file locking to work locally. Hence it is only +- * interesting for network filesystems and similar. +- */ +- int (*flock) (const char *, struct fuse_file_info *, int op); +- +- /** +- * Allocates space for an open file +- * +- * This function ensures that required space is allocated for specified +- * file. If this function returns success then any subsequent write +- * request to specified range is guaranteed not to fail because of lack +- * of space on the file system media. +- */ +- int (*fallocate) (const char *, int, off_t, off_t, +- struct fuse_file_info *); +- +- /** +- * Copy a range of data from one file to another +- * +- * Performs an optimized copy between two file descriptors without the +- * additional cost of transferring data through the FUSE kernel module +- * to user space (glibc) and then back into the FUSE filesystem again. +- * +- * In case this method is not implemented, glibc falls back to reading +- * data from the source and writing to the destination. Effectively +- * doing an inefficient copy of the data. +- */ +- ssize_t (*copy_file_range) (const char *path_in, +- struct fuse_file_info *fi_in, +- off_t offset_in, const char *path_out, +- struct fuse_file_info *fi_out, +- off_t offset_out, size_t size, int flags); +- +- /** +- * Find next data or hole after the specified offset +- */ +- off_t (*lseek) (const char *, off_t off, int whence, struct fuse_file_info *); ++ /** ++ * Get file attributes. ++ * ++ * Similar to stat(). The 'st_dev' and 'st_blksize' fields are ++ * ignored. The 'st_ino' field is ignored except if the 'use_ino' ++ * mount option is given. In that case it is passed to userspace, ++ * but libfuse and the kernel will still assign a different ++ * inode for internal use (called the "nodeid"). ++ * ++ * `fi` will always be NULL if the file is not currently open, but ++ * may also be NULL if the file is open. ++ */ ++ int (*getattr)(const char *, struct stat *, struct fuse_file_info *fi); ++ ++ /** ++ * Read the target of a symbolic link ++ * ++ * The buffer should be filled with a null terminated string. The ++ * buffer size argument includes the space for the terminating ++ * null character. If the linkname is too long to fit in the ++ * buffer, it should be truncated. The return value should be 0 ++ * for success. ++ */ ++ int (*readlink)(const char *, char *, size_t); ++ ++ /** ++ * Create a file node ++ * ++ * This is called for creation of all non-directory, non-symlink ++ * nodes. If the filesystem defines a create() method, then for ++ * regular files that will be called instead. ++ */ ++ int (*mknod)(const char *, mode_t, dev_t); ++ ++ /** ++ * Create a directory ++ * ++ * Note that the mode argument may not have the type specification ++ * bits set, i.e. S_ISDIR(mode) can be false. To obtain the ++ * correct directory type bits use mode|S_IFDIR ++ */ ++ int (*mkdir)(const char *, mode_t); ++ ++ /** Remove a file */ ++ int (*unlink)(const char *); ++ ++ /** Remove a directory */ ++ int (*rmdir)(const char *); ++ ++ /** Create a symbolic link */ ++ int (*symlink)(const char *, const char *); ++ ++ /** ++ * Rename a file ++ * ++ * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If ++ * RENAME_NOREPLACE is specified, the filesystem must not ++ * overwrite *newname* if it exists and return an error ++ * instead. If `RENAME_EXCHANGE` is specified, the filesystem ++ * must atomically exchange the two files, i.e. both must ++ * exist and neither may be deleted. ++ */ ++ int (*rename)(const char *, const char *, unsigned int flags); ++ ++ /** Create a hard link to a file */ ++ int (*link)(const char *, const char *); ++ ++ /** ++ * Change the permission bits of a file ++ * ++ * `fi` will always be NULL if the file is not currenlty open, but ++ * may also be NULL if the file is open. ++ */ ++ int (*chmod)(const char *, mode_t, struct fuse_file_info *fi); ++ ++ /** ++ * Change the owner and group of a file ++ * ++ * `fi` will always be NULL if the file is not currenlty open, but ++ * may also be NULL if the file is open. ++ * ++ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is ++ * expected to reset the setuid and setgid bits. ++ */ ++ int (*chown)(const char *, uid_t, gid_t, struct fuse_file_info *fi); ++ ++ /** ++ * Change the size of a file ++ * ++ * `fi` will always be NULL if the file is not currenlty open, but ++ * may also be NULL if the file is open. ++ * ++ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is ++ * expected to reset the setuid and setgid bits. ++ */ ++ int (*truncate)(const char *, off_t, struct fuse_file_info *fi); ++ ++ /** ++ * Open a file ++ * ++ * Open flags are available in fi->flags. The following rules ++ * apply. ++ * ++ * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be ++ * filtered out / handled by the kernel. ++ * ++ * - Access modes (O_RDONLY, O_WRONLY, O_RDWR, O_EXEC, O_SEARCH) ++ * should be used by the filesystem to check if the operation is ++ * permitted. If the ``-o default_permissions`` mount option is ++ * given, this check is already done by the kernel before calling ++ * open() and may thus be omitted by the filesystem. ++ * ++ * - When writeback caching is enabled, the kernel may send ++ * read requests even for files opened with O_WRONLY. The ++ * filesystem should be prepared to handle this. ++ * ++ * - When writeback caching is disabled, the filesystem is ++ * expected to properly handle the O_APPEND flag and ensure ++ * that each write is appending to the end of the file. ++ * ++ * - When writeback caching is enabled, the kernel will ++ * handle O_APPEND. However, unless all changes to the file ++ * come through the kernel this will not work reliably. The ++ * filesystem should thus either ignore the O_APPEND flag ++ * (and let the kernel handle it), or return an error ++ * (indicating that reliably O_APPEND is not available). ++ * ++ * Filesystem may store an arbitrary file handle (pointer, ++ * index, etc) in fi->fh, and use this in other all other file ++ * operations (read, write, flush, release, fsync). ++ * ++ * Filesystem may also implement stateless file I/O and not store ++ * anything in fi->fh. ++ * ++ * There are also some flags (direct_io, keep_cache) which the ++ * filesystem may set in fi, to change the way the file is opened. ++ * See fuse_file_info structure in for more details. ++ * ++ * If this request is answered with an error code of ENOSYS ++ * and FUSE_CAP_NO_OPEN_SUPPORT is set in ++ * `fuse_conn_info.capable`, this is treated as success and ++ * future calls to open will also succeed without being send ++ * to the filesystem process. ++ * ++ */ ++ int (*open)(const char *, struct fuse_file_info *); ++ ++ /** ++ * Read data from an open file ++ * ++ * Read should return exactly the number of bytes requested except ++ * on EOF or error, otherwise the rest of the data will be ++ * substituted with zeroes. An exception to this is when the ++ * 'direct_io' mount option is specified, in which case the return ++ * value of the read system call will reflect the return value of ++ * this operation. ++ */ ++ int (*read)(const char *, char *, size_t, off_t, struct fuse_file_info *); ++ ++ /** ++ * Write data to an open file ++ * ++ * Write should return exactly the number of bytes requested ++ * except on error. An exception to this is when the 'direct_io' ++ * mount option is specified (see read operation). ++ * ++ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is ++ * expected to reset the setuid and setgid bits. ++ */ ++ int (*write)(const char *, const char *, size_t, off_t, ++ struct fuse_file_info *); ++ ++ /** ++ * Get file system statistics ++ * ++ * The 'f_favail', 'f_fsid' and 'f_flag' fields are ignored ++ */ ++ int (*statfs)(const char *, struct statvfs *); ++ ++ /** ++ * Possibly flush cached data ++ * ++ * BIG NOTE: This is not equivalent to fsync(). It's not a ++ * request to sync dirty data. ++ * ++ * Flush is called on each close() of a file descriptor, as opposed to ++ * release which is called on the close of the last file descriptor for ++ * a file. Under Linux, errors returned by flush() will be passed to ++ * userspace as errors from close(), so flush() is a good place to write ++ * back any cached dirty data. However, many applications ignore errors ++ * on close(), and on non-Linux systems, close() may succeed even if flush() ++ * returns an error. For these reasons, filesystems should not assume ++ * that errors returned by flush will ever be noticed or even ++ * delivered. ++ * ++ * NOTE: The flush() method may be called more than once for each ++ * open(). This happens if more than one file descriptor refers to an ++ * open file handle, e.g. due to dup(), dup2() or fork() calls. It is ++ * not possible to determine if a flush is final, so each flush should ++ * be treated equally. Multiple write-flush sequences are relatively ++ * rare, so this shouldn't be a problem. ++ * ++ * Filesystems shouldn't assume that flush will be called at any ++ * particular point. It may be called more times than expected, or not ++ * at all. ++ * ++ * [close]: ++ * http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html ++ */ ++ int (*flush)(const char *, struct fuse_file_info *); ++ ++ /** ++ * Release an open file ++ * ++ * Release is called when there are no more references to an open ++ * file: all file descriptors are closed and all memory mappings ++ * are unmapped. ++ * ++ * For every open() call there will be exactly one release() call ++ * with the same flags and file handle. It is possible to ++ * have a file opened more than once, in which case only the last ++ * release will mean, that no more reads/writes will happen on the ++ * file. The return value of release is ignored. ++ */ ++ int (*release)(const char *, struct fuse_file_info *); ++ ++ /* ++ * Synchronize file contents ++ * ++ * If the datasync parameter is non-zero, then only the user data ++ * should be flushed, not the meta data. ++ */ ++ int (*fsync)(const char *, int, struct fuse_file_info *); ++ ++ /** Set extended attributes */ ++ int (*setxattr)(const char *, const char *, const char *, size_t, int); ++ ++ /** Get extended attributes */ ++ int (*getxattr)(const char *, const char *, char *, size_t); ++ ++ /** List extended attributes */ ++ int (*listxattr)(const char *, char *, size_t); ++ ++ /** Remove extended attributes */ ++ int (*removexattr)(const char *, const char *); ++ ++ /* ++ * Open directory ++ * ++ * Unless the 'default_permissions' mount option is given, ++ * this method should check if opendir is permitted for this ++ * directory. Optionally opendir may also return an arbitrary ++ * filehandle in the fuse_file_info structure, which will be ++ * passed to readdir, releasedir and fsyncdir. ++ */ ++ int (*opendir)(const char *, struct fuse_file_info *); ++ ++ /* ++ * Read directory ++ * ++ * The filesystem may choose between two modes of operation: ++ * ++ * 1) The readdir implementation ignores the offset parameter, and ++ * passes zero to the filler function's offset. The filler ++ * function will not return '1' (unless an error happens), so the ++ * whole directory is read in a single readdir operation. ++ * ++ * 2) The readdir implementation keeps track of the offsets of the ++ * directory entries. It uses the offset parameter and always ++ * passes non-zero offset to the filler function. When the buffer ++ * is full (or an error happens) the filler function will return ++ * '1'. ++ */ ++ int (*readdir)(const char *, void *, fuse_fill_dir_t, off_t, ++ struct fuse_file_info *, enum fuse_readdir_flags); ++ ++ /** ++ * Release directory ++ */ ++ int (*releasedir)(const char *, struct fuse_file_info *); ++ ++ /** ++ * Synchronize directory contents ++ * ++ * If the datasync parameter is non-zero, then only the user data ++ * should be flushed, not the meta data ++ */ ++ int (*fsyncdir)(const char *, int, struct fuse_file_info *); ++ ++ /** ++ * Initialize filesystem ++ * ++ * The return value will passed in the `private_data` field of ++ * `struct fuse_context` to all file operations, and as a ++ * parameter to the destroy() method. It overrides the initial ++ * value provided to fuse_main() / fuse_new(). ++ */ ++ void *(*init)(struct fuse_conn_info *conn, struct fuse_config *cfg); ++ ++ /** ++ * Clean up filesystem ++ * ++ * Called on filesystem exit. ++ */ ++ void (*destroy)(void *private_data); ++ ++ /** ++ * Check file access permissions ++ * ++ * This will be called for the access() system call. If the ++ * 'default_permissions' mount option is given, this method is not ++ * called. ++ * ++ * This method is not called under Linux kernel versions 2.4.x ++ */ ++ int (*access)(const char *, int); ++ ++ /** ++ * Create and open a file ++ * ++ * If the file does not exist, first create it with the specified ++ * mode, and then open it. ++ * ++ * If this method is not implemented or under Linux kernel ++ * versions earlier than 2.6.15, the mknod() and open() methods ++ * will be called instead. ++ */ ++ int (*create)(const char *, mode_t, struct fuse_file_info *); ++ ++ /** ++ * Perform POSIX file locking operation ++ * ++ * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW. ++ * ++ * For the meaning of fields in 'struct flock' see the man page ++ * for fcntl(2). The l_whence field will always be set to ++ * SEEK_SET. ++ * ++ * For checking lock ownership, the 'fuse_file_info->owner' ++ * argument must be used. ++ * ++ * For F_GETLK operation, the library will first check currently ++ * held locks, and if a conflicting lock is found it will return ++ * information without calling this method. This ensures, that ++ * for local locks the l_pid field is correctly filled in. The ++ * results may not be accurate in case of race conditions and in ++ * the presence of hard links, but it's unlikely that an ++ * application would rely on accurate GETLK results in these ++ * cases. If a conflicting lock is not found, this method will be ++ * called, and the filesystem may fill out l_pid by a meaningful ++ * value, or it may leave this field zero. ++ * ++ * For F_SETLK and F_SETLKW the l_pid field will be set to the pid ++ * of the process performing the locking operation. ++ * ++ * Note: if this method is not implemented, the kernel will still ++ * allow file locking to work locally. Hence it is only ++ * interesting for network filesystems and similar. ++ */ ++ int (*lock)(const char *, struct fuse_file_info *, int cmd, struct flock *); ++ ++ /** ++ * Change the access and modification times of a file with ++ * nanosecond resolution ++ * ++ * This supersedes the old utime() interface. New applications ++ * should use this. ++ * ++ * `fi` will always be NULL if the file is not currenlty open, but ++ * may also be NULL if the file is open. ++ * ++ * See the utimensat(2) man page for details. ++ */ ++ int (*utimens)(const char *, const struct timespec tv[2], ++ struct fuse_file_info *fi); ++ ++ /** ++ * Map block index within file to block index within device ++ * ++ * Note: This makes sense only for block device backed filesystems ++ * mounted with the 'blkdev' option ++ */ ++ int (*bmap)(const char *, size_t blocksize, uint64_t *idx); ++ ++ /** ++ * Ioctl ++ * ++ * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in ++ * 64bit environment. The size and direction of data is ++ * determined by _IOC_*() decoding of cmd. For _IOC_NONE, ++ * data will be NULL, for _IOC_WRITE data is out area, for ++ * _IOC_READ in area and if both are set in/out area. In all ++ * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes. ++ * ++ * If flags has FUSE_IOCTL_DIR then the fuse_file_info refers to a ++ * directory file handle. ++ * ++ * Note : the unsigned long request submitted by the application ++ * is truncated to 32 bits. ++ */ ++ int (*ioctl)(const char *, unsigned int cmd, void *arg, ++ struct fuse_file_info *, unsigned int flags, void *data); ++ ++ /** ++ * Poll for IO readiness events ++ * ++ * Note: If ph is non-NULL, the client should notify ++ * when IO readiness events occur by calling ++ * fuse_notify_poll() with the specified ph. ++ * ++ * Regardless of the number of times poll with a non-NULL ph ++ * is received, single notification is enough to clear all. ++ * Notifying more times incurs overhead but doesn't harm ++ * correctness. ++ * ++ * The callee is responsible for destroying ph with ++ * fuse_pollhandle_destroy() when no longer in use. ++ */ ++ int (*poll)(const char *, struct fuse_file_info *, ++ struct fuse_pollhandle *ph, unsigned *reventsp); ++ ++ /* ++ * Write contents of buffer to an open file ++ * ++ * Similar to the write() method, but data is supplied in a ++ * generic buffer. Use fuse_buf_copy() to transfer data to ++ * the destination. ++ * ++ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is ++ * expected to reset the setuid and setgid bits. ++ */ ++ int (*write_buf)(const char *, struct fuse_bufvec *buf, off_t off, ++ struct fuse_file_info *); ++ ++ /* ++ * Store data from an open file in a buffer ++ * ++ * Similar to the read() method, but data is stored and ++ * returned in a generic buffer. ++ * ++ * No actual copying of data has to take place, the source ++ * file descriptor may simply be stored in the buffer for ++ * later data transfer. ++ * ++ * The buffer must be allocated dynamically and stored at the ++ * location pointed to by bufp. If the buffer contains memory ++ * regions, they too must be allocated using malloc(). The ++ * allocated memory will be freed by the caller. ++ */ ++ int (*read_buf)(const char *, struct fuse_bufvec **bufp, size_t size, ++ off_t off, struct fuse_file_info *); ++ /** ++ * Perform BSD file locking operation ++ * ++ * The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN ++ * ++ * Nonblocking requests will be indicated by ORing LOCK_NB to ++ * the above operations ++ * ++ * For more information see the flock(2) manual page. ++ * ++ * Additionally fi->owner will be set to a value unique to ++ * this open file. This same value will be supplied to ++ * ->release() when the file is released. ++ * ++ * Note: if this method is not implemented, the kernel will still ++ * allow file locking to work locally. Hence it is only ++ * interesting for network filesystems and similar. ++ */ ++ int (*flock)(const char *, struct fuse_file_info *, int op); ++ ++ /** ++ * Allocates space for an open file ++ * ++ * This function ensures that required space is allocated for specified ++ * file. If this function returns success then any subsequent write ++ * request to specified range is guaranteed not to fail because of lack ++ * of space on the file system media. ++ */ ++ int (*fallocate)(const char *, int, off_t, off_t, struct fuse_file_info *); ++ ++ /** ++ * Copy a range of data from one file to another ++ * ++ * Performs an optimized copy between two file descriptors without the ++ * additional cost of transferring data through the FUSE kernel module ++ * to user space (glibc) and then back into the FUSE filesystem again. ++ * ++ * In case this method is not implemented, glibc falls back to reading ++ * data from the source and writing to the destination. Effectively ++ * doing an inefficient copy of the data. ++ */ ++ ssize_t (*copy_file_range)(const char *path_in, ++ struct fuse_file_info *fi_in, off_t offset_in, ++ const char *path_out, ++ struct fuse_file_info *fi_out, off_t offset_out, ++ size_t size, int flags); ++ ++ /** ++ * Find next data or hole after the specified offset ++ */ ++ off_t (*lseek)(const char *, off_t off, int whence, ++ struct fuse_file_info *); + }; + +-/** Extra context that may be needed by some filesystems ++/* ++ * Extra context that may be needed by some filesystems + * + * The uid, gid and pid fields are not filled in case of a writepage + * operation. + */ + struct fuse_context { +- /** Pointer to the fuse object */ +- struct fuse *fuse; ++ /** Pointer to the fuse object */ ++ struct fuse *fuse; + +- /** User ID of the calling process */ +- uid_t uid; ++ /** User ID of the calling process */ ++ uid_t uid; + +- /** Group ID of the calling process */ +- gid_t gid; ++ /** Group ID of the calling process */ ++ gid_t gid; + +- /** Process ID of the calling thread */ +- pid_t pid; ++ /** Process ID of the calling thread */ ++ pid_t pid; + +- /** Private filesystem data */ +- void *private_data; ++ /** Private filesystem data */ ++ void *private_data; + +- /** Umask of the calling process */ +- mode_t umask; ++ /** Umask of the calling process */ ++ mode_t umask; + }; + + /** +@@ -859,15 +880,15 @@ struct fuse_context { + * Example usage, see hello.c + */ + /* +- int fuse_main(int argc, char *argv[], const struct fuse_operations *op, +- void *private_data); +-*/ +-#define fuse_main(argc, argv, op, private_data) \ +- fuse_main_real(argc, argv, op, sizeof(*(op)), private_data) ++ * int fuse_main(int argc, char *argv[], const struct fuse_operations *op, ++ * void *private_data); ++ */ ++#define fuse_main(argc, argv, op, private_data) \ ++ fuse_main_real(argc, argv, op, sizeof(*(op)), private_data) + +-/* ----------------------------------------------------------- * +- * More detailed API * +- * ----------------------------------------------------------- */ ++/* ++ * More detailed API ++ */ + + /** + * Print available options (high- and low-level) to stdout. This is +@@ -910,12 +931,13 @@ void fuse_lib_help(struct fuse_args *args); + * @return the created FUSE handle + */ + #if FUSE_USE_VERSION == 30 +-struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op, +- size_t op_size, void *private_data); ++struct fuse *fuse_new_30(struct fuse_args *args, ++ const struct fuse_operations *op, size_t op_size, ++ void *private_data); + #define fuse_new(args, op, size, data) fuse_new_30(args, op, size, data) + #else + struct fuse *fuse_new(struct fuse_args *args, const struct fuse_operations *op, +- size_t op_size, void *private_data); ++ size_t op_size, void *private_data); + #endif + + /** +@@ -940,7 +962,7 @@ void fuse_unmount(struct fuse *f); + /** + * Destroy the FUSE handle. + * +- * NOTE: This function does not unmount the filesystem. If this is ++ * NOTE: This function does not unmount the filesystem. If this is + * needed, call fuse_unmount() before calling this function. + * + * @param f the FUSE handle +@@ -1030,7 +1052,7 @@ int fuse_invalidate_path(struct fuse *f, const char *path); + * Do not call this directly, use fuse_main() + */ + int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, +- size_t op_size, void *private_data); ++ size_t op_size, void *private_data); + + /** + * Start the cleanup thread when using option "remember". +@@ -1081,89 +1103,87 @@ struct fuse_fs; + */ + + int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf, +- struct fuse_file_info *fi); +-int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, +- const char *newpath, unsigned int flags); ++ struct fuse_file_info *fi); ++int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, const char *newpath, ++ unsigned int flags); + int fuse_fs_unlink(struct fuse_fs *fs, const char *path); + int fuse_fs_rmdir(struct fuse_fs *fs, const char *path); +-int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, +- const char *path); ++int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path); + int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath); +-int fuse_fs_release(struct fuse_fs *fs, const char *path, +- struct fuse_file_info *fi); ++int fuse_fs_release(struct fuse_fs *fs, const char *path, ++ struct fuse_file_info *fi); + int fuse_fs_open(struct fuse_fs *fs, const char *path, +- struct fuse_file_info *fi); ++ struct fuse_file_info *fi); + int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size, +- off_t off, struct fuse_file_info *fi); ++ off_t off, struct fuse_file_info *fi); + int fuse_fs_read_buf(struct fuse_fs *fs, const char *path, +- struct fuse_bufvec **bufp, size_t size, off_t off, +- struct fuse_file_info *fi); ++ struct fuse_bufvec **bufp, size_t size, off_t off, ++ struct fuse_file_info *fi); + int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf, +- size_t size, off_t off, struct fuse_file_info *fi); ++ size_t size, off_t off, struct fuse_file_info *fi); + int fuse_fs_write_buf(struct fuse_fs *fs, const char *path, +- struct fuse_bufvec *buf, off_t off, +- struct fuse_file_info *fi); ++ struct fuse_bufvec *buf, off_t off, ++ struct fuse_file_info *fi); + int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync, +- struct fuse_file_info *fi); ++ struct fuse_file_info *fi); + int fuse_fs_flush(struct fuse_fs *fs, const char *path, +- struct fuse_file_info *fi); ++ struct fuse_file_info *fi); + int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf); + int fuse_fs_opendir(struct fuse_fs *fs, const char *path, +- struct fuse_file_info *fi); ++ struct fuse_file_info *fi); + int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf, +- fuse_fill_dir_t filler, off_t off, +- struct fuse_file_info *fi, enum fuse_readdir_flags flags); ++ fuse_fill_dir_t filler, off_t off, ++ struct fuse_file_info *fi, enum fuse_readdir_flags flags); + int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync, +- struct fuse_file_info *fi); ++ struct fuse_file_info *fi); + int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, +- struct fuse_file_info *fi); ++ struct fuse_file_info *fi); + int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode, +- struct fuse_file_info *fi); ++ struct fuse_file_info *fi); + int fuse_fs_lock(struct fuse_fs *fs, const char *path, +- struct fuse_file_info *fi, int cmd, struct flock *lock); ++ struct fuse_file_info *fi, int cmd, struct flock *lock); + int fuse_fs_flock(struct fuse_fs *fs, const char *path, +- struct fuse_file_info *fi, int op); ++ struct fuse_file_info *fi, int op); + int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode, +- struct fuse_file_info *fi); ++ struct fuse_file_info *fi); + int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid, +- struct fuse_file_info *fi); ++ struct fuse_file_info *fi); + int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size, +- struct fuse_file_info *fi); ++ struct fuse_file_info *fi); + int fuse_fs_utimens(struct fuse_fs *fs, const char *path, +- const struct timespec tv[2], struct fuse_file_info *fi); ++ const struct timespec tv[2], struct fuse_file_info *fi); + int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask); + int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf, +- size_t len); ++ size_t len); + int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode, +- dev_t rdev); ++ dev_t rdev); + int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode); + int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name, +- const char *value, size_t size, int flags); ++ const char *value, size_t size, int flags); + int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name, +- char *value, size_t size); ++ char *value, size_t size); + int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list, +- size_t size); +-int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, +- const char *name); ++ size_t size); ++int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name); + int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize, +- uint64_t *idx); ++ uint64_t *idx); + int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd, +- void *arg, struct fuse_file_info *fi, unsigned int flags, +- void *data); ++ void *arg, struct fuse_file_info *fi, unsigned int flags, ++ void *data); + int fuse_fs_poll(struct fuse_fs *fs, const char *path, +- struct fuse_file_info *fi, struct fuse_pollhandle *ph, +- unsigned *reventsp); ++ struct fuse_file_info *fi, struct fuse_pollhandle *ph, ++ unsigned *reventsp); + int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode, +- off_t offset, off_t length, struct fuse_file_info *fi); ++ off_t offset, off_t length, struct fuse_file_info *fi); + ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in, +- struct fuse_file_info *fi_in, off_t off_in, +- const char *path_out, +- struct fuse_file_info *fi_out, off_t off_out, +- size_t len, int flags); ++ struct fuse_file_info *fi_in, off_t off_in, ++ const char *path_out, ++ struct fuse_file_info *fi_out, off_t off_out, ++ size_t len, int flags); + off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence, +- struct fuse_file_info *fi); ++ struct fuse_file_info *fi); + void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn, +- struct fuse_config *cfg); ++ struct fuse_config *cfg); + void fuse_fs_destroy(struct fuse_fs *fs); + + int fuse_notify_poll(struct fuse_pollhandle *ph); +@@ -1182,7 +1202,7 @@ int fuse_notify_poll(struct fuse_pollhandle *ph); + * @return a new filesystem object + */ + struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, +- void *private_data); ++ void *private_data); + + /** + * Factory for creating filesystem objects +@@ -1199,7 +1219,7 @@ struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, + * @return the new filesystem object + */ + typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args, +- struct fuse_fs *fs[]); ++ struct fuse_fs *fs[]); + /** + * Register filesystem module + * +@@ -1211,7 +1231,7 @@ typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args, + * @param factory_ the factory function for this filesystem module + */ + #define FUSE_REGISTER_MODULE(name_, factory_) \ +- fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_ ++ fuse_module_factory_t fuse_module_##name_##_factory = factory_ + + /** Get session from fuse object */ + struct fuse_session *fuse_get_session(struct fuse *f); +diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h +index bf8f8cc865..bd9bf861f0 100644 +--- a/tools/virtiofsd/fuse_common.h ++++ b/tools/virtiofsd/fuse_common.h +@@ -1,21 +1,23 @@ +-/* FUSE: Filesystem in Userspace +- Copyright (C) 2001-2007 Miklos Szeredi +- +- This program can be distributed under the terms of the GNU LGPLv2. +- See the file COPYING.LIB. +-*/ ++/* ++ * FUSE: Filesystem in Userspace ++ * Copyright (C) 2001-2007 Miklos Szeredi ++ * ++ * This program can be distributed under the terms of the GNU LGPLv2. ++ * See the file COPYING.LIB. ++ */ + + /** @file */ + + #if !defined(FUSE_H_) && !defined(FUSE_LOWLEVEL_H_) +-#error "Never include directly; use or instead." ++#error \ ++ "Never include directly; use or instead." + #endif + + #ifndef FUSE_COMMON_H_ + #define FUSE_COMMON_H_ + +-#include "fuse_opt.h" + #include "fuse_log.h" ++#include "fuse_opt.h" + #include + #include + +@@ -25,7 +27,7 @@ + /** Minor version of FUSE library interface */ + #define FUSE_MINOR_VERSION 2 + +-#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) ++#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) + #define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) + + /** +@@ -38,67 +40,83 @@ + * descriptors can share a single file handle. + */ + struct fuse_file_info { +- /** Open flags. Available in open() and release() */ +- int flags; +- +- /** In case of a write operation indicates if this was caused +- by a delayed write from the page cache. If so, then the +- context's pid, uid, and gid fields will not be valid, and +- the *fh* value may not match the *fh* value that would +- have been sent with the corresponding individual write +- requests if write caching had been disabled. */ +- unsigned int writepage : 1; +- +- /** Can be filled in by open, to use direct I/O on this file. */ +- unsigned int direct_io : 1; +- +- /** Can be filled in by open. It signals the kernel that any +- currently cached file data (ie., data that the filesystem +- provided the last time the file was open) need not be +- invalidated. Has no effect when set in other contexts (in +- particular it does nothing when set by opendir()). */ +- unsigned int keep_cache : 1; +- +- /** Indicates a flush operation. Set in flush operation, also +- maybe set in highlevel lock operation and lowlevel release +- operation. */ +- unsigned int flush : 1; +- +- /** Can be filled in by open, to indicate that the file is not +- seekable. */ +- unsigned int nonseekable : 1; +- +- /* Indicates that flock locks for this file should be +- released. If set, lock_owner shall contain a valid value. +- May only be set in ->release(). */ +- unsigned int flock_release : 1; +- +- /** Can be filled in by opendir. It signals the kernel to +- enable caching of entries returned by readdir(). Has no +- effect when set in other contexts (in particular it does +- nothing when set by open()). */ +- unsigned int cache_readdir : 1; +- +- /** Padding. Reserved for future use*/ +- unsigned int padding : 25; +- unsigned int padding2 : 32; +- +- /** File handle id. May be filled in by filesystem in create, +- * open, and opendir(). Available in most other file operations on the +- * same file handle. */ +- uint64_t fh; +- +- /** Lock owner id. Available in locking operations and flush */ +- uint64_t lock_owner; +- +- /** Requested poll events. Available in ->poll. Only set on kernels +- which support it. If unsupported, this field is set to zero. */ +- uint32_t poll_events; ++ /** Open flags. Available in open() and release() */ ++ int flags; ++ ++ /* ++ * In case of a write operation indicates if this was caused ++ * by a delayed write from the page cache. If so, then the ++ * context's pid, uid, and gid fields will not be valid, and ++ * the *fh* value may not match the *fh* value that would ++ * have been sent with the corresponding individual write ++ * requests if write caching had been disabled. ++ */ ++ unsigned int writepage:1; ++ ++ /** Can be filled in by open, to use direct I/O on this file. */ ++ unsigned int direct_io:1; ++ ++ /* ++ * Can be filled in by open. It signals the kernel that any ++ * currently cached file data (ie., data that the filesystem ++ * provided the last time the file was open) need not be ++ * invalidated. Has no effect when set in other contexts (in ++ * particular it does nothing when set by opendir()). ++ */ ++ unsigned int keep_cache:1; ++ ++ /* ++ * Indicates a flush operation. Set in flush operation, also ++ * maybe set in highlevel lock operation and lowlevel release ++ * operation. ++ */ ++ unsigned int flush:1; ++ ++ /* ++ * Can be filled in by open, to indicate that the file is not ++ * seekable. ++ */ ++ unsigned int nonseekable:1; ++ ++ /* ++ * Indicates that flock locks for this file should be ++ * released. If set, lock_owner shall contain a valid value. ++ * May only be set in ->release(). ++ */ ++ unsigned int flock_release:1; ++ ++ /* ++ * Can be filled in by opendir. It signals the kernel to ++ * enable caching of entries returned by readdir(). Has no ++ * effect when set in other contexts (in particular it does ++ * nothing when set by open()). ++ */ ++ unsigned int cache_readdir:1; ++ ++ /** Padding. Reserved for future use*/ ++ unsigned int padding:25; ++ unsigned int padding2:32; ++ ++ /* ++ * File handle id. May be filled in by filesystem in create, ++ * open, and opendir(). Available in most other file operations on the ++ * same file handle. ++ */ ++ uint64_t fh; ++ ++ /** Lock owner id. Available in locking operations and flush */ ++ uint64_t lock_owner; ++ ++ /* ++ * Requested poll events. Available in ->poll. Only set on kernels ++ * which support it. If unsupported, this field is set to zero. ++ */ ++ uint32_t poll_events; + }; + +-/************************************************************************** +- * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' * +- **************************************************************************/ ++/* ++ * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' ++ */ + + /** + * Indicates that the filesystem supports asynchronous read requests. +@@ -110,7 +128,7 @@ struct fuse_file_info { + * + * This feature is enabled by default when supported by the kernel. + */ +-#define FUSE_CAP_ASYNC_READ (1 << 0) ++#define FUSE_CAP_ASYNC_READ (1 << 0) + + /** + * Indicates that the filesystem supports "remote" locking. +@@ -118,7 +136,7 @@ struct fuse_file_info { + * This feature is enabled by default when supported by the kernel, + * and if getlk() and setlk() handlers are implemented. + */ +-#define FUSE_CAP_POSIX_LOCKS (1 << 1) ++#define FUSE_CAP_POSIX_LOCKS (1 << 1) + + /** + * Indicates that the filesystem supports the O_TRUNC open flag. If +@@ -127,14 +145,14 @@ struct fuse_file_info { + * + * This feature is enabled by default when supported by the kernel. + */ +-#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3) ++#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3) + + /** + * Indicates that the filesystem supports lookups of "." and "..". + * + * This feature is disabled by default. + */ +-#define FUSE_CAP_EXPORT_SUPPORT (1 << 4) ++#define FUSE_CAP_EXPORT_SUPPORT (1 << 4) + + /** + * Indicates that the kernel should not apply the umask to the +@@ -142,7 +160,7 @@ struct fuse_file_info { + * + * This feature is disabled by default. + */ +-#define FUSE_CAP_DONT_MASK (1 << 6) ++#define FUSE_CAP_DONT_MASK (1 << 6) + + /** + * Indicates that libfuse should try to use splice() when writing to +@@ -150,7 +168,7 @@ struct fuse_file_info { + * + * This feature is disabled by default. + */ +-#define FUSE_CAP_SPLICE_WRITE (1 << 7) ++#define FUSE_CAP_SPLICE_WRITE (1 << 7) + + /** + * Indicates that libfuse should try to move pages instead of copying when +@@ -158,7 +176,7 @@ struct fuse_file_info { + * + * This feature is disabled by default. + */ +-#define FUSE_CAP_SPLICE_MOVE (1 << 8) ++#define FUSE_CAP_SPLICE_MOVE (1 << 8) + + /** + * Indicates that libfuse should try to use splice() when reading from +@@ -167,7 +185,7 @@ struct fuse_file_info { + * This feature is enabled by default when supported by the kernel and + * if the filesystem implements a write_buf() handler. + */ +-#define FUSE_CAP_SPLICE_READ (1 << 9) ++#define FUSE_CAP_SPLICE_READ (1 << 9) + + /** + * If set, the calls to flock(2) will be emulated using POSIX locks and must +@@ -180,14 +198,14 @@ struct fuse_file_info { + * This feature is enabled by default when supported by the kernel and + * if the filesystem implements a flock() handler. + */ +-#define FUSE_CAP_FLOCK_LOCKS (1 << 10) ++#define FUSE_CAP_FLOCK_LOCKS (1 << 10) + + /** + * Indicates that the filesystem supports ioctl's on directories. + * + * This feature is enabled by default when supported by the kernel. + */ +-#define FUSE_CAP_IOCTL_DIR (1 << 11) ++#define FUSE_CAP_IOCTL_DIR (1 << 11) + + /** + * Traditionally, while a file is open the FUSE kernel module only +@@ -209,7 +227,7 @@ struct fuse_file_info { + * + * This feature is enabled by default when supported by the kernel. + */ +-#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12) ++#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12) + + /** + * Indicates that the filesystem supports readdirplus. +@@ -217,7 +235,7 @@ struct fuse_file_info { + * This feature is enabled by default when supported by the kernel and if the + * filesystem implements a readdirplus() handler. + */ +-#define FUSE_CAP_READDIRPLUS (1 << 13) ++#define FUSE_CAP_READDIRPLUS (1 << 13) + + /** + * Indicates that the filesystem supports adaptive readdirplus. +@@ -245,7 +263,7 @@ struct fuse_file_info { + * if the filesystem implements both a readdirplus() and a readdir() + * handler. + */ +-#define FUSE_CAP_READDIRPLUS_AUTO (1 << 14) ++#define FUSE_CAP_READDIRPLUS_AUTO (1 << 14) + + /** + * Indicates that the filesystem supports asynchronous direct I/O submission. +@@ -256,7 +274,7 @@ struct fuse_file_info { + * + * This feature is enabled by default when supported by the kernel. + */ +-#define FUSE_CAP_ASYNC_DIO (1 << 15) ++#define FUSE_CAP_ASYNC_DIO (1 << 15) + + /** + * Indicates that writeback caching should be enabled. This means that +@@ -265,7 +283,7 @@ struct fuse_file_info { + * + * This feature is disabled by default. + */ +-#define FUSE_CAP_WRITEBACK_CACHE (1 << 16) ++#define FUSE_CAP_WRITEBACK_CACHE (1 << 16) + + /** + * Indicates support for zero-message opens. If this flag is set in +@@ -278,7 +296,7 @@ struct fuse_file_info { + * Setting (or unsetting) this flag in the `want` field has *no + * effect*. + */ +-#define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17) ++#define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17) + + /** + * Indicates support for parallel directory operations. If this flag +@@ -288,7 +306,7 @@ struct fuse_file_info { + * + * This feature is enabled by default when supported by the kernel. + */ +-#define FUSE_CAP_PARALLEL_DIROPS (1 << 18) ++#define FUSE_CAP_PARALLEL_DIROPS (1 << 18) + + /** + * Indicates support for POSIX ACLs. +@@ -307,7 +325,7 @@ struct fuse_file_info { + * + * This feature is disabled by default. + */ +-#define FUSE_CAP_POSIX_ACL (1 << 19) ++#define FUSE_CAP_POSIX_ACL (1 << 19) + + /** + * Indicates that the filesystem is responsible for unsetting +@@ -316,7 +334,7 @@ struct fuse_file_info { + * + * This feature is enabled by default when supported by the kernel. + */ +-#define FUSE_CAP_HANDLE_KILLPRIV (1 << 20) ++#define FUSE_CAP_HANDLE_KILLPRIV (1 << 20) + + /** + * Indicates support for zero-message opendirs. If this flag is set in +@@ -328,7 +346,7 @@ struct fuse_file_info { + * + * Setting (or unsetting) this flag in the `want` field has *no effect*. + */ +-#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24) ++#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24) + + /** + * Ioctl flags +@@ -340,12 +358,12 @@ struct fuse_file_info { + * + * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs + */ +-#define FUSE_IOCTL_COMPAT (1 << 0) +-#define FUSE_IOCTL_UNRESTRICTED (1 << 1) +-#define FUSE_IOCTL_RETRY (1 << 2) +-#define FUSE_IOCTL_DIR (1 << 4) ++#define FUSE_IOCTL_COMPAT (1 << 0) ++#define FUSE_IOCTL_UNRESTRICTED (1 << 1) ++#define FUSE_IOCTL_RETRY (1 << 2) ++#define FUSE_IOCTL_DIR (1 << 4) + +-#define FUSE_IOCTL_MAX_IOV 256 ++#define FUSE_IOCTL_MAX_IOV 256 + + /** + * Connection information, passed to the ->init() method +@@ -355,114 +373,114 @@ struct fuse_file_info { + * value must usually be smaller than the indicated value. + */ + struct fuse_conn_info { +- /** +- * Major version of the protocol (read-only) +- */ +- unsigned proto_major; +- +- /** +- * Minor version of the protocol (read-only) +- */ +- unsigned proto_minor; +- +- /** +- * Maximum size of the write buffer +- */ +- unsigned max_write; +- +- /** +- * Maximum size of read requests. A value of zero indicates no +- * limit. However, even if the filesystem does not specify a +- * limit, the maximum size of read requests will still be +- * limited by the kernel. +- * +- * NOTE: For the time being, the maximum size of read requests +- * must be set both here *and* passed to fuse_session_new() +- * using the ``-o max_read=`` mount option. At some point +- * in the future, specifying the mount option will no longer +- * be necessary. +- */ +- unsigned max_read; +- +- /** +- * Maximum readahead +- */ +- unsigned max_readahead; +- +- /** +- * Capability flags that the kernel supports (read-only) +- */ +- unsigned capable; +- +- /** +- * Capability flags that the filesystem wants to enable. +- * +- * libfuse attempts to initialize this field with +- * reasonable default values before calling the init() handler. +- */ +- unsigned want; +- +- /** +- * Maximum number of pending "background" requests. A +- * background request is any type of request for which the +- * total number is not limited by other means. As of kernel +- * 4.8, only two types of requests fall into this category: +- * +- * 1. Read-ahead requests +- * 2. Asynchronous direct I/O requests +- * +- * Read-ahead requests are generated (if max_readahead is +- * non-zero) by the kernel to preemptively fill its caches +- * when it anticipates that userspace will soon read more +- * data. +- * +- * Asynchronous direct I/O requests are generated if +- * FUSE_CAP_ASYNC_DIO is enabled and userspace submits a large +- * direct I/O request. In this case the kernel will internally +- * split it up into multiple smaller requests and submit them +- * to the filesystem concurrently. +- * +- * Note that the following requests are *not* background +- * requests: writeback requests (limited by the kernel's +- * flusher algorithm), regular (i.e., synchronous and +- * buffered) userspace read/write requests (limited to one per +- * thread), asynchronous read requests (Linux's io_submit(2) +- * call actually blocks, so these are also limited to one per +- * thread). +- */ +- unsigned max_background; +- +- /** +- * Kernel congestion threshold parameter. If the number of pending +- * background requests exceeds this number, the FUSE kernel module will +- * mark the filesystem as "congested". This instructs the kernel to +- * expect that queued requests will take some time to complete, and to +- * adjust its algorithms accordingly (e.g. by putting a waiting thread +- * to sleep instead of using a busy-loop). +- */ +- unsigned congestion_threshold; +- +- /** +- * When FUSE_CAP_WRITEBACK_CACHE is enabled, the kernel is responsible +- * for updating mtime and ctime when write requests are received. The +- * updated values are passed to the filesystem with setattr() requests. +- * However, if the filesystem does not support the full resolution of +- * the kernel timestamps (nanoseconds), the mtime and ctime values used +- * by kernel and filesystem will differ (and result in an apparent +- * change of times after a cache flush). +- * +- * To prevent this problem, this variable can be used to inform the +- * kernel about the timestamp granularity supported by the file-system. +- * The value should be power of 10. The default is 1, i.e. full +- * nano-second resolution. Filesystems supporting only second resolution +- * should set this to 1000000000. +- */ +- unsigned time_gran; +- +- /** +- * For future use. +- */ +- unsigned reserved[22]; ++ /** ++ * Major version of the protocol (read-only) ++ */ ++ unsigned proto_major; ++ ++ /** ++ * Minor version of the protocol (read-only) ++ */ ++ unsigned proto_minor; ++ ++ /** ++ * Maximum size of the write buffer ++ */ ++ unsigned max_write; ++ ++ /** ++ * Maximum size of read requests. A value of zero indicates no ++ * limit. However, even if the filesystem does not specify a ++ * limit, the maximum size of read requests will still be ++ * limited by the kernel. ++ * ++ * NOTE: For the time being, the maximum size of read requests ++ * must be set both here *and* passed to fuse_session_new() ++ * using the ``-o max_read=`` mount option. At some point ++ * in the future, specifying the mount option will no longer ++ * be necessary. ++ */ ++ unsigned max_read; ++ ++ /** ++ * Maximum readahead ++ */ ++ unsigned max_readahead; ++ ++ /** ++ * Capability flags that the kernel supports (read-only) ++ */ ++ unsigned capable; ++ ++ /** ++ * Capability flags that the filesystem wants to enable. ++ * ++ * libfuse attempts to initialize this field with ++ * reasonable default values before calling the init() handler. ++ */ ++ unsigned want; ++ ++ /** ++ * Maximum number of pending "background" requests. A ++ * background request is any type of request for which the ++ * total number is not limited by other means. As of kernel ++ * 4.8, only two types of requests fall into this category: ++ * ++ * 1. Read-ahead requests ++ * 2. Asynchronous direct I/O requests ++ * ++ * Read-ahead requests are generated (if max_readahead is ++ * non-zero) by the kernel to preemptively fill its caches ++ * when it anticipates that userspace will soon read more ++ * data. ++ * ++ * Asynchronous direct I/O requests are generated if ++ * FUSE_CAP_ASYNC_DIO is enabled and userspace submits a large ++ * direct I/O request. In this case the kernel will internally ++ * split it up into multiple smaller requests and submit them ++ * to the filesystem concurrently. ++ * ++ * Note that the following requests are *not* background ++ * requests: writeback requests (limited by the kernel's ++ * flusher algorithm), regular (i.e., synchronous and ++ * buffered) userspace read/write requests (limited to one per ++ * thread), asynchronous read requests (Linux's io_submit(2) ++ * call actually blocks, so these are also limited to one per ++ * thread). ++ */ ++ unsigned max_background; ++ ++ /** ++ * Kernel congestion threshold parameter. If the number of pending ++ * background requests exceeds this number, the FUSE kernel module will ++ * mark the filesystem as "congested". This instructs the kernel to ++ * expect that queued requests will take some time to complete, and to ++ * adjust its algorithms accordingly (e.g. by putting a waiting thread ++ * to sleep instead of using a busy-loop). ++ */ ++ unsigned congestion_threshold; ++ ++ /** ++ * When FUSE_CAP_WRITEBACK_CACHE is enabled, the kernel is responsible ++ * for updating mtime and ctime when write requests are received. The ++ * updated values are passed to the filesystem with setattr() requests. ++ * However, if the filesystem does not support the full resolution of ++ * the kernel timestamps (nanoseconds), the mtime and ctime values used ++ * by kernel and filesystem will differ (and result in an apparent ++ * change of times after a cache flush). ++ * ++ * To prevent this problem, this variable can be used to inform the ++ * kernel about the timestamp granularity supported by the file-system. ++ * The value should be power of 10. The default is 1, i.e. full ++ * nano-second resolution. Filesystems supporting only second resolution ++ * should set this to 1000000000. ++ */ ++ unsigned time_gran; ++ ++ /** ++ * For future use. ++ */ ++ unsigned reserved[22]; + }; + + struct fuse_session; +@@ -489,21 +507,20 @@ struct fuse_conn_info_opts; + * -o async_read sets FUSE_CAP_ASYNC_READ in conn->want + * -o sync_read unsets FUSE_CAP_ASYNC_READ in conn->want + * -o atomic_o_trunc sets FUSE_CAP_ATOMIC_O_TRUNC in conn->want +- * -o no_remote_lock Equivalent to -o no_remote_flock,no_remote_posix_lock +- * -o no_remote_flock Unsets FUSE_CAP_FLOCK_LOCKS in conn->want +- * -o no_remote_posix_lock Unsets FUSE_CAP_POSIX_LOCKS in conn->want +- * -o [no_]splice_write (un-)sets FUSE_CAP_SPLICE_WRITE in conn->want +- * -o [no_]splice_move (un-)sets FUSE_CAP_SPLICE_MOVE in conn->want +- * -o [no_]splice_read (un-)sets FUSE_CAP_SPLICE_READ in conn->want +- * -o [no_]auto_inval_data (un-)sets FUSE_CAP_AUTO_INVAL_DATA in conn->want +- * -o readdirplus=no unsets FUSE_CAP_READDIRPLUS in conn->want +- * -o readdirplus=yes sets FUSE_CAP_READDIRPLUS and unsets +- * FUSE_CAP_READDIRPLUS_AUTO in conn->want +- * -o readdirplus=auto sets FUSE_CAP_READDIRPLUS and +- * FUSE_CAP_READDIRPLUS_AUTO in conn->want +- * -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in conn->want +- * -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in conn->want +- * -o time_gran=N sets conn->time_gran ++ * -o no_remote_lock Equivalent to -o ++ *no_remote_flock,no_remote_posix_lock -o no_remote_flock Unsets ++ *FUSE_CAP_FLOCK_LOCKS in conn->want -o no_remote_posix_lock Unsets ++ *FUSE_CAP_POSIX_LOCKS in conn->want -o [no_]splice_write (un-)sets ++ *FUSE_CAP_SPLICE_WRITE in conn->want -o [no_]splice_move (un-)sets ++ *FUSE_CAP_SPLICE_MOVE in conn->want -o [no_]splice_read (un-)sets ++ *FUSE_CAP_SPLICE_READ in conn->want -o [no_]auto_inval_data (un-)sets ++ *FUSE_CAP_AUTO_INVAL_DATA in conn->want -o readdirplus=no unsets ++ *FUSE_CAP_READDIRPLUS in conn->want -o readdirplus=yes sets ++ *FUSE_CAP_READDIRPLUS and unsets FUSE_CAP_READDIRPLUS_AUTO in conn->want -o ++ *readdirplus=auto sets FUSE_CAP_READDIRPLUS and FUSE_CAP_READDIRPLUS_AUTO ++ *in conn->want -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in ++ *conn->want -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in ++ *conn->want -o time_gran=N sets conn->time_gran + * + * Known options will be removed from *args*, unknown options will be + * passed through unchanged. +@@ -511,7 +528,7 @@ struct fuse_conn_info_opts; + * @param args argument vector (input+output) + * @return parsed options + **/ +-struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args); ++struct fuse_conn_info_opts *fuse_parse_conn_info_opts(struct fuse_args *args); + + /** + * This function applies the (parsed) parameters in *opts* to the +@@ -521,7 +538,7 @@ struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args); + * option has been explicitly set. + */ + void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, +- struct fuse_conn_info *conn); ++ struct fuse_conn_info *conn); + + /** + * Go into the background +@@ -552,81 +569,81 @@ const char *fuse_pkgversion(void); + */ + void fuse_pollhandle_destroy(struct fuse_pollhandle *ph); + +-/* ----------------------------------------------------------- * +- * Data buffer * +- * ----------------------------------------------------------- */ ++/* ++ * Data buffer ++ */ + + /** + * Buffer flags + */ + enum fuse_buf_flags { +- /** +- * Buffer contains a file descriptor +- * +- * If this flag is set, the .fd field is valid, otherwise the +- * .mem fields is valid. +- */ +- FUSE_BUF_IS_FD = (1 << 1), +- +- /** +- * Seek on the file descriptor +- * +- * If this flag is set then the .pos field is valid and is +- * used to seek to the given offset before performing +- * operation on file descriptor. +- */ +- FUSE_BUF_FD_SEEK = (1 << 2), +- +- /** +- * Retry operation on file descriptor +- * +- * If this flag is set then retry operation on file descriptor +- * until .size bytes have been copied or an error or EOF is +- * detected. +- */ +- FUSE_BUF_FD_RETRY = (1 << 3), ++ /** ++ * Buffer contains a file descriptor ++ * ++ * If this flag is set, the .fd field is valid, otherwise the ++ * .mem fields is valid. ++ */ ++ FUSE_BUF_IS_FD = (1 << 1), ++ ++ /** ++ * Seek on the file descriptor ++ * ++ * If this flag is set then the .pos field is valid and is ++ * used to seek to the given offset before performing ++ * operation on file descriptor. ++ */ ++ FUSE_BUF_FD_SEEK = (1 << 2), ++ ++ /** ++ * Retry operation on file descriptor ++ * ++ * If this flag is set then retry operation on file descriptor ++ * until .size bytes have been copied or an error or EOF is ++ * detected. ++ */ ++ FUSE_BUF_FD_RETRY = (1 << 3), + }; + + /** + * Buffer copy flags + */ + enum fuse_buf_copy_flags { +- /** +- * Don't use splice(2) +- * +- * Always fall back to using read and write instead of +- * splice(2) to copy data from one file descriptor to another. +- * +- * If this flag is not set, then only fall back if splice is +- * unavailable. +- */ +- FUSE_BUF_NO_SPLICE = (1 << 1), +- +- /** +- * Force splice +- * +- * Always use splice(2) to copy data from one file descriptor +- * to another. If splice is not available, return -EINVAL. +- */ +- FUSE_BUF_FORCE_SPLICE = (1 << 2), +- +- /** +- * Try to move data with splice. +- * +- * If splice is used, try to move pages from the source to the +- * destination instead of copying. See documentation of +- * SPLICE_F_MOVE in splice(2) man page. +- */ +- FUSE_BUF_SPLICE_MOVE = (1 << 3), +- +- /** +- * Don't block on the pipe when copying data with splice +- * +- * Makes the operations on the pipe non-blocking (if the pipe +- * is full or empty). See SPLICE_F_NONBLOCK in the splice(2) +- * man page. +- */ +- FUSE_BUF_SPLICE_NONBLOCK= (1 << 4), ++ /** ++ * Don't use splice(2) ++ * ++ * Always fall back to using read and write instead of ++ * splice(2) to copy data from one file descriptor to another. ++ * ++ * If this flag is not set, then only fall back if splice is ++ * unavailable. ++ */ ++ FUSE_BUF_NO_SPLICE = (1 << 1), ++ ++ /** ++ * Force splice ++ * ++ * Always use splice(2) to copy data from one file descriptor ++ * to another. If splice is not available, return -EINVAL. ++ */ ++ FUSE_BUF_FORCE_SPLICE = (1 << 2), ++ ++ /** ++ * Try to move data with splice. ++ * ++ * If splice is used, try to move pages from the source to the ++ * destination instead of copying. See documentation of ++ * SPLICE_F_MOVE in splice(2) man page. ++ */ ++ FUSE_BUF_SPLICE_MOVE = (1 << 3), ++ ++ /** ++ * Don't block on the pipe when copying data with splice ++ * ++ * Makes the operations on the pipe non-blocking (if the pipe ++ * is full or empty). See SPLICE_F_NONBLOCK in the splice(2) ++ * man page. ++ */ ++ FUSE_BUF_SPLICE_NONBLOCK = (1 << 4), + }; + + /** +@@ -636,36 +653,36 @@ enum fuse_buf_copy_flags { + * be supplied as a memory pointer or as a file descriptor + */ + struct fuse_buf { +- /** +- * Size of data in bytes +- */ +- size_t size; +- +- /** +- * Buffer flags +- */ +- enum fuse_buf_flags flags; +- +- /** +- * Memory pointer +- * +- * Used unless FUSE_BUF_IS_FD flag is set. +- */ +- void *mem; +- +- /** +- * File descriptor +- * +- * Used if FUSE_BUF_IS_FD flag is set. +- */ +- int fd; +- +- /** +- * File position +- * +- * Used if FUSE_BUF_FD_SEEK flag is set. +- */ +- off_t pos; ++ /** ++ * Size of data in bytes ++ */ ++ size_t size; ++ ++ /** ++ * Buffer flags ++ */ ++ enum fuse_buf_flags flags; ++ ++ /** ++ * Memory pointer ++ * ++ * Used unless FUSE_BUF_IS_FD flag is set. ++ */ ++ void *mem; ++ ++ /** ++ * File descriptor ++ * ++ * Used if FUSE_BUF_IS_FD flag is set. ++ */ ++ int fd; ++ ++ /** ++ * File position ++ * ++ * Used if FUSE_BUF_FD_SEEK flag is set. ++ */ ++ off_t pos; + }; + + /** +@@ -677,41 +694,39 @@ struct fuse_buf { + * Allocate dynamically to add more than one buffer. + */ + struct fuse_bufvec { +- /** +- * Number of buffers in the array +- */ +- size_t count; +- +- /** +- * Index of current buffer within the array +- */ +- size_t idx; +- +- /** +- * Current offset within the current buffer +- */ +- size_t off; +- +- /** +- * Array of buffers +- */ +- struct fuse_buf buf[1]; ++ /** ++ * Number of buffers in the array ++ */ ++ size_t count; ++ ++ /** ++ * Index of current buffer within the array ++ */ ++ size_t idx; ++ ++ /** ++ * Current offset within the current buffer ++ */ ++ size_t off; ++ ++ /** ++ * Array of buffers ++ */ ++ struct fuse_buf buf[1]; + }; + + /* Initialize bufvec with a single buffer of given size */ +-#define FUSE_BUFVEC_INIT(size__) \ +- ((struct fuse_bufvec) { \ +- /* .count= */ 1, \ +- /* .idx = */ 0, \ +- /* .off = */ 0, \ +- /* .buf = */ { /* [0] = */ { \ +- /* .size = */ (size__), \ +- /* .flags = */ (enum fuse_buf_flags) 0, \ +- /* .mem = */ NULL, \ +- /* .fd = */ -1, \ +- /* .pos = */ 0, \ +- } } \ +- } ) ++#define FUSE_BUFVEC_INIT(size__) \ ++ ((struct fuse_bufvec){ /* .count= */ 1, \ ++ /* .idx = */ 0, \ ++ /* .off = */ 0, /* .buf = */ \ ++ { /* [0] = */ { \ ++ /* .size = */ (size__), \ ++ /* .flags = */ (enum fuse_buf_flags)0, \ ++ /* .mem = */ NULL, \ ++ /* .fd = */ -1, \ ++ /* .pos = */ 0, \ ++ } } }) + + /** + * Get total size of data in a fuse buffer vector +@@ -730,16 +745,16 @@ size_t fuse_buf_size(const struct fuse_bufvec *bufv); + * @return actual number of bytes copied or -errno on error + */ + ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, +- enum fuse_buf_copy_flags flags); ++ enum fuse_buf_copy_flags flags); + +-/* ----------------------------------------------------------- * +- * Signal handling * +- * ----------------------------------------------------------- */ ++/* ++ * Signal handling ++ */ + + /** + * Exit session on HUP, TERM and INT signals and ignore PIPE signal + * +- * Stores session in a global variable. May only be called once per ++ * Stores session in a global variable. May only be called once per + * process until fuse_remove_signal_handlers() is called. + * + * Once either of the POSIX signals arrives, the signal handler calls +@@ -766,12 +781,12 @@ int fuse_set_signal_handlers(struct fuse_session *se); + */ + void fuse_remove_signal_handlers(struct fuse_session *se); + +-/* ----------------------------------------------------------- * +- * Compatibility stuff * +- * ----------------------------------------------------------- */ ++/* ++ * Compatibility stuff ++ */ + + #if !defined(FUSE_USE_VERSION) || FUSE_USE_VERSION < 30 +-# error only API version 30 or greater is supported ++#error only API version 30 or greater is supported + #endif + + +@@ -781,11 +796,14 @@ void fuse_remove_signal_handlers(struct fuse_session *se); + * On 32bit systems please add -D_FILE_OFFSET_BITS=64 to your compile flags! + */ + +-#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus ++#if defined(__GNUC__) && \ ++ (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && \ ++ !defined __cplusplus + _Static_assert(sizeof(off_t) == 8, "fuse: off_t must be 64bit"); + #else +-struct _fuse_off_t_must_be_64bit_dummy_struct \ +- { unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); }; ++struct _fuse_off_t_must_be_64bit_dummy_struct { ++ unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); ++}; + #endif + + #endif /* FUSE_COMMON_H_ */ +diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h +index b39522e3ca..e63cb58388 100644 +--- a/tools/virtiofsd/fuse_i.h ++++ b/tools/virtiofsd/fuse_i.h +@@ -1,71 +1,71 @@ + /* +- FUSE: Filesystem in Userspace +- Copyright (C) 2001-2007 Miklos Szeredi +- +- This program can be distributed under the terms of the GNU LGPLv2. +- See the file COPYING.LIB +-*/ ++ * FUSE: Filesystem in Userspace ++ * Copyright (C) 2001-2007 Miklos Szeredi ++ * ++ * This program can be distributed under the terms of the GNU LGPLv2. ++ * See the file COPYING.LIB ++ */ + + #include "fuse.h" + #include "fuse_lowlevel.h" + + struct fuse_req { +- struct fuse_session *se; +- uint64_t unique; +- int ctr; +- pthread_mutex_t lock; +- struct fuse_ctx ctx; +- struct fuse_chan *ch; +- int interrupted; +- unsigned int ioctl_64bit : 1; +- union { +- struct { +- uint64_t unique; +- } i; +- struct { +- fuse_interrupt_func_t func; +- void *data; +- } ni; +- } u; +- struct fuse_req *next; +- struct fuse_req *prev; ++ struct fuse_session *se; ++ uint64_t unique; ++ int ctr; ++ pthread_mutex_t lock; ++ struct fuse_ctx ctx; ++ struct fuse_chan *ch; ++ int interrupted; ++ unsigned int ioctl_64bit:1; ++ union { ++ struct { ++ uint64_t unique; ++ } i; ++ struct { ++ fuse_interrupt_func_t func; ++ void *data; ++ } ni; ++ } u; ++ struct fuse_req *next; ++ struct fuse_req *prev; + }; + + struct fuse_notify_req { +- uint64_t unique; +- void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t, +- const void *, const struct fuse_buf *); +- struct fuse_notify_req *next; +- struct fuse_notify_req *prev; ++ uint64_t unique; ++ void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t, ++ const void *, const struct fuse_buf *); ++ struct fuse_notify_req *next; ++ struct fuse_notify_req *prev; + }; + + struct fuse_session { +- char *mountpoint; +- volatile int exited; +- int fd; +- int debug; +- int deny_others; +- struct fuse_lowlevel_ops op; +- int got_init; +- struct cuse_data *cuse_data; +- void *userdata; +- uid_t owner; +- struct fuse_conn_info conn; +- struct fuse_req list; +- struct fuse_req interrupts; +- pthread_mutex_t lock; +- int got_destroy; +- int broken_splice_nonblock; +- uint64_t notify_ctr; +- struct fuse_notify_req notify_list; +- size_t bufsize; +- int error; ++ char *mountpoint; ++ volatile int exited; ++ int fd; ++ int debug; ++ int deny_others; ++ struct fuse_lowlevel_ops op; ++ int got_init; ++ struct cuse_data *cuse_data; ++ void *userdata; ++ uid_t owner; ++ struct fuse_conn_info conn; ++ struct fuse_req list; ++ struct fuse_req interrupts; ++ pthread_mutex_t lock; ++ int got_destroy; ++ int broken_splice_nonblock; ++ uint64_t notify_ctr; ++ struct fuse_notify_req notify_list; ++ size_t bufsize; ++ int error; + }; + + struct fuse_chan { +- pthread_mutex_t lock; +- int ctr; +- int fd; ++ pthread_mutex_t lock; ++ int ctr; ++ int fd; + }; + + /** +@@ -76,19 +76,20 @@ struct fuse_chan { + * + */ + struct fuse_module { +- char *name; +- fuse_module_factory_t factory; +- struct fuse_module *next; +- struct fusemod_so *so; +- int ctr; ++ char *name; ++ fuse_module_factory_t factory; ++ struct fuse_module *next; ++ struct fusemod_so *so; ++ int ctr; + }; + + int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, +- int count); ++ int count); + void fuse_free_req(fuse_req_t req); + + void fuse_session_process_buf_int(struct fuse_session *se, +- const struct fuse_buf *buf, struct fuse_chan *ch); ++ const struct fuse_buf *buf, ++ struct fuse_chan *ch); + + + #define FUSE_MAX_MAX_PAGES 256 +diff --git a/tools/virtiofsd/fuse_log.c b/tools/virtiofsd/fuse_log.c +index 0d268ab014..11345f9ec8 100644 +--- a/tools/virtiofsd/fuse_log.c ++++ b/tools/virtiofsd/fuse_log.c +@@ -1,40 +1,40 @@ + /* +- FUSE: Filesystem in Userspace +- Copyright (C) 2019 Red Hat, Inc. +- +- Logging API. +- +- This program can be distributed under the terms of the GNU LGPLv2. +- See the file COPYING.LIB +-*/ ++ * FUSE: Filesystem in Userspace ++ * Copyright (C) 2019 Red Hat, Inc. ++ * ++ * Logging API. ++ * ++ * This program can be distributed under the terms of the GNU LGPLv2. ++ * See the file COPYING.LIB ++ */ + + #include "fuse_log.h" + + #include + #include + +-static void default_log_func( +- __attribute__(( unused )) enum fuse_log_level level, +- const char *fmt, va_list ap) ++static void default_log_func(__attribute__((unused)) enum fuse_log_level level, ++ const char *fmt, va_list ap) + { +- vfprintf(stderr, fmt, ap); ++ vfprintf(stderr, fmt, ap); + } + + static fuse_log_func_t log_func = default_log_func; + + void fuse_set_log_func(fuse_log_func_t func) + { +- if (!func) +- func = default_log_func; ++ if (!func) { ++ func = default_log_func; ++ } + +- log_func = func; ++ log_func = func; + } + + void fuse_log(enum fuse_log_level level, const char *fmt, ...) + { +- va_list ap; ++ va_list ap; + +- va_start(ap, fmt); +- log_func(level, fmt, ap); +- va_end(ap); ++ va_start(ap, fmt); ++ log_func(level, fmt, ap); ++ va_end(ap); + } +diff --git a/tools/virtiofsd/fuse_log.h b/tools/virtiofsd/fuse_log.h +index 0af700da6b..bf6c11ff11 100644 +--- a/tools/virtiofsd/fuse_log.h ++++ b/tools/virtiofsd/fuse_log.h +@@ -1,10 +1,10 @@ + /* +- FUSE: Filesystem in Userspace +- Copyright (C) 2019 Red Hat, Inc. +- +- This program can be distributed under the terms of the GNU LGPLv2. +- See the file COPYING.LIB. +-*/ ++ * FUSE: Filesystem in Userspace ++ * Copyright (C) 2019 Red Hat, Inc. ++ * ++ * This program can be distributed under the terms of the GNU LGPLv2. ++ * See the file COPYING.LIB. ++ */ + + #ifndef FUSE_LOG_H_ + #define FUSE_LOG_H_ +@@ -22,14 +22,14 @@ + * These levels correspond to syslog(2) log levels since they are widely used. + */ + enum fuse_log_level { +- FUSE_LOG_EMERG, +- FUSE_LOG_ALERT, +- FUSE_LOG_CRIT, +- FUSE_LOG_ERR, +- FUSE_LOG_WARNING, +- FUSE_LOG_NOTICE, +- FUSE_LOG_INFO, +- FUSE_LOG_DEBUG ++ FUSE_LOG_EMERG, ++ FUSE_LOG_ALERT, ++ FUSE_LOG_CRIT, ++ FUSE_LOG_ERR, ++ FUSE_LOG_WARNING, ++ FUSE_LOG_NOTICE, ++ FUSE_LOG_INFO, ++ FUSE_LOG_DEBUG + }; + + /** +@@ -45,8 +45,8 @@ enum fuse_log_level { + * @param fmt sprintf-style format string including newline + * @param ap format string arguments + */ +-typedef void (*fuse_log_func_t)(enum fuse_log_level level, +- const char *fmt, va_list ap); ++typedef void (*fuse_log_func_t)(enum fuse_log_level level, const char *fmt, ++ va_list ap); + + /** + * Install a custom log handler function. +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index e6fa247924..5c9cb52f2a 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -1,2380 +1,2515 @@ + /* +- FUSE: Filesystem in Userspace +- Copyright (C) 2001-2007 Miklos Szeredi +- +- Implementation of (most of) the low-level FUSE API. The session loop +- functions are implemented in separate files. +- +- This program can be distributed under the terms of the GNU LGPLv2. +- See the file COPYING.LIB +-*/ ++ * FUSE: Filesystem in Userspace ++ * Copyright (C) 2001-2007 Miklos Szeredi ++ * ++ * Implementation of (most of) the low-level FUSE API. The session loop ++ * functions are implemented in separate files. ++ * ++ * This program can be distributed under the terms of the GNU LGPLv2. ++ * See the file COPYING.LIB ++ */ + + #define _GNU_SOURCE + + #include "config.h" + #include "fuse_i.h" + #include "fuse_kernel.h" +-#include "fuse_opt.h" + #include "fuse_misc.h" ++#include "fuse_opt.h" + ++#include ++#include ++#include ++#include + #include + #include +-#include + #include +-#include +-#include +-#include +-#include + #include +- ++#include + + + #define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) + #define OFFSET_MAX 0x7fffffffffffffffLL + +-#define container_of(ptr, type, member) ({ \ +- const typeof( ((type *)0)->member ) *__mptr = (ptr); \ +- (type *)( (char *)__mptr - offsetof(type,member) );}) ++#define container_of(ptr, type, member) \ ++ ({ \ ++ const typeof(((type *)0)->member) *__mptr = (ptr); \ ++ (type *)((char *)__mptr - offsetof(type, member)); \ ++ }) + + struct fuse_pollhandle { +- uint64_t kh; +- struct fuse_session *se; ++ uint64_t kh; ++ struct fuse_session *se; + }; + + static size_t pagesize; + + static __attribute__((constructor)) void fuse_ll_init_pagesize(void) + { +- pagesize = getpagesize(); ++ pagesize = getpagesize(); + } + + static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr) + { +- attr->ino = stbuf->st_ino; +- attr->mode = stbuf->st_mode; +- attr->nlink = stbuf->st_nlink; +- attr->uid = stbuf->st_uid; +- attr->gid = stbuf->st_gid; +- attr->rdev = stbuf->st_rdev; +- attr->size = stbuf->st_size; +- attr->blksize = stbuf->st_blksize; +- attr->blocks = stbuf->st_blocks; +- attr->atime = stbuf->st_atime; +- attr->mtime = stbuf->st_mtime; +- attr->ctime = stbuf->st_ctime; +- attr->atimensec = ST_ATIM_NSEC(stbuf); +- attr->mtimensec = ST_MTIM_NSEC(stbuf); +- attr->ctimensec = ST_CTIM_NSEC(stbuf); ++ attr->ino = stbuf->st_ino; ++ attr->mode = stbuf->st_mode; ++ attr->nlink = stbuf->st_nlink; ++ attr->uid = stbuf->st_uid; ++ attr->gid = stbuf->st_gid; ++ attr->rdev = stbuf->st_rdev; ++ attr->size = stbuf->st_size; ++ attr->blksize = stbuf->st_blksize; ++ attr->blocks = stbuf->st_blocks; ++ attr->atime = stbuf->st_atime; ++ attr->mtime = stbuf->st_mtime; ++ attr->ctime = stbuf->st_ctime; ++ attr->atimensec = ST_ATIM_NSEC(stbuf); ++ attr->mtimensec = ST_MTIM_NSEC(stbuf); ++ attr->ctimensec = ST_CTIM_NSEC(stbuf); + } + + static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf) + { +- stbuf->st_mode = attr->mode; +- stbuf->st_uid = attr->uid; +- stbuf->st_gid = attr->gid; +- stbuf->st_size = attr->size; +- stbuf->st_atime = attr->atime; +- stbuf->st_mtime = attr->mtime; +- stbuf->st_ctime = attr->ctime; +- ST_ATIM_NSEC_SET(stbuf, attr->atimensec); +- ST_MTIM_NSEC_SET(stbuf, attr->mtimensec); +- ST_CTIM_NSEC_SET(stbuf, attr->ctimensec); ++ stbuf->st_mode = attr->mode; ++ stbuf->st_uid = attr->uid; ++ stbuf->st_gid = attr->gid; ++ stbuf->st_size = attr->size; ++ stbuf->st_atime = attr->atime; ++ stbuf->st_mtime = attr->mtime; ++ stbuf->st_ctime = attr->ctime; ++ ST_ATIM_NSEC_SET(stbuf, attr->atimensec); ++ ST_MTIM_NSEC_SET(stbuf, attr->mtimensec); ++ ST_CTIM_NSEC_SET(stbuf, attr->ctimensec); + } + +-static size_t iov_length(const struct iovec *iov, size_t count) ++static size_t iov_length(const struct iovec *iov, size_t count) + { +- size_t seg; +- size_t ret = 0; ++ size_t seg; ++ size_t ret = 0; + +- for (seg = 0; seg < count; seg++) +- ret += iov[seg].iov_len; +- return ret; ++ for (seg = 0; seg < count; seg++) { ++ ret += iov[seg].iov_len; ++ } ++ return ret; + } + + static void list_init_req(struct fuse_req *req) + { +- req->next = req; +- req->prev = req; ++ req->next = req; ++ req->prev = req; + } + + static void list_del_req(struct fuse_req *req) + { +- struct fuse_req *prev = req->prev; +- struct fuse_req *next = req->next; +- prev->next = next; +- next->prev = prev; ++ struct fuse_req *prev = req->prev; ++ struct fuse_req *next = req->next; ++ prev->next = next; ++ next->prev = prev; + } + + static void list_add_req(struct fuse_req *req, struct fuse_req *next) + { +- struct fuse_req *prev = next->prev; +- req->next = next; +- req->prev = prev; +- prev->next = req; +- next->prev = req; ++ struct fuse_req *prev = next->prev; ++ req->next = next; ++ req->prev = prev; ++ prev->next = req; ++ next->prev = req; + } + + static void destroy_req(fuse_req_t req) + { +- pthread_mutex_destroy(&req->lock); +- free(req); ++ pthread_mutex_destroy(&req->lock); ++ free(req); + } + + void fuse_free_req(fuse_req_t req) + { +- int ctr; +- struct fuse_session *se = req->se; ++ int ctr; ++ struct fuse_session *se = req->se; + +- pthread_mutex_lock(&se->lock); +- req->u.ni.func = NULL; +- req->u.ni.data = NULL; +- list_del_req(req); +- ctr = --req->ctr; +- req->ch = NULL; +- pthread_mutex_unlock(&se->lock); +- if (!ctr) +- destroy_req(req); ++ pthread_mutex_lock(&se->lock); ++ req->u.ni.func = NULL; ++ req->u.ni.data = NULL; ++ list_del_req(req); ++ ctr = --req->ctr; ++ req->ch = NULL; ++ pthread_mutex_unlock(&se->lock); ++ if (!ctr) { ++ destroy_req(req); ++ } + } + + static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se) + { +- struct fuse_req *req; ++ struct fuse_req *req; + +- req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req)); +- if (req == NULL) { +- fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n"); +- } else { +- req->se = se; +- req->ctr = 1; +- list_init_req(req); +- fuse_mutex_init(&req->lock); +- } ++ req = (struct fuse_req *)calloc(1, sizeof(struct fuse_req)); ++ if (req == NULL) { ++ fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n"); ++ } else { ++ req->se = se; ++ req->ctr = 1; ++ list_init_req(req); ++ fuse_mutex_init(&req->lock); ++ } + +- return req; ++ return req; + } + + /* Send data. If *ch* is NULL, send via session master fd */ + static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, +- struct iovec *iov, int count) ++ struct iovec *iov, int count) + { +- struct fuse_out_header *out = iov[0].iov_base; ++ struct fuse_out_header *out = iov[0].iov_base; + +- out->len = iov_length(iov, count); +- if (se->debug) { +- if (out->unique == 0) { +- fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n", +- out->error, out->len); +- } else if (out->error) { +- fuse_log(FUSE_LOG_DEBUG, +- " unique: %llu, error: %i (%s), outsize: %i\n", +- (unsigned long long) out->unique, out->error, +- strerror(-out->error), out->len); +- } else { +- fuse_log(FUSE_LOG_DEBUG, +- " unique: %llu, success, outsize: %i\n", +- (unsigned long long) out->unique, out->len); +- } +- } ++ out->len = iov_length(iov, count); ++ if (se->debug) { ++ if (out->unique == 0) { ++ fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n", out->error, ++ out->len); ++ } else if (out->error) { ++ fuse_log(FUSE_LOG_DEBUG, ++ " unique: %llu, error: %i (%s), outsize: %i\n", ++ (unsigned long long)out->unique, out->error, ++ strerror(-out->error), out->len); ++ } else { ++ fuse_log(FUSE_LOG_DEBUG, " unique: %llu, success, outsize: %i\n", ++ (unsigned long long)out->unique, out->len); ++ } ++ } + +- abort(); /* virtio should have taken it before here */ +- return 0; ++ abort(); /* virtio should have taken it before here */ ++ return 0; + } + + + int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, +- int count) ++ int count) + { +- struct fuse_out_header out; ++ struct fuse_out_header out; + +- if (error <= -1000 || error > 0) { +- fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error); +- error = -ERANGE; +- } ++ if (error <= -1000 || error > 0) { ++ fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error); ++ error = -ERANGE; ++ } + +- out.unique = req->unique; +- out.error = error; ++ out.unique = req->unique; ++ out.error = error; + +- iov[0].iov_base = &out; +- iov[0].iov_len = sizeof(struct fuse_out_header); ++ iov[0].iov_base = &out; ++ iov[0].iov_len = sizeof(struct fuse_out_header); + +- return fuse_send_msg(req->se, req->ch, iov, count); ++ return fuse_send_msg(req->se, req->ch, iov, count); + } + + static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov, +- int count) ++ int count) + { +- int res; ++ int res; + +- res = fuse_send_reply_iov_nofree(req, error, iov, count); +- fuse_free_req(req); +- return res; ++ res = fuse_send_reply_iov_nofree(req, error, iov, count); ++ fuse_free_req(req); ++ return res; + } + + static int send_reply(fuse_req_t req, int error, const void *arg, +- size_t argsize) ++ size_t argsize) + { +- struct iovec iov[2]; +- int count = 1; +- if (argsize) { +- iov[1].iov_base = (void *) arg; +- iov[1].iov_len = argsize; +- count++; +- } +- return send_reply_iov(req, error, iov, count); ++ struct iovec iov[2]; ++ int count = 1; ++ if (argsize) { ++ iov[1].iov_base = (void *)arg; ++ iov[1].iov_len = argsize; ++ count++; ++ } ++ return send_reply_iov(req, error, iov, count); + } + + int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count) + { +- int res; +- struct iovec *padded_iov; ++ int res; ++ struct iovec *padded_iov; + +- padded_iov = malloc((count + 1) * sizeof(struct iovec)); +- if (padded_iov == NULL) +- return fuse_reply_err(req, ENOMEM); ++ padded_iov = malloc((count + 1) * sizeof(struct iovec)); ++ if (padded_iov == NULL) { ++ return fuse_reply_err(req, ENOMEM); ++ } + +- memcpy(padded_iov + 1, iov, count * sizeof(struct iovec)); +- count++; ++ memcpy(padded_iov + 1, iov, count * sizeof(struct iovec)); ++ count++; + +- res = send_reply_iov(req, 0, padded_iov, count); +- free(padded_iov); ++ res = send_reply_iov(req, 0, padded_iov, count); ++ free(padded_iov); + +- return res; ++ return res; + } + + +-/* `buf` is allowed to be empty so that the proper size may be +- allocated by the caller */ ++/* ++ * 'buf` is allowed to be empty so that the proper size may be ++ * allocated by the caller ++ */ + size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, +- const char *name, const struct stat *stbuf, off_t off) ++ const char *name, const struct stat *stbuf, off_t off) + { +- (void)req; +- size_t namelen; +- size_t entlen; +- size_t entlen_padded; +- struct fuse_dirent *dirent; ++ (void)req; ++ size_t namelen; ++ size_t entlen; ++ size_t entlen_padded; ++ struct fuse_dirent *dirent; + +- namelen = strlen(name); +- entlen = FUSE_NAME_OFFSET + namelen; +- entlen_padded = FUSE_DIRENT_ALIGN(entlen); ++ namelen = strlen(name); ++ entlen = FUSE_NAME_OFFSET + namelen; ++ entlen_padded = FUSE_DIRENT_ALIGN(entlen); + +- if ((buf == NULL) || (entlen_padded > bufsize)) +- return entlen_padded; ++ if ((buf == NULL) || (entlen_padded > bufsize)) { ++ return entlen_padded; ++ } + +- dirent = (struct fuse_dirent*) buf; +- dirent->ino = stbuf->st_ino; +- dirent->off = off; +- dirent->namelen = namelen; +- dirent->type = (stbuf->st_mode & S_IFMT) >> 12; +- memcpy(dirent->name, name, namelen); +- memset(dirent->name + namelen, 0, entlen_padded - entlen); ++ dirent = (struct fuse_dirent *)buf; ++ dirent->ino = stbuf->st_ino; ++ dirent->off = off; ++ dirent->namelen = namelen; ++ dirent->type = (stbuf->st_mode & S_IFMT) >> 12; ++ memcpy(dirent->name, name, namelen); ++ memset(dirent->name + namelen, 0, entlen_padded - entlen); + +- return entlen_padded; ++ return entlen_padded; + } + + static void convert_statfs(const struct statvfs *stbuf, +- struct fuse_kstatfs *kstatfs) ++ struct fuse_kstatfs *kstatfs) + { +- kstatfs->bsize = stbuf->f_bsize; +- kstatfs->frsize = stbuf->f_frsize; +- kstatfs->blocks = stbuf->f_blocks; +- kstatfs->bfree = stbuf->f_bfree; +- kstatfs->bavail = stbuf->f_bavail; +- kstatfs->files = stbuf->f_files; +- kstatfs->ffree = stbuf->f_ffree; +- kstatfs->namelen = stbuf->f_namemax; ++ kstatfs->bsize = stbuf->f_bsize; ++ kstatfs->frsize = stbuf->f_frsize; ++ kstatfs->blocks = stbuf->f_blocks; ++ kstatfs->bfree = stbuf->f_bfree; ++ kstatfs->bavail = stbuf->f_bavail; ++ kstatfs->files = stbuf->f_files; ++ kstatfs->ffree = stbuf->f_ffree; ++ kstatfs->namelen = stbuf->f_namemax; + } + + static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize) + { +- return send_reply(req, 0, arg, argsize); ++ return send_reply(req, 0, arg, argsize); + } + + int fuse_reply_err(fuse_req_t req, int err) + { +- return send_reply(req, -err, NULL, 0); ++ return send_reply(req, -err, NULL, 0); + } + + void fuse_reply_none(fuse_req_t req) + { +- fuse_free_req(req); ++ fuse_free_req(req); + } + + static unsigned long calc_timeout_sec(double t) + { +- if (t > (double) ULONG_MAX) +- return ULONG_MAX; +- else if (t < 0.0) +- return 0; +- else +- return (unsigned long) t; ++ if (t > (double)ULONG_MAX) { ++ return ULONG_MAX; ++ } else if (t < 0.0) { ++ return 0; ++ } else { ++ return (unsigned long)t; ++ } + } + + static unsigned int calc_timeout_nsec(double t) + { +- double f = t - (double) calc_timeout_sec(t); +- if (f < 0.0) +- return 0; +- else if (f >= 0.999999999) +- return 999999999; +- else +- return (unsigned int) (f * 1.0e9); ++ double f = t - (double)calc_timeout_sec(t); ++ if (f < 0.0) { ++ return 0; ++ } else if (f >= 0.999999999) { ++ return 999999999; ++ } else { ++ return (unsigned int)(f * 1.0e9); ++ } + } + + static void fill_entry(struct fuse_entry_out *arg, +- const struct fuse_entry_param *e) ++ const struct fuse_entry_param *e) + { +- arg->nodeid = e->ino; +- arg->generation = e->generation; +- arg->entry_valid = calc_timeout_sec(e->entry_timeout); +- arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout); +- arg->attr_valid = calc_timeout_sec(e->attr_timeout); +- arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout); +- convert_stat(&e->attr, &arg->attr); ++ arg->nodeid = e->ino; ++ arg->generation = e->generation; ++ arg->entry_valid = calc_timeout_sec(e->entry_timeout); ++ arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout); ++ arg->attr_valid = calc_timeout_sec(e->attr_timeout); ++ arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout); ++ convert_stat(&e->attr, &arg->attr); + } + +-/* `buf` is allowed to be empty so that the proper size may be +- allocated by the caller */ ++/* ++ * `buf` is allowed to be empty so that the proper size may be ++ * allocated by the caller ++ */ + size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, +- const char *name, +- const struct fuse_entry_param *e, off_t off) +-{ +- (void)req; +- size_t namelen; +- size_t entlen; +- size_t entlen_padded; +- +- namelen = strlen(name); +- entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen; +- entlen_padded = FUSE_DIRENT_ALIGN(entlen); +- if ((buf == NULL) || (entlen_padded > bufsize)) +- return entlen_padded; +- +- struct fuse_direntplus *dp = (struct fuse_direntplus *) buf; +- memset(&dp->entry_out, 0, sizeof(dp->entry_out)); +- fill_entry(&dp->entry_out, e); +- +- struct fuse_dirent *dirent = &dp->dirent; +- dirent->ino = e->attr.st_ino; +- dirent->off = off; +- dirent->namelen = namelen; +- dirent->type = (e->attr.st_mode & S_IFMT) >> 12; +- memcpy(dirent->name, name, namelen); +- memset(dirent->name + namelen, 0, entlen_padded - entlen); +- +- return entlen_padded; +-} +- +-static void fill_open(struct fuse_open_out *arg, +- const struct fuse_file_info *f) +-{ +- arg->fh = f->fh; +- if (f->direct_io) +- arg->open_flags |= FOPEN_DIRECT_IO; +- if (f->keep_cache) +- arg->open_flags |= FOPEN_KEEP_CACHE; +- if (f->cache_readdir) +- arg->open_flags |= FOPEN_CACHE_DIR; +- if (f->nonseekable) +- arg->open_flags |= FOPEN_NONSEEKABLE; ++ const char *name, ++ const struct fuse_entry_param *e, off_t off) ++{ ++ (void)req; ++ size_t namelen; ++ size_t entlen; ++ size_t entlen_padded; ++ ++ namelen = strlen(name); ++ entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen; ++ entlen_padded = FUSE_DIRENT_ALIGN(entlen); ++ if ((buf == NULL) || (entlen_padded > bufsize)) { ++ return entlen_padded; ++ } ++ ++ struct fuse_direntplus *dp = (struct fuse_direntplus *)buf; ++ memset(&dp->entry_out, 0, sizeof(dp->entry_out)); ++ fill_entry(&dp->entry_out, e); ++ ++ struct fuse_dirent *dirent = &dp->dirent; ++ dirent->ino = e->attr.st_ino; ++ dirent->off = off; ++ dirent->namelen = namelen; ++ dirent->type = (e->attr.st_mode & S_IFMT) >> 12; ++ memcpy(dirent->name, name, namelen); ++ memset(dirent->name + namelen, 0, entlen_padded - entlen); ++ ++ return entlen_padded; ++} ++ ++static void fill_open(struct fuse_open_out *arg, const struct fuse_file_info *f) ++{ ++ arg->fh = f->fh; ++ if (f->direct_io) { ++ arg->open_flags |= FOPEN_DIRECT_IO; ++ } ++ if (f->keep_cache) { ++ arg->open_flags |= FOPEN_KEEP_CACHE; ++ } ++ if (f->cache_readdir) { ++ arg->open_flags |= FOPEN_CACHE_DIR; ++ } ++ if (f->nonseekable) { ++ arg->open_flags |= FOPEN_NONSEEKABLE; ++ } + } + + int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e) + { +- struct fuse_entry_out arg; +- size_t size = req->se->conn.proto_minor < 9 ? +- FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg); ++ struct fuse_entry_out arg; ++ size_t size = req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ENTRY_OUT_SIZE : ++ sizeof(arg); + +- /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant +- negative entry */ +- if (!e->ino && req->se->conn.proto_minor < 4) +- return fuse_reply_err(req, ENOENT); ++ /* ++ * before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant ++ * negative entry ++ */ ++ if (!e->ino && req->se->conn.proto_minor < 4) { ++ return fuse_reply_err(req, ENOENT); ++ } + +- memset(&arg, 0, sizeof(arg)); +- fill_entry(&arg, e); +- return send_reply_ok(req, &arg, size); ++ memset(&arg, 0, sizeof(arg)); ++ fill_entry(&arg, e); ++ return send_reply_ok(req, &arg, size); + } + + int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, +- const struct fuse_file_info *f) ++ const struct fuse_file_info *f) + { +- char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)]; +- size_t entrysize = req->se->conn.proto_minor < 9 ? +- FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out); +- struct fuse_entry_out *earg = (struct fuse_entry_out *) buf; +- struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize); ++ char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)]; ++ size_t entrysize = req->se->conn.proto_minor < 9 ? ++ FUSE_COMPAT_ENTRY_OUT_SIZE : ++ sizeof(struct fuse_entry_out); ++ struct fuse_entry_out *earg = (struct fuse_entry_out *)buf; ++ struct fuse_open_out *oarg = (struct fuse_open_out *)(buf + entrysize); + +- memset(buf, 0, sizeof(buf)); +- fill_entry(earg, e); +- fill_open(oarg, f); +- return send_reply_ok(req, buf, +- entrysize + sizeof(struct fuse_open_out)); ++ memset(buf, 0, sizeof(buf)); ++ fill_entry(earg, e); ++ fill_open(oarg, f); ++ return send_reply_ok(req, buf, entrysize + sizeof(struct fuse_open_out)); + } + + int fuse_reply_attr(fuse_req_t req, const struct stat *attr, +- double attr_timeout) ++ double attr_timeout) + { +- struct fuse_attr_out arg; +- size_t size = req->se->conn.proto_minor < 9 ? +- FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg); ++ struct fuse_attr_out arg; ++ size_t size = ++ req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg); + +- memset(&arg, 0, sizeof(arg)); +- arg.attr_valid = calc_timeout_sec(attr_timeout); +- arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout); +- convert_stat(attr, &arg.attr); ++ memset(&arg, 0, sizeof(arg)); ++ arg.attr_valid = calc_timeout_sec(attr_timeout); ++ arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout); ++ convert_stat(attr, &arg.attr); + +- return send_reply_ok(req, &arg, size); ++ return send_reply_ok(req, &arg, size); + } + + int fuse_reply_readlink(fuse_req_t req, const char *linkname) + { +- return send_reply_ok(req, linkname, strlen(linkname)); ++ return send_reply_ok(req, linkname, strlen(linkname)); + } + + int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f) + { +- struct fuse_open_out arg; ++ struct fuse_open_out arg; + +- memset(&arg, 0, sizeof(arg)); +- fill_open(&arg, f); +- return send_reply_ok(req, &arg, sizeof(arg)); ++ memset(&arg, 0, sizeof(arg)); ++ fill_open(&arg, f); ++ return send_reply_ok(req, &arg, sizeof(arg)); + } + + int fuse_reply_write(fuse_req_t req, size_t count) + { +- struct fuse_write_out arg; ++ struct fuse_write_out arg; + +- memset(&arg, 0, sizeof(arg)); +- arg.size = count; ++ memset(&arg, 0, sizeof(arg)); ++ arg.size = count; + +- return send_reply_ok(req, &arg, sizeof(arg)); ++ return send_reply_ok(req, &arg, sizeof(arg)); + } + + int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size) + { +- return send_reply_ok(req, buf, size); ++ return send_reply_ok(req, buf, size); + } + + static int fuse_send_data_iov_fallback(struct fuse_session *se, +- struct fuse_chan *ch, +- struct iovec *iov, int iov_count, +- struct fuse_bufvec *buf, +- size_t len) ++ struct fuse_chan *ch, struct iovec *iov, ++ int iov_count, struct fuse_bufvec *buf, ++ size_t len) + { +- /* Optimize common case */ +- if (buf->count == 1 && buf->idx == 0 && buf->off == 0 && +- !(buf->buf[0].flags & FUSE_BUF_IS_FD)) { +- /* FIXME: also avoid memory copy if there are multiple buffers +- but none of them contain an fd */ ++ /* Optimize common case */ ++ if (buf->count == 1 && buf->idx == 0 && buf->off == 0 && ++ !(buf->buf[0].flags & FUSE_BUF_IS_FD)) { ++ /* ++ * FIXME: also avoid memory copy if there are multiple buffers ++ * but none of them contain an fd ++ */ + +- iov[iov_count].iov_base = buf->buf[0].mem; +- iov[iov_count].iov_len = len; +- iov_count++; +- return fuse_send_msg(se, ch, iov, iov_count); +- } ++ iov[iov_count].iov_base = buf->buf[0].mem; ++ iov[iov_count].iov_len = len; ++ iov_count++; ++ return fuse_send_msg(se, ch, iov, iov_count); ++ } + +- abort(); /* Will have taken vhost path */ +- return 0; ++ abort(); /* Will have taken vhost path */ ++ return 0; + } + + static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, +- struct iovec *iov, int iov_count, +- struct fuse_bufvec *buf, unsigned int flags) ++ struct iovec *iov, int iov_count, ++ struct fuse_bufvec *buf, unsigned int flags) + { +- size_t len = fuse_buf_size(buf); +- (void) flags; ++ size_t len = fuse_buf_size(buf); ++ (void)flags; + +- return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); ++ return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); + } + + int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, +- enum fuse_buf_copy_flags flags) ++ enum fuse_buf_copy_flags flags) + { +- struct iovec iov[2]; +- struct fuse_out_header out; +- int res; ++ struct iovec iov[2]; ++ struct fuse_out_header out; ++ int res; + +- iov[0].iov_base = &out; +- iov[0].iov_len = sizeof(struct fuse_out_header); ++ iov[0].iov_base = &out; ++ iov[0].iov_len = sizeof(struct fuse_out_header); + +- out.unique = req->unique; +- out.error = 0; ++ out.unique = req->unique; ++ out.error = 0; + +- res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags); +- if (res <= 0) { +- fuse_free_req(req); +- return res; +- } else { +- return fuse_reply_err(req, res); +- } ++ res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags); ++ if (res <= 0) { ++ fuse_free_req(req); ++ return res; ++ } else { ++ return fuse_reply_err(req, res); ++ } + } + + int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf) + { +- struct fuse_statfs_out arg; +- size_t size = req->se->conn.proto_minor < 4 ? +- FUSE_COMPAT_STATFS_SIZE : sizeof(arg); ++ struct fuse_statfs_out arg; ++ size_t size = ++ req->se->conn.proto_minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(arg); + +- memset(&arg, 0, sizeof(arg)); +- convert_statfs(stbuf, &arg.st); ++ memset(&arg, 0, sizeof(arg)); ++ convert_statfs(stbuf, &arg.st); + +- return send_reply_ok(req, &arg, size); ++ return send_reply_ok(req, &arg, size); + } + + int fuse_reply_xattr(fuse_req_t req, size_t count) + { +- struct fuse_getxattr_out arg; ++ struct fuse_getxattr_out arg; + +- memset(&arg, 0, sizeof(arg)); +- arg.size = count; ++ memset(&arg, 0, sizeof(arg)); ++ arg.size = count; + +- return send_reply_ok(req, &arg, sizeof(arg)); ++ return send_reply_ok(req, &arg, sizeof(arg)); + } + + int fuse_reply_lock(fuse_req_t req, const struct flock *lock) + { +- struct fuse_lk_out arg; ++ struct fuse_lk_out arg; + +- memset(&arg, 0, sizeof(arg)); +- arg.lk.type = lock->l_type; +- if (lock->l_type != F_UNLCK) { +- arg.lk.start = lock->l_start; +- if (lock->l_len == 0) +- arg.lk.end = OFFSET_MAX; +- else +- arg.lk.end = lock->l_start + lock->l_len - 1; +- } +- arg.lk.pid = lock->l_pid; +- return send_reply_ok(req, &arg, sizeof(arg)); ++ memset(&arg, 0, sizeof(arg)); ++ arg.lk.type = lock->l_type; ++ if (lock->l_type != F_UNLCK) { ++ arg.lk.start = lock->l_start; ++ if (lock->l_len == 0) { ++ arg.lk.end = OFFSET_MAX; ++ } else { ++ arg.lk.end = lock->l_start + lock->l_len - 1; ++ } ++ } ++ arg.lk.pid = lock->l_pid; ++ return send_reply_ok(req, &arg, sizeof(arg)); + } + + int fuse_reply_bmap(fuse_req_t req, uint64_t idx) + { +- struct fuse_bmap_out arg; ++ struct fuse_bmap_out arg; + +- memset(&arg, 0, sizeof(arg)); +- arg.block = idx; ++ memset(&arg, 0, sizeof(arg)); ++ arg.block = idx; + +- return send_reply_ok(req, &arg, sizeof(arg)); ++ return send_reply_ok(req, &arg, sizeof(arg)); + } + + static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov, +- size_t count) +-{ +- struct fuse_ioctl_iovec *fiov; +- size_t i; +- +- fiov = malloc(sizeof(fiov[0]) * count); +- if (!fiov) +- return NULL; +- +- for (i = 0; i < count; i++) { +- fiov[i].base = (uintptr_t) iov[i].iov_base; +- fiov[i].len = iov[i].iov_len; +- } +- +- return fiov; +-} +- +-int fuse_reply_ioctl_retry(fuse_req_t req, +- const struct iovec *in_iov, size_t in_count, +- const struct iovec *out_iov, size_t out_count) +-{ +- struct fuse_ioctl_out arg; +- struct fuse_ioctl_iovec *in_fiov = NULL; +- struct fuse_ioctl_iovec *out_fiov = NULL; +- struct iovec iov[4]; +- size_t count = 1; +- int res; +- +- memset(&arg, 0, sizeof(arg)); +- arg.flags |= FUSE_IOCTL_RETRY; +- arg.in_iovs = in_count; +- arg.out_iovs = out_count; +- iov[count].iov_base = &arg; +- iov[count].iov_len = sizeof(arg); +- count++; +- +- if (req->se->conn.proto_minor < 16) { +- if (in_count) { +- iov[count].iov_base = (void *)in_iov; +- iov[count].iov_len = sizeof(in_iov[0]) * in_count; +- count++; +- } +- +- if (out_count) { +- iov[count].iov_base = (void *)out_iov; +- iov[count].iov_len = sizeof(out_iov[0]) * out_count; +- count++; +- } +- } else { +- /* Can't handle non-compat 64bit ioctls on 32bit */ +- if (sizeof(void *) == 4 && req->ioctl_64bit) { +- res = fuse_reply_err(req, EINVAL); +- goto out; +- } +- +- if (in_count) { +- in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count); +- if (!in_fiov) +- goto enomem; +- +- iov[count].iov_base = (void *)in_fiov; +- iov[count].iov_len = sizeof(in_fiov[0]) * in_count; +- count++; +- } +- if (out_count) { +- out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count); +- if (!out_fiov) +- goto enomem; +- +- iov[count].iov_base = (void *)out_fiov; +- iov[count].iov_len = sizeof(out_fiov[0]) * out_count; +- count++; +- } +- } +- +- res = send_reply_iov(req, 0, iov, count); ++ size_t count) ++{ ++ struct fuse_ioctl_iovec *fiov; ++ size_t i; ++ ++ fiov = malloc(sizeof(fiov[0]) * count); ++ if (!fiov) { ++ return NULL; ++ } ++ ++ for (i = 0; i < count; i++) { ++ fiov[i].base = (uintptr_t)iov[i].iov_base; ++ fiov[i].len = iov[i].iov_len; ++ } ++ ++ return fiov; ++} ++ ++int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, ++ size_t in_count, const struct iovec *out_iov, ++ size_t out_count) ++{ ++ struct fuse_ioctl_out arg; ++ struct fuse_ioctl_iovec *in_fiov = NULL; ++ struct fuse_ioctl_iovec *out_fiov = NULL; ++ struct iovec iov[4]; ++ size_t count = 1; ++ int res; ++ ++ memset(&arg, 0, sizeof(arg)); ++ arg.flags |= FUSE_IOCTL_RETRY; ++ arg.in_iovs = in_count; ++ arg.out_iovs = out_count; ++ iov[count].iov_base = &arg; ++ iov[count].iov_len = sizeof(arg); ++ count++; ++ ++ if (req->se->conn.proto_minor < 16) { ++ if (in_count) { ++ iov[count].iov_base = (void *)in_iov; ++ iov[count].iov_len = sizeof(in_iov[0]) * in_count; ++ count++; ++ } ++ ++ if (out_count) { ++ iov[count].iov_base = (void *)out_iov; ++ iov[count].iov_len = sizeof(out_iov[0]) * out_count; ++ count++; ++ } ++ } else { ++ /* Can't handle non-compat 64bit ioctls on 32bit */ ++ if (sizeof(void *) == 4 && req->ioctl_64bit) { ++ res = fuse_reply_err(req, EINVAL); ++ goto out; ++ } ++ ++ if (in_count) { ++ in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count); ++ if (!in_fiov) { ++ goto enomem; ++ } ++ ++ iov[count].iov_base = (void *)in_fiov; ++ iov[count].iov_len = sizeof(in_fiov[0]) * in_count; ++ count++; ++ } ++ if (out_count) { ++ out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count); ++ if (!out_fiov) { ++ goto enomem; ++ } ++ ++ iov[count].iov_base = (void *)out_fiov; ++ iov[count].iov_len = sizeof(out_fiov[0]) * out_count; ++ count++; ++ } ++ } ++ ++ res = send_reply_iov(req, 0, iov, count); + out: +- free(in_fiov); +- free(out_fiov); ++ free(in_fiov); ++ free(out_fiov); + +- return res; ++ return res; + + enomem: +- res = fuse_reply_err(req, ENOMEM); +- goto out; ++ res = fuse_reply_err(req, ENOMEM); ++ goto out; + } + + int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size) + { +- struct fuse_ioctl_out arg; +- struct iovec iov[3]; +- size_t count = 1; ++ struct fuse_ioctl_out arg; ++ struct iovec iov[3]; ++ size_t count = 1; + +- memset(&arg, 0, sizeof(arg)); +- arg.result = result; +- iov[count].iov_base = &arg; +- iov[count].iov_len = sizeof(arg); +- count++; ++ memset(&arg, 0, sizeof(arg)); ++ arg.result = result; ++ iov[count].iov_base = &arg; ++ iov[count].iov_len = sizeof(arg); ++ count++; + +- if (size) { +- iov[count].iov_base = (char *) buf; +- iov[count].iov_len = size; +- count++; +- } ++ if (size) { ++ iov[count].iov_base = (char *)buf; ++ iov[count].iov_len = size; ++ count++; ++ } + +- return send_reply_iov(req, 0, iov, count); ++ return send_reply_iov(req, 0, iov, count); + } + + int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, +- int count) ++ int count) + { +- struct iovec *padded_iov; +- struct fuse_ioctl_out arg; +- int res; ++ struct iovec *padded_iov; ++ struct fuse_ioctl_out arg; ++ int res; + +- padded_iov = malloc((count + 2) * sizeof(struct iovec)); +- if (padded_iov == NULL) +- return fuse_reply_err(req, ENOMEM); ++ padded_iov = malloc((count + 2) * sizeof(struct iovec)); ++ if (padded_iov == NULL) { ++ return fuse_reply_err(req, ENOMEM); ++ } + +- memset(&arg, 0, sizeof(arg)); +- arg.result = result; +- padded_iov[1].iov_base = &arg; +- padded_iov[1].iov_len = sizeof(arg); ++ memset(&arg, 0, sizeof(arg)); ++ arg.result = result; ++ padded_iov[1].iov_base = &arg; ++ padded_iov[1].iov_len = sizeof(arg); + +- memcpy(&padded_iov[2], iov, count * sizeof(struct iovec)); ++ memcpy(&padded_iov[2], iov, count * sizeof(struct iovec)); + +- res = send_reply_iov(req, 0, padded_iov, count + 2); +- free(padded_iov); ++ res = send_reply_iov(req, 0, padded_iov, count + 2); ++ free(padded_iov); + +- return res; ++ return res; + } + + int fuse_reply_poll(fuse_req_t req, unsigned revents) + { +- struct fuse_poll_out arg; ++ struct fuse_poll_out arg; + +- memset(&arg, 0, sizeof(arg)); +- arg.revents = revents; ++ memset(&arg, 0, sizeof(arg)); ++ arg.revents = revents; + +- return send_reply_ok(req, &arg, sizeof(arg)); ++ return send_reply_ok(req, &arg, sizeof(arg)); + } + + int fuse_reply_lseek(fuse_req_t req, off_t off) + { +- struct fuse_lseek_out arg; ++ struct fuse_lseek_out arg; + +- memset(&arg, 0, sizeof(arg)); +- arg.offset = off; ++ memset(&arg, 0, sizeof(arg)); ++ arg.offset = off; + +- return send_reply_ok(req, &arg, sizeof(arg)); ++ return send_reply_ok(req, &arg, sizeof(arg)); + } + + static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- char *name = (char *) inarg; ++ char *name = (char *)inarg; + +- if (req->se->op.lookup) +- req->se->op.lookup(req, nodeid, name); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.lookup) { ++ req->se->op.lookup(req, nodeid, name); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg; ++ struct fuse_forget_in *arg = (struct fuse_forget_in *)inarg; + +- if (req->se->op.forget) +- req->se->op.forget(req, nodeid, arg->nlookup); +- else +- fuse_reply_none(req); ++ if (req->se->op.forget) { ++ req->se->op.forget(req, nodeid, arg->nlookup); ++ } else { ++ fuse_reply_none(req); ++ } + } + + static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid, +- const void *inarg) ++ const void *inarg) + { +- struct fuse_batch_forget_in *arg = (void *) inarg; +- struct fuse_forget_one *param = (void *) PARAM(arg); +- unsigned int i; ++ struct fuse_batch_forget_in *arg = (void *)inarg; ++ struct fuse_forget_one *param = (void *)PARAM(arg); ++ unsigned int i; + +- (void) nodeid; ++ (void)nodeid; + +- if (req->se->op.forget_multi) { +- req->se->op.forget_multi(req, arg->count, +- (struct fuse_forget_data *) param); +- } else if (req->se->op.forget) { +- for (i = 0; i < arg->count; i++) { +- struct fuse_forget_one *forget = ¶m[i]; +- struct fuse_req *dummy_req; ++ if (req->se->op.forget_multi) { ++ req->se->op.forget_multi(req, arg->count, ++ (struct fuse_forget_data *)param); ++ } else if (req->se->op.forget) { ++ for (i = 0; i < arg->count; i++) { ++ struct fuse_forget_one *forget = ¶m[i]; ++ struct fuse_req *dummy_req; + +- dummy_req = fuse_ll_alloc_req(req->se); +- if (dummy_req == NULL) +- break; ++ dummy_req = fuse_ll_alloc_req(req->se); ++ if (dummy_req == NULL) { ++ break; ++ } + +- dummy_req->unique = req->unique; +- dummy_req->ctx = req->ctx; +- dummy_req->ch = NULL; ++ dummy_req->unique = req->unique; ++ dummy_req->ctx = req->ctx; ++ dummy_req->ch = NULL; + +- req->se->op.forget(dummy_req, forget->nodeid, +- forget->nlookup); +- } +- fuse_reply_none(req); +- } else { +- fuse_reply_none(req); +- } ++ req->se->op.forget(dummy_req, forget->nodeid, forget->nlookup); ++ } ++ fuse_reply_none(req); ++ } else { ++ fuse_reply_none(req); ++ } + } + + static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_file_info *fip = NULL; +- struct fuse_file_info fi; ++ struct fuse_file_info *fip = NULL; ++ struct fuse_file_info fi; + +- if (req->se->conn.proto_minor >= 9) { +- struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg; ++ if (req->se->conn.proto_minor >= 9) { ++ struct fuse_getattr_in *arg = (struct fuse_getattr_in *)inarg; + +- if (arg->getattr_flags & FUSE_GETATTR_FH) { +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; +- fip = &fi; +- } +- } ++ if (arg->getattr_flags & FUSE_GETATTR_FH) { ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ fip = &fi; ++ } ++ } + +- if (req->se->op.getattr) +- req->se->op.getattr(req, nodeid, fip); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.getattr) { ++ req->se->op.getattr(req, nodeid, fip); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg; +- +- if (req->se->op.setattr) { +- struct fuse_file_info *fi = NULL; +- struct fuse_file_info fi_store; +- struct stat stbuf; +- memset(&stbuf, 0, sizeof(stbuf)); +- convert_attr(arg, &stbuf); +- if (arg->valid & FATTR_FH) { +- arg->valid &= ~FATTR_FH; +- memset(&fi_store, 0, sizeof(fi_store)); +- fi = &fi_store; +- fi->fh = arg->fh; +- } +- arg->valid &= +- FUSE_SET_ATTR_MODE | +- FUSE_SET_ATTR_UID | +- FUSE_SET_ATTR_GID | +- FUSE_SET_ATTR_SIZE | +- FUSE_SET_ATTR_ATIME | +- FUSE_SET_ATTR_MTIME | +- FUSE_SET_ATTR_ATIME_NOW | +- FUSE_SET_ATTR_MTIME_NOW | +- FUSE_SET_ATTR_CTIME; +- +- req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi); +- } else +- fuse_reply_err(req, ENOSYS); ++ struct fuse_setattr_in *arg = (struct fuse_setattr_in *)inarg; ++ ++ if (req->se->op.setattr) { ++ struct fuse_file_info *fi = NULL; ++ struct fuse_file_info fi_store; ++ struct stat stbuf; ++ memset(&stbuf, 0, sizeof(stbuf)); ++ convert_attr(arg, &stbuf); ++ if (arg->valid & FATTR_FH) { ++ arg->valid &= ~FATTR_FH; ++ memset(&fi_store, 0, sizeof(fi_store)); ++ fi = &fi_store; ++ fi->fh = arg->fh; ++ } ++ arg->valid &= FUSE_SET_ATTR_MODE | FUSE_SET_ATTR_UID | ++ FUSE_SET_ATTR_GID | FUSE_SET_ATTR_SIZE | ++ FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME | ++ FUSE_SET_ATTR_ATIME_NOW | FUSE_SET_ATTR_MTIME_NOW | ++ FUSE_SET_ATTR_CTIME; ++ ++ req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_access_in *arg = (struct fuse_access_in *) inarg; ++ struct fuse_access_in *arg = (struct fuse_access_in *)inarg; + +- if (req->se->op.access) +- req->se->op.access(req, nodeid, arg->mask); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.access) { ++ req->se->op.access(req, nodeid, arg->mask); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- (void) inarg; ++ (void)inarg; + +- if (req->se->op.readlink) +- req->se->op.readlink(req, nodeid); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.readlink) { ++ req->se->op.readlink(req, nodeid); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg; +- char *name = PARAM(arg); ++ struct fuse_mknod_in *arg = (struct fuse_mknod_in *)inarg; ++ char *name = PARAM(arg); + +- if (req->se->conn.proto_minor >= 12) +- req->ctx.umask = arg->umask; +- else +- name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE; ++ if (req->se->conn.proto_minor >= 12) { ++ req->ctx.umask = arg->umask; ++ } else { ++ name = (char *)inarg + FUSE_COMPAT_MKNOD_IN_SIZE; ++ } + +- if (req->se->op.mknod) +- req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.mknod) { ++ req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg; ++ struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *)inarg; + +- if (req->se->conn.proto_minor >= 12) +- req->ctx.umask = arg->umask; ++ if (req->se->conn.proto_minor >= 12) { ++ req->ctx.umask = arg->umask; ++ } + +- if (req->se->op.mkdir) +- req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.mkdir) { ++ req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- char *name = (char *) inarg; ++ char *name = (char *)inarg; + +- if (req->se->op.unlink) +- req->se->op.unlink(req, nodeid, name); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.unlink) { ++ req->se->op.unlink(req, nodeid, name); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- char *name = (char *) inarg; ++ char *name = (char *)inarg; + +- if (req->se->op.rmdir) +- req->se->op.rmdir(req, nodeid, name); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.rmdir) { ++ req->se->op.rmdir(req, nodeid, name); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- char *name = (char *) inarg; +- char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1; ++ char *name = (char *)inarg; ++ char *linkname = ((char *)inarg) + strlen((char *)inarg) + 1; + +- if (req->se->op.symlink) +- req->se->op.symlink(req, linkname, nodeid, name); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.symlink) { ++ req->se->op.symlink(req, linkname, nodeid, name); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg; +- char *oldname = PARAM(arg); +- char *newname = oldname + strlen(oldname) + 1; ++ struct fuse_rename_in *arg = (struct fuse_rename_in *)inarg; ++ char *oldname = PARAM(arg); ++ char *newname = oldname + strlen(oldname) + 1; + +- if (req->se->op.rename) +- req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, +- 0); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.rename) { ++ req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, 0); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_rename2_in *arg = (struct fuse_rename2_in *) inarg; +- char *oldname = PARAM(arg); +- char *newname = oldname + strlen(oldname) + 1; ++ struct fuse_rename2_in *arg = (struct fuse_rename2_in *)inarg; ++ char *oldname = PARAM(arg); ++ char *newname = oldname + strlen(oldname) + 1; + +- if (req->se->op.rename) +- req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, +- arg->flags); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.rename) { ++ req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, ++ arg->flags); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_link_in *arg = (struct fuse_link_in *) inarg; ++ struct fuse_link_in *arg = (struct fuse_link_in *)inarg; + +- if (req->se->op.link) +- req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg)); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.link) { ++ req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg)); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_create_in *arg = (struct fuse_create_in *) inarg; ++ struct fuse_create_in *arg = (struct fuse_create_in *)inarg; + +- if (req->se->op.create) { +- struct fuse_file_info fi; +- char *name = PARAM(arg); ++ if (req->se->op.create) { ++ struct fuse_file_info fi; ++ char *name = PARAM(arg); + +- memset(&fi, 0, sizeof(fi)); +- fi.flags = arg->flags; ++ memset(&fi, 0, sizeof(fi)); ++ fi.flags = arg->flags; + +- if (req->se->conn.proto_minor >= 12) +- req->ctx.umask = arg->umask; +- else +- name = (char *) inarg + sizeof(struct fuse_open_in); ++ if (req->se->conn.proto_minor >= 12) { ++ req->ctx.umask = arg->umask; ++ } else { ++ name = (char *)inarg + sizeof(struct fuse_open_in); ++ } + +- req->se->op.create(req, nodeid, name, arg->mode, &fi); +- } else +- fuse_reply_err(req, ENOSYS); ++ req->se->op.create(req, nodeid, name, arg->mode, &fi); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_open_in *arg = (struct fuse_open_in *) inarg; +- struct fuse_file_info fi; ++ struct fuse_open_in *arg = (struct fuse_open_in *)inarg; ++ struct fuse_file_info fi; + +- memset(&fi, 0, sizeof(fi)); +- fi.flags = arg->flags; ++ memset(&fi, 0, sizeof(fi)); ++ fi.flags = arg->flags; + +- if (req->se->op.open) +- req->se->op.open(req, nodeid, &fi); +- else +- fuse_reply_open(req, &fi); ++ if (req->se->op.open) { ++ req->se->op.open(req, nodeid, &fi); ++ } else { ++ fuse_reply_open(req, &fi); ++ } + } + + static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_read_in *arg = (struct fuse_read_in *) inarg; ++ struct fuse_read_in *arg = (struct fuse_read_in *)inarg; + +- if (req->se->op.read) { +- struct fuse_file_info fi; ++ if (req->se->op.read) { ++ struct fuse_file_info fi; + +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; +- if (req->se->conn.proto_minor >= 9) { +- fi.lock_owner = arg->lock_owner; +- fi.flags = arg->flags; +- } +- req->se->op.read(req, nodeid, arg->size, arg->offset, &fi); +- } else +- fuse_reply_err(req, ENOSYS); ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ if (req->se->conn.proto_minor >= 9) { ++ fi.lock_owner = arg->lock_owner; ++ fi.flags = arg->flags; ++ } ++ req->se->op.read(req, nodeid, arg->size, arg->offset, &fi); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_write_in *arg = (struct fuse_write_in *) inarg; +- struct fuse_file_info fi; +- char *param; ++ struct fuse_write_in *arg = (struct fuse_write_in *)inarg; ++ struct fuse_file_info fi; ++ char *param; + +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; +- fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0; ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0; + +- if (req->se->conn.proto_minor < 9) { +- param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE; +- } else { +- fi.lock_owner = arg->lock_owner; +- fi.flags = arg->flags; +- param = PARAM(arg); +- } ++ if (req->se->conn.proto_minor < 9) { ++ param = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE; ++ } else { ++ fi.lock_owner = arg->lock_owner; ++ fi.flags = arg->flags; ++ param = PARAM(arg); ++ } + +- if (req->se->op.write) +- req->se->op.write(req, nodeid, param, arg->size, +- arg->offset, &fi); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.write) { ++ req->se->op.write(req, nodeid, param, arg->size, arg->offset, &fi); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, +- const struct fuse_buf *ibuf) +-{ +- struct fuse_session *se = req->se; +- struct fuse_bufvec bufv = { +- .buf[0] = *ibuf, +- .count = 1, +- }; +- struct fuse_write_in *arg = (struct fuse_write_in *) inarg; +- struct fuse_file_info fi; +- +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; +- fi.writepage = arg->write_flags & FUSE_WRITE_CACHE; +- +- if (se->conn.proto_minor < 9) { +- bufv.buf[0].mem = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE; +- bufv.buf[0].size -= sizeof(struct fuse_in_header) + +- FUSE_COMPAT_WRITE_IN_SIZE; +- assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD)); +- } else { +- fi.lock_owner = arg->lock_owner; +- fi.flags = arg->flags; +- if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) +- bufv.buf[0].mem = PARAM(arg); +- +- bufv.buf[0].size -= sizeof(struct fuse_in_header) + +- sizeof(struct fuse_write_in); +- } +- if (bufv.buf[0].size < arg->size) { +- fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n"); +- fuse_reply_err(req, EIO); +- return; +- } +- bufv.buf[0].size = arg->size; +- +- se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi); ++ const struct fuse_buf *ibuf) ++{ ++ struct fuse_session *se = req->se; ++ struct fuse_bufvec bufv = { ++ .buf[0] = *ibuf, ++ .count = 1, ++ }; ++ struct fuse_write_in *arg = (struct fuse_write_in *)inarg; ++ struct fuse_file_info fi; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ fi.writepage = arg->write_flags & FUSE_WRITE_CACHE; ++ ++ if (se->conn.proto_minor < 9) { ++ bufv.buf[0].mem = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE; ++ bufv.buf[0].size -= ++ sizeof(struct fuse_in_header) + FUSE_COMPAT_WRITE_IN_SIZE; ++ assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD)); ++ } else { ++ fi.lock_owner = arg->lock_owner; ++ fi.flags = arg->flags; ++ if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) { ++ bufv.buf[0].mem = PARAM(arg); ++ } ++ ++ bufv.buf[0].size -= ++ sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); ++ } ++ if (bufv.buf[0].size < arg->size) { ++ fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n"); ++ fuse_reply_err(req, EIO); ++ return; ++ } ++ bufv.buf[0].size = arg->size; ++ ++ se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi); + } + + static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg; +- struct fuse_file_info fi; ++ struct fuse_flush_in *arg = (struct fuse_flush_in *)inarg; ++ struct fuse_file_info fi; + +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; +- fi.flush = 1; +- if (req->se->conn.proto_minor >= 7) +- fi.lock_owner = arg->lock_owner; ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ fi.flush = 1; ++ if (req->se->conn.proto_minor >= 7) { ++ fi.lock_owner = arg->lock_owner; ++ } + +- if (req->se->op.flush) +- req->se->op.flush(req, nodeid, &fi); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.flush) { ++ req->se->op.flush(req, nodeid, &fi); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_release_in *arg = (struct fuse_release_in *) inarg; +- struct fuse_file_info fi; ++ struct fuse_release_in *arg = (struct fuse_release_in *)inarg; ++ struct fuse_file_info fi; + +- memset(&fi, 0, sizeof(fi)); +- fi.flags = arg->flags; +- fi.fh = arg->fh; +- if (req->se->conn.proto_minor >= 8) { +- fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; +- fi.lock_owner = arg->lock_owner; +- } +- if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) { +- fi.flock_release = 1; +- fi.lock_owner = arg->lock_owner; +- } ++ memset(&fi, 0, sizeof(fi)); ++ fi.flags = arg->flags; ++ fi.fh = arg->fh; ++ if (req->se->conn.proto_minor >= 8) { ++ fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; ++ fi.lock_owner = arg->lock_owner; ++ } ++ if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) { ++ fi.flock_release = 1; ++ fi.lock_owner = arg->lock_owner; ++ } + +- if (req->se->op.release) +- req->se->op.release(req, nodeid, &fi); +- else +- fuse_reply_err(req, 0); ++ if (req->se->op.release) { ++ req->se->op.release(req, nodeid, &fi); ++ } else { ++ fuse_reply_err(req, 0); ++ } + } + + static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; +- struct fuse_file_info fi; +- int datasync = arg->fsync_flags & 1; ++ struct fuse_fsync_in *arg = (struct fuse_fsync_in *)inarg; ++ struct fuse_file_info fi; ++ int datasync = arg->fsync_flags & 1; + +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; + +- if (req->se->op.fsync) +- req->se->op.fsync(req, nodeid, datasync, &fi); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.fsync) { ++ req->se->op.fsync(req, nodeid, datasync, &fi); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_open_in *arg = (struct fuse_open_in *) inarg; +- struct fuse_file_info fi; ++ struct fuse_open_in *arg = (struct fuse_open_in *)inarg; ++ struct fuse_file_info fi; + +- memset(&fi, 0, sizeof(fi)); +- fi.flags = arg->flags; ++ memset(&fi, 0, sizeof(fi)); ++ fi.flags = arg->flags; + +- if (req->se->op.opendir) +- req->se->op.opendir(req, nodeid, &fi); +- else +- fuse_reply_open(req, &fi); ++ if (req->se->op.opendir) { ++ req->se->op.opendir(req, nodeid, &fi); ++ } else { ++ fuse_reply_open(req, &fi); ++ } + } + + static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_read_in *arg = (struct fuse_read_in *) inarg; +- struct fuse_file_info fi; ++ struct fuse_read_in *arg = (struct fuse_read_in *)inarg; ++ struct fuse_file_info fi; + +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; + +- if (req->se->op.readdir) +- req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.readdir) { ++ req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_read_in *arg = (struct fuse_read_in *) inarg; +- struct fuse_file_info fi; ++ struct fuse_read_in *arg = (struct fuse_read_in *)inarg; ++ struct fuse_file_info fi; + +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; + +- if (req->se->op.readdirplus) +- req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.readdirplus) { ++ req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_release_in *arg = (struct fuse_release_in *) inarg; +- struct fuse_file_info fi; ++ struct fuse_release_in *arg = (struct fuse_release_in *)inarg; ++ struct fuse_file_info fi; + +- memset(&fi, 0, sizeof(fi)); +- fi.flags = arg->flags; +- fi.fh = arg->fh; ++ memset(&fi, 0, sizeof(fi)); ++ fi.flags = arg->flags; ++ fi.fh = arg->fh; + +- if (req->se->op.releasedir) +- req->se->op.releasedir(req, nodeid, &fi); +- else +- fuse_reply_err(req, 0); ++ if (req->se->op.releasedir) { ++ req->se->op.releasedir(req, nodeid, &fi); ++ } else { ++ fuse_reply_err(req, 0); ++ } + } + + static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; +- struct fuse_file_info fi; +- int datasync = arg->fsync_flags & 1; ++ struct fuse_fsync_in *arg = (struct fuse_fsync_in *)inarg; ++ struct fuse_file_info fi; ++ int datasync = arg->fsync_flags & 1; + +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; + +- if (req->se->op.fsyncdir) +- req->se->op.fsyncdir(req, nodeid, datasync, &fi); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.fsyncdir) { ++ req->se->op.fsyncdir(req, nodeid, datasync, &fi); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- (void) nodeid; +- (void) inarg; ++ (void)nodeid; ++ (void)inarg; + +- if (req->se->op.statfs) +- req->se->op.statfs(req, nodeid); +- else { +- struct statvfs buf = { +- .f_namemax = 255, +- .f_bsize = 512, +- }; +- fuse_reply_statfs(req, &buf); +- } ++ if (req->se->op.statfs) { ++ req->se->op.statfs(req, nodeid); ++ } else { ++ struct statvfs buf = { ++ .f_namemax = 255, ++ .f_bsize = 512, ++ }; ++ fuse_reply_statfs(req, &buf); ++ } + } + + static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg; +- char *name = PARAM(arg); +- char *value = name + strlen(name) + 1; ++ struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *)inarg; ++ char *name = PARAM(arg); ++ char *value = name + strlen(name) + 1; + +- if (req->se->op.setxattr) +- req->se->op.setxattr(req, nodeid, name, value, arg->size, +- arg->flags); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.setxattr) { ++ req->se->op.setxattr(req, nodeid, name, value, arg->size, arg->flags); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; ++ struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *)inarg; + +- if (req->se->op.getxattr) +- req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.getxattr) { ++ req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; ++ struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *)inarg; + +- if (req->se->op.listxattr) +- req->se->op.listxattr(req, nodeid, arg->size); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.listxattr) { ++ req->se->op.listxattr(req, nodeid, arg->size); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- char *name = (char *) inarg; ++ char *name = (char *)inarg; + +- if (req->se->op.removexattr) +- req->se->op.removexattr(req, nodeid, name); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.removexattr) { ++ req->se->op.removexattr(req, nodeid, name); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void convert_fuse_file_lock(struct fuse_file_lock *fl, +- struct flock *flock) ++ struct flock *flock) + { +- memset(flock, 0, sizeof(struct flock)); +- flock->l_type = fl->type; +- flock->l_whence = SEEK_SET; +- flock->l_start = fl->start; +- if (fl->end == OFFSET_MAX) +- flock->l_len = 0; +- else +- flock->l_len = fl->end - fl->start + 1; +- flock->l_pid = fl->pid; ++ memset(flock, 0, sizeof(struct flock)); ++ flock->l_type = fl->type; ++ flock->l_whence = SEEK_SET; ++ flock->l_start = fl->start; ++ if (fl->end == OFFSET_MAX) { ++ flock->l_len = 0; ++ } else { ++ flock->l_len = fl->end - fl->start + 1; ++ } ++ flock->l_pid = fl->pid; + } + + static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg; +- struct fuse_file_info fi; +- struct flock flock; ++ struct fuse_lk_in *arg = (struct fuse_lk_in *)inarg; ++ struct fuse_file_info fi; ++ struct flock flock; + +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; +- fi.lock_owner = arg->owner; ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ fi.lock_owner = arg->owner; + +- convert_fuse_file_lock(&arg->lk, &flock); +- if (req->se->op.getlk) +- req->se->op.getlk(req, nodeid, &fi, &flock); +- else +- fuse_reply_err(req, ENOSYS); ++ convert_fuse_file_lock(&arg->lk, &flock); ++ if (req->se->op.getlk) { ++ req->se->op.getlk(req, nodeid, &fi, &flock); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, +- const void *inarg, int sleep) +-{ +- struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg; +- struct fuse_file_info fi; +- struct flock flock; +- +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; +- fi.lock_owner = arg->owner; +- +- if (arg->lk_flags & FUSE_LK_FLOCK) { +- int op = 0; +- +- switch (arg->lk.type) { +- case F_RDLCK: +- op = LOCK_SH; +- break; +- case F_WRLCK: +- op = LOCK_EX; +- break; +- case F_UNLCK: +- op = LOCK_UN; +- break; +- } +- if (!sleep) +- op |= LOCK_NB; +- +- if (req->se->op.flock) +- req->se->op.flock(req, nodeid, &fi, op); +- else +- fuse_reply_err(req, ENOSYS); +- } else { +- convert_fuse_file_lock(&arg->lk, &flock); +- if (req->se->op.setlk) +- req->se->op.setlk(req, nodeid, &fi, &flock, sleep); +- else +- fuse_reply_err(req, ENOSYS); +- } ++ const void *inarg, int sleep) ++{ ++ struct fuse_lk_in *arg = (struct fuse_lk_in *)inarg; ++ struct fuse_file_info fi; ++ struct flock flock; ++ ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ fi.lock_owner = arg->owner; ++ ++ if (arg->lk_flags & FUSE_LK_FLOCK) { ++ int op = 0; ++ ++ switch (arg->lk.type) { ++ case F_RDLCK: ++ op = LOCK_SH; ++ break; ++ case F_WRLCK: ++ op = LOCK_EX; ++ break; ++ case F_UNLCK: ++ op = LOCK_UN; ++ break; ++ } ++ if (!sleep) { ++ op |= LOCK_NB; ++ } ++ ++ if (req->se->op.flock) { ++ req->se->op.flock(req, nodeid, &fi, op); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } ++ } else { ++ convert_fuse_file_lock(&arg->lk, &flock); ++ if (req->se->op.setlk) { ++ req->se->op.setlk(req, nodeid, &fi, &flock, sleep); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } ++ } + } + + static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- do_setlk_common(req, nodeid, inarg, 0); ++ do_setlk_common(req, nodeid, inarg, 0); + } + + static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- do_setlk_common(req, nodeid, inarg, 1); ++ do_setlk_common(req, nodeid, inarg, 1); + } + + static int find_interrupted(struct fuse_session *se, struct fuse_req *req) + { +- struct fuse_req *curr; +- +- for (curr = se->list.next; curr != &se->list; curr = curr->next) { +- if (curr->unique == req->u.i.unique) { +- fuse_interrupt_func_t func; +- void *data; +- +- curr->ctr++; +- pthread_mutex_unlock(&se->lock); +- +- /* Ugh, ugly locking */ +- pthread_mutex_lock(&curr->lock); +- pthread_mutex_lock(&se->lock); +- curr->interrupted = 1; +- func = curr->u.ni.func; +- data = curr->u.ni.data; +- pthread_mutex_unlock(&se->lock); +- if (func) +- func(curr, data); +- pthread_mutex_unlock(&curr->lock); +- +- pthread_mutex_lock(&se->lock); +- curr->ctr--; +- if (!curr->ctr) +- destroy_req(curr); +- +- return 1; +- } +- } +- for (curr = se->interrupts.next; curr != &se->interrupts; +- curr = curr->next) { +- if (curr->u.i.unique == req->u.i.unique) +- return 1; +- } +- return 0; ++ struct fuse_req *curr; ++ ++ for (curr = se->list.next; curr != &se->list; curr = curr->next) { ++ if (curr->unique == req->u.i.unique) { ++ fuse_interrupt_func_t func; ++ void *data; ++ ++ curr->ctr++; ++ pthread_mutex_unlock(&se->lock); ++ ++ /* Ugh, ugly locking */ ++ pthread_mutex_lock(&curr->lock); ++ pthread_mutex_lock(&se->lock); ++ curr->interrupted = 1; ++ func = curr->u.ni.func; ++ data = curr->u.ni.data; ++ pthread_mutex_unlock(&se->lock); ++ if (func) { ++ func(curr, data); ++ } ++ pthread_mutex_unlock(&curr->lock); ++ ++ pthread_mutex_lock(&se->lock); ++ curr->ctr--; ++ if (!curr->ctr) { ++ destroy_req(curr); ++ } ++ ++ return 1; ++ } ++ } ++ for (curr = se->interrupts.next; curr != &se->interrupts; ++ curr = curr->next) { ++ if (curr->u.i.unique == req->u.i.unique) { ++ return 1; ++ } ++ } ++ return 0; + } + + static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg; +- struct fuse_session *se = req->se; ++ struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *)inarg; ++ struct fuse_session *se = req->se; + +- (void) nodeid; +- if (se->debug) +- fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n", +- (unsigned long long) arg->unique); ++ (void)nodeid; ++ if (se->debug) { ++ fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n", ++ (unsigned long long)arg->unique); ++ } + +- req->u.i.unique = arg->unique; ++ req->u.i.unique = arg->unique; + +- pthread_mutex_lock(&se->lock); +- if (find_interrupted(se, req)) +- destroy_req(req); +- else +- list_add_req(req, &se->interrupts); +- pthread_mutex_unlock(&se->lock); ++ pthread_mutex_lock(&se->lock); ++ if (find_interrupted(se, req)) { ++ destroy_req(req); ++ } else { ++ list_add_req(req, &se->interrupts); ++ } ++ pthread_mutex_unlock(&se->lock); + } + + static struct fuse_req *check_interrupt(struct fuse_session *se, +- struct fuse_req *req) +-{ +- struct fuse_req *curr; +- +- for (curr = se->interrupts.next; curr != &se->interrupts; +- curr = curr->next) { +- if (curr->u.i.unique == req->unique) { +- req->interrupted = 1; +- list_del_req(curr); +- free(curr); +- return NULL; +- } +- } +- curr = se->interrupts.next; +- if (curr != &se->interrupts) { +- list_del_req(curr); +- list_init_req(curr); +- return curr; +- } else +- return NULL; ++ struct fuse_req *req) ++{ ++ struct fuse_req *curr; ++ ++ for (curr = se->interrupts.next; curr != &se->interrupts; ++ curr = curr->next) { ++ if (curr->u.i.unique == req->unique) { ++ req->interrupted = 1; ++ list_del_req(curr); ++ free(curr); ++ return NULL; ++ } ++ } ++ curr = se->interrupts.next; ++ if (curr != &se->interrupts) { ++ list_del_req(curr); ++ list_init_req(curr); ++ return curr; ++ } else { ++ return NULL; ++ } + } + + static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg; ++ struct fuse_bmap_in *arg = (struct fuse_bmap_in *)inarg; + +- if (req->se->op.bmap) +- req->se->op.bmap(req, nodeid, arg->blocksize, arg->block); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.bmap) { ++ req->se->op.bmap(req, nodeid, arg->blocksize, arg->block); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg; +- unsigned int flags = arg->flags; +- void *in_buf = arg->in_size ? PARAM(arg) : NULL; +- struct fuse_file_info fi; ++ struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *)inarg; ++ unsigned int flags = arg->flags; ++ void *in_buf = arg->in_size ? PARAM(arg) : NULL; ++ struct fuse_file_info fi; + +- if (flags & FUSE_IOCTL_DIR && +- !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) { +- fuse_reply_err(req, ENOTTY); +- return; +- } ++ if (flags & FUSE_IOCTL_DIR && !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) { ++ fuse_reply_err(req, ENOTTY); ++ return; ++ } + +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; + +- if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 && +- !(flags & FUSE_IOCTL_32BIT)) { +- req->ioctl_64bit = 1; +- } ++ if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 && ++ !(flags & FUSE_IOCTL_32BIT)) { ++ req->ioctl_64bit = 1; ++ } + +- if (req->se->op.ioctl) +- req->se->op.ioctl(req, nodeid, arg->cmd, +- (void *)(uintptr_t)arg->arg, &fi, flags, +- in_buf, arg->in_size, arg->out_size); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.ioctl) { ++ req->se->op.ioctl(req, nodeid, arg->cmd, (void *)(uintptr_t)arg->arg, ++ &fi, flags, in_buf, arg->in_size, arg->out_size); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + void fuse_pollhandle_destroy(struct fuse_pollhandle *ph) + { +- free(ph); ++ free(ph); + } + + static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg; +- struct fuse_file_info fi; ++ struct fuse_poll_in *arg = (struct fuse_poll_in *)inarg; ++ struct fuse_file_info fi; + +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; +- fi.poll_events = arg->events; ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ fi.poll_events = arg->events; + +- if (req->se->op.poll) { +- struct fuse_pollhandle *ph = NULL; ++ if (req->se->op.poll) { ++ struct fuse_pollhandle *ph = NULL; + +- if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) { +- ph = malloc(sizeof(struct fuse_pollhandle)); +- if (ph == NULL) { +- fuse_reply_err(req, ENOMEM); +- return; +- } +- ph->kh = arg->kh; +- ph->se = req->se; +- } ++ if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) { ++ ph = malloc(sizeof(struct fuse_pollhandle)); ++ if (ph == NULL) { ++ fuse_reply_err(req, ENOMEM); ++ return; ++ } ++ ph->kh = arg->kh; ++ ph->se = req->se; ++ } + +- req->se->op.poll(req, nodeid, &fi, ph); +- } else { +- fuse_reply_err(req, ENOSYS); +- } ++ req->se->op.poll(req, nodeid, &fi, ph); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg; +- struct fuse_file_info fi; ++ struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *)inarg; ++ struct fuse_file_info fi; + +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; + +- if (req->se->op.fallocate) +- req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.fallocate) { ++ req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, ++ &fi); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + +-static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void *inarg) ++static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, ++ const void *inarg) + { +- struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in *) inarg; +- struct fuse_file_info fi_in, fi_out; ++ struct fuse_copy_file_range_in *arg = ++ (struct fuse_copy_file_range_in *)inarg; ++ struct fuse_file_info fi_in, fi_out; + +- memset(&fi_in, 0, sizeof(fi_in)); +- fi_in.fh = arg->fh_in; ++ memset(&fi_in, 0, sizeof(fi_in)); ++ fi_in.fh = arg->fh_in; + +- memset(&fi_out, 0, sizeof(fi_out)); +- fi_out.fh = arg->fh_out; ++ memset(&fi_out, 0, sizeof(fi_out)); ++ fi_out.fh = arg->fh_out; + + +- if (req->se->op.copy_file_range) +- req->se->op.copy_file_range(req, nodeid_in, arg->off_in, +- &fi_in, arg->nodeid_out, +- arg->off_out, &fi_out, arg->len, +- arg->flags); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.copy_file_range) { ++ req->se->op.copy_file_range(req, nodeid_in, arg->off_in, &fi_in, ++ arg->nodeid_out, arg->off_out, &fi_out, ++ arg->len, arg->flags); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_lseek_in *arg = (struct fuse_lseek_in *) inarg; +- struct fuse_file_info fi; ++ struct fuse_lseek_in *arg = (struct fuse_lseek_in *)inarg; ++ struct fuse_file_info fi; + +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; + +- if (req->se->op.lseek) +- req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi); +- else +- fuse_reply_err(req, ENOSYS); ++ if (req->se->op.lseek) { ++ req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi); ++ } else { ++ fuse_reply_err(req, ENOSYS); ++ } + } + + static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_init_in *arg = (struct fuse_init_in *) inarg; +- struct fuse_init_out outarg; +- struct fuse_session *se = req->se; +- size_t bufsize = se->bufsize; +- size_t outargsize = sizeof(outarg); +- +- (void) nodeid; +- if (se->debug) { +- fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor); +- if (arg->major == 7 && arg->minor >= 6) { +- fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags); +- fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n", +- arg->max_readahead); +- } +- } +- se->conn.proto_major = arg->major; +- se->conn.proto_minor = arg->minor; +- se->conn.capable = 0; +- se->conn.want = 0; +- +- memset(&outarg, 0, sizeof(outarg)); +- outarg.major = FUSE_KERNEL_VERSION; +- outarg.minor = FUSE_KERNEL_MINOR_VERSION; +- +- if (arg->major < 7) { +- fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n", +- arg->major, arg->minor); +- fuse_reply_err(req, EPROTO); +- return; +- } +- +- if (arg->major > 7) { +- /* Wait for a second INIT request with a 7.X version */ +- send_reply_ok(req, &outarg, sizeof(outarg)); +- return; +- } +- +- if (arg->minor >= 6) { +- if (arg->max_readahead < se->conn.max_readahead) +- se->conn.max_readahead = arg->max_readahead; +- if (arg->flags & FUSE_ASYNC_READ) +- se->conn.capable |= FUSE_CAP_ASYNC_READ; +- if (arg->flags & FUSE_POSIX_LOCKS) +- se->conn.capable |= FUSE_CAP_POSIX_LOCKS; +- if (arg->flags & FUSE_ATOMIC_O_TRUNC) +- se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC; +- if (arg->flags & FUSE_EXPORT_SUPPORT) +- se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT; +- if (arg->flags & FUSE_DONT_MASK) +- se->conn.capable |= FUSE_CAP_DONT_MASK; +- if (arg->flags & FUSE_FLOCK_LOCKS) +- se->conn.capable |= FUSE_CAP_FLOCK_LOCKS; +- if (arg->flags & FUSE_AUTO_INVAL_DATA) +- se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA; +- if (arg->flags & FUSE_DO_READDIRPLUS) +- se->conn.capable |= FUSE_CAP_READDIRPLUS; +- if (arg->flags & FUSE_READDIRPLUS_AUTO) +- se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO; +- if (arg->flags & FUSE_ASYNC_DIO) +- se->conn.capable |= FUSE_CAP_ASYNC_DIO; +- if (arg->flags & FUSE_WRITEBACK_CACHE) +- se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE; +- if (arg->flags & FUSE_NO_OPEN_SUPPORT) +- se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT; +- if (arg->flags & FUSE_PARALLEL_DIROPS) +- se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS; +- if (arg->flags & FUSE_POSIX_ACL) +- se->conn.capable |= FUSE_CAP_POSIX_ACL; +- if (arg->flags & FUSE_HANDLE_KILLPRIV) +- se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV; +- if (arg->flags & FUSE_NO_OPENDIR_SUPPORT) +- se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT; +- if (!(arg->flags & FUSE_MAX_PAGES)) { +- size_t max_bufsize = +- FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() +- + FUSE_BUFFER_HEADER_SIZE; +- if (bufsize > max_bufsize) { +- bufsize = max_bufsize; +- } +- } +- } else { +- se->conn.max_readahead = 0; +- } +- +- if (se->conn.proto_minor >= 14) { ++ struct fuse_init_in *arg = (struct fuse_init_in *)inarg; ++ struct fuse_init_out outarg; ++ struct fuse_session *se = req->se; ++ size_t bufsize = se->bufsize; ++ size_t outargsize = sizeof(outarg); ++ ++ (void)nodeid; ++ if (se->debug) { ++ fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor); ++ if (arg->major == 7 && arg->minor >= 6) { ++ fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags); ++ fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n", ++ arg->max_readahead); ++ } ++ } ++ se->conn.proto_major = arg->major; ++ se->conn.proto_minor = arg->minor; ++ se->conn.capable = 0; ++ se->conn.want = 0; ++ ++ memset(&outarg, 0, sizeof(outarg)); ++ outarg.major = FUSE_KERNEL_VERSION; ++ outarg.minor = FUSE_KERNEL_MINOR_VERSION; ++ ++ if (arg->major < 7) { ++ fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n", ++ arg->major, arg->minor); ++ fuse_reply_err(req, EPROTO); ++ return; ++ } ++ ++ if (arg->major > 7) { ++ /* Wait for a second INIT request with a 7.X version */ ++ send_reply_ok(req, &outarg, sizeof(outarg)); ++ return; ++ } ++ ++ if (arg->minor >= 6) { ++ if (arg->max_readahead < se->conn.max_readahead) { ++ se->conn.max_readahead = arg->max_readahead; ++ } ++ if (arg->flags & FUSE_ASYNC_READ) { ++ se->conn.capable |= FUSE_CAP_ASYNC_READ; ++ } ++ if (arg->flags & FUSE_POSIX_LOCKS) { ++ se->conn.capable |= FUSE_CAP_POSIX_LOCKS; ++ } ++ if (arg->flags & FUSE_ATOMIC_O_TRUNC) { ++ se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC; ++ } ++ if (arg->flags & FUSE_EXPORT_SUPPORT) { ++ se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT; ++ } ++ if (arg->flags & FUSE_DONT_MASK) { ++ se->conn.capable |= FUSE_CAP_DONT_MASK; ++ } ++ if (arg->flags & FUSE_FLOCK_LOCKS) { ++ se->conn.capable |= FUSE_CAP_FLOCK_LOCKS; ++ } ++ if (arg->flags & FUSE_AUTO_INVAL_DATA) { ++ se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA; ++ } ++ if (arg->flags & FUSE_DO_READDIRPLUS) { ++ se->conn.capable |= FUSE_CAP_READDIRPLUS; ++ } ++ if (arg->flags & FUSE_READDIRPLUS_AUTO) { ++ se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO; ++ } ++ if (arg->flags & FUSE_ASYNC_DIO) { ++ se->conn.capable |= FUSE_CAP_ASYNC_DIO; ++ } ++ if (arg->flags & FUSE_WRITEBACK_CACHE) { ++ se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE; ++ } ++ if (arg->flags & FUSE_NO_OPEN_SUPPORT) { ++ se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT; ++ } ++ if (arg->flags & FUSE_PARALLEL_DIROPS) { ++ se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS; ++ } ++ if (arg->flags & FUSE_POSIX_ACL) { ++ se->conn.capable |= FUSE_CAP_POSIX_ACL; ++ } ++ if (arg->flags & FUSE_HANDLE_KILLPRIV) { ++ se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV; ++ } ++ if (arg->flags & FUSE_NO_OPENDIR_SUPPORT) { ++ se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT; ++ } ++ if (!(arg->flags & FUSE_MAX_PAGES)) { ++ size_t max_bufsize = ++ FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() + ++ FUSE_BUFFER_HEADER_SIZE; ++ if (bufsize > max_bufsize) { ++ bufsize = max_bufsize; ++ } ++ } ++ } else { ++ se->conn.max_readahead = 0; ++ } ++ ++ if (se->conn.proto_minor >= 14) { + #ifdef HAVE_SPLICE + #ifdef HAVE_VMSPLICE +- se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; ++ se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; + #endif +- se->conn.capable |= FUSE_CAP_SPLICE_READ; ++ se->conn.capable |= FUSE_CAP_SPLICE_READ; + #endif +- } +- if (se->conn.proto_minor >= 18) +- se->conn.capable |= FUSE_CAP_IOCTL_DIR; +- +- /* Default settings for modern filesystems. +- * +- * Most of these capabilities were disabled by default in +- * libfuse2 for backwards compatibility reasons. In libfuse3, +- * we can finally enable them by default (as long as they're +- * supported by the kernel). +- */ +-#define LL_SET_DEFAULT(cond, cap) \ +- if ((cond) && (se->conn.capable & (cap))) \ +- se->conn.want |= (cap) +- LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ); +- LL_SET_DEFAULT(1, FUSE_CAP_PARALLEL_DIROPS); +- LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA); +- LL_SET_DEFAULT(1, FUSE_CAP_HANDLE_KILLPRIV); +- LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO); +- LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR); +- LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC); +- LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ); +- LL_SET_DEFAULT(se->op.getlk && se->op.setlk, +- FUSE_CAP_POSIX_LOCKS); +- LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS); +- LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS); +- LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir, +- FUSE_CAP_READDIRPLUS_AUTO); +- se->conn.time_gran = 1; +- +- if (bufsize < FUSE_MIN_READ_BUFFER) { +- fuse_log(FUSE_LOG_ERR, "fuse: warning: buffer size too small: %zu\n", +- bufsize); +- bufsize = FUSE_MIN_READ_BUFFER; +- } +- se->bufsize = bufsize; +- +- if (se->conn.max_write > bufsize - FUSE_BUFFER_HEADER_SIZE) +- se->conn.max_write = bufsize - FUSE_BUFFER_HEADER_SIZE; +- +- se->got_init = 1; +- if (se->op.init) +- se->op.init(se->userdata, &se->conn); +- +- if (se->conn.want & (~se->conn.capable)) { +- fuse_log(FUSE_LOG_ERR, "fuse: error: filesystem requested capabilities " +- "0x%x that are not supported by kernel, aborting.\n", +- se->conn.want & (~se->conn.capable)); +- fuse_reply_err(req, EPROTO); +- se->error = -EPROTO; +- fuse_session_exit(se); +- return; +- } +- +- if (se->conn.max_write < bufsize - FUSE_BUFFER_HEADER_SIZE) { +- se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE; +- } +- if (arg->flags & FUSE_MAX_PAGES) { +- outarg.flags |= FUSE_MAX_PAGES; +- outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1; +- } +- +- /* Always enable big writes, this is superseded +- by the max_write option */ +- outarg.flags |= FUSE_BIG_WRITES; +- +- if (se->conn.want & FUSE_CAP_ASYNC_READ) +- outarg.flags |= FUSE_ASYNC_READ; +- if (se->conn.want & FUSE_CAP_POSIX_LOCKS) +- outarg.flags |= FUSE_POSIX_LOCKS; +- if (se->conn.want & FUSE_CAP_ATOMIC_O_TRUNC) +- outarg.flags |= FUSE_ATOMIC_O_TRUNC; +- if (se->conn.want & FUSE_CAP_EXPORT_SUPPORT) +- outarg.flags |= FUSE_EXPORT_SUPPORT; +- if (se->conn.want & FUSE_CAP_DONT_MASK) +- outarg.flags |= FUSE_DONT_MASK; +- if (se->conn.want & FUSE_CAP_FLOCK_LOCKS) +- outarg.flags |= FUSE_FLOCK_LOCKS; +- if (se->conn.want & FUSE_CAP_AUTO_INVAL_DATA) +- outarg.flags |= FUSE_AUTO_INVAL_DATA; +- if (se->conn.want & FUSE_CAP_READDIRPLUS) +- outarg.flags |= FUSE_DO_READDIRPLUS; +- if (se->conn.want & FUSE_CAP_READDIRPLUS_AUTO) +- outarg.flags |= FUSE_READDIRPLUS_AUTO; +- if (se->conn.want & FUSE_CAP_ASYNC_DIO) +- outarg.flags |= FUSE_ASYNC_DIO; +- if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE) +- outarg.flags |= FUSE_WRITEBACK_CACHE; +- if (se->conn.want & FUSE_CAP_POSIX_ACL) +- outarg.flags |= FUSE_POSIX_ACL; +- outarg.max_readahead = se->conn.max_readahead; +- outarg.max_write = se->conn.max_write; +- if (se->conn.proto_minor >= 13) { +- if (se->conn.max_background >= (1 << 16)) +- se->conn.max_background = (1 << 16) - 1; +- if (se->conn.congestion_threshold > se->conn.max_background) +- se->conn.congestion_threshold = se->conn.max_background; +- if (!se->conn.congestion_threshold) { +- se->conn.congestion_threshold = +- se->conn.max_background * 3 / 4; +- } +- +- outarg.max_background = se->conn.max_background; +- outarg.congestion_threshold = se->conn.congestion_threshold; +- } +- if (se->conn.proto_minor >= 23) +- outarg.time_gran = se->conn.time_gran; +- +- if (se->debug) { +- fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor); +- fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); +- fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n", +- outarg.max_readahead); +- fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write); +- fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n", +- outarg.max_background); +- fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n", +- outarg.congestion_threshold); +- fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", +- outarg.time_gran); +- } +- if (arg->minor < 5) +- outargsize = FUSE_COMPAT_INIT_OUT_SIZE; +- else if (arg->minor < 23) +- outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE; +- +- send_reply_ok(req, &outarg, outargsize); ++ } ++ if (se->conn.proto_minor >= 18) { ++ se->conn.capable |= FUSE_CAP_IOCTL_DIR; ++ } ++ ++ /* ++ * Default settings for modern filesystems. ++ * ++ * Most of these capabilities were disabled by default in ++ * libfuse2 for backwards compatibility reasons. In libfuse3, ++ * we can finally enable them by default (as long as they're ++ * supported by the kernel). ++ */ ++#define LL_SET_DEFAULT(cond, cap) \ ++ if ((cond) && (se->conn.capable & (cap))) \ ++ se->conn.want |= (cap) ++ LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ); ++ LL_SET_DEFAULT(1, FUSE_CAP_PARALLEL_DIROPS); ++ LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA); ++ LL_SET_DEFAULT(1, FUSE_CAP_HANDLE_KILLPRIV); ++ LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO); ++ LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR); ++ LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC); ++ LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ); ++ LL_SET_DEFAULT(se->op.getlk && se->op.setlk, FUSE_CAP_POSIX_LOCKS); ++ LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS); ++ LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS); ++ LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir, ++ FUSE_CAP_READDIRPLUS_AUTO); ++ se->conn.time_gran = 1; ++ ++ if (bufsize < FUSE_MIN_READ_BUFFER) { ++ fuse_log(FUSE_LOG_ERR, "fuse: warning: buffer size too small: %zu\n", ++ bufsize); ++ bufsize = FUSE_MIN_READ_BUFFER; ++ } ++ se->bufsize = bufsize; ++ ++ if (se->conn.max_write > bufsize - FUSE_BUFFER_HEADER_SIZE) { ++ se->conn.max_write = bufsize - FUSE_BUFFER_HEADER_SIZE; ++ } ++ ++ se->got_init = 1; ++ if (se->op.init) { ++ se->op.init(se->userdata, &se->conn); ++ } ++ ++ if (se->conn.want & (~se->conn.capable)) { ++ fuse_log(FUSE_LOG_ERR, ++ "fuse: error: filesystem requested capabilities " ++ "0x%x that are not supported by kernel, aborting.\n", ++ se->conn.want & (~se->conn.capable)); ++ fuse_reply_err(req, EPROTO); ++ se->error = -EPROTO; ++ fuse_session_exit(se); ++ return; ++ } ++ ++ if (se->conn.max_write < bufsize - FUSE_BUFFER_HEADER_SIZE) { ++ se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE; ++ } ++ if (arg->flags & FUSE_MAX_PAGES) { ++ outarg.flags |= FUSE_MAX_PAGES; ++ outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1; ++ } ++ ++ /* ++ * Always enable big writes, this is superseded ++ * by the max_write option ++ */ ++ outarg.flags |= FUSE_BIG_WRITES; ++ ++ if (se->conn.want & FUSE_CAP_ASYNC_READ) { ++ outarg.flags |= FUSE_ASYNC_READ; ++ } ++ if (se->conn.want & FUSE_CAP_POSIX_LOCKS) { ++ outarg.flags |= FUSE_POSIX_LOCKS; ++ } ++ if (se->conn.want & FUSE_CAP_ATOMIC_O_TRUNC) { ++ outarg.flags |= FUSE_ATOMIC_O_TRUNC; ++ } ++ if (se->conn.want & FUSE_CAP_EXPORT_SUPPORT) { ++ outarg.flags |= FUSE_EXPORT_SUPPORT; ++ } ++ if (se->conn.want & FUSE_CAP_DONT_MASK) { ++ outarg.flags |= FUSE_DONT_MASK; ++ } ++ if (se->conn.want & FUSE_CAP_FLOCK_LOCKS) { ++ outarg.flags |= FUSE_FLOCK_LOCKS; ++ } ++ if (se->conn.want & FUSE_CAP_AUTO_INVAL_DATA) { ++ outarg.flags |= FUSE_AUTO_INVAL_DATA; ++ } ++ if (se->conn.want & FUSE_CAP_READDIRPLUS) { ++ outarg.flags |= FUSE_DO_READDIRPLUS; ++ } ++ if (se->conn.want & FUSE_CAP_READDIRPLUS_AUTO) { ++ outarg.flags |= FUSE_READDIRPLUS_AUTO; ++ } ++ if (se->conn.want & FUSE_CAP_ASYNC_DIO) { ++ outarg.flags |= FUSE_ASYNC_DIO; ++ } ++ if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE) { ++ outarg.flags |= FUSE_WRITEBACK_CACHE; ++ } ++ if (se->conn.want & FUSE_CAP_POSIX_ACL) { ++ outarg.flags |= FUSE_POSIX_ACL; ++ } ++ outarg.max_readahead = se->conn.max_readahead; ++ outarg.max_write = se->conn.max_write; ++ if (se->conn.proto_minor >= 13) { ++ if (se->conn.max_background >= (1 << 16)) { ++ se->conn.max_background = (1 << 16) - 1; ++ } ++ if (se->conn.congestion_threshold > se->conn.max_background) { ++ se->conn.congestion_threshold = se->conn.max_background; ++ } ++ if (!se->conn.congestion_threshold) { ++ se->conn.congestion_threshold = se->conn.max_background * 3 / 4; ++ } ++ ++ outarg.max_background = se->conn.max_background; ++ outarg.congestion_threshold = se->conn.congestion_threshold; ++ } ++ if (se->conn.proto_minor >= 23) { ++ outarg.time_gran = se->conn.time_gran; ++ } ++ ++ if (se->debug) { ++ fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, ++ outarg.minor); ++ fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); ++ fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n", ++ outarg.max_readahead); ++ fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write); ++ fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n", ++ outarg.max_background); ++ fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n", ++ outarg.congestion_threshold); ++ fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", outarg.time_gran); ++ } ++ if (arg->minor < 5) { ++ outargsize = FUSE_COMPAT_INIT_OUT_SIZE; ++ } else if (arg->minor < 23) { ++ outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE; ++ } ++ ++ send_reply_ok(req, &outarg, outargsize); + } + + static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { +- struct fuse_session *se = req->se; ++ struct fuse_session *se = req->se; + +- (void) nodeid; +- (void) inarg; ++ (void)nodeid; ++ (void)inarg; + +- se->got_destroy = 1; +- if (se->op.destroy) +- se->op.destroy(se->userdata); ++ se->got_destroy = 1; ++ if (se->op.destroy) { ++ se->op.destroy(se->userdata); ++ } + +- send_reply_ok(req, NULL, 0); ++ send_reply_ok(req, NULL, 0); + } + + static void list_del_nreq(struct fuse_notify_req *nreq) + { +- struct fuse_notify_req *prev = nreq->prev; +- struct fuse_notify_req *next = nreq->next; +- prev->next = next; +- next->prev = prev; ++ struct fuse_notify_req *prev = nreq->prev; ++ struct fuse_notify_req *next = nreq->next; ++ prev->next = next; ++ next->prev = prev; + } + + static void list_add_nreq(struct fuse_notify_req *nreq, +- struct fuse_notify_req *next) ++ struct fuse_notify_req *next) + { +- struct fuse_notify_req *prev = next->prev; +- nreq->next = next; +- nreq->prev = prev; +- prev->next = nreq; +- next->prev = nreq; ++ struct fuse_notify_req *prev = next->prev; ++ nreq->next = next; ++ nreq->prev = prev; ++ prev->next = nreq; ++ next->prev = nreq; + } + + static void list_init_nreq(struct fuse_notify_req *nreq) + { +- nreq->next = nreq; +- nreq->prev = nreq; ++ nreq->next = nreq; ++ nreq->prev = nreq; + } + + static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid, +- const void *inarg, const struct fuse_buf *buf) ++ const void *inarg, const struct fuse_buf *buf) + { +- struct fuse_session *se = req->se; +- struct fuse_notify_req *nreq; +- struct fuse_notify_req *head; ++ struct fuse_session *se = req->se; ++ struct fuse_notify_req *nreq; ++ struct fuse_notify_req *head; + +- pthread_mutex_lock(&se->lock); +- head = &se->notify_list; +- for (nreq = head->next; nreq != head; nreq = nreq->next) { +- if (nreq->unique == req->unique) { +- list_del_nreq(nreq); +- break; +- } +- } +- pthread_mutex_unlock(&se->lock); ++ pthread_mutex_lock(&se->lock); ++ head = &se->notify_list; ++ for (nreq = head->next; nreq != head; nreq = nreq->next) { ++ if (nreq->unique == req->unique) { ++ list_del_nreq(nreq); ++ break; ++ } ++ } ++ pthread_mutex_unlock(&se->lock); + +- if (nreq != head) +- nreq->reply(nreq, req, nodeid, inarg, buf); ++ if (nreq != head) { ++ nreq->reply(nreq, req, nodeid, inarg, buf); ++ } + } + + static int send_notify_iov(struct fuse_session *se, int notify_code, +- struct iovec *iov, int count) ++ struct iovec *iov, int count) + { +- struct fuse_out_header out; ++ struct fuse_out_header out; + +- if (!se->got_init) +- return -ENOTCONN; ++ if (!se->got_init) { ++ return -ENOTCONN; ++ } + +- out.unique = 0; +- out.error = notify_code; +- iov[0].iov_base = &out; +- iov[0].iov_len = sizeof(struct fuse_out_header); ++ out.unique = 0; ++ out.error = notify_code; ++ iov[0].iov_base = &out; ++ iov[0].iov_len = sizeof(struct fuse_out_header); + +- return fuse_send_msg(se, NULL, iov, count); ++ return fuse_send_msg(se, NULL, iov, count); + } + + int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph) + { +- if (ph != NULL) { +- struct fuse_notify_poll_wakeup_out outarg; +- struct iovec iov[2]; ++ if (ph != NULL) { ++ struct fuse_notify_poll_wakeup_out outarg; ++ struct iovec iov[2]; + +- outarg.kh = ph->kh; ++ outarg.kh = ph->kh; + +- iov[1].iov_base = &outarg; +- iov[1].iov_len = sizeof(outarg); ++ iov[1].iov_base = &outarg; ++ iov[1].iov_len = sizeof(outarg); + +- return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2); +- } else { +- return 0; +- } ++ return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2); ++ } else { ++ return 0; ++ } + } + + int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, +- off_t off, off_t len) ++ off_t off, off_t len) + { +- struct fuse_notify_inval_inode_out outarg; +- struct iovec iov[2]; ++ struct fuse_notify_inval_inode_out outarg; ++ struct iovec iov[2]; ++ ++ if (!se) { ++ return -EINVAL; ++ } + +- if (!se) +- return -EINVAL; ++ if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) { ++ return -ENOSYS; ++ } + +- if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) +- return -ENOSYS; +- +- outarg.ino = ino; +- outarg.off = off; +- outarg.len = len; ++ outarg.ino = ino; ++ outarg.off = off; ++ outarg.len = len; + +- iov[1].iov_base = &outarg; +- iov[1].iov_len = sizeof(outarg); ++ iov[1].iov_base = &outarg; ++ iov[1].iov_len = sizeof(outarg); + +- return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2); ++ return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2); + } + + int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, +- const char *name, size_t namelen) ++ const char *name, size_t namelen) + { +- struct fuse_notify_inval_entry_out outarg; +- struct iovec iov[3]; ++ struct fuse_notify_inval_entry_out outarg; ++ struct iovec iov[3]; ++ ++ if (!se) { ++ return -EINVAL; ++ } + +- if (!se) +- return -EINVAL; +- +- if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) +- return -ENOSYS; ++ if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) { ++ return -ENOSYS; ++ } + +- outarg.parent = parent; +- outarg.namelen = namelen; +- outarg.padding = 0; ++ outarg.parent = parent; ++ outarg.namelen = namelen; ++ outarg.padding = 0; + +- iov[1].iov_base = &outarg; +- iov[1].iov_len = sizeof(outarg); +- iov[2].iov_base = (void *)name; +- iov[2].iov_len = namelen + 1; ++ iov[1].iov_base = &outarg; ++ iov[1].iov_len = sizeof(outarg); ++ iov[2].iov_base = (void *)name; ++ iov[2].iov_len = namelen + 1; + +- return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3); ++ return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3); + } + +-int fuse_lowlevel_notify_delete(struct fuse_session *se, +- fuse_ino_t parent, fuse_ino_t child, +- const char *name, size_t namelen) ++int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, ++ fuse_ino_t child, const char *name, ++ size_t namelen) + { +- struct fuse_notify_delete_out outarg; +- struct iovec iov[3]; ++ struct fuse_notify_delete_out outarg; ++ struct iovec iov[3]; + +- if (!se) +- return -EINVAL; ++ if (!se) { ++ return -EINVAL; ++ } + +- if (se->conn.proto_major < 6 || se->conn.proto_minor < 18) +- return -ENOSYS; ++ if (se->conn.proto_major < 6 || se->conn.proto_minor < 18) { ++ return -ENOSYS; ++ } + +- outarg.parent = parent; +- outarg.child = child; +- outarg.namelen = namelen; +- outarg.padding = 0; ++ outarg.parent = parent; ++ outarg.child = child; ++ outarg.namelen = namelen; ++ outarg.padding = 0; + +- iov[1].iov_base = &outarg; +- iov[1].iov_len = sizeof(outarg); +- iov[2].iov_base = (void *)name; +- iov[2].iov_len = namelen + 1; ++ iov[1].iov_base = &outarg; ++ iov[1].iov_len = sizeof(outarg); ++ iov[2].iov_base = (void *)name; ++ iov[2].iov_len = namelen + 1; + +- return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3); ++ return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3); + } + + int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, +- off_t offset, struct fuse_bufvec *bufv, +- enum fuse_buf_copy_flags flags) ++ off_t offset, struct fuse_bufvec *bufv, ++ enum fuse_buf_copy_flags flags) + { +- struct fuse_out_header out; +- struct fuse_notify_store_out outarg; +- struct iovec iov[3]; +- size_t size = fuse_buf_size(bufv); +- int res; ++ struct fuse_out_header out; ++ struct fuse_notify_store_out outarg; ++ struct iovec iov[3]; ++ size_t size = fuse_buf_size(bufv); ++ int res; + +- if (!se) +- return -EINVAL; ++ if (!se) { ++ return -EINVAL; ++ } + +- if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) +- return -ENOSYS; ++ if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) { ++ return -ENOSYS; ++ } + +- out.unique = 0; +- out.error = FUSE_NOTIFY_STORE; ++ out.unique = 0; ++ out.error = FUSE_NOTIFY_STORE; + +- outarg.nodeid = ino; +- outarg.offset = offset; +- outarg.size = size; +- outarg.padding = 0; ++ outarg.nodeid = ino; ++ outarg.offset = offset; ++ outarg.size = size; ++ outarg.padding = 0; + +- iov[0].iov_base = &out; +- iov[0].iov_len = sizeof(out); +- iov[1].iov_base = &outarg; +- iov[1].iov_len = sizeof(outarg); ++ iov[0].iov_base = &out; ++ iov[0].iov_len = sizeof(out); ++ iov[1].iov_base = &outarg; ++ iov[1].iov_len = sizeof(outarg); + +- res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags); +- if (res > 0) +- res = -res; ++ res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags); ++ if (res > 0) { ++ res = -res; ++ } + +- return res; ++ return res; + } + + struct fuse_retrieve_req { +- struct fuse_notify_req nreq; +- void *cookie; ++ struct fuse_notify_req nreq; ++ void *cookie; + }; + +-static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq, +- fuse_req_t req, fuse_ino_t ino, +- const void *inarg, +- const struct fuse_buf *ibuf) +-{ +- struct fuse_session *se = req->se; +- struct fuse_retrieve_req *rreq = +- container_of(nreq, struct fuse_retrieve_req, nreq); +- const struct fuse_notify_retrieve_in *arg = inarg; +- struct fuse_bufvec bufv = { +- .buf[0] = *ibuf, +- .count = 1, +- }; +- +- if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) +- bufv.buf[0].mem = PARAM(arg); +- +- bufv.buf[0].size -= sizeof(struct fuse_in_header) + +- sizeof(struct fuse_notify_retrieve_in); +- +- if (bufv.buf[0].size < arg->size) { +- fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n"); +- fuse_reply_none(req); +- goto out; +- } +- bufv.buf[0].size = arg->size; +- +- if (se->op.retrieve_reply) { +- se->op.retrieve_reply(req, rreq->cookie, ino, +- arg->offset, &bufv); +- } else { +- fuse_reply_none(req); +- } ++static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq, fuse_req_t req, ++ fuse_ino_t ino, const void *inarg, ++ const struct fuse_buf *ibuf) ++{ ++ struct fuse_session *se = req->se; ++ struct fuse_retrieve_req *rreq = ++ container_of(nreq, struct fuse_retrieve_req, nreq); ++ const struct fuse_notify_retrieve_in *arg = inarg; ++ struct fuse_bufvec bufv = { ++ .buf[0] = *ibuf, ++ .count = 1, ++ }; ++ ++ if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) { ++ bufv.buf[0].mem = PARAM(arg); ++ } ++ ++ bufv.buf[0].size -= ++ sizeof(struct fuse_in_header) + sizeof(struct fuse_notify_retrieve_in); ++ ++ if (bufv.buf[0].size < arg->size) { ++ fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n"); ++ fuse_reply_none(req); ++ goto out; ++ } ++ bufv.buf[0].size = arg->size; ++ ++ if (se->op.retrieve_reply) { ++ se->op.retrieve_reply(req, rreq->cookie, ino, arg->offset, &bufv); ++ } else { ++ fuse_reply_none(req); ++ } + out: +- free(rreq); ++ free(rreq); + } + + int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, +- size_t size, off_t offset, void *cookie) ++ size_t size, off_t offset, void *cookie) + { +- struct fuse_notify_retrieve_out outarg; +- struct iovec iov[2]; +- struct fuse_retrieve_req *rreq; +- int err; ++ struct fuse_notify_retrieve_out outarg; ++ struct iovec iov[2]; ++ struct fuse_retrieve_req *rreq; ++ int err; + +- if (!se) +- return -EINVAL; ++ if (!se) { ++ return -EINVAL; ++ } + +- if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) +- return -ENOSYS; ++ if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) { ++ return -ENOSYS; ++ } + +- rreq = malloc(sizeof(*rreq)); +- if (rreq == NULL) +- return -ENOMEM; ++ rreq = malloc(sizeof(*rreq)); ++ if (rreq == NULL) { ++ return -ENOMEM; ++ } + +- pthread_mutex_lock(&se->lock); +- rreq->cookie = cookie; +- rreq->nreq.unique = se->notify_ctr++; +- rreq->nreq.reply = fuse_ll_retrieve_reply; +- list_add_nreq(&rreq->nreq, &se->notify_list); +- pthread_mutex_unlock(&se->lock); ++ pthread_mutex_lock(&se->lock); ++ rreq->cookie = cookie; ++ rreq->nreq.unique = se->notify_ctr++; ++ rreq->nreq.reply = fuse_ll_retrieve_reply; ++ list_add_nreq(&rreq->nreq, &se->notify_list); ++ pthread_mutex_unlock(&se->lock); + +- outarg.notify_unique = rreq->nreq.unique; +- outarg.nodeid = ino; +- outarg.offset = offset; +- outarg.size = size; +- outarg.padding = 0; ++ outarg.notify_unique = rreq->nreq.unique; ++ outarg.nodeid = ino; ++ outarg.offset = offset; ++ outarg.size = size; ++ outarg.padding = 0; + +- iov[1].iov_base = &outarg; +- iov[1].iov_len = sizeof(outarg); ++ iov[1].iov_base = &outarg; ++ iov[1].iov_len = sizeof(outarg); + +- err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2); +- if (err) { +- pthread_mutex_lock(&se->lock); +- list_del_nreq(&rreq->nreq); +- pthread_mutex_unlock(&se->lock); +- free(rreq); +- } ++ err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2); ++ if (err) { ++ pthread_mutex_lock(&se->lock); ++ list_del_nreq(&rreq->nreq); ++ pthread_mutex_unlock(&se->lock); ++ free(rreq); ++ } + +- return err; ++ return err; + } + + void *fuse_req_userdata(fuse_req_t req) + { +- return req->se->userdata; ++ return req->se->userdata; + } + + const struct fuse_ctx *fuse_req_ctx(fuse_req_t req) + { +- return &req->ctx; ++ return &req->ctx; + } + + void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, +- void *data) ++ void *data) + { +- pthread_mutex_lock(&req->lock); +- pthread_mutex_lock(&req->se->lock); +- req->u.ni.func = func; +- req->u.ni.data = data; +- pthread_mutex_unlock(&req->se->lock); +- if (req->interrupted && func) +- func(req, data); +- pthread_mutex_unlock(&req->lock); ++ pthread_mutex_lock(&req->lock); ++ pthread_mutex_lock(&req->se->lock); ++ req->u.ni.func = func; ++ req->u.ni.data = data; ++ pthread_mutex_unlock(&req->se->lock); ++ if (req->interrupted && func) { ++ func(req, data); ++ } ++ pthread_mutex_unlock(&req->lock); + } + + int fuse_req_interrupted(fuse_req_t req) + { +- int interrupted; ++ int interrupted; + +- pthread_mutex_lock(&req->se->lock); +- interrupted = req->interrupted; +- pthread_mutex_unlock(&req->se->lock); ++ pthread_mutex_lock(&req->se->lock); ++ interrupted = req->interrupted; ++ pthread_mutex_unlock(&req->se->lock); + +- return interrupted; ++ return interrupted; + } + + static struct { +- void (*func)(fuse_req_t, fuse_ino_t, const void *); +- const char *name; ++ void (*func)(fuse_req_t, fuse_ino_t, const void *); ++ const char *name; + } fuse_ll_ops[] = { +- [FUSE_LOOKUP] = { do_lookup, "LOOKUP" }, +- [FUSE_FORGET] = { do_forget, "FORGET" }, +- [FUSE_GETATTR] = { do_getattr, "GETATTR" }, +- [FUSE_SETATTR] = { do_setattr, "SETATTR" }, +- [FUSE_READLINK] = { do_readlink, "READLINK" }, +- [FUSE_SYMLINK] = { do_symlink, "SYMLINK" }, +- [FUSE_MKNOD] = { do_mknod, "MKNOD" }, +- [FUSE_MKDIR] = { do_mkdir, "MKDIR" }, +- [FUSE_UNLINK] = { do_unlink, "UNLINK" }, +- [FUSE_RMDIR] = { do_rmdir, "RMDIR" }, +- [FUSE_RENAME] = { do_rename, "RENAME" }, +- [FUSE_LINK] = { do_link, "LINK" }, +- [FUSE_OPEN] = { do_open, "OPEN" }, +- [FUSE_READ] = { do_read, "READ" }, +- [FUSE_WRITE] = { do_write, "WRITE" }, +- [FUSE_STATFS] = { do_statfs, "STATFS" }, +- [FUSE_RELEASE] = { do_release, "RELEASE" }, +- [FUSE_FSYNC] = { do_fsync, "FSYNC" }, +- [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" }, +- [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" }, +- [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" }, +- [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" }, +- [FUSE_FLUSH] = { do_flush, "FLUSH" }, +- [FUSE_INIT] = { do_init, "INIT" }, +- [FUSE_OPENDIR] = { do_opendir, "OPENDIR" }, +- [FUSE_READDIR] = { do_readdir, "READDIR" }, +- [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" }, +- [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" }, +- [FUSE_GETLK] = { do_getlk, "GETLK" }, +- [FUSE_SETLK] = { do_setlk, "SETLK" }, +- [FUSE_SETLKW] = { do_setlkw, "SETLKW" }, +- [FUSE_ACCESS] = { do_access, "ACCESS" }, +- [FUSE_CREATE] = { do_create, "CREATE" }, +- [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" }, +- [FUSE_BMAP] = { do_bmap, "BMAP" }, +- [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, +- [FUSE_POLL] = { do_poll, "POLL" }, +- [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" }, +- [FUSE_DESTROY] = { do_destroy, "DESTROY" }, +- [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" }, +- [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" }, +- [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"}, +- [FUSE_RENAME2] = { do_rename2, "RENAME2" }, +- [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" }, +- [FUSE_LSEEK] = { do_lseek, "LSEEK" }, ++ [FUSE_LOOKUP] = { do_lookup, "LOOKUP" }, ++ [FUSE_FORGET] = { do_forget, "FORGET" }, ++ [FUSE_GETATTR] = { do_getattr, "GETATTR" }, ++ [FUSE_SETATTR] = { do_setattr, "SETATTR" }, ++ [FUSE_READLINK] = { do_readlink, "READLINK" }, ++ [FUSE_SYMLINK] = { do_symlink, "SYMLINK" }, ++ [FUSE_MKNOD] = { do_mknod, "MKNOD" }, ++ [FUSE_MKDIR] = { do_mkdir, "MKDIR" }, ++ [FUSE_UNLINK] = { do_unlink, "UNLINK" }, ++ [FUSE_RMDIR] = { do_rmdir, "RMDIR" }, ++ [FUSE_RENAME] = { do_rename, "RENAME" }, ++ [FUSE_LINK] = { do_link, "LINK" }, ++ [FUSE_OPEN] = { do_open, "OPEN" }, ++ [FUSE_READ] = { do_read, "READ" }, ++ [FUSE_WRITE] = { do_write, "WRITE" }, ++ [FUSE_STATFS] = { do_statfs, "STATFS" }, ++ [FUSE_RELEASE] = { do_release, "RELEASE" }, ++ [FUSE_FSYNC] = { do_fsync, "FSYNC" }, ++ [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" }, ++ [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" }, ++ [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" }, ++ [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" }, ++ [FUSE_FLUSH] = { do_flush, "FLUSH" }, ++ [FUSE_INIT] = { do_init, "INIT" }, ++ [FUSE_OPENDIR] = { do_opendir, "OPENDIR" }, ++ [FUSE_READDIR] = { do_readdir, "READDIR" }, ++ [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" }, ++ [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" }, ++ [FUSE_GETLK] = { do_getlk, "GETLK" }, ++ [FUSE_SETLK] = { do_setlk, "SETLK" }, ++ [FUSE_SETLKW] = { do_setlkw, "SETLKW" }, ++ [FUSE_ACCESS] = { do_access, "ACCESS" }, ++ [FUSE_CREATE] = { do_create, "CREATE" }, ++ [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" }, ++ [FUSE_BMAP] = { do_bmap, "BMAP" }, ++ [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, ++ [FUSE_POLL] = { do_poll, "POLL" }, ++ [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" }, ++ [FUSE_DESTROY] = { do_destroy, "DESTROY" }, ++ [FUSE_NOTIFY_REPLY] = { (void *)1, "NOTIFY_REPLY" }, ++ [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" }, ++ [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS" }, ++ [FUSE_RENAME2] = { do_rename2, "RENAME2" }, ++ [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" }, ++ [FUSE_LSEEK] = { do_lseek, "LSEEK" }, + }; + + #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) + + static const char *opname(enum fuse_opcode opcode) + { +- if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name) +- return "???"; +- else +- return fuse_ll_ops[opcode].name; ++ if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name) { ++ return "???"; ++ } else { ++ return fuse_ll_ops[opcode].name; ++ } + } + + void fuse_session_process_buf(struct fuse_session *se, +- const struct fuse_buf *buf) ++ const struct fuse_buf *buf) + { +- fuse_session_process_buf_int(se, buf, NULL); ++ fuse_session_process_buf_int(se, buf, NULL); + } + + void fuse_session_process_buf_int(struct fuse_session *se, +- const struct fuse_buf *buf, struct fuse_chan *ch) +-{ +- struct fuse_in_header *in; +- const void *inarg; +- struct fuse_req *req; +- int err; +- +- in = buf->mem; +- +- if (se->debug) { +- fuse_log(FUSE_LOG_DEBUG, +- "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n", +- (unsigned long long) in->unique, +- opname((enum fuse_opcode) in->opcode), in->opcode, +- (unsigned long long) in->nodeid, buf->size, in->pid); +- } +- +- req = fuse_ll_alloc_req(se); +- if (req == NULL) { +- struct fuse_out_header out = { +- .unique = in->unique, +- .error = -ENOMEM, +- }; +- struct iovec iov = { +- .iov_base = &out, +- .iov_len = sizeof(struct fuse_out_header), +- }; +- +- fuse_send_msg(se, ch, &iov, 1); +- return; +- } +- +- req->unique = in->unique; +- req->ctx.uid = in->uid; +- req->ctx.gid = in->gid; +- req->ctx.pid = in->pid; +- req->ch = ch; +- +- err = EIO; +- if (!se->got_init) { +- enum fuse_opcode expected; +- +- expected = se->cuse_data ? CUSE_INIT : FUSE_INIT; +- if (in->opcode != expected) +- goto reply_err; +- } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT) +- goto reply_err; +- +- err = EACCES; +- /* Implement -o allow_root */ +- if (se->deny_others && in->uid != se->owner && in->uid != 0 && +- in->opcode != FUSE_INIT && in->opcode != FUSE_READ && +- in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC && +- in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR && +- in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR && +- in->opcode != FUSE_NOTIFY_REPLY && +- in->opcode != FUSE_READDIRPLUS) +- goto reply_err; +- +- err = ENOSYS; +- if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) +- goto reply_err; +- if (in->opcode != FUSE_INTERRUPT) { +- struct fuse_req *intr; +- pthread_mutex_lock(&se->lock); +- intr = check_interrupt(se, req); +- list_add_req(req, &se->list); +- pthread_mutex_unlock(&se->lock); +- if (intr) +- fuse_reply_err(intr, EAGAIN); +- } +- +- inarg = (void *) &in[1]; +- if (in->opcode == FUSE_WRITE && se->op.write_buf) +- do_write_buf(req, in->nodeid, inarg, buf); +- else if (in->opcode == FUSE_NOTIFY_REPLY) +- do_notify_reply(req, in->nodeid, inarg, buf); +- else +- fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); +- +- return; ++ const struct fuse_buf *buf, ++ struct fuse_chan *ch) ++{ ++ struct fuse_in_header *in; ++ const void *inarg; ++ struct fuse_req *req; ++ int err; ++ ++ in = buf->mem; ++ ++ if (se->debug) { ++ fuse_log(FUSE_LOG_DEBUG, ++ "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, " ++ "pid: %u\n", ++ (unsigned long long)in->unique, ++ opname((enum fuse_opcode)in->opcode), in->opcode, ++ (unsigned long long)in->nodeid, buf->size, in->pid); ++ } ++ ++ req = fuse_ll_alloc_req(se); ++ if (req == NULL) { ++ struct fuse_out_header out = { ++ .unique = in->unique, ++ .error = -ENOMEM, ++ }; ++ struct iovec iov = { ++ .iov_base = &out, ++ .iov_len = sizeof(struct fuse_out_header), ++ }; ++ ++ fuse_send_msg(se, ch, &iov, 1); ++ return; ++ } ++ ++ req->unique = in->unique; ++ req->ctx.uid = in->uid; ++ req->ctx.gid = in->gid; ++ req->ctx.pid = in->pid; ++ req->ch = ch; ++ ++ err = EIO; ++ if (!se->got_init) { ++ enum fuse_opcode expected; ++ ++ expected = se->cuse_data ? CUSE_INIT : FUSE_INIT; ++ if (in->opcode != expected) { ++ goto reply_err; ++ } ++ } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT) { ++ goto reply_err; ++ } ++ ++ err = EACCES; ++ /* Implement -o allow_root */ ++ if (se->deny_others && in->uid != se->owner && in->uid != 0 && ++ in->opcode != FUSE_INIT && in->opcode != FUSE_READ && ++ in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC && ++ in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR && ++ in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR && ++ in->opcode != FUSE_NOTIFY_REPLY && in->opcode != FUSE_READDIRPLUS) { ++ goto reply_err; ++ } ++ ++ err = ENOSYS; ++ if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) { ++ goto reply_err; ++ } ++ if (in->opcode != FUSE_INTERRUPT) { ++ struct fuse_req *intr; ++ pthread_mutex_lock(&se->lock); ++ intr = check_interrupt(se, req); ++ list_add_req(req, &se->list); ++ pthread_mutex_unlock(&se->lock); ++ if (intr) { ++ fuse_reply_err(intr, EAGAIN); ++ } ++ } ++ ++ inarg = (void *)&in[1]; ++ if (in->opcode == FUSE_WRITE && se->op.write_buf) { ++ do_write_buf(req, in->nodeid, inarg, buf); ++ } else if (in->opcode == FUSE_NOTIFY_REPLY) { ++ do_notify_reply(req, in->nodeid, inarg, buf); ++ } else { ++ fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); ++ } ++ ++ return; + + reply_err: +- fuse_reply_err(req, err); ++ fuse_reply_err(req, err); + } + +-#define LL_OPTION(n,o,v) \ +- { n, offsetof(struct fuse_session, o), v } ++#define LL_OPTION(n, o, v) \ ++ { \ ++ n, offsetof(struct fuse_session, o), v \ ++ } + + static const struct fuse_opt fuse_ll_opts[] = { +- LL_OPTION("debug", debug, 1), +- LL_OPTION("-d", debug, 1), +- LL_OPTION("--debug", debug, 1), +- LL_OPTION("allow_root", deny_others, 1), +- FUSE_OPT_END ++ LL_OPTION("debug", debug, 1), LL_OPTION("-d", debug, 1), ++ LL_OPTION("--debug", debug, 1), LL_OPTION("allow_root", deny_others, 1), ++ FUSE_OPT_END + }; + + void fuse_lowlevel_version(void) + { +- printf("using FUSE kernel interface version %i.%i\n", +- FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); ++ printf("using FUSE kernel interface version %i.%i\n", FUSE_KERNEL_VERSION, ++ FUSE_KERNEL_MINOR_VERSION); + } + + void fuse_lowlevel_help(void) + { +- /* These are not all options, but the ones that are +- potentially of interest to an end-user */ +- printf( +-" -o allow_root allow access by root\n" +-); ++ /* ++ * These are not all options, but the ones that are ++ * potentially of interest to an end-user ++ */ ++ printf(" -o allow_root allow access by root\n"); + } + + void fuse_session_destroy(struct fuse_session *se) + { +- if (se->got_init && !se->got_destroy) { +- if (se->op.destroy) +- se->op.destroy(se->userdata); +- } +- pthread_mutex_destroy(&se->lock); +- free(se->cuse_data); +- if (se->fd != -1) +- close(se->fd); +- free(se); ++ if (se->got_init && !se->got_destroy) { ++ if (se->op.destroy) { ++ se->op.destroy(se->userdata); ++ } ++ } ++ pthread_mutex_destroy(&se->lock); ++ free(se->cuse_data); ++ if (se->fd != -1) { ++ close(se->fd); ++ } ++ free(se); + } + + + struct fuse_session *fuse_session_new(struct fuse_args *args, +- const struct fuse_lowlevel_ops *op, +- size_t op_size, void *userdata) +-{ +- struct fuse_session *se; +- +- if (sizeof(struct fuse_lowlevel_ops) < op_size) { +- fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n"); +- op_size = sizeof(struct fuse_lowlevel_ops); +- } +- +- if (args->argc == 0) { +- fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n"); +- return NULL; +- } +- +- se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session)); +- if (se == NULL) { +- fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n"); +- goto out1; +- } +- se->fd = -1; +- se->conn.max_write = UINT_MAX; +- se->conn.max_readahead = UINT_MAX; +- +- /* Parse options */ +- if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1) +- goto out2; +- if(args->argc == 1 && +- args->argv[0][0] == '-') { +- fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but " +- "will be ignored\n"); +- } else if (args->argc != 1) { +- int i; +- fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `"); +- for(i = 1; i < args->argc-1; i++) +- fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]); +- fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]); +- goto out4; +- } +- +- se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() + +- FUSE_BUFFER_HEADER_SIZE; +- +- list_init_req(&se->list); +- list_init_req(&se->interrupts); +- list_init_nreq(&se->notify_list); +- se->notify_ctr = 1; +- fuse_mutex_init(&se->lock); +- +- memcpy(&se->op, op, op_size); +- se->owner = getuid(); +- se->userdata = userdata; +- +- return se; ++ const struct fuse_lowlevel_ops *op, ++ size_t op_size, void *userdata) ++{ ++ struct fuse_session *se; ++ ++ if (sizeof(struct fuse_lowlevel_ops) < op_size) { ++ fuse_log( ++ FUSE_LOG_ERR, ++ "fuse: warning: library too old, some operations may not work\n"); ++ op_size = sizeof(struct fuse_lowlevel_ops); ++ } ++ ++ if (args->argc == 0) { ++ fuse_log(FUSE_LOG_ERR, ++ "fuse: empty argv passed to fuse_session_new().\n"); ++ return NULL; ++ } ++ ++ se = (struct fuse_session *)calloc(1, sizeof(struct fuse_session)); ++ if (se == NULL) { ++ fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n"); ++ goto out1; ++ } ++ se->fd = -1; ++ se->conn.max_write = UINT_MAX; ++ se->conn.max_readahead = UINT_MAX; ++ ++ /* Parse options */ ++ if (fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1) { ++ goto out2; ++ } ++ if (args->argc == 1 && args->argv[0][0] == '-') { ++ fuse_log(FUSE_LOG_ERR, ++ "fuse: warning: argv[0] looks like an option, but " ++ "will be ignored\n"); ++ } else if (args->argc != 1) { ++ int i; ++ fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `"); ++ for (i = 1; i < args->argc - 1; i++) { ++ fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]); ++ } ++ fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]); ++ goto out4; ++ } ++ ++ se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() + FUSE_BUFFER_HEADER_SIZE; ++ ++ list_init_req(&se->list); ++ list_init_req(&se->interrupts); ++ list_init_nreq(&se->notify_list); ++ se->notify_ctr = 1; ++ fuse_mutex_init(&se->lock); ++ ++ memcpy(&se->op, op, op_size); ++ se->owner = getuid(); ++ se->userdata = userdata; ++ ++ return se; + + out4: +- fuse_opt_free_args(args); ++ fuse_opt_free_args(args); + out2: +- free(se); ++ free(se); + out1: +- return NULL; ++ return NULL; + } + + int fuse_session_mount(struct fuse_session *se, const char *mountpoint) + { +- int fd; +- +- /* +- * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos +- * would ensue. +- */ +- do { +- fd = open("/dev/null", O_RDWR); +- if (fd > 2) +- close(fd); +- } while (fd >= 0 && fd <= 2); +- +- /* +- * To allow FUSE daemons to run without privileges, the caller may open +- * /dev/fuse before launching the file system and pass on the file +- * descriptor by specifying /dev/fd/N as the mount point. Note that the +- * parent process takes care of performing the mount in this case. +- */ +- fd = fuse_mnt_parse_fuse_fd(mountpoint); +- if (fd != -1) { +- if (fcntl(fd, F_GETFD) == -1) { +- fuse_log(FUSE_LOG_ERR, +- "fuse: Invalid file descriptor /dev/fd/%u\n", +- fd); +- return -1; +- } +- se->fd = fd; +- return 0; +- } +- +- /* Open channel */ +- fd = fuse_kern_mount(mountpoint, se->mo); +- if (fd == -1) +- return -1; +- se->fd = fd; +- +- /* Save mountpoint */ +- se->mountpoint = strdup(mountpoint); +- if (se->mountpoint == NULL) +- goto error_out; +- +- return 0; ++ int fd; ++ ++ /* ++ * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos ++ * would ensue. ++ */ ++ do { ++ fd = open("/dev/null", O_RDWR); ++ if (fd > 2) { ++ close(fd); ++ } ++ } while (fd >= 0 && fd <= 2); ++ ++ /* ++ * To allow FUSE daemons to run without privileges, the caller may open ++ * /dev/fuse before launching the file system and pass on the file ++ * descriptor by specifying /dev/fd/N as the mount point. Note that the ++ * parent process takes care of performing the mount in this case. ++ */ ++ fd = fuse_mnt_parse_fuse_fd(mountpoint); ++ if (fd != -1) { ++ if (fcntl(fd, F_GETFD) == -1) { ++ fuse_log(FUSE_LOG_ERR, "fuse: Invalid file descriptor /dev/fd/%u\n", ++ fd); ++ return -1; ++ } ++ se->fd = fd; ++ return 0; ++ } ++ ++ /* Open channel */ ++ fd = fuse_kern_mount(mountpoint, se->mo); ++ if (fd == -1) { ++ return -1; ++ } ++ se->fd = fd; ++ ++ /* Save mountpoint */ ++ se->mountpoint = strdup(mountpoint); ++ if (se->mountpoint == NULL) { ++ goto error_out; ++ } ++ ++ return 0; + + error_out: +- fuse_kern_unmount(mountpoint, fd); +- return -1; ++ fuse_kern_unmount(mountpoint, fd); ++ return -1; + } + + int fuse_session_fd(struct fuse_session *se) + { +- return se->fd; ++ return se->fd; + } + + void fuse_session_unmount(struct fuse_session *se) +@@ -2384,61 +2519,66 @@ void fuse_session_unmount(struct fuse_session *se) + #ifdef linux + int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) + { +- char *buf; +- size_t bufsize = 1024; +- char path[128]; +- int ret; +- int fd; +- unsigned long pid = req->ctx.pid; +- char *s; ++ char *buf; ++ size_t bufsize = 1024; ++ char path[128]; ++ int ret; ++ int fd; ++ unsigned long pid = req->ctx.pid; ++ char *s; + +- sprintf(path, "/proc/%lu/task/%lu/status", pid, pid); ++ sprintf(path, "/proc/%lu/task/%lu/status", pid, pid); + + retry: +- buf = malloc(bufsize); +- if (buf == NULL) +- return -ENOMEM; +- +- ret = -EIO; +- fd = open(path, O_RDONLY); +- if (fd == -1) +- goto out_free; +- +- ret = read(fd, buf, bufsize); +- close(fd); +- if (ret < 0) { +- ret = -EIO; +- goto out_free; +- } +- +- if ((size_t)ret == bufsize) { +- free(buf); +- bufsize *= 4; +- goto retry; +- } +- +- ret = -EIO; +- s = strstr(buf, "\nGroups:"); +- if (s == NULL) +- goto out_free; +- +- s += 8; +- ret = 0; +- while (1) { +- char *end; +- unsigned long val = strtoul(s, &end, 0); +- if (end == s) +- break; +- +- s = end; +- if (ret < size) +- list[ret] = val; +- ret++; +- } ++ buf = malloc(bufsize); ++ if (buf == NULL) { ++ return -ENOMEM; ++ } ++ ++ ret = -EIO; ++ fd = open(path, O_RDONLY); ++ if (fd == -1) { ++ goto out_free; ++ } ++ ++ ret = read(fd, buf, bufsize); ++ close(fd); ++ if (ret < 0) { ++ ret = -EIO; ++ goto out_free; ++ } ++ ++ if ((size_t)ret == bufsize) { ++ free(buf); ++ bufsize *= 4; ++ goto retry; ++ } ++ ++ ret = -EIO; ++ s = strstr(buf, "\nGroups:"); ++ if (s == NULL) { ++ goto out_free; ++ } ++ ++ s += 8; ++ ret = 0; ++ while (1) { ++ char *end; ++ unsigned long val = strtoul(s, &end, 0); ++ if (end == s) { ++ break; ++ } ++ ++ s = end; ++ if (ret < size) { ++ list[ret] = val; ++ } ++ ret++; ++ } + + out_free: +- free(buf); +- return ret; ++ free(buf); ++ return ret; + } + #else /* linux */ + /* +@@ -2446,23 +2586,25 @@ out_free: + */ + int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) + { +- (void) req; (void) size; (void) list; +- return -ENOSYS; ++ (void)req; ++ (void)size; ++ (void)list; ++ return -ENOSYS; + } + #endif + + void fuse_session_exit(struct fuse_session *se) + { +- se->exited = 1; ++ se->exited = 1; + } + + void fuse_session_reset(struct fuse_session *se) + { +- se->exited = 0; +- se->error = 0; ++ se->exited = 0; ++ se->error = 0; + } + + int fuse_session_exited(struct fuse_session *se) + { +- return se->exited; ++ return se->exited; + } +diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h +index 6b1adfcfd1..adb9054bb1 100644 +--- a/tools/virtiofsd/fuse_lowlevel.h ++++ b/tools/virtiofsd/fuse_lowlevel.h +@@ -1,15 +1,16 @@ + /* +- FUSE: Filesystem in Userspace +- Copyright (C) 2001-2007 Miklos Szeredi +- +- This program can be distributed under the terms of the GNU LGPLv2. +- See the file COPYING.LIB. +-*/ ++ * FUSE: Filesystem in Userspace ++ * Copyright (C) 2001-2007 Miklos Szeredi ++ * ++ * This program can be distributed under the terms of the GNU LGPLv2. ++ * See the file COPYING.LIB. ++ */ + + #ifndef FUSE_LOWLEVEL_H_ + #define FUSE_LOWLEVEL_H_ + +-/** @file ++/** ++ * @file + * + * Low level API + * +@@ -24,16 +25,16 @@ + + #include "fuse_common.h" + +-#include + #include +-#include + #include + #include ++#include + #include ++#include + +-/* ----------------------------------------------------------- * +- * Miscellaneous definitions * +- * ----------------------------------------------------------- */ ++/* ++ * Miscellaneous definitions ++ */ + + /** The node ID of the root inode */ + #define FUSE_ROOT_ID 1 +@@ -53,47 +54,54 @@ struct fuse_session; + + /** Directory entry parameters supplied to fuse_reply_entry() */ + struct fuse_entry_param { +- /** Unique inode number +- * +- * In lookup, zero means negative entry (from version 2.5) +- * Returning ENOENT also means negative entry, but by setting zero +- * ino the kernel may cache negative entries for entry_timeout +- * seconds. +- */ +- fuse_ino_t ino; +- +- /** Generation number for this entry. +- * +- * If the file system will be exported over NFS, the +- * ino/generation pairs need to be unique over the file +- * system's lifetime (rather than just the mount time). So if +- * the file system reuses an inode after it has been deleted, +- * it must assign a new, previously unused generation number +- * to the inode at the same time. +- * +- */ +- uint64_t generation; +- +- /** Inode attributes. +- * +- * Even if attr_timeout == 0, attr must be correct. For example, +- * for open(), FUSE uses attr.st_size from lookup() to determine +- * how many bytes to request. If this value is not correct, +- * incorrect data will be returned. +- */ +- struct stat attr; +- +- /** Validity timeout (in seconds) for inode attributes. If +- attributes only change as a result of requests that come +- through the kernel, this should be set to a very large +- value. */ +- double attr_timeout; +- +- /** Validity timeout (in seconds) for the name. If directory +- entries are changed/deleted only as a result of requests +- that come through the kernel, this should be set to a very +- large value. */ +- double entry_timeout; ++ /** ++ * Unique inode number ++ * ++ * In lookup, zero means negative entry (from version 2.5) ++ * Returning ENOENT also means negative entry, but by setting zero ++ * ino the kernel may cache negative entries for entry_timeout ++ * seconds. ++ */ ++ fuse_ino_t ino; ++ ++ /** ++ * Generation number for this entry. ++ * ++ * If the file system will be exported over NFS, the ++ * ino/generation pairs need to be unique over the file ++ * system's lifetime (rather than just the mount time). So if ++ * the file system reuses an inode after it has been deleted, ++ * it must assign a new, previously unused generation number ++ * to the inode at the same time. ++ * ++ */ ++ uint64_t generation; ++ ++ /** ++ * Inode attributes. ++ * ++ * Even if attr_timeout == 0, attr must be correct. For example, ++ * for open(), FUSE uses attr.st_size from lookup() to determine ++ * how many bytes to request. If this value is not correct, ++ * incorrect data will be returned. ++ */ ++ struct stat attr; ++ ++ /** ++ * Validity timeout (in seconds) for inode attributes. If ++ * attributes only change as a result of requests that come ++ * through the kernel, this should be set to a very large ++ * value. ++ */ ++ double attr_timeout; ++ ++ /** ++ * Validity timeout (in seconds) for the name. If directory ++ * entries are changed/deleted only as a result of requests ++ * that come through the kernel, this should be set to a very ++ * large value. ++ */ ++ double entry_timeout; + }; + + /** +@@ -105,38 +113,38 @@ struct fuse_entry_param { + * there is no valid uid/pid/gid that could be reported. + */ + struct fuse_ctx { +- /** User ID of the calling process */ +- uid_t uid; ++ /** User ID of the calling process */ ++ uid_t uid; + +- /** Group ID of the calling process */ +- gid_t gid; ++ /** Group ID of the calling process */ ++ gid_t gid; + +- /** Thread ID of the calling process */ +- pid_t pid; ++ /** Thread ID of the calling process */ ++ pid_t pid; + +- /** Umask of the calling process */ +- mode_t umask; ++ /** Umask of the calling process */ ++ mode_t umask; + }; + + struct fuse_forget_data { +- fuse_ino_t ino; +- uint64_t nlookup; ++ fuse_ino_t ino; ++ uint64_t nlookup; + }; + + /* 'to_set' flags in setattr */ +-#define FUSE_SET_ATTR_MODE (1 << 0) +-#define FUSE_SET_ATTR_UID (1 << 1) +-#define FUSE_SET_ATTR_GID (1 << 2) +-#define FUSE_SET_ATTR_SIZE (1 << 3) +-#define FUSE_SET_ATTR_ATIME (1 << 4) +-#define FUSE_SET_ATTR_MTIME (1 << 5) +-#define FUSE_SET_ATTR_ATIME_NOW (1 << 7) +-#define FUSE_SET_ATTR_MTIME_NOW (1 << 8) +-#define FUSE_SET_ATTR_CTIME (1 << 10) +- +-/* ----------------------------------------------------------- * +- * Request methods and replies * +- * ----------------------------------------------------------- */ ++#define FUSE_SET_ATTR_MODE (1 << 0) ++#define FUSE_SET_ATTR_UID (1 << 1) ++#define FUSE_SET_ATTR_GID (1 << 2) ++#define FUSE_SET_ATTR_SIZE (1 << 3) ++#define FUSE_SET_ATTR_ATIME (1 << 4) ++#define FUSE_SET_ATTR_MTIME (1 << 5) ++#define FUSE_SET_ATTR_ATIME_NOW (1 << 7) ++#define FUSE_SET_ATTR_MTIME_NOW (1 << 8) ++#define FUSE_SET_ATTR_CTIME (1 << 10) ++ ++/* ++ * Request methods and replies ++ */ + + /** + * Low level filesystem operations +@@ -166,1075 +174,1069 @@ struct fuse_forget_data { + * this file will not be called. + */ + struct fuse_lowlevel_ops { +- /** +- * Initialize filesystem +- * +- * This function is called when libfuse establishes +- * communication with the FUSE kernel module. The file system +- * should use this module to inspect and/or modify the +- * connection parameters provided in the `conn` structure. +- * +- * Note that some parameters may be overwritten by options +- * passed to fuse_session_new() which take precedence over the +- * values set in this handler. +- * +- * There's no reply to this function +- * +- * @param userdata the user data passed to fuse_session_new() +- */ +- void (*init) (void *userdata, struct fuse_conn_info *conn); +- +- /** +- * Clean up filesystem. +- * +- * Called on filesystem exit. When this method is called, the +- * connection to the kernel may be gone already, so that eg. calls +- * to fuse_lowlevel_notify_* will fail. +- * +- * There's no reply to this function +- * +- * @param userdata the user data passed to fuse_session_new() +- */ +- void (*destroy) (void *userdata); +- +- /** +- * Look up a directory entry by name and get its attributes. +- * +- * Valid replies: +- * fuse_reply_entry +- * fuse_reply_err +- * +- * @param req request handle +- * @param parent inode number of the parent directory +- * @param name the name to look up +- */ +- void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name); +- +- /** +- * Forget about an inode +- * +- * This function is called when the kernel removes an inode +- * from its internal caches. +- * +- * The inode's lookup count increases by one for every call to +- * fuse_reply_entry and fuse_reply_create. The nlookup parameter +- * indicates by how much the lookup count should be decreased. +- * +- * Inodes with a non-zero lookup count may receive request from +- * the kernel even after calls to unlink, rmdir or (when +- * overwriting an existing file) rename. Filesystems must handle +- * such requests properly and it is recommended to defer removal +- * of the inode until the lookup count reaches zero. Calls to +- * unlink, rmdir or rename will be followed closely by forget +- * unless the file or directory is open, in which case the +- * kernel issues forget only after the release or releasedir +- * calls. +- * +- * Note that if a file system will be exported over NFS the +- * inodes lifetime must extend even beyond forget. See the +- * generation field in struct fuse_entry_param above. +- * +- * On unmount the lookup count for all inodes implicitly drops +- * to zero. It is not guaranteed that the file system will +- * receive corresponding forget messages for the affected +- * inodes. +- * +- * Valid replies: +- * fuse_reply_none +- * +- * @param req request handle +- * @param ino the inode number +- * @param nlookup the number of lookups to forget +- */ +- void (*forget) (fuse_req_t req, fuse_ino_t ino, uint64_t nlookup); +- +- /** +- * Get file attributes. +- * +- * If writeback caching is enabled, the kernel may have a +- * better idea of a file's length than the FUSE file system +- * (eg if there has been a write that extended the file size, +- * but that has not yet been passed to the filesystem.n +- * +- * In this case, the st_size value provided by the file system +- * will be ignored. +- * +- * Valid replies: +- * fuse_reply_attr +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param fi for future use, currently always NULL +- */ +- void (*getattr) (fuse_req_t req, fuse_ino_t ino, +- struct fuse_file_info *fi); +- +- /** +- * Set file attributes +- * +- * In the 'attr' argument only members indicated by the 'to_set' +- * bitmask contain valid values. Other members contain undefined +- * values. +- * +- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is +- * expected to reset the setuid and setgid bits if the file +- * size or owner is being changed. +- * +- * If the setattr was invoked from the ftruncate() system call +- * under Linux kernel versions 2.6.15 or later, the fi->fh will +- * contain the value set by the open method or will be undefined +- * if the open method didn't set any value. Otherwise (not +- * ftruncate call, or kernel version earlier than 2.6.15) the fi +- * parameter will be NULL. +- * +- * Valid replies: +- * fuse_reply_attr +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param attr the attributes +- * @param to_set bit mask of attributes which should be set +- * @param fi file information, or NULL +- */ +- void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr, +- int to_set, struct fuse_file_info *fi); +- +- /** +- * Read symbolic link +- * +- * Valid replies: +- * fuse_reply_readlink +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- */ +- void (*readlink) (fuse_req_t req, fuse_ino_t ino); +- +- /** +- * Create file node +- * +- * Create a regular file, character device, block device, fifo or +- * socket node. +- * +- * Valid replies: +- * fuse_reply_entry +- * fuse_reply_err +- * +- * @param req request handle +- * @param parent inode number of the parent directory +- * @param name to create +- * @param mode file type and mode with which to create the new file +- * @param rdev the device number (only valid if created file is a device) +- */ +- void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name, +- mode_t mode, dev_t rdev); +- +- /** +- * Create a directory +- * +- * Valid replies: +- * fuse_reply_entry +- * fuse_reply_err +- * +- * @param req request handle +- * @param parent inode number of the parent directory +- * @param name to create +- * @param mode with which to create the new file +- */ +- void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name, +- mode_t mode); +- +- /** +- * Remove a file +- * +- * If the file's inode's lookup count is non-zero, the file +- * system is expected to postpone any removal of the inode +- * until the lookup count reaches zero (see description of the +- * forget function). +- * +- * Valid replies: +- * fuse_reply_err +- * +- * @param req request handle +- * @param parent inode number of the parent directory +- * @param name to remove +- */ +- void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name); +- +- /** +- * Remove a directory +- * +- * If the directory's inode's lookup count is non-zero, the +- * file system is expected to postpone any removal of the +- * inode until the lookup count reaches zero (see description +- * of the forget function). +- * +- * Valid replies: +- * fuse_reply_err +- * +- * @param req request handle +- * @param parent inode number of the parent directory +- * @param name to remove +- */ +- void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name); +- +- /** +- * Create a symbolic link +- * +- * Valid replies: +- * fuse_reply_entry +- * fuse_reply_err +- * +- * @param req request handle +- * @param link the contents of the symbolic link +- * @param parent inode number of the parent directory +- * @param name to create +- */ +- void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent, +- const char *name); +- +- /** Rename a file +- * +- * If the target exists it should be atomically replaced. If +- * the target's inode's lookup count is non-zero, the file +- * system is expected to postpone any removal of the inode +- * until the lookup count reaches zero (see description of the +- * forget function). +- * +- * If this request is answered with an error code of ENOSYS, this is +- * treated as a permanent failure with error code EINVAL, i.e. all +- * future bmap requests will fail with EINVAL without being +- * send to the filesystem process. +- * +- * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If +- * RENAME_NOREPLACE is specified, the filesystem must not +- * overwrite *newname* if it exists and return an error +- * instead. If `RENAME_EXCHANGE` is specified, the filesystem +- * must atomically exchange the two files, i.e. both must +- * exist and neither may be deleted. +- * +- * Valid replies: +- * fuse_reply_err +- * +- * @param req request handle +- * @param parent inode number of the old parent directory +- * @param name old name +- * @param newparent inode number of the new parent directory +- * @param newname new name +- */ +- void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name, +- fuse_ino_t newparent, const char *newname, +- unsigned int flags); +- +- /** +- * Create a hard link +- * +- * Valid replies: +- * fuse_reply_entry +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the old inode number +- * @param newparent inode number of the new parent directory +- * @param newname new name to create +- */ +- void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, +- const char *newname); +- +- /** +- * Open a file +- * +- * Open flags are available in fi->flags. The following rules +- * apply. +- * +- * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be +- * filtered out / handled by the kernel. +- * +- * - Access modes (O_RDONLY, O_WRONLY, O_RDWR) should be used +- * by the filesystem to check if the operation is +- * permitted. If the ``-o default_permissions`` mount +- * option is given, this check is already done by the +- * kernel before calling open() and may thus be omitted by +- * the filesystem. +- * +- * - When writeback caching is enabled, the kernel may send +- * read requests even for files opened with O_WRONLY. The +- * filesystem should be prepared to handle this. +- * +- * - When writeback caching is disabled, the filesystem is +- * expected to properly handle the O_APPEND flag and ensure +- * that each write is appending to the end of the file. +- * +- * - When writeback caching is enabled, the kernel will +- * handle O_APPEND. However, unless all changes to the file +- * come through the kernel this will not work reliably. The +- * filesystem should thus either ignore the O_APPEND flag +- * (and let the kernel handle it), or return an error +- * (indicating that reliably O_APPEND is not available). +- * +- * Filesystem may store an arbitrary file handle (pointer, +- * index, etc) in fi->fh, and use this in other all other file +- * operations (read, write, flush, release, fsync). +- * +- * Filesystem may also implement stateless file I/O and not store +- * anything in fi->fh. +- * +- * There are also some flags (direct_io, keep_cache) which the +- * filesystem may set in fi, to change the way the file is opened. +- * See fuse_file_info structure in for more details. +- * +- * If this request is answered with an error code of ENOSYS +- * and FUSE_CAP_NO_OPEN_SUPPORT is set in +- * `fuse_conn_info.capable`, this is treated as success and +- * future calls to open and release will also succeed without being +- * sent to the filesystem process. +- * +- * Valid replies: +- * fuse_reply_open +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param fi file information +- */ +- void (*open) (fuse_req_t req, fuse_ino_t ino, +- struct fuse_file_info *fi); +- +- /** +- * Read data +- * +- * Read should send exactly the number of bytes requested except +- * on EOF or error, otherwise the rest of the data will be +- * substituted with zeroes. An exception to this is when the file +- * has been opened in 'direct_io' mode, in which case the return +- * value of the read system call will reflect the return value of +- * this operation. +- * +- * fi->fh will contain the value set by the open method, or will +- * be undefined if the open method didn't set any value. +- * +- * Valid replies: +- * fuse_reply_buf +- * fuse_reply_iov +- * fuse_reply_data +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param size number of bytes to read +- * @param off offset to read from +- * @param fi file information +- */ +- void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, +- struct fuse_file_info *fi); +- +- /** +- * Write data +- * +- * Write should return exactly the number of bytes requested +- * except on error. An exception to this is when the file has +- * been opened in 'direct_io' mode, in which case the return value +- * of the write system call will reflect the return value of this +- * operation. +- * +- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is +- * expected to reset the setuid and setgid bits. +- * +- * fi->fh will contain the value set by the open method, or will +- * be undefined if the open method didn't set any value. +- * +- * Valid replies: +- * fuse_reply_write +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param buf data to write +- * @param size number of bytes to write +- * @param off offset to write to +- * @param fi file information +- */ +- void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf, +- size_t size, off_t off, struct fuse_file_info *fi); +- +- /** +- * Flush method +- * +- * This is called on each close() of the opened file. +- * +- * Since file descriptors can be duplicated (dup, dup2, fork), for +- * one open call there may be many flush calls. +- * +- * Filesystems shouldn't assume that flush will always be called +- * after some writes, or that if will be called at all. +- * +- * fi->fh will contain the value set by the open method, or will +- * be undefined if the open method didn't set any value. +- * +- * NOTE: the name of the method is misleading, since (unlike +- * fsync) the filesystem is not forced to flush pending writes. +- * One reason to flush data is if the filesystem wants to return +- * write errors during close. However, such use is non-portable +- * because POSIX does not require [close] to wait for delayed I/O to +- * complete. +- * +- * If the filesystem supports file locking operations (setlk, +- * getlk) it should remove all locks belonging to 'fi->owner'. +- * +- * If this request is answered with an error code of ENOSYS, +- * this is treated as success and future calls to flush() will +- * succeed automatically without being send to the filesystem +- * process. +- * +- * Valid replies: +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param fi file information +- * +- * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html +- */ +- void (*flush) (fuse_req_t req, fuse_ino_t ino, +- struct fuse_file_info *fi); +- +- /** +- * Release an open file +- * +- * Release is called when there are no more references to an open +- * file: all file descriptors are closed and all memory mappings +- * are unmapped. +- * +- * For every open call there will be exactly one release call (unless +- * the filesystem is force-unmounted). +- * +- * The filesystem may reply with an error, but error values are +- * not returned to close() or munmap() which triggered the +- * release. +- * +- * fi->fh will contain the value set by the open method, or will +- * be undefined if the open method didn't set any value. +- * fi->flags will contain the same flags as for open. +- * +- * Valid replies: +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param fi file information +- */ +- void (*release) (fuse_req_t req, fuse_ino_t ino, +- struct fuse_file_info *fi); +- +- /** +- * Synchronize file contents +- * +- * If the datasync parameter is non-zero, then only the user data +- * should be flushed, not the meta data. +- * +- * If this request is answered with an error code of ENOSYS, +- * this is treated as success and future calls to fsync() will +- * succeed automatically without being send to the filesystem +- * process. +- * +- * Valid replies: +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param datasync flag indicating if only data should be flushed +- * @param fi file information +- */ +- void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync, +- struct fuse_file_info *fi); +- +- /** +- * Open a directory +- * +- * Filesystem may store an arbitrary file handle (pointer, index, +- * etc) in fi->fh, and use this in other all other directory +- * stream operations (readdir, releasedir, fsyncdir). +- * +- * If this request is answered with an error code of ENOSYS and +- * FUSE_CAP_NO_OPENDIR_SUPPORT is set in `fuse_conn_info.capable`, +- * this is treated as success and future calls to opendir and +- * releasedir will also succeed without being sent to the filesystem +- * process. In addition, the kernel will cache readdir results +- * as if opendir returned FOPEN_KEEP_CACHE | FOPEN_CACHE_DIR. +- * +- * Valid replies: +- * fuse_reply_open +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param fi file information +- */ +- void (*opendir) (fuse_req_t req, fuse_ino_t ino, +- struct fuse_file_info *fi); +- +- /** +- * Read directory +- * +- * Send a buffer filled using fuse_add_direntry(), with size not +- * exceeding the requested size. Send an empty buffer on end of +- * stream. +- * +- * fi->fh will contain the value set by the opendir method, or +- * will be undefined if the opendir method didn't set any value. +- * +- * Returning a directory entry from readdir() does not affect +- * its lookup count. +- * +- * If off_t is non-zero, then it will correspond to one of the off_t +- * values that was previously returned by readdir() for the same +- * directory handle. In this case, readdir() should skip over entries +- * coming before the position defined by the off_t value. If entries +- * are added or removed while the directory handle is open, they filesystem +- * may still include the entries that have been removed, and may not +- * report the entries that have been created. However, addition or +- * removal of entries must never cause readdir() to skip over unrelated +- * entries or to report them more than once. This means +- * that off_t can not be a simple index that enumerates the entries +- * that have been returned but must contain sufficient information to +- * uniquely determine the next directory entry to return even when the +- * set of entries is changing. +- * +- * The function does not have to report the '.' and '..' +- * entries, but is allowed to do so. Note that, if readdir does +- * not return '.' or '..', they will not be implicitly returned, +- * and this behavior is observable by the caller. +- * +- * Valid replies: +- * fuse_reply_buf +- * fuse_reply_data +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param size maximum number of bytes to send +- * @param off offset to continue reading the directory stream +- * @param fi file information +- */ +- void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, +- struct fuse_file_info *fi); +- +- /** +- * Release an open directory +- * +- * For every opendir call there will be exactly one releasedir +- * call (unless the filesystem is force-unmounted). +- * +- * fi->fh will contain the value set by the opendir method, or +- * will be undefined if the opendir method didn't set any value. +- * +- * Valid replies: +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param fi file information +- */ +- void (*releasedir) (fuse_req_t req, fuse_ino_t ino, +- struct fuse_file_info *fi); +- +- /** +- * Synchronize directory contents +- * +- * If the datasync parameter is non-zero, then only the directory +- * contents should be flushed, not the meta data. +- * +- * fi->fh will contain the value set by the opendir method, or +- * will be undefined if the opendir method didn't set any value. +- * +- * If this request is answered with an error code of ENOSYS, +- * this is treated as success and future calls to fsyncdir() will +- * succeed automatically without being send to the filesystem +- * process. +- * +- * Valid replies: +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param datasync flag indicating if only data should be flushed +- * @param fi file information +- */ +- void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync, +- struct fuse_file_info *fi); +- +- /** +- * Get file system statistics +- * +- * Valid replies: +- * fuse_reply_statfs +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number, zero means "undefined" +- */ +- void (*statfs) (fuse_req_t req, fuse_ino_t ino); +- +- /** +- * Set an extended attribute +- * +- * If this request is answered with an error code of ENOSYS, this is +- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all +- * future setxattr() requests will fail with EOPNOTSUPP without being +- * send to the filesystem process. +- * +- * Valid replies: +- * fuse_reply_err +- */ +- void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, +- const char *value, size_t size, int flags); +- +- /** +- * Get an extended attribute +- * +- * If size is zero, the size of the value should be sent with +- * fuse_reply_xattr. +- * +- * If the size is non-zero, and the value fits in the buffer, the +- * value should be sent with fuse_reply_buf. +- * +- * If the size is too small for the value, the ERANGE error should +- * be sent. +- * +- * If this request is answered with an error code of ENOSYS, this is +- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all +- * future getxattr() requests will fail with EOPNOTSUPP without being +- * send to the filesystem process. +- * +- * Valid replies: +- * fuse_reply_buf +- * fuse_reply_data +- * fuse_reply_xattr +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param name of the extended attribute +- * @param size maximum size of the value to send +- */ +- void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, +- size_t size); +- +- /** +- * List extended attribute names +- * +- * If size is zero, the total size of the attribute list should be +- * sent with fuse_reply_xattr. +- * +- * If the size is non-zero, and the null character separated +- * attribute list fits in the buffer, the list should be sent with +- * fuse_reply_buf. +- * +- * If the size is too small for the list, the ERANGE error should +- * be sent. +- * +- * If this request is answered with an error code of ENOSYS, this is +- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all +- * future listxattr() requests will fail with EOPNOTSUPP without being +- * send to the filesystem process. +- * +- * Valid replies: +- * fuse_reply_buf +- * fuse_reply_data +- * fuse_reply_xattr +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param size maximum size of the list to send +- */ +- void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size); +- +- /** +- * Remove an extended attribute +- * +- * If this request is answered with an error code of ENOSYS, this is +- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all +- * future removexattr() requests will fail with EOPNOTSUPP without being +- * send to the filesystem process. +- * +- * Valid replies: +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param name of the extended attribute +- */ +- void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name); +- +- /** +- * Check file access permissions +- * +- * This will be called for the access() and chdir() system +- * calls. If the 'default_permissions' mount option is given, +- * this method is not called. +- * +- * This method is not called under Linux kernel versions 2.4.x +- * +- * If this request is answered with an error code of ENOSYS, this is +- * treated as a permanent success, i.e. this and all future access() +- * requests will succeed without being send to the filesystem process. +- * +- * Valid replies: +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param mask requested access mode +- */ +- void (*access) (fuse_req_t req, fuse_ino_t ino, int mask); +- +- /** +- * Create and open a file +- * +- * If the file does not exist, first create it with the specified +- * mode, and then open it. +- * +- * See the description of the open handler for more +- * information. +- * +- * If this method is not implemented or under Linux kernel +- * versions earlier than 2.6.15, the mknod() and open() methods +- * will be called instead. +- * +- * If this request is answered with an error code of ENOSYS, the handler +- * is treated as not implemented (i.e., for this and future requests the +- * mknod() and open() handlers will be called instead). +- * +- * Valid replies: +- * fuse_reply_create +- * fuse_reply_err +- * +- * @param req request handle +- * @param parent inode number of the parent directory +- * @param name to create +- * @param mode file type and mode with which to create the new file +- * @param fi file information +- */ +- void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name, +- mode_t mode, struct fuse_file_info *fi); +- +- /** +- * Test for a POSIX file lock +- * +- * Valid replies: +- * fuse_reply_lock +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param fi file information +- * @param lock the region/type to test +- */ +- void (*getlk) (fuse_req_t req, fuse_ino_t ino, +- struct fuse_file_info *fi, struct flock *lock); +- +- /** +- * Acquire, modify or release a POSIX file lock +- * +- * For POSIX threads (NPTL) there's a 1-1 relation between pid and +- * owner, but otherwise this is not always the case. For checking +- * lock ownership, 'fi->owner' must be used. The l_pid field in +- * 'struct flock' should only be used to fill in this field in +- * getlk(). +- * +- * Note: if the locking methods are not implemented, the kernel +- * will still allow file locking to work locally. Hence these are +- * only interesting for network filesystems and similar. +- * +- * Valid replies: +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param fi file information +- * @param lock the region/type to set +- * @param sleep locking operation may sleep +- */ +- void (*setlk) (fuse_req_t req, fuse_ino_t ino, +- struct fuse_file_info *fi, +- struct flock *lock, int sleep); +- +- /** +- * Map block index within file to block index within device +- * +- * Note: This makes sense only for block device backed filesystems +- * mounted with the 'blkdev' option +- * +- * If this request is answered with an error code of ENOSYS, this is +- * treated as a permanent failure, i.e. all future bmap() requests will +- * fail with the same error code without being send to the filesystem +- * process. +- * +- * Valid replies: +- * fuse_reply_bmap +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param blocksize unit of block index +- * @param idx block index within file +- */ +- void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize, +- uint64_t idx); +- +- /** +- * Ioctl +- * +- * Note: For unrestricted ioctls (not allowed for FUSE +- * servers), data in and out areas can be discovered by giving +- * iovs and setting FUSE_IOCTL_RETRY in *flags*. For +- * restricted ioctls, kernel prepares in/out data area +- * according to the information encoded in cmd. +- * +- * Valid replies: +- * fuse_reply_ioctl_retry +- * fuse_reply_ioctl +- * fuse_reply_ioctl_iov +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param cmd ioctl command +- * @param arg ioctl argument +- * @param fi file information +- * @param flags for FUSE_IOCTL_* flags +- * @param in_buf data fetched from the caller +- * @param in_bufsz number of fetched bytes +- * @param out_bufsz maximum size of output data +- * +- * Note : the unsigned long request submitted by the application +- * is truncated to 32 bits. +- */ +- void (*ioctl) (fuse_req_t req, fuse_ino_t ino, unsigned int cmd, +- void *arg, struct fuse_file_info *fi, unsigned flags, +- const void *in_buf, size_t in_bufsz, size_t out_bufsz); +- +- /** +- * Poll for IO readiness +- * +- * Note: If ph is non-NULL, the client should notify +- * when IO readiness events occur by calling +- * fuse_lowlevel_notify_poll() with the specified ph. +- * +- * Regardless of the number of times poll with a non-NULL ph +- * is received, single notification is enough to clear all. +- * Notifying more times incurs overhead but doesn't harm +- * correctness. +- * +- * The callee is responsible for destroying ph with +- * fuse_pollhandle_destroy() when no longer in use. +- * +- * If this request is answered with an error code of ENOSYS, this is +- * treated as success (with a kernel-defined default poll-mask) and +- * future calls to pull() will succeed the same way without being send +- * to the filesystem process. +- * +- * Valid replies: +- * fuse_reply_poll +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param fi file information +- * @param ph poll handle to be used for notification +- */ +- void (*poll) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, +- struct fuse_pollhandle *ph); +- +- /** +- * Write data made available in a buffer +- * +- * This is a more generic version of the ->write() method. If +- * FUSE_CAP_SPLICE_READ is set in fuse_conn_info.want and the +- * kernel supports splicing from the fuse device, then the +- * data will be made available in pipe for supporting zero +- * copy data transfer. +- * +- * buf->count is guaranteed to be one (and thus buf->idx is +- * always zero). The write_buf handler must ensure that +- * bufv->off is correctly updated (reflecting the number of +- * bytes read from bufv->buf[0]). +- * +- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is +- * expected to reset the setuid and setgid bits. +- * +- * Valid replies: +- * fuse_reply_write +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param bufv buffer containing the data +- * @param off offset to write to +- * @param fi file information +- */ +- void (*write_buf) (fuse_req_t req, fuse_ino_t ino, +- struct fuse_bufvec *bufv, off_t off, +- struct fuse_file_info *fi); +- +- /** +- * Callback function for the retrieve request +- * +- * Valid replies: +- * fuse_reply_none +- * +- * @param req request handle +- * @param cookie user data supplied to fuse_lowlevel_notify_retrieve() +- * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve() +- * @param offset the offset supplied to fuse_lowlevel_notify_retrieve() +- * @param bufv the buffer containing the returned data +- */ +- void (*retrieve_reply) (fuse_req_t req, void *cookie, fuse_ino_t ino, +- off_t offset, struct fuse_bufvec *bufv); +- +- /** +- * Forget about multiple inodes +- * +- * See description of the forget function for more +- * information. +- * +- * Valid replies: +- * fuse_reply_none +- * +- * @param req request handle +- */ +- void (*forget_multi) (fuse_req_t req, size_t count, +- struct fuse_forget_data *forgets); +- +- /** +- * Acquire, modify or release a BSD file lock +- * +- * Note: if the locking methods are not implemented, the kernel +- * will still allow file locking to work locally. Hence these are +- * only interesting for network filesystems and similar. +- * +- * Valid replies: +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param fi file information +- * @param op the locking operation, see flock(2) +- */ +- void (*flock) (fuse_req_t req, fuse_ino_t ino, +- struct fuse_file_info *fi, int op); +- +- /** +- * Allocate requested space. If this function returns success then +- * subsequent writes to the specified range shall not fail due to the lack +- * of free space on the file system storage media. +- * +- * If this request is answered with an error code of ENOSYS, this is +- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all +- * future fallocate() requests will fail with EOPNOTSUPP without being +- * send to the filesystem process. +- * +- * Valid replies: +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param offset starting point for allocated region +- * @param length size of allocated region +- * @param mode determines the operation to be performed on the given range, +- * see fallocate(2) +- */ +- void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode, +- off_t offset, off_t length, struct fuse_file_info *fi); +- +- /** +- * Read directory with attributes +- * +- * Send a buffer filled using fuse_add_direntry_plus(), with size not +- * exceeding the requested size. Send an empty buffer on end of +- * stream. +- * +- * fi->fh will contain the value set by the opendir method, or +- * will be undefined if the opendir method didn't set any value. +- * +- * In contrast to readdir() (which does not affect the lookup counts), +- * the lookup count of every entry returned by readdirplus(), except "." +- * and "..", is incremented by one. +- * +- * Valid replies: +- * fuse_reply_buf +- * fuse_reply_data +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param size maximum number of bytes to send +- * @param off offset to continue reading the directory stream +- * @param fi file information +- */ +- void (*readdirplus) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, +- struct fuse_file_info *fi); +- +- /** +- * Copy a range of data from one file to another +- * +- * Performs an optimized copy between two file descriptors without the +- * additional cost of transferring data through the FUSE kernel module +- * to user space (glibc) and then back into the FUSE filesystem again. +- * +- * In case this method is not implemented, glibc falls back to reading +- * data from the source and writing to the destination. Effectively +- * doing an inefficient copy of the data. +- * +- * If this request is answered with an error code of ENOSYS, this is +- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all +- * future copy_file_range() requests will fail with EOPNOTSUPP without +- * being send to the filesystem process. +- * +- * Valid replies: +- * fuse_reply_write +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino_in the inode number or the source file +- * @param off_in starting point from were the data should be read +- * @param fi_in file information of the source file +- * @param ino_out the inode number or the destination file +- * @param off_out starting point where the data should be written +- * @param fi_out file information of the destination file +- * @param len maximum size of the data to copy +- * @param flags passed along with the copy_file_range() syscall +- */ +- void (*copy_file_range) (fuse_req_t req, fuse_ino_t ino_in, +- off_t off_in, struct fuse_file_info *fi_in, +- fuse_ino_t ino_out, off_t off_out, +- struct fuse_file_info *fi_out, size_t len, +- int flags); +- +- /** +- * Find next data or hole after the specified offset +- * +- * If this request is answered with an error code of ENOSYS, this is +- * treated as a permanent failure, i.e. all future lseek() requests will +- * fail with the same error code without being send to the filesystem +- * process. +- * +- * Valid replies: +- * fuse_reply_lseek +- * fuse_reply_err +- * +- * @param req request handle +- * @param ino the inode number +- * @param off offset to start search from +- * @param whence either SEEK_DATA or SEEK_HOLE +- * @param fi file information +- */ +- void (*lseek) (fuse_req_t req, fuse_ino_t ino, off_t off, int whence, +- struct fuse_file_info *fi); ++ /** ++ * Initialize filesystem ++ * ++ * This function is called when libfuse establishes ++ * communication with the FUSE kernel module. The file system ++ * should use this module to inspect and/or modify the ++ * connection parameters provided in the `conn` structure. ++ * ++ * Note that some parameters may be overwritten by options ++ * passed to fuse_session_new() which take precedence over the ++ * values set in this handler. ++ * ++ * There's no reply to this function ++ * ++ * @param userdata the user data passed to fuse_session_new() ++ */ ++ void (*init)(void *userdata, struct fuse_conn_info *conn); ++ ++ /** ++ * Clean up filesystem. ++ * ++ * Called on filesystem exit. When this method is called, the ++ * connection to the kernel may be gone already, so that eg. calls ++ * to fuse_lowlevel_notify_* will fail. ++ * ++ * There's no reply to this function ++ * ++ * @param userdata the user data passed to fuse_session_new() ++ */ ++ void (*destroy)(void *userdata); ++ ++ /** ++ * Look up a directory entry by name and get its attributes. ++ * ++ * Valid replies: ++ * fuse_reply_entry ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param parent inode number of the parent directory ++ * @param name the name to look up ++ */ ++ void (*lookup)(fuse_req_t req, fuse_ino_t parent, const char *name); ++ ++ /** ++ * Forget about an inode ++ * ++ * This function is called when the kernel removes an inode ++ * from its internal caches. ++ * ++ * The inode's lookup count increases by one for every call to ++ * fuse_reply_entry and fuse_reply_create. The nlookup parameter ++ * indicates by how much the lookup count should be decreased. ++ * ++ * Inodes with a non-zero lookup count may receive request from ++ * the kernel even after calls to unlink, rmdir or (when ++ * overwriting an existing file) rename. Filesystems must handle ++ * such requests properly and it is recommended to defer removal ++ * of the inode until the lookup count reaches zero. Calls to ++ * unlink, rmdir or rename will be followed closely by forget ++ * unless the file or directory is open, in which case the ++ * kernel issues forget only after the release or releasedir ++ * calls. ++ * ++ * Note that if a file system will be exported over NFS the ++ * inodes lifetime must extend even beyond forget. See the ++ * generation field in struct fuse_entry_param above. ++ * ++ * On unmount the lookup count for all inodes implicitly drops ++ * to zero. It is not guaranteed that the file system will ++ * receive corresponding forget messages for the affected ++ * inodes. ++ * ++ * Valid replies: ++ * fuse_reply_none ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param nlookup the number of lookups to forget ++ */ ++ void (*forget)(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup); ++ ++ /** ++ * Get file attributes. ++ * ++ * If writeback caching is enabled, the kernel may have a ++ * better idea of a file's length than the FUSE file system ++ * (eg if there has been a write that extended the file size, ++ * but that has not yet been passed to the filesystem.n ++ * ++ * In this case, the st_size value provided by the file system ++ * will be ignored. ++ * ++ * Valid replies: ++ * fuse_reply_attr ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi for future use, currently always NULL ++ */ ++ void (*getattr)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); ++ ++ /** ++ * Set file attributes ++ * ++ * In the 'attr' argument only members indicated by the 'to_set' ++ * bitmask contain valid values. Other members contain undefined ++ * values. ++ * ++ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is ++ * expected to reset the setuid and setgid bits if the file ++ * size or owner is being changed. ++ * ++ * If the setattr was invoked from the ftruncate() system call ++ * under Linux kernel versions 2.6.15 or later, the fi->fh will ++ * contain the value set by the open method or will be undefined ++ * if the open method didn't set any value. Otherwise (not ++ * ftruncate call, or kernel version earlier than 2.6.15) the fi ++ * parameter will be NULL. ++ * ++ * Valid replies: ++ * fuse_reply_attr ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param attr the attributes ++ * @param to_set bit mask of attributes which should be set ++ * @param fi file information, or NULL ++ */ ++ void (*setattr)(fuse_req_t req, fuse_ino_t ino, struct stat *attr, ++ int to_set, struct fuse_file_info *fi); ++ ++ /** ++ * Read symbolic link ++ * ++ * Valid replies: ++ * fuse_reply_readlink ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ */ ++ void (*readlink)(fuse_req_t req, fuse_ino_t ino); ++ ++ /** ++ * Create file node ++ * ++ * Create a regular file, character device, block device, fifo or ++ * socket node. ++ * ++ * Valid replies: ++ * fuse_reply_entry ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param parent inode number of the parent directory ++ * @param name to create ++ * @param mode file type and mode with which to create the new file ++ * @param rdev the device number (only valid if created file is a device) ++ */ ++ void (*mknod)(fuse_req_t req, fuse_ino_t parent, const char *name, ++ mode_t mode, dev_t rdev); ++ ++ /** ++ * Create a directory ++ * ++ * Valid replies: ++ * fuse_reply_entry ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param parent inode number of the parent directory ++ * @param name to create ++ * @param mode with which to create the new file ++ */ ++ void (*mkdir)(fuse_req_t req, fuse_ino_t parent, const char *name, ++ mode_t mode); ++ ++ /** ++ * Remove a file ++ * ++ * If the file's inode's lookup count is non-zero, the file ++ * system is expected to postpone any removal of the inode ++ * until the lookup count reaches zero (see description of the ++ * forget function). ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param parent inode number of the parent directory ++ * @param name to remove ++ */ ++ void (*unlink)(fuse_req_t req, fuse_ino_t parent, const char *name); ++ ++ /** ++ * Remove a directory ++ * ++ * If the directory's inode's lookup count is non-zero, the ++ * file system is expected to postpone any removal of the ++ * inode until the lookup count reaches zero (see description ++ * of the forget function). ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param parent inode number of the parent directory ++ * @param name to remove ++ */ ++ void (*rmdir)(fuse_req_t req, fuse_ino_t parent, const char *name); ++ ++ /** ++ * Create a symbolic link ++ * ++ * Valid replies: ++ * fuse_reply_entry ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param link the contents of the symbolic link ++ * @param parent inode number of the parent directory ++ * @param name to create ++ */ ++ void (*symlink)(fuse_req_t req, const char *link, fuse_ino_t parent, ++ const char *name); ++ ++ /** ++ * Rename a file ++ * ++ * If the target exists it should be atomically replaced. If ++ * the target's inode's lookup count is non-zero, the file ++ * system is expected to postpone any removal of the inode ++ * until the lookup count reaches zero (see description of the ++ * forget function). ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure with error code EINVAL, i.e. all ++ * future bmap requests will fail with EINVAL without being ++ * send to the filesystem process. ++ * ++ * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If ++ * RENAME_NOREPLACE is specified, the filesystem must not ++ * overwrite *newname* if it exists and return an error ++ * instead. If `RENAME_EXCHANGE` is specified, the filesystem ++ * must atomically exchange the two files, i.e. both must ++ * exist and neither may be deleted. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param parent inode number of the old parent directory ++ * @param name old name ++ * @param newparent inode number of the new parent directory ++ * @param newname new name ++ */ ++ void (*rename)(fuse_req_t req, fuse_ino_t parent, const char *name, ++ fuse_ino_t newparent, const char *newname, ++ unsigned int flags); ++ ++ /** ++ * Create a hard link ++ * ++ * Valid replies: ++ * fuse_reply_entry ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the old inode number ++ * @param newparent inode number of the new parent directory ++ * @param newname new name to create ++ */ ++ void (*link)(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, ++ const char *newname); ++ ++ /** ++ * Open a file ++ * ++ * Open flags are available in fi->flags. The following rules ++ * apply. ++ * ++ * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be ++ * filtered out / handled by the kernel. ++ * ++ * - Access modes (O_RDONLY, O_WRONLY, O_RDWR) should be used ++ * by the filesystem to check if the operation is ++ * permitted. If the ``-o default_permissions`` mount ++ * option is given, this check is already done by the ++ * kernel before calling open() and may thus be omitted by ++ * the filesystem. ++ * ++ * - When writeback caching is enabled, the kernel may send ++ * read requests even for files opened with O_WRONLY. The ++ * filesystem should be prepared to handle this. ++ * ++ * - When writeback caching is disabled, the filesystem is ++ * expected to properly handle the O_APPEND flag and ensure ++ * that each write is appending to the end of the file. ++ * ++ * - When writeback caching is enabled, the kernel will ++ * handle O_APPEND. However, unless all changes to the file ++ * come through the kernel this will not work reliably. The ++ * filesystem should thus either ignore the O_APPEND flag ++ * (and let the kernel handle it), or return an error ++ * (indicating that reliably O_APPEND is not available). ++ * ++ * Filesystem may store an arbitrary file handle (pointer, ++ * index, etc) in fi->fh, and use this in other all other file ++ * operations (read, write, flush, release, fsync). ++ * ++ * Filesystem may also implement stateless file I/O and not store ++ * anything in fi->fh. ++ * ++ * There are also some flags (direct_io, keep_cache) which the ++ * filesystem may set in fi, to change the way the file is opened. ++ * See fuse_file_info structure in for more details. ++ * ++ * If this request is answered with an error code of ENOSYS ++ * and FUSE_CAP_NO_OPEN_SUPPORT is set in ++ * `fuse_conn_info.capable`, this is treated as success and ++ * future calls to open and release will also succeed without being ++ * sent to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_open ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ */ ++ void (*open)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); ++ ++ /** ++ * Read data ++ * ++ * Read should send exactly the number of bytes requested except ++ * on EOF or error, otherwise the rest of the data will be ++ * substituted with zeroes. An exception to this is when the file ++ * has been opened in 'direct_io' mode, in which case the return ++ * value of the read system call will reflect the return value of ++ * this operation. ++ * ++ * fi->fh will contain the value set by the open method, or will ++ * be undefined if the open method didn't set any value. ++ * ++ * Valid replies: ++ * fuse_reply_buf ++ * fuse_reply_iov ++ * fuse_reply_data ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param size number of bytes to read ++ * @param off offset to read from ++ * @param fi file information ++ */ ++ void (*read)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Write data ++ * ++ * Write should return exactly the number of bytes requested ++ * except on error. An exception to this is when the file has ++ * been opened in 'direct_io' mode, in which case the return value ++ * of the write system call will reflect the return value of this ++ * operation. ++ * ++ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is ++ * expected to reset the setuid and setgid bits. ++ * ++ * fi->fh will contain the value set by the open method, or will ++ * be undefined if the open method didn't set any value. ++ * ++ * Valid replies: ++ * fuse_reply_write ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param buf data to write ++ * @param size number of bytes to write ++ * @param off offset to write to ++ * @param fi file information ++ */ ++ void (*write)(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, ++ off_t off, struct fuse_file_info *fi); ++ ++ /** ++ * Flush method ++ * ++ * This is called on each close() of the opened file. ++ * ++ * Since file descriptors can be duplicated (dup, dup2, fork), for ++ * one open call there may be many flush calls. ++ * ++ * Filesystems shouldn't assume that flush will always be called ++ * after some writes, or that if will be called at all. ++ * ++ * fi->fh will contain the value set by the open method, or will ++ * be undefined if the open method didn't set any value. ++ * ++ * NOTE: the name of the method is misleading, since (unlike ++ * fsync) the filesystem is not forced to flush pending writes. ++ * One reason to flush data is if the filesystem wants to return ++ * write errors during close. However, such use is non-portable ++ * because POSIX does not require [close] to wait for delayed I/O to ++ * complete. ++ * ++ * If the filesystem supports file locking operations (setlk, ++ * getlk) it should remove all locks belonging to 'fi->owner'. ++ * ++ * If this request is answered with an error code of ENOSYS, ++ * this is treated as success and future calls to flush() will ++ * succeed automatically without being send to the filesystem ++ * process. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ * ++ * [close]: ++ * http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html ++ */ ++ void (*flush)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); ++ ++ /** ++ * Release an open file ++ * ++ * Release is called when there are no more references to an open ++ * file: all file descriptors are closed and all memory mappings ++ * are unmapped. ++ * ++ * For every open call there will be exactly one release call (unless ++ * the filesystem is force-unmounted). ++ * ++ * The filesystem may reply with an error, but error values are ++ * not returned to close() or munmap() which triggered the ++ * release. ++ * ++ * fi->fh will contain the value set by the open method, or will ++ * be undefined if the open method didn't set any value. ++ * fi->flags will contain the same flags as for open. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ */ ++ void (*release)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); ++ ++ /** ++ * Synchronize file contents ++ * ++ * If the datasync parameter is non-zero, then only the user data ++ * should be flushed, not the meta data. ++ * ++ * If this request is answered with an error code of ENOSYS, ++ * this is treated as success and future calls to fsync() will ++ * succeed automatically without being send to the filesystem ++ * process. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param datasync flag indicating if only data should be flushed ++ * @param fi file information ++ */ ++ void (*fsync)(fuse_req_t req, fuse_ino_t ino, int datasync, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Open a directory ++ * ++ * Filesystem may store an arbitrary file handle (pointer, index, ++ * etc) in fi->fh, and use this in other all other directory ++ * stream operations (readdir, releasedir, fsyncdir). ++ * ++ * If this request is answered with an error code of ENOSYS and ++ * FUSE_CAP_NO_OPENDIR_SUPPORT is set in `fuse_conn_info.capable`, ++ * this is treated as success and future calls to opendir and ++ * releasedir will also succeed without being sent to the filesystem ++ * process. In addition, the kernel will cache readdir results ++ * as if opendir returned FOPEN_KEEP_CACHE | FOPEN_CACHE_DIR. ++ * ++ * Valid replies: ++ * fuse_reply_open ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ */ ++ void (*opendir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); ++ ++ /** ++ * Read directory ++ * ++ * Send a buffer filled using fuse_add_direntry(), with size not ++ * exceeding the requested size. Send an empty buffer on end of ++ * stream. ++ * ++ * fi->fh will contain the value set by the opendir method, or ++ * will be undefined if the opendir method didn't set any value. ++ * ++ * Returning a directory entry from readdir() does not affect ++ * its lookup count. ++ * ++ * If off_t is non-zero, then it will correspond to one of the off_t ++ * values that was previously returned by readdir() for the same ++ * directory handle. In this case, readdir() should skip over entries ++ * coming before the position defined by the off_t value. If entries ++ * are added or removed while the directory handle is open, they filesystem ++ * may still include the entries that have been removed, and may not ++ * report the entries that have been created. However, addition or ++ * removal of entries must never cause readdir() to skip over unrelated ++ * entries or to report them more than once. This means ++ * that off_t can not be a simple index that enumerates the entries ++ * that have been returned but must contain sufficient information to ++ * uniquely determine the next directory entry to return even when the ++ * set of entries is changing. ++ * ++ * The function does not have to report the '.' and '..' ++ * entries, but is allowed to do so. Note that, if readdir does ++ * not return '.' or '..', they will not be implicitly returned, ++ * and this behavior is observable by the caller. ++ * ++ * Valid replies: ++ * fuse_reply_buf ++ * fuse_reply_data ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param size maximum number of bytes to send ++ * @param off offset to continue reading the directory stream ++ * @param fi file information ++ */ ++ void (*readdir)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Release an open directory ++ * ++ * For every opendir call there will be exactly one releasedir ++ * call (unless the filesystem is force-unmounted). ++ * ++ * fi->fh will contain the value set by the opendir method, or ++ * will be undefined if the opendir method didn't set any value. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ */ ++ void (*releasedir)(fuse_req_t req, fuse_ino_t ino, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Synchronize directory contents ++ * ++ * If the datasync parameter is non-zero, then only the directory ++ * contents should be flushed, not the meta data. ++ * ++ * fi->fh will contain the value set by the opendir method, or ++ * will be undefined if the opendir method didn't set any value. ++ * ++ * If this request is answered with an error code of ENOSYS, ++ * this is treated as success and future calls to fsyncdir() will ++ * succeed automatically without being send to the filesystem ++ * process. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param datasync flag indicating if only data should be flushed ++ * @param fi file information ++ */ ++ void (*fsyncdir)(fuse_req_t req, fuse_ino_t ino, int datasync, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Get file system statistics ++ * ++ * Valid replies: ++ * fuse_reply_statfs ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number, zero means "undefined" ++ */ ++ void (*statfs)(fuse_req_t req, fuse_ino_t ino); ++ ++ /** ++ * Set an extended attribute ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all ++ * future setxattr() requests will fail with EOPNOTSUPP without being ++ * send to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ */ ++ void (*setxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, ++ const char *value, size_t size, int flags); ++ ++ /** ++ * Get an extended attribute ++ * ++ * If size is zero, the size of the value should be sent with ++ * fuse_reply_xattr. ++ * ++ * If the size is non-zero, and the value fits in the buffer, the ++ * value should be sent with fuse_reply_buf. ++ * ++ * If the size is too small for the value, the ERANGE error should ++ * be sent. ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all ++ * future getxattr() requests will fail with EOPNOTSUPP without being ++ * send to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_buf ++ * fuse_reply_data ++ * fuse_reply_xattr ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param name of the extended attribute ++ * @param size maximum size of the value to send ++ */ ++ void (*getxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, ++ size_t size); ++ ++ /** ++ * List extended attribute names ++ * ++ * If size is zero, the total size of the attribute list should be ++ * sent with fuse_reply_xattr. ++ * ++ * If the size is non-zero, and the null character separated ++ * attribute list fits in the buffer, the list should be sent with ++ * fuse_reply_buf. ++ * ++ * If the size is too small for the list, the ERANGE error should ++ * be sent. ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all ++ * future listxattr() requests will fail with EOPNOTSUPP without being ++ * send to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_buf ++ * fuse_reply_data ++ * fuse_reply_xattr ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param size maximum size of the list to send ++ */ ++ void (*listxattr)(fuse_req_t req, fuse_ino_t ino, size_t size); ++ ++ /** ++ * Remove an extended attribute ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all ++ * future removexattr() requests will fail with EOPNOTSUPP without being ++ * send to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param name of the extended attribute ++ */ ++ void (*removexattr)(fuse_req_t req, fuse_ino_t ino, const char *name); ++ ++ /** ++ * Check file access permissions ++ * ++ * This will be called for the access() and chdir() system ++ * calls. If the 'default_permissions' mount option is given, ++ * this method is not called. ++ * ++ * This method is not called under Linux kernel versions 2.4.x ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent success, i.e. this and all future access() ++ * requests will succeed without being send to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param mask requested access mode ++ */ ++ void (*access)(fuse_req_t req, fuse_ino_t ino, int mask); ++ ++ /** ++ * Create and open a file ++ * ++ * If the file does not exist, first create it with the specified ++ * mode, and then open it. ++ * ++ * See the description of the open handler for more ++ * information. ++ * ++ * If this method is not implemented or under Linux kernel ++ * versions earlier than 2.6.15, the mknod() and open() methods ++ * will be called instead. ++ * ++ * If this request is answered with an error code of ENOSYS, the handler ++ * is treated as not implemented (i.e., for this and future requests the ++ * mknod() and open() handlers will be called instead). ++ * ++ * Valid replies: ++ * fuse_reply_create ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param parent inode number of the parent directory ++ * @param name to create ++ * @param mode file type and mode with which to create the new file ++ * @param fi file information ++ */ ++ void (*create)(fuse_req_t req, fuse_ino_t parent, const char *name, ++ mode_t mode, struct fuse_file_info *fi); ++ ++ /** ++ * Test for a POSIX file lock ++ * ++ * Valid replies: ++ * fuse_reply_lock ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ * @param lock the region/type to test ++ */ ++ void (*getlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, ++ struct flock *lock); ++ ++ /** ++ * Acquire, modify or release a POSIX file lock ++ * ++ * For POSIX threads (NPTL) there's a 1-1 relation between pid and ++ * owner, but otherwise this is not always the case. For checking ++ * lock ownership, 'fi->owner' must be used. The l_pid field in ++ * 'struct flock' should only be used to fill in this field in ++ * getlk(). ++ * ++ * Note: if the locking methods are not implemented, the kernel ++ * will still allow file locking to work locally. Hence these are ++ * only interesting for network filesystems and similar. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ * @param lock the region/type to set ++ * @param sleep locking operation may sleep ++ */ ++ void (*setlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, ++ struct flock *lock, int sleep); ++ ++ /** ++ * Map block index within file to block index within device ++ * ++ * Note: This makes sense only for block device backed filesystems ++ * mounted with the 'blkdev' option ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure, i.e. all future bmap() requests will ++ * fail with the same error code without being send to the filesystem ++ * process. ++ * ++ * Valid replies: ++ * fuse_reply_bmap ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param blocksize unit of block index ++ * @param idx block index within file ++ */ ++ void (*bmap)(fuse_req_t req, fuse_ino_t ino, size_t blocksize, ++ uint64_t idx); ++ ++ /** ++ * Ioctl ++ * ++ * Note: For unrestricted ioctls (not allowed for FUSE ++ * servers), data in and out areas can be discovered by giving ++ * iovs and setting FUSE_IOCTL_RETRY in *flags*. For ++ * restricted ioctls, kernel prepares in/out data area ++ * according to the information encoded in cmd. ++ * ++ * Valid replies: ++ * fuse_reply_ioctl_retry ++ * fuse_reply_ioctl ++ * fuse_reply_ioctl_iov ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param cmd ioctl command ++ * @param arg ioctl argument ++ * @param fi file information ++ * @param flags for FUSE_IOCTL_* flags ++ * @param in_buf data fetched from the caller ++ * @param in_bufsz number of fetched bytes ++ * @param out_bufsz maximum size of output data ++ * ++ * Note : the unsigned long request submitted by the application ++ * is truncated to 32 bits. ++ */ ++ void (*ioctl)(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg, ++ struct fuse_file_info *fi, unsigned flags, const void *in_buf, ++ size_t in_bufsz, size_t out_bufsz); ++ ++ /** ++ * Poll for IO readiness ++ * ++ * Note: If ph is non-NULL, the client should notify ++ * when IO readiness events occur by calling ++ * fuse_lowlevel_notify_poll() with the specified ph. ++ * ++ * Regardless of the number of times poll with a non-NULL ph ++ * is received, single notification is enough to clear all. ++ * Notifying more times incurs overhead but doesn't harm ++ * correctness. ++ * ++ * The callee is responsible for destroying ph with ++ * fuse_pollhandle_destroy() when no longer in use. ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as success (with a kernel-defined default poll-mask) and ++ * future calls to pull() will succeed the same way without being send ++ * to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_poll ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ * @param ph poll handle to be used for notification ++ */ ++ void (*poll)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, ++ struct fuse_pollhandle *ph); ++ ++ /** ++ * Write data made available in a buffer ++ * ++ * This is a more generic version of the ->write() method. If ++ * FUSE_CAP_SPLICE_READ is set in fuse_conn_info.want and the ++ * kernel supports splicing from the fuse device, then the ++ * data will be made available in pipe for supporting zero ++ * copy data transfer. ++ * ++ * buf->count is guaranteed to be one (and thus buf->idx is ++ * always zero). The write_buf handler must ensure that ++ * bufv->off is correctly updated (reflecting the number of ++ * bytes read from bufv->buf[0]). ++ * ++ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is ++ * expected to reset the setuid and setgid bits. ++ * ++ * Valid replies: ++ * fuse_reply_write ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param bufv buffer containing the data ++ * @param off offset to write to ++ * @param fi file information ++ */ ++ void (*write_buf)(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv, ++ off_t off, struct fuse_file_info *fi); ++ ++ /** ++ * Callback function for the retrieve request ++ * ++ * Valid replies: ++ * fuse_reply_none ++ * ++ * @param req request handle ++ * @param cookie user data supplied to fuse_lowlevel_notify_retrieve() ++ * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve() ++ * @param offset the offset supplied to fuse_lowlevel_notify_retrieve() ++ * @param bufv the buffer containing the returned data ++ */ ++ void (*retrieve_reply)(fuse_req_t req, void *cookie, fuse_ino_t ino, ++ off_t offset, struct fuse_bufvec *bufv); ++ ++ /** ++ * Forget about multiple inodes ++ * ++ * See description of the forget function for more ++ * information. ++ * ++ * Valid replies: ++ * fuse_reply_none ++ * ++ * @param req request handle ++ */ ++ void (*forget_multi)(fuse_req_t req, size_t count, ++ struct fuse_forget_data *forgets); ++ ++ /** ++ * Acquire, modify or release a BSD file lock ++ * ++ * Note: if the locking methods are not implemented, the kernel ++ * will still allow file locking to work locally. Hence these are ++ * only interesting for network filesystems and similar. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param fi file information ++ * @param op the locking operation, see flock(2) ++ */ ++ void (*flock)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, ++ int op); ++ ++ /** ++ * Allocate requested space. If this function returns success then ++ * subsequent writes to the specified range shall not fail due to the lack ++ * of free space on the file system storage media. ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all ++ * future fallocate() requests will fail with EOPNOTSUPP without being ++ * send to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param offset starting point for allocated region ++ * @param length size of allocated region ++ * @param mode determines the operation to be performed on the given range, ++ * see fallocate(2) ++ */ ++ void (*fallocate)(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, ++ off_t length, struct fuse_file_info *fi); ++ ++ /** ++ * Read directory with attributes ++ * ++ * Send a buffer filled using fuse_add_direntry_plus(), with size not ++ * exceeding the requested size. Send an empty buffer on end of ++ * stream. ++ * ++ * fi->fh will contain the value set by the opendir method, or ++ * will be undefined if the opendir method didn't set any value. ++ * ++ * In contrast to readdir() (which does not affect the lookup counts), ++ * the lookup count of every entry returned by readdirplus(), except "." ++ * and "..", is incremented by one. ++ * ++ * Valid replies: ++ * fuse_reply_buf ++ * fuse_reply_data ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param size maximum number of bytes to send ++ * @param off offset to continue reading the directory stream ++ * @param fi file information ++ */ ++ void (*readdirplus)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, ++ struct fuse_file_info *fi); ++ ++ /** ++ * Copy a range of data from one file to another ++ * ++ * Performs an optimized copy between two file descriptors without the ++ * additional cost of transferring data through the FUSE kernel module ++ * to user space (glibc) and then back into the FUSE filesystem again. ++ * ++ * In case this method is not implemented, glibc falls back to reading ++ * data from the source and writing to the destination. Effectively ++ * doing an inefficient copy of the data. ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all ++ * future copy_file_range() requests will fail with EOPNOTSUPP without ++ * being send to the filesystem process. ++ * ++ * Valid replies: ++ * fuse_reply_write ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino_in the inode number or the source file ++ * @param off_in starting point from were the data should be read ++ * @param fi_in file information of the source file ++ * @param ino_out the inode number or the destination file ++ * @param off_out starting point where the data should be written ++ * @param fi_out file information of the destination file ++ * @param len maximum size of the data to copy ++ * @param flags passed along with the copy_file_range() syscall ++ */ ++ void (*copy_file_range)(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, ++ struct fuse_file_info *fi_in, fuse_ino_t ino_out, ++ off_t off_out, struct fuse_file_info *fi_out, ++ size_t len, int flags); ++ ++ /** ++ * Find next data or hole after the specified offset ++ * ++ * If this request is answered with an error code of ENOSYS, this is ++ * treated as a permanent failure, i.e. all future lseek() requests will ++ * fail with the same error code without being send to the filesystem ++ * process. ++ * ++ * Valid replies: ++ * fuse_reply_lseek ++ * fuse_reply_err ++ * ++ * @param req request handle ++ * @param ino the inode number ++ * @param off offset to start search from ++ * @param whence either SEEK_DATA or SEEK_HOLE ++ * @param fi file information ++ */ ++ void (*lseek)(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, ++ struct fuse_file_info *fi); + }; + + /** +@@ -1305,7 +1307,7 @@ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e); + * @return zero for success, -errno for failure to send reply + */ + int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, +- const struct fuse_file_info *fi); ++ const struct fuse_file_info *fi); + + /** + * Reply with attributes +@@ -1315,11 +1317,11 @@ int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, + * + * @param req request handle + * @param attr the attributes +- * @param attr_timeout validity timeout (in seconds) for the attributes ++ * @param attr_timeout validity timeout (in seconds) for the attributes + * @return zero for success, -errno for failure to send reply + */ + int fuse_reply_attr(fuse_req_t req, const struct stat *attr, +- double attr_timeout); ++ double attr_timeout); + + /** + * Reply with the contents of a symbolic link +@@ -1417,7 +1419,7 @@ int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size); + * @return zero for success, -errno for failure to send reply + */ + int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, +- enum fuse_buf_copy_flags flags); ++ enum fuse_buf_copy_flags flags); + + /** + * Reply with data vector +@@ -1480,9 +1482,9 @@ int fuse_reply_lock(fuse_req_t req, const struct flock *lock); + */ + int fuse_reply_bmap(fuse_req_t req, uint64_t idx); + +-/* ----------------------------------------------------------- * +- * Filling a buffer in readdir * +- * ----------------------------------------------------------- */ ++/* ++ * Filling a buffer in readdir ++ */ + + /** + * Add a directory entry to the buffer +@@ -1512,8 +1514,7 @@ int fuse_reply_bmap(fuse_req_t req, uint64_t idx); + * @return the space needed for the entry + */ + size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, +- const char *name, const struct stat *stbuf, +- off_t off); ++ const char *name, const struct stat *stbuf, off_t off); + + /** + * Add a directory entry to the buffer with the attributes +@@ -1529,8 +1530,8 @@ size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, + * @return the space needed for the entry + */ + size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, +- const char *name, +- const struct fuse_entry_param *e, off_t off); ++ const char *name, ++ const struct fuse_entry_param *e, off_t off); + + /** + * Reply to ask for data fetch and output buffer preparation. ioctl +@@ -1547,9 +1548,9 @@ size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, + * @param out_count number of entries in out_iov + * @return zero for success, -errno for failure to send reply + */ +-int fuse_reply_ioctl_retry(fuse_req_t req, +- const struct iovec *in_iov, size_t in_count, +- const struct iovec *out_iov, size_t out_count); ++int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, ++ size_t in_count, const struct iovec *out_iov, ++ size_t out_count); + + /** + * Reply to finish ioctl +@@ -1576,7 +1577,7 @@ int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size); + * @param count the size of vector + */ + int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, +- int count); ++ int count); + + /** + * Reply with poll result event mask +@@ -1598,9 +1599,9 @@ int fuse_reply_poll(fuse_req_t req, unsigned revents); + */ + int fuse_reply_lseek(fuse_req_t req, off_t off); + +-/* ----------------------------------------------------------- * +- * Notification * +- * ----------------------------------------------------------- */ ++/* ++ * Notification ++ */ + + /** + * Notify IO readiness event +@@ -1635,7 +1636,7 @@ int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph); + * @return zero for success, -errno for failure + */ + int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, +- off_t off, off_t len); ++ off_t off, off_t len); + + /** + * Notify to invalidate parent attributes and the dentry matching +@@ -1663,7 +1664,7 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, + * @return zero for success, -errno for failure + */ + int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, +- const char *name, size_t namelen); ++ const char *name, size_t namelen); + + /** + * This function behaves like fuse_lowlevel_notify_inval_entry() with +@@ -1693,9 +1694,9 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, + * @param namelen strlen() of file name + * @return zero for success, -errno for failure + */ +-int fuse_lowlevel_notify_delete(struct fuse_session *se, +- fuse_ino_t parent, fuse_ino_t child, +- const char *name, size_t namelen); ++int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, ++ fuse_ino_t child, const char *name, ++ size_t namelen); + + /** + * Store data to the kernel buffers +@@ -1723,8 +1724,8 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, + * @return zero for success, -errno for failure + */ + int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, +- off_t offset, struct fuse_bufvec *bufv, +- enum fuse_buf_copy_flags flags); ++ off_t offset, struct fuse_bufvec *bufv, ++ enum fuse_buf_copy_flags flags); + /** + * Retrieve data from the kernel buffers + * +@@ -1755,12 +1756,12 @@ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, + * @return zero for success, -errno for failure + */ + int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, +- size_t size, off_t offset, void *cookie); ++ size_t size, off_t offset, void *cookie); + + +-/* ----------------------------------------------------------- * +- * Utility functions * +- * ----------------------------------------------------------- */ ++/* ++ * Utility functions ++ */ + + /** + * Get the userdata from the request +@@ -1822,7 +1823,7 @@ typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data); + * @param data user data passed to the callback function + */ + void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, +- void *data); ++ void *data); + + /** + * Check if a request has already been interrupted +@@ -1833,9 +1834,9 @@ void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, + int fuse_req_interrupted(fuse_req_t req); + + +-/* ----------------------------------------------------------- * +- * Inquiry functions * +- * ----------------------------------------------------------- */ ++/* ++ * Inquiry functions ++ */ + + /** + * Print low-level version information to stdout. +@@ -1854,18 +1855,18 @@ void fuse_lowlevel_help(void); + */ + void fuse_cmdline_help(void); + +-/* ----------------------------------------------------------- * +- * Filesystem setup & teardown * +- * ----------------------------------------------------------- */ ++/* ++ * Filesystem setup & teardown ++ */ + + struct fuse_cmdline_opts { +- int foreground; +- int debug; +- int nodefault_subtype; +- char *mountpoint; +- int show_version; +- int show_help; +- unsigned int max_idle_threads; ++ int foreground; ++ int debug; ++ int nodefault_subtype; ++ char *mountpoint; ++ int show_version; ++ int show_help; ++ unsigned int max_idle_threads; + }; + + /** +@@ -1886,8 +1887,7 @@ struct fuse_cmdline_opts { + * @param opts output argument for parsed options + * @return 0 on success, -1 on failure + */ +-int fuse_parse_cmdline(struct fuse_args *args, +- struct fuse_cmdline_opts *opts); ++int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts); + + /** + * Create a low level session. +@@ -1918,8 +1918,8 @@ int fuse_parse_cmdline(struct fuse_args *args, + * @return the fuse session on success, NULL on failure + **/ + struct fuse_session *fuse_session_new(struct fuse_args *args, +- const struct fuse_lowlevel_ops *op, +- size_t op_size, void *userdata); ++ const struct fuse_lowlevel_ops *op, ++ size_t op_size, void *userdata); + + /** + * Mount a FUSE file system. +@@ -2014,9 +2014,9 @@ void fuse_session_unmount(struct fuse_session *se); + */ + void fuse_session_destroy(struct fuse_session *se); + +-/* ----------------------------------------------------------- * +- * Custom event loop support * +- * ----------------------------------------------------------- */ ++/* ++ * Custom event loop support ++ */ + + /** + * Return file descriptor for communication with kernel. +@@ -2043,7 +2043,7 @@ int fuse_session_fd(struct fuse_session *se); + * @param buf the fuse_buf containing the request + */ + void fuse_session_process_buf(struct fuse_session *se, +- const struct fuse_buf *buf); ++ const struct fuse_buf *buf); + + /** + * Read a raw request from the kernel into the supplied buffer. +diff --git a/tools/virtiofsd/fuse_misc.h b/tools/virtiofsd/fuse_misc.h +index 2f6663ed7d..f252baa752 100644 +--- a/tools/virtiofsd/fuse_misc.h ++++ b/tools/virtiofsd/fuse_misc.h +@@ -1,18 +1,18 @@ + /* +- FUSE: Filesystem in Userspace +- Copyright (C) 2001-2007 Miklos Szeredi +- +- This program can be distributed under the terms of the GNU LGPLv2. +- See the file COPYING.LIB +-*/ ++ * FUSE: Filesystem in Userspace ++ * Copyright (C) 2001-2007 Miklos Szeredi ++ * ++ * This program can be distributed under the terms of the GNU LGPLv2. ++ * See the file COPYING.LIB ++ */ + + #include + + /* +- Versioned symbols cannot be used in some cases because it +- - confuse the dynamic linker in uClibc +- - not supported on MacOSX (in MachO binary format) +-*/ ++ * Versioned symbols cannot be used in some cases because it ++ * - confuse the dynamic linker in uClibc ++ * - not supported on MacOSX (in MachO binary format) ++ */ + #if (!defined(__UCLIBC__) && !defined(__APPLE__)) + #define FUSE_SYMVER(x) __asm__(x) + #else +@@ -25,11 +25,11 @@ + /* Is this hack still needed? */ + static inline void fuse_mutex_init(pthread_mutex_t *mut) + { +- pthread_mutexattr_t attr; +- pthread_mutexattr_init(&attr); +- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); +- pthread_mutex_init(mut, &attr); +- pthread_mutexattr_destroy(&attr); ++ pthread_mutexattr_t attr; ++ pthread_mutexattr_init(&attr); ++ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); ++ pthread_mutex_init(mut, &attr); ++ pthread_mutexattr_destroy(&attr); + } + #endif + +diff --git a/tools/virtiofsd/fuse_opt.c b/tools/virtiofsd/fuse_opt.c +index 93066b926e..edd36f4a3b 100644 +--- a/tools/virtiofsd/fuse_opt.c ++++ b/tools/virtiofsd/fuse_opt.c +@@ -1,423 +1,450 @@ + /* +- FUSE: Filesystem in Userspace +- Copyright (C) 2001-2007 Miklos Szeredi +- +- Implementation of option parsing routines (dealing with `struct +- fuse_args`). +- +- This program can be distributed under the terms of the GNU LGPLv2. +- See the file COPYING.LIB +-*/ ++ * FUSE: Filesystem in Userspace ++ * Copyright (C) 2001-2007 Miklos Szeredi ++ * ++ * Implementation of option parsing routines (dealing with `struct ++ * fuse_args`). ++ * ++ * This program can be distributed under the terms of the GNU LGPLv2. ++ * See the file COPYING.LIB ++ */ + ++#include "fuse_opt.h" + #include "config.h" + #include "fuse_i.h" +-#include "fuse_opt.h" + #include "fuse_misc.h" + ++#include + #include + #include + #include +-#include + + struct fuse_opt_context { +- void *data; +- const struct fuse_opt *opt; +- fuse_opt_proc_t proc; +- int argctr; +- int argc; +- char **argv; +- struct fuse_args outargs; +- char *opts; +- int nonopt; ++ void *data; ++ const struct fuse_opt *opt; ++ fuse_opt_proc_t proc; ++ int argctr; ++ int argc; ++ char **argv; ++ struct fuse_args outargs; ++ char *opts; ++ int nonopt; + }; + + void fuse_opt_free_args(struct fuse_args *args) + { +- if (args) { +- if (args->argv && args->allocated) { +- int i; +- for (i = 0; i < args->argc; i++) +- free(args->argv[i]); +- free(args->argv); +- } +- args->argc = 0; +- args->argv = NULL; +- args->allocated = 0; +- } ++ if (args) { ++ if (args->argv && args->allocated) { ++ int i; ++ for (i = 0; i < args->argc; i++) { ++ free(args->argv[i]); ++ } ++ free(args->argv); ++ } ++ args->argc = 0; ++ args->argv = NULL; ++ args->allocated = 0; ++ } + } + + static int alloc_failed(void) + { +- fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); +- return -1; ++ fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); ++ return -1; + } + + int fuse_opt_add_arg(struct fuse_args *args, const char *arg) + { +- char **newargv; +- char *newarg; +- +- assert(!args->argv || args->allocated); +- +- newarg = strdup(arg); +- if (!newarg) +- return alloc_failed(); +- +- newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); +- if (!newargv) { +- free(newarg); +- return alloc_failed(); +- } +- +- args->argv = newargv; +- args->allocated = 1; +- args->argv[args->argc++] = newarg; +- args->argv[args->argc] = NULL; +- return 0; ++ char **newargv; ++ char *newarg; ++ ++ assert(!args->argv || args->allocated); ++ ++ newarg = strdup(arg); ++ if (!newarg) { ++ return alloc_failed(); ++ } ++ ++ newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); ++ if (!newargv) { ++ free(newarg); ++ return alloc_failed(); ++ } ++ ++ args->argv = newargv; ++ args->allocated = 1; ++ args->argv[args->argc++] = newarg; ++ args->argv[args->argc] = NULL; ++ return 0; + } + + static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos, +- const char *arg) ++ const char *arg) + { +- assert(pos <= args->argc); +- if (fuse_opt_add_arg(args, arg) == -1) +- return -1; +- +- if (pos != args->argc - 1) { +- char *newarg = args->argv[args->argc - 1]; +- memmove(&args->argv[pos + 1], &args->argv[pos], +- sizeof(char *) * (args->argc - pos - 1)); +- args->argv[pos] = newarg; +- } +- return 0; ++ assert(pos <= args->argc); ++ if (fuse_opt_add_arg(args, arg) == -1) { ++ return -1; ++ } ++ ++ if (pos != args->argc - 1) { ++ char *newarg = args->argv[args->argc - 1]; ++ memmove(&args->argv[pos + 1], &args->argv[pos], ++ sizeof(char *) * (args->argc - pos - 1)); ++ args->argv[pos] = newarg; ++ } ++ return 0; + } + + int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) + { +- return fuse_opt_insert_arg_common(args, pos, arg); ++ return fuse_opt_insert_arg_common(args, pos, arg); + } + + static int next_arg(struct fuse_opt_context *ctx, const char *opt) + { +- if (ctx->argctr + 1 >= ctx->argc) { +- fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt); +- return -1; +- } +- ctx->argctr++; +- return 0; ++ if (ctx->argctr + 1 >= ctx->argc) { ++ fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt); ++ return -1; ++ } ++ ctx->argctr++; ++ return 0; + } + + static int add_arg(struct fuse_opt_context *ctx, const char *arg) + { +- return fuse_opt_add_arg(&ctx->outargs, arg); ++ return fuse_opt_add_arg(&ctx->outargs, arg); + } + + static int add_opt_common(char **opts, const char *opt, int esc) + { +- unsigned oldlen = *opts ? strlen(*opts) : 0; +- char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1); +- +- if (!d) +- return alloc_failed(); +- +- *opts = d; +- if (oldlen) { +- d += oldlen; +- *d++ = ','; +- } +- +- for (; *opt; opt++) { +- if (esc && (*opt == ',' || *opt == '\\')) +- *d++ = '\\'; +- *d++ = *opt; +- } +- *d = '\0'; +- +- return 0; ++ unsigned oldlen = *opts ? strlen(*opts) : 0; ++ char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1); ++ ++ if (!d) { ++ return alloc_failed(); ++ } ++ ++ *opts = d; ++ if (oldlen) { ++ d += oldlen; ++ *d++ = ','; ++ } ++ ++ for (; *opt; opt++) { ++ if (esc && (*opt == ',' || *opt == '\\')) { ++ *d++ = '\\'; ++ } ++ *d++ = *opt; ++ } ++ *d = '\0'; ++ ++ return 0; + } + + int fuse_opt_add_opt(char **opts, const char *opt) + { +- return add_opt_common(opts, opt, 0); ++ return add_opt_common(opts, opt, 0); + } + + int fuse_opt_add_opt_escaped(char **opts, const char *opt) + { +- return add_opt_common(opts, opt, 1); ++ return add_opt_common(opts, opt, 1); + } + + static int add_opt(struct fuse_opt_context *ctx, const char *opt) + { +- return add_opt_common(&ctx->opts, opt, 1); ++ return add_opt_common(&ctx->opts, opt, 1); + } + + static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key, +- int iso) ++ int iso) + { +- if (key == FUSE_OPT_KEY_DISCARD) +- return 0; +- +- if (key != FUSE_OPT_KEY_KEEP && ctx->proc) { +- int res = ctx->proc(ctx->data, arg, key, &ctx->outargs); +- if (res == -1 || !res) +- return res; +- } +- if (iso) +- return add_opt(ctx, arg); +- else +- return add_arg(ctx, arg); ++ if (key == FUSE_OPT_KEY_DISCARD) { ++ return 0; ++ } ++ ++ if (key != FUSE_OPT_KEY_KEEP && ctx->proc) { ++ int res = ctx->proc(ctx->data, arg, key, &ctx->outargs); ++ if (res == -1 || !res) { ++ return res; ++ } ++ } ++ if (iso) { ++ return add_opt(ctx, arg); ++ } else { ++ return add_arg(ctx, arg); ++ } + } + + static int match_template(const char *t, const char *arg, unsigned *sepp) + { +- int arglen = strlen(arg); +- const char *sep = strchr(t, '='); +- sep = sep ? sep : strchr(t, ' '); +- if (sep && (!sep[1] || sep[1] == '%')) { +- int tlen = sep - t; +- if (sep[0] == '=') +- tlen ++; +- if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { +- *sepp = sep - t; +- return 1; +- } +- } +- if (strcmp(t, arg) == 0) { +- *sepp = 0; +- return 1; +- } +- return 0; ++ int arglen = strlen(arg); ++ const char *sep = strchr(t, '='); ++ sep = sep ? sep : strchr(t, ' '); ++ if (sep && (!sep[1] || sep[1] == '%')) { ++ int tlen = sep - t; ++ if (sep[0] == '=') { ++ tlen++; ++ } ++ if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { ++ *sepp = sep - t; ++ return 1; ++ } ++ } ++ if (strcmp(t, arg) == 0) { ++ *sepp = 0; ++ return 1; ++ } ++ return 0; + } + + static const struct fuse_opt *find_opt(const struct fuse_opt *opt, +- const char *arg, unsigned *sepp) ++ const char *arg, unsigned *sepp) + { +- for (; opt && opt->templ; opt++) +- if (match_template(opt->templ, arg, sepp)) +- return opt; +- return NULL; ++ for (; opt && opt->templ; opt++) { ++ if (match_template(opt->templ, arg, sepp)) { ++ return opt; ++ } ++ } ++ return NULL; + } + + int fuse_opt_match(const struct fuse_opt *opts, const char *opt) + { +- unsigned dummy; +- return find_opt(opts, opt, &dummy) ? 1 : 0; ++ unsigned dummy; ++ return find_opt(opts, opt, &dummy) ? 1 : 0; + } + + static int process_opt_param(void *var, const char *format, const char *param, +- const char *arg) ++ const char *arg) + { +- assert(format[0] == '%'); +- if (format[1] == 's') { +- char **s = var; +- char *copy = strdup(param); +- if (!copy) +- return alloc_failed(); +- +- free(*s); +- *s = copy; +- } else { +- if (sscanf(param, format, var) != 1) { +- fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n", arg); +- return -1; +- } +- } +- return 0; ++ assert(format[0] == '%'); ++ if (format[1] == 's') { ++ char **s = var; ++ char *copy = strdup(param); ++ if (!copy) { ++ return alloc_failed(); ++ } ++ ++ free(*s); ++ *s = copy; ++ } else { ++ if (sscanf(param, format, var) != 1) { ++ fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n", ++ arg); ++ return -1; ++ } ++ } ++ return 0; + } + +-static int process_opt(struct fuse_opt_context *ctx, +- const struct fuse_opt *opt, unsigned sep, +- const char *arg, int iso) ++static int process_opt(struct fuse_opt_context *ctx, const struct fuse_opt *opt, ++ unsigned sep, const char *arg, int iso) + { +- if (opt->offset == -1U) { +- if (call_proc(ctx, arg, opt->value, iso) == -1) +- return -1; +- } else { +- void *var = (char *)ctx->data + opt->offset; +- if (sep && opt->templ[sep + 1]) { +- const char *param = arg + sep; +- if (opt->templ[sep] == '=') +- param ++; +- if (process_opt_param(var, opt->templ + sep + 1, +- param, arg) == -1) +- return -1; +- } else +- *(int *)var = opt->value; +- } +- return 0; ++ if (opt->offset == -1U) { ++ if (call_proc(ctx, arg, opt->value, iso) == -1) { ++ return -1; ++ } ++ } else { ++ void *var = (char *)ctx->data + opt->offset; ++ if (sep && opt->templ[sep + 1]) { ++ const char *param = arg + sep; ++ if (opt->templ[sep] == '=') { ++ param++; ++ } ++ if (process_opt_param(var, opt->templ + sep + 1, param, arg) == ++ -1) { ++ return -1; ++ } ++ } else { ++ *(int *)var = opt->value; ++ } ++ } ++ return 0; + } + + static int process_opt_sep_arg(struct fuse_opt_context *ctx, +- const struct fuse_opt *opt, unsigned sep, +- const char *arg, int iso) ++ const struct fuse_opt *opt, unsigned sep, ++ const char *arg, int iso) + { +- int res; +- char *newarg; +- char *param; +- +- if (next_arg(ctx, arg) == -1) +- return -1; +- +- param = ctx->argv[ctx->argctr]; +- newarg = malloc(sep + strlen(param) + 1); +- if (!newarg) +- return alloc_failed(); +- +- memcpy(newarg, arg, sep); +- strcpy(newarg + sep, param); +- res = process_opt(ctx, opt, sep, newarg, iso); +- free(newarg); +- +- return res; ++ int res; ++ char *newarg; ++ char *param; ++ ++ if (next_arg(ctx, arg) == -1) { ++ return -1; ++ } ++ ++ param = ctx->argv[ctx->argctr]; ++ newarg = malloc(sep + strlen(param) + 1); ++ if (!newarg) { ++ return alloc_failed(); ++ } ++ ++ memcpy(newarg, arg, sep); ++ strcpy(newarg + sep, param); ++ res = process_opt(ctx, opt, sep, newarg, iso); ++ free(newarg); ++ ++ return res; + } + + static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso) + { +- unsigned sep; +- const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); +- if (opt) { +- for (; opt; opt = find_opt(opt + 1, arg, &sep)) { +- int res; +- if (sep && opt->templ[sep] == ' ' && !arg[sep]) +- res = process_opt_sep_arg(ctx, opt, sep, arg, +- iso); +- else +- res = process_opt(ctx, opt, sep, arg, iso); +- if (res == -1) +- return -1; +- } +- return 0; +- } else +- return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); ++ unsigned sep; ++ const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); ++ if (opt) { ++ for (; opt; opt = find_opt(opt + 1, arg, &sep)) { ++ int res; ++ if (sep && opt->templ[sep] == ' ' && !arg[sep]) { ++ res = process_opt_sep_arg(ctx, opt, sep, arg, iso); ++ } else { ++ res = process_opt(ctx, opt, sep, arg, iso); ++ } ++ if (res == -1) { ++ return -1; ++ } ++ } ++ return 0; ++ } else { ++ return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); ++ } + } + + static int process_real_option_group(struct fuse_opt_context *ctx, char *opts) + { +- char *s = opts; +- char *d = s; +- int end = 0; +- +- while (!end) { +- if (*s == '\0') +- end = 1; +- if (*s == ',' || end) { +- int res; +- +- *d = '\0'; +- res = process_gopt(ctx, opts, 1); +- if (res == -1) +- return -1; +- d = opts; +- } else { +- if (s[0] == '\\' && s[1] != '\0') { +- s++; +- if (s[0] >= '0' && s[0] <= '3' && +- s[1] >= '0' && s[1] <= '7' && +- s[2] >= '0' && s[2] <= '7') { +- *d++ = (s[0] - '0') * 0100 + +- (s[1] - '0') * 0010 + +- (s[2] - '0'); +- s += 2; +- } else { +- *d++ = *s; +- } +- } else { +- *d++ = *s; +- } +- } +- s++; +- } +- +- return 0; ++ char *s = opts; ++ char *d = s; ++ int end = 0; ++ ++ while (!end) { ++ if (*s == '\0') { ++ end = 1; ++ } ++ if (*s == ',' || end) { ++ int res; ++ ++ *d = '\0'; ++ res = process_gopt(ctx, opts, 1); ++ if (res == -1) { ++ return -1; ++ } ++ d = opts; ++ } else { ++ if (s[0] == '\\' && s[1] != '\0') { ++ s++; ++ if (s[0] >= '0' && s[0] <= '3' && s[1] >= '0' && s[1] <= '7' && ++ s[2] >= '0' && s[2] <= '7') { ++ *d++ = (s[0] - '0') * 0100 + (s[1] - '0') * 0010 + ++ (s[2] - '0'); ++ s += 2; ++ } else { ++ *d++ = *s; ++ } ++ } else { ++ *d++ = *s; ++ } ++ } ++ s++; ++ } ++ ++ return 0; + } + + static int process_option_group(struct fuse_opt_context *ctx, const char *opts) + { +- int res; +- char *copy = strdup(opts); +- +- if (!copy) { +- fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); +- return -1; +- } +- res = process_real_option_group(ctx, copy); +- free(copy); +- return res; ++ int res; ++ char *copy = strdup(opts); ++ ++ if (!copy) { ++ fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); ++ return -1; ++ } ++ res = process_real_option_group(ctx, copy); ++ free(copy); ++ return res; + } + + static int process_one(struct fuse_opt_context *ctx, const char *arg) + { +- if (ctx->nonopt || arg[0] != '-') +- return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); +- else if (arg[1] == 'o') { +- if (arg[2]) +- return process_option_group(ctx, arg + 2); +- else { +- if (next_arg(ctx, arg) == -1) +- return -1; +- +- return process_option_group(ctx, +- ctx->argv[ctx->argctr]); +- } +- } else if (arg[1] == '-' && !arg[2]) { +- if (add_arg(ctx, arg) == -1) +- return -1; +- ctx->nonopt = ctx->outargs.argc; +- return 0; +- } else +- return process_gopt(ctx, arg, 0); ++ if (ctx->nonopt || arg[0] != '-') { ++ return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); ++ } else if (arg[1] == 'o') { ++ if (arg[2]) { ++ return process_option_group(ctx, arg + 2); ++ } else { ++ if (next_arg(ctx, arg) == -1) { ++ return -1; ++ } ++ ++ return process_option_group(ctx, ctx->argv[ctx->argctr]); ++ } ++ } else if (arg[1] == '-' && !arg[2]) { ++ if (add_arg(ctx, arg) == -1) { ++ return -1; ++ } ++ ctx->nonopt = ctx->outargs.argc; ++ return 0; ++ } else { ++ return process_gopt(ctx, arg, 0); ++ } + } + + static int opt_parse(struct fuse_opt_context *ctx) + { +- if (ctx->argc) { +- if (add_arg(ctx, ctx->argv[0]) == -1) +- return -1; +- } +- +- for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) +- if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) +- return -1; +- +- if (ctx->opts) { +- if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 || +- fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) +- return -1; +- } +- +- /* If option separator ("--") is the last argument, remove it */ +- if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc && +- strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) { +- free(ctx->outargs.argv[ctx->outargs.argc - 1]); +- ctx->outargs.argv[--ctx->outargs.argc] = NULL; +- } +- +- return 0; ++ if (ctx->argc) { ++ if (add_arg(ctx, ctx->argv[0]) == -1) { ++ return -1; ++ } ++ } ++ ++ for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) { ++ if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) { ++ return -1; ++ } ++ } ++ ++ if (ctx->opts) { ++ if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 || ++ fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) { ++ return -1; ++ } ++ } ++ ++ /* If option separator ("--") is the last argument, remove it */ ++ if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc && ++ strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) { ++ free(ctx->outargs.argv[ctx->outargs.argc - 1]); ++ ctx->outargs.argv[--ctx->outargs.argc] = NULL; ++ } ++ ++ return 0; + } + + int fuse_opt_parse(struct fuse_args *args, void *data, +- const struct fuse_opt opts[], fuse_opt_proc_t proc) ++ const struct fuse_opt opts[], fuse_opt_proc_t proc) + { +- int res; +- struct fuse_opt_context ctx = { +- .data = data, +- .opt = opts, +- .proc = proc, +- }; +- +- if (!args || !args->argv || !args->argc) +- return 0; +- +- ctx.argc = args->argc; +- ctx.argv = args->argv; +- +- res = opt_parse(&ctx); +- if (res != -1) { +- struct fuse_args tmp = *args; +- *args = ctx.outargs; +- ctx.outargs = tmp; +- } +- free(ctx.opts); +- fuse_opt_free_args(&ctx.outargs); +- return res; ++ int res; ++ struct fuse_opt_context ctx = { ++ .data = data, ++ .opt = opts, ++ .proc = proc, ++ }; ++ ++ if (!args || !args->argv || !args->argc) { ++ return 0; ++ } ++ ++ ctx.argc = args->argc; ++ ctx.argv = args->argv; ++ ++ res = opt_parse(&ctx); ++ if (res != -1) { ++ struct fuse_args tmp = *args; ++ *args = ctx.outargs; ++ ctx.outargs = tmp; ++ } ++ free(ctx.opts); ++ fuse_opt_free_args(&ctx.outargs); ++ return res; + } +diff --git a/tools/virtiofsd/fuse_opt.h b/tools/virtiofsd/fuse_opt.h +index 69102555be..8f59b4d301 100644 +--- a/tools/virtiofsd/fuse_opt.h ++++ b/tools/virtiofsd/fuse_opt.h +@@ -1,10 +1,10 @@ + /* +- FUSE: Filesystem in Userspace +- Copyright (C) 2001-2007 Miklos Szeredi +- +- This program can be distributed under the terms of the GNU LGPLv2. +- See the file COPYING.LIB. +-*/ ++ * FUSE: Filesystem in Userspace ++ * Copyright (C) 2001-2007 Miklos Szeredi ++ * ++ * This program can be distributed under the terms of the GNU LGPLv2. ++ * See the file COPYING.LIB. ++ */ + + #ifndef FUSE_OPT_H_ + #define FUSE_OPT_H_ +@@ -37,7 +37,7 @@ + * + * - 'offsetof(struct foo, member)' actions i) and iii) + * +- * - -1 action ii) ++ * - -1 action ii) + * + * The 'offsetof()' macro is defined in the header. + * +@@ -48,7 +48,7 @@ + * + * The types of templates are: + * +- * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only ++ * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only + * themselves. Invalid values are "--" and anything beginning + * with "-o" + * +@@ -71,58 +71,67 @@ + * freed. + */ + struct fuse_opt { +- /** Matching template and optional parameter formatting */ +- const char *templ; ++ /** Matching template and optional parameter formatting */ ++ const char *templ; + +- /** +- * Offset of variable within 'data' parameter of fuse_opt_parse() +- * or -1 +- */ +- unsigned long offset; ++ /** ++ * Offset of variable within 'data' parameter of fuse_opt_parse() ++ * or -1 ++ */ ++ unsigned long offset; + +- /** +- * Value to set the variable to, or to be passed as 'key' to the +- * processing function. Ignored if template has a format +- */ +- int value; ++ /** ++ * Value to set the variable to, or to be passed as 'key' to the ++ * processing function. Ignored if template has a format ++ */ ++ int value; + }; + + /** +- * Key option. In case of a match, the processing function will be ++ * Key option. In case of a match, the processing function will be + * called with the specified key. + */ +-#define FUSE_OPT_KEY(templ, key) { templ, -1U, key } ++#define FUSE_OPT_KEY(templ, key) \ ++ { \ ++ templ, -1U, key \ ++ } + + /** +- * Last option. An array of 'struct fuse_opt' must end with a NULL ++ * Last option. An array of 'struct fuse_opt' must end with a NULL + * template value + */ +-#define FUSE_OPT_END { NULL, 0, 0 } ++#define FUSE_OPT_END \ ++ { \ ++ NULL, 0, 0 \ ++ } + + /** + * Argument list + */ + struct fuse_args { +- /** Argument count */ +- int argc; ++ /** Argument count */ ++ int argc; + +- /** Argument vector. NULL terminated */ +- char **argv; ++ /** Argument vector. NULL terminated */ ++ char **argv; + +- /** Is 'argv' allocated? */ +- int allocated; ++ /** Is 'argv' allocated? */ ++ int allocated; + }; + + /** + * Initializer for 'struct fuse_args' + */ +-#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 } ++#define FUSE_ARGS_INIT(argc, argv) \ ++ { \ ++ argc, argv, 0 \ ++ } + + /** + * Key value passed to the processing function if an option did not + * match any template + */ +-#define FUSE_OPT_KEY_OPT -1 ++#define FUSE_OPT_KEY_OPT -1 + + /** + * Key value passed to the processing function for all non-options +@@ -130,7 +139,7 @@ struct fuse_args { + * Non-options are the arguments beginning with a character other than + * '-' or all arguments after the special '--' option + */ +-#define FUSE_OPT_KEY_NONOPT -2 ++#define FUSE_OPT_KEY_NONOPT -2 + + /** + * Special key value for options to keep +@@ -174,7 +183,7 @@ struct fuse_args { + * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept + */ + typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, +- struct fuse_args *outargs); ++ struct fuse_args *outargs); + + /** + * Option parsing function +@@ -197,7 +206,7 @@ typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, + * @return -1 on error, 0 on success + */ + int fuse_opt_parse(struct fuse_args *args, void *data, +- const struct fuse_opt opts[], fuse_opt_proc_t proc); ++ const struct fuse_opt opts[], fuse_opt_proc_t proc); + + /** + * Add an option to a comma separated option list +diff --git a/tools/virtiofsd/fuse_signals.c b/tools/virtiofsd/fuse_signals.c +index 4271947bd4..19d6791cb9 100644 +--- a/tools/virtiofsd/fuse_signals.c ++++ b/tools/virtiofsd/fuse_signals.c +@@ -1,91 +1,95 @@ + /* +- FUSE: Filesystem in Userspace +- Copyright (C) 2001-2007 Miklos Szeredi +- +- Utility functions for setting signal handlers. +- +- This program can be distributed under the terms of the GNU LGPLv2. +- See the file COPYING.LIB +-*/ ++ * FUSE: Filesystem in Userspace ++ * Copyright (C) 2001-2007 Miklos Szeredi ++ * ++ * Utility functions for setting signal handlers. ++ * ++ * This program can be distributed under the terms of the GNU LGPLv2. ++ * See the file COPYING.LIB ++ */ + + #include "config.h" +-#include "fuse_lowlevel.h" + #include "fuse_i.h" ++#include "fuse_lowlevel.h" + +-#include +-#include + #include ++#include + #include ++#include + + static struct fuse_session *fuse_instance; + + static void exit_handler(int sig) + { +- if (fuse_instance) { +- fuse_session_exit(fuse_instance); +- if(sig <= 0) { +- fuse_log(FUSE_LOG_ERR, "assertion error: signal value <= 0\n"); +- abort(); +- } +- fuse_instance->error = sig; +- } ++ if (fuse_instance) { ++ fuse_session_exit(fuse_instance); ++ if (sig <= 0) { ++ fuse_log(FUSE_LOG_ERR, "assertion error: signal value <= 0\n"); ++ abort(); ++ } ++ fuse_instance->error = sig; ++ } + } + + static void do_nothing(int sig) + { +- (void) sig; ++ (void)sig; + } + + static int set_one_signal_handler(int sig, void (*handler)(int), int remove) + { +- struct sigaction sa; +- struct sigaction old_sa; ++ struct sigaction sa; ++ struct sigaction old_sa; + +- memset(&sa, 0, sizeof(struct sigaction)); +- sa.sa_handler = remove ? SIG_DFL : handler; +- sigemptyset(&(sa.sa_mask)); +- sa.sa_flags = 0; ++ memset(&sa, 0, sizeof(struct sigaction)); ++ sa.sa_handler = remove ? SIG_DFL : handler; ++ sigemptyset(&(sa.sa_mask)); ++ sa.sa_flags = 0; + +- if (sigaction(sig, NULL, &old_sa) == -1) { +- perror("fuse: cannot get old signal handler"); +- return -1; +- } ++ if (sigaction(sig, NULL, &old_sa) == -1) { ++ perror("fuse: cannot get old signal handler"); ++ return -1; ++ } + +- if (old_sa.sa_handler == (remove ? handler : SIG_DFL) && +- sigaction(sig, &sa, NULL) == -1) { +- perror("fuse: cannot set signal handler"); +- return -1; +- } +- return 0; ++ if (old_sa.sa_handler == (remove ? handler : SIG_DFL) && ++ sigaction(sig, &sa, NULL) == -1) { ++ perror("fuse: cannot set signal handler"); ++ return -1; ++ } ++ return 0; + } + + int fuse_set_signal_handlers(struct fuse_session *se) + { +- /* If we used SIG_IGN instead of the do_nothing function, +- then we would be unable to tell if we set SIG_IGN (and +- thus should reset to SIG_DFL in fuse_remove_signal_handlers) +- or if it was already set to SIG_IGN (and should be left +- untouched. */ +- if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 || +- set_one_signal_handler(SIGINT, exit_handler, 0) == -1 || +- set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 || +- set_one_signal_handler(SIGPIPE, do_nothing, 0) == -1) +- return -1; ++ /* ++ * If we used SIG_IGN instead of the do_nothing function, ++ * then we would be unable to tell if we set SIG_IGN (and ++ * thus should reset to SIG_DFL in fuse_remove_signal_handlers) ++ * or if it was already set to SIG_IGN (and should be left ++ * untouched. ++ */ ++ if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 || ++ set_one_signal_handler(SIGINT, exit_handler, 0) == -1 || ++ set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 || ++ set_one_signal_handler(SIGPIPE, do_nothing, 0) == -1) { ++ return -1; ++ } + +- fuse_instance = se; +- return 0; ++ fuse_instance = se; ++ return 0; + } + + void fuse_remove_signal_handlers(struct fuse_session *se) + { +- if (fuse_instance != se) +- fuse_log(FUSE_LOG_ERR, +- "fuse: fuse_remove_signal_handlers: unknown session\n"); +- else +- fuse_instance = NULL; ++ if (fuse_instance != se) { ++ fuse_log(FUSE_LOG_ERR, ++ "fuse: fuse_remove_signal_handlers: unknown session\n"); ++ } else { ++ fuse_instance = NULL; ++ } + +- set_one_signal_handler(SIGHUP, exit_handler, 1); +- set_one_signal_handler(SIGINT, exit_handler, 1); +- set_one_signal_handler(SIGTERM, exit_handler, 1); +- set_one_signal_handler(SIGPIPE, do_nothing, 1); ++ set_one_signal_handler(SIGHUP, exit_handler, 1); ++ set_one_signal_handler(SIGINT, exit_handler, 1); ++ set_one_signal_handler(SIGTERM, exit_handler, 1); ++ set_one_signal_handler(SIGPIPE, do_nothing, 1); + } +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index 5a2e64c6d0..5711dd2660 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -1,297 +1,309 @@ + /* +- FUSE: Filesystem in Userspace +- Copyright (C) 2001-2007 Miklos Szeredi ++ * FUSE: Filesystem in Userspace ++ * Copyright (C) 2001-2007 Miklos Szeredi ++ * ++ * Helper functions to create (simple) standalone programs. With the ++ * aid of these functions it should be possible to create full FUSE ++ * file system by implementing nothing but the request handlers. + +- Helper functions to create (simple) standalone programs. With the +- aid of these functions it should be possible to create full FUSE +- file system by implementing nothing but the request handlers. +- +- This program can be distributed under the terms of the GNU LGPLv2. +- See the file COPYING.LIB. +-*/ ++ * This program can be distributed under the terms of the GNU LGPLv2. ++ * See the file COPYING.LIB. ++ */ + + #include "config.h" + #include "fuse_i.h" ++#include "fuse_lowlevel.h" + #include "fuse_misc.h" + #include "fuse_opt.h" +-#include "fuse_lowlevel.h" + #include "mount_util.h" + ++#include ++#include ++#include + #include + #include +-#include +-#include + #include +-#include +-#include + #include ++#include + +-#define FUSE_HELPER_OPT(t, p) \ +- { t, offsetof(struct fuse_cmdline_opts, p), 1 } ++#define FUSE_HELPER_OPT(t, p) \ ++ { \ ++ t, offsetof(struct fuse_cmdline_opts, p), 1 \ ++ } + + static const struct fuse_opt fuse_helper_opts[] = { +- FUSE_HELPER_OPT("-h", show_help), +- FUSE_HELPER_OPT("--help", show_help), +- FUSE_HELPER_OPT("-V", show_version), +- FUSE_HELPER_OPT("--version", show_version), +- FUSE_HELPER_OPT("-d", debug), +- FUSE_HELPER_OPT("debug", debug), +- FUSE_HELPER_OPT("-d", foreground), +- FUSE_HELPER_OPT("debug", foreground), +- FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), +- FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), +- FUSE_HELPER_OPT("-f", foreground), +- FUSE_HELPER_OPT("fsname=", nodefault_subtype), +- FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), +- FUSE_HELPER_OPT("subtype=", nodefault_subtype), +- FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), +- FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads), +- FUSE_OPT_END ++ FUSE_HELPER_OPT("-h", show_help), ++ FUSE_HELPER_OPT("--help", show_help), ++ FUSE_HELPER_OPT("-V", show_version), ++ FUSE_HELPER_OPT("--version", show_version), ++ FUSE_HELPER_OPT("-d", debug), ++ FUSE_HELPER_OPT("debug", debug), ++ FUSE_HELPER_OPT("-d", foreground), ++ FUSE_HELPER_OPT("debug", foreground), ++ FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), ++ FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), ++ FUSE_HELPER_OPT("-f", foreground), ++ FUSE_HELPER_OPT("fsname=", nodefault_subtype), ++ FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), ++ FUSE_HELPER_OPT("subtype=", nodefault_subtype), ++ FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), ++ FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads), ++ FUSE_OPT_END + }; + + struct fuse_conn_info_opts { +- int atomic_o_trunc; +- int no_remote_posix_lock; +- int no_remote_flock; +- int splice_write; +- int splice_move; +- int splice_read; +- int no_splice_write; +- int no_splice_move; +- int no_splice_read; +- int auto_inval_data; +- int no_auto_inval_data; +- int no_readdirplus; +- int no_readdirplus_auto; +- int async_dio; +- int no_async_dio; +- int writeback_cache; +- int no_writeback_cache; +- int async_read; +- int sync_read; +- unsigned max_write; +- unsigned max_readahead; +- unsigned max_background; +- unsigned congestion_threshold; +- unsigned time_gran; +- int set_max_write; +- int set_max_readahead; +- int set_max_background; +- int set_congestion_threshold; +- int set_time_gran; ++ int atomic_o_trunc; ++ int no_remote_posix_lock; ++ int no_remote_flock; ++ int splice_write; ++ int splice_move; ++ int splice_read; ++ int no_splice_write; ++ int no_splice_move; ++ int no_splice_read; ++ int auto_inval_data; ++ int no_auto_inval_data; ++ int no_readdirplus; ++ int no_readdirplus_auto; ++ int async_dio; ++ int no_async_dio; ++ int writeback_cache; ++ int no_writeback_cache; ++ int async_read; ++ int sync_read; ++ unsigned max_write; ++ unsigned max_readahead; ++ unsigned max_background; ++ unsigned congestion_threshold; ++ unsigned time_gran; ++ int set_max_write; ++ int set_max_readahead; ++ int set_max_background; ++ int set_congestion_threshold; ++ int set_time_gran; + }; + +-#define CONN_OPTION(t, p, v) \ +- { t, offsetof(struct fuse_conn_info_opts, p), v } ++#define CONN_OPTION(t, p, v) \ ++ { \ ++ t, offsetof(struct fuse_conn_info_opts, p), v \ ++ } + static const struct fuse_opt conn_info_opt_spec[] = { +- CONN_OPTION("max_write=%u", max_write, 0), +- CONN_OPTION("max_write=", set_max_write, 1), +- CONN_OPTION("max_readahead=%u", max_readahead, 0), +- CONN_OPTION("max_readahead=", set_max_readahead, 1), +- CONN_OPTION("max_background=%u", max_background, 0), +- CONN_OPTION("max_background=", set_max_background, 1), +- CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0), +- CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1), +- CONN_OPTION("sync_read", sync_read, 1), +- CONN_OPTION("async_read", async_read, 1), +- CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1), +- CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1), +- CONN_OPTION("no_remote_lock", no_remote_flock, 1), +- CONN_OPTION("no_remote_flock", no_remote_flock, 1), +- CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1), +- CONN_OPTION("splice_write", splice_write, 1), +- CONN_OPTION("no_splice_write", no_splice_write, 1), +- CONN_OPTION("splice_move", splice_move, 1), +- CONN_OPTION("no_splice_move", no_splice_move, 1), +- CONN_OPTION("splice_read", splice_read, 1), +- CONN_OPTION("no_splice_read", no_splice_read, 1), +- CONN_OPTION("auto_inval_data", auto_inval_data, 1), +- CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1), +- CONN_OPTION("readdirplus=no", no_readdirplus, 1), +- CONN_OPTION("readdirplus=yes", no_readdirplus, 0), +- CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1), +- CONN_OPTION("readdirplus=auto", no_readdirplus, 0), +- CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0), +- CONN_OPTION("async_dio", async_dio, 1), +- CONN_OPTION("no_async_dio", no_async_dio, 1), +- CONN_OPTION("writeback_cache", writeback_cache, 1), +- CONN_OPTION("no_writeback_cache", no_writeback_cache, 1), +- CONN_OPTION("time_gran=%u", time_gran, 0), +- CONN_OPTION("time_gran=", set_time_gran, 1), +- FUSE_OPT_END ++ CONN_OPTION("max_write=%u", max_write, 0), ++ CONN_OPTION("max_write=", set_max_write, 1), ++ CONN_OPTION("max_readahead=%u", max_readahead, 0), ++ CONN_OPTION("max_readahead=", set_max_readahead, 1), ++ CONN_OPTION("max_background=%u", max_background, 0), ++ CONN_OPTION("max_background=", set_max_background, 1), ++ CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0), ++ CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1), ++ CONN_OPTION("sync_read", sync_read, 1), ++ CONN_OPTION("async_read", async_read, 1), ++ CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1), ++ CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1), ++ CONN_OPTION("no_remote_lock", no_remote_flock, 1), ++ CONN_OPTION("no_remote_flock", no_remote_flock, 1), ++ CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1), ++ CONN_OPTION("splice_write", splice_write, 1), ++ CONN_OPTION("no_splice_write", no_splice_write, 1), ++ CONN_OPTION("splice_move", splice_move, 1), ++ CONN_OPTION("no_splice_move", no_splice_move, 1), ++ CONN_OPTION("splice_read", splice_read, 1), ++ CONN_OPTION("no_splice_read", no_splice_read, 1), ++ CONN_OPTION("auto_inval_data", auto_inval_data, 1), ++ CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1), ++ CONN_OPTION("readdirplus=no", no_readdirplus, 1), ++ CONN_OPTION("readdirplus=yes", no_readdirplus, 0), ++ CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1), ++ CONN_OPTION("readdirplus=auto", no_readdirplus, 0), ++ CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0), ++ CONN_OPTION("async_dio", async_dio, 1), ++ CONN_OPTION("no_async_dio", no_async_dio, 1), ++ CONN_OPTION("writeback_cache", writeback_cache, 1), ++ CONN_OPTION("no_writeback_cache", no_writeback_cache, 1), ++ CONN_OPTION("time_gran=%u", time_gran, 0), ++ CONN_OPTION("time_gran=", set_time_gran, 1), ++ FUSE_OPT_END + }; + + + void fuse_cmdline_help(void) + { +- printf(" -h --help print help\n" +- " -V --version print version\n" +- " -d -o debug enable debug output (implies -f)\n" +- " -f foreground operation\n" +- " -o max_idle_threads the maximum number of idle worker threads\n" +- " allowed (default: 10)\n"); ++ printf( ++ " -h --help print help\n" ++ " -V --version print version\n" ++ " -d -o debug enable debug output (implies -f)\n" ++ " -f foreground operation\n" ++ " -o max_idle_threads the maximum number of idle worker threads\n" ++ " allowed (default: 10)\n"); + } + + static int fuse_helper_opt_proc(void *data, const char *arg, int key, +- struct fuse_args *outargs) ++ struct fuse_args *outargs) + { +- (void) outargs; +- struct fuse_cmdline_opts *opts = data; +- +- switch (key) { +- case FUSE_OPT_KEY_NONOPT: +- if (!opts->mountpoint) { +- if (fuse_mnt_parse_fuse_fd(arg) != -1) { +- return fuse_opt_add_opt(&opts->mountpoint, arg); +- } +- +- char mountpoint[PATH_MAX] = ""; +- if (realpath(arg, mountpoint) == NULL) { +- fuse_log(FUSE_LOG_ERR, +- "fuse: bad mount point `%s': %s\n", +- arg, strerror(errno)); +- return -1; +- } +- return fuse_opt_add_opt(&opts->mountpoint, mountpoint); +- } else { +- fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg); +- return -1; +- } +- +- default: +- /* Pass through unknown options */ +- return 1; +- } ++ (void)outargs; ++ struct fuse_cmdline_opts *opts = data; ++ ++ switch (key) { ++ case FUSE_OPT_KEY_NONOPT: ++ if (!opts->mountpoint) { ++ if (fuse_mnt_parse_fuse_fd(arg) != -1) { ++ return fuse_opt_add_opt(&opts->mountpoint, arg); ++ } ++ ++ char mountpoint[PATH_MAX] = ""; ++ if (realpath(arg, mountpoint) == NULL) { ++ fuse_log(FUSE_LOG_ERR, "fuse: bad mount point `%s': %s\n", arg, ++ strerror(errno)); ++ return -1; ++ } ++ return fuse_opt_add_opt(&opts->mountpoint, mountpoint); ++ } else { ++ fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg); ++ return -1; ++ } ++ ++ default: ++ /* Pass through unknown options */ ++ return 1; ++ } + } + +-int fuse_parse_cmdline(struct fuse_args *args, +- struct fuse_cmdline_opts *opts) ++int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts) + { +- memset(opts, 0, sizeof(struct fuse_cmdline_opts)); ++ memset(opts, 0, sizeof(struct fuse_cmdline_opts)); + +- opts->max_idle_threads = 10; ++ opts->max_idle_threads = 10; + +- if (fuse_opt_parse(args, opts, fuse_helper_opts, +- fuse_helper_opt_proc) == -1) +- return -1; ++ if (fuse_opt_parse(args, opts, fuse_helper_opts, fuse_helper_opt_proc) == ++ -1) { ++ return -1; ++ } + +- return 0; ++ return 0; + } + + + int fuse_daemonize(int foreground) + { +- if (!foreground) { +- int nullfd; +- int waiter[2]; +- char completed; +- +- if (pipe(waiter)) { +- perror("fuse_daemonize: pipe"); +- return -1; +- } +- +- /* +- * demonize current process by forking it and killing the +- * parent. This makes current process as a child of 'init'. +- */ +- switch(fork()) { +- case -1: +- perror("fuse_daemonize: fork"); +- return -1; +- case 0: +- break; +- default: +- (void) read(waiter[0], &completed, sizeof(completed)); +- _exit(0); +- } +- +- if (setsid() == -1) { +- perror("fuse_daemonize: setsid"); +- return -1; +- } +- +- (void) chdir("/"); +- +- nullfd = open("/dev/null", O_RDWR, 0); +- if (nullfd != -1) { +- (void) dup2(nullfd, 0); +- (void) dup2(nullfd, 1); +- (void) dup2(nullfd, 2); +- if (nullfd > 2) +- close(nullfd); +- } +- +- /* Propagate completion of daemon initialization */ +- completed = 1; +- (void) write(waiter[1], &completed, sizeof(completed)); +- close(waiter[0]); +- close(waiter[1]); +- } else { +- (void) chdir("/"); +- } +- return 0; ++ if (!foreground) { ++ int nullfd; ++ int waiter[2]; ++ char completed; ++ ++ if (pipe(waiter)) { ++ perror("fuse_daemonize: pipe"); ++ return -1; ++ } ++ ++ /* ++ * demonize current process by forking it and killing the ++ * parent. This makes current process as a child of 'init'. ++ */ ++ switch (fork()) { ++ case -1: ++ perror("fuse_daemonize: fork"); ++ return -1; ++ case 0: ++ break; ++ default: ++ (void)read(waiter[0], &completed, sizeof(completed)); ++ _exit(0); ++ } ++ ++ if (setsid() == -1) { ++ perror("fuse_daemonize: setsid"); ++ return -1; ++ } ++ ++ (void)chdir("/"); ++ ++ nullfd = open("/dev/null", O_RDWR, 0); ++ if (nullfd != -1) { ++ (void)dup2(nullfd, 0); ++ (void)dup2(nullfd, 1); ++ (void)dup2(nullfd, 2); ++ if (nullfd > 2) { ++ close(nullfd); ++ } ++ } ++ ++ /* Propagate completion of daemon initialization */ ++ completed = 1; ++ (void)write(waiter[1], &completed, sizeof(completed)); ++ close(waiter[0]); ++ close(waiter[1]); ++ } else { ++ (void)chdir("/"); ++ } ++ return 0; + } + + void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, +- struct fuse_conn_info *conn) ++ struct fuse_conn_info *conn) + { +- if(opts->set_max_write) +- conn->max_write = opts->max_write; +- if(opts->set_max_background) +- conn->max_background = opts->max_background; +- if(opts->set_congestion_threshold) +- conn->congestion_threshold = opts->congestion_threshold; +- if(opts->set_time_gran) +- conn->time_gran = opts->time_gran; +- if(opts->set_max_readahead) +- conn->max_readahead = opts->max_readahead; +- +-#define LL_ENABLE(cond,cap) \ +- if (cond) conn->want |= (cap) +-#define LL_DISABLE(cond,cap) \ +- if (cond) conn->want &= ~(cap) +- +- LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ); +- LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ); +- +- LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE); +- LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE); +- +- LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE); +- LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE); +- +- LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); +- LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); +- +- LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS); +- LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO); +- +- LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO); +- LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO); +- +- LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE); +- LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE); +- +- LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ); +- LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ); +- +- LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS); +- LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS); ++ if (opts->set_max_write) { ++ conn->max_write = opts->max_write; ++ } ++ if (opts->set_max_background) { ++ conn->max_background = opts->max_background; ++ } ++ if (opts->set_congestion_threshold) { ++ conn->congestion_threshold = opts->congestion_threshold; ++ } ++ if (opts->set_time_gran) { ++ conn->time_gran = opts->time_gran; ++ } ++ if (opts->set_max_readahead) { ++ conn->max_readahead = opts->max_readahead; ++ } ++ ++#define LL_ENABLE(cond, cap) \ ++ if (cond) \ ++ conn->want |= (cap) ++#define LL_DISABLE(cond, cap) \ ++ if (cond) \ ++ conn->want &= ~(cap) ++ ++ LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ); ++ LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ); ++ ++ LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE); ++ LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE); ++ ++ LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE); ++ LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE); ++ ++ LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); ++ LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); ++ ++ LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS); ++ LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO); ++ ++ LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO); ++ LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO); ++ ++ LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE); ++ LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE); ++ ++ LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ); ++ LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ); ++ ++ LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS); ++ LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS); + } + +-struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args) ++struct fuse_conn_info_opts *fuse_parse_conn_info_opts(struct fuse_args *args) + { +- struct fuse_conn_info_opts *opts; +- +- opts = calloc(1, sizeof(struct fuse_conn_info_opts)); +- if(opts == NULL) { +- fuse_log(FUSE_LOG_ERR, "calloc failed\n"); +- return NULL; +- } +- if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) { +- free(opts); +- return NULL; +- } +- return opts; ++ struct fuse_conn_info_opts *opts; ++ ++ opts = calloc(1, sizeof(struct fuse_conn_info_opts)); ++ if (opts == NULL) { ++ fuse_log(FUSE_LOG_ERR, "calloc failed\n"); ++ return NULL; ++ } ++ if (fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) { ++ free(opts); ++ return NULL; ++ } ++ return opts; + } +diff --git a/tools/virtiofsd/passthrough_helpers.h b/tools/virtiofsd/passthrough_helpers.h +index 7c5f561fbc..0b98275ed5 100644 +--- a/tools/virtiofsd/passthrough_helpers.h ++++ b/tools/virtiofsd/passthrough_helpers.h +@@ -28,23 +28,24 @@ + * operation + */ + static int mknod_wrapper(int dirfd, const char *path, const char *link, +- int mode, dev_t rdev) ++ int mode, dev_t rdev) + { +- int res; ++ int res; + +- if (S_ISREG(mode)) { +- res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode); +- if (res >= 0) +- res = close(res); +- } else if (S_ISDIR(mode)) { +- res = mkdirat(dirfd, path, mode); +- } else if (S_ISLNK(mode) && link != NULL) { +- res = symlinkat(link, dirfd, path); +- } else if (S_ISFIFO(mode)) { +- res = mkfifoat(dirfd, path, mode); +- } else { +- res = mknodat(dirfd, path, mode, rdev); +- } ++ if (S_ISREG(mode)) { ++ res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode); ++ if (res >= 0) { ++ res = close(res); ++ } ++ } else if (S_ISDIR(mode)) { ++ res = mkdirat(dirfd, path, mode); ++ } else if (S_ISLNK(mode) && link != NULL) { ++ res = symlinkat(link, dirfd, path); ++ } else if (S_ISFIFO(mode)) { ++ res = mkfifoat(dirfd, path, mode); ++ } else { ++ res = mknodat(dirfd, path, mode, rdev); ++ } + +- return res; ++ return res; + } +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index e5f7115bc1..c5850ef803 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1,12 +1,12 @@ + /* +- FUSE: Filesystem in Userspace +- Copyright (C) 2001-2007 Miklos Szeredi +- +- This program can be distributed under the terms of the GNU GPLv2. +- See the file COPYING. +-*/ ++ * FUSE: Filesystem in Userspace ++ * Copyright (C) 2001-2007 Miklos Szeredi ++ * ++ * This program can be distributed under the terms of the GNU GPLv2. ++ * See the file COPYING. ++ */ + +-/** @file ++/* + * + * This file system mirrors the existing file system hierarchy of the + * system, starting at the root file system. This is implemented by +@@ -28,7 +28,8 @@ + * + * Compile with: + * +- * gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o passthrough_ll ++ * gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o ++ * passthrough_ll + * + * ## Source code ## + * \include passthrough_ll.c +@@ -39,1299 +40,1365 @@ + + #include "config.h" + +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include + #include ++#include + #include ++#include + #include ++#include + #include ++#include ++#include ++#include ++#include ++#include + #include + #include ++#include + + #include "passthrough_helpers.h" + +-/* We are re-using pointers to our `struct lo_inode` and `struct +- lo_dirp` elements as inodes. This means that we must be able to +- store uintptr_t values in a fuse_ino_t variable. The following +- incantation checks this condition at compile time. */ +-#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus ++/* ++ * We are re-using pointers to our `struct lo_inode` and `struct ++ * lo_dirp` elements as inodes. This means that we must be able to ++ * store uintptr_t values in a fuse_ino_t variable. The following ++ * incantation checks this condition at compile time. ++ */ ++#if defined(__GNUC__) && \ ++ (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && \ ++ !defined __cplusplus + _Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t), +- "fuse_ino_t too small to hold uintptr_t values!"); ++ "fuse_ino_t too small to hold uintptr_t values!"); + #else +-struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \ +- { unsigned _uintptr_to_must_hold_fuse_ino_t: +- ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); }; ++struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { ++ unsigned _uintptr_to_must_hold_fuse_ino_t ++ : ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); ++}; + #endif + + struct lo_inode { +- struct lo_inode *next; /* protected by lo->mutex */ +- struct lo_inode *prev; /* protected by lo->mutex */ +- int fd; +- bool is_symlink; +- ino_t ino; +- dev_t dev; +- uint64_t refcount; /* protected by lo->mutex */ ++ struct lo_inode *next; /* protected by lo->mutex */ ++ struct lo_inode *prev; /* protected by lo->mutex */ ++ int fd; ++ bool is_symlink; ++ ino_t ino; ++ dev_t dev; ++ uint64_t refcount; /* protected by lo->mutex */ + }; + + enum { +- CACHE_NEVER, +- CACHE_NORMAL, +- CACHE_ALWAYS, ++ CACHE_NEVER, ++ CACHE_NORMAL, ++ CACHE_ALWAYS, + }; + + struct lo_data { +- pthread_mutex_t mutex; +- int debug; +- int writeback; +- int flock; +- int xattr; +- const char *source; +- double timeout; +- int cache; +- int timeout_set; +- struct lo_inode root; /* protected by lo->mutex */ ++ pthread_mutex_t mutex; ++ int debug; ++ int writeback; ++ int flock; ++ int xattr; ++ const char *source; ++ double timeout; ++ int cache; ++ int timeout_set; ++ struct lo_inode root; /* protected by lo->mutex */ + }; + + static const struct fuse_opt lo_opts[] = { +- { "writeback", +- offsetof(struct lo_data, writeback), 1 }, +- { "no_writeback", +- offsetof(struct lo_data, writeback), 0 }, +- { "source=%s", +- offsetof(struct lo_data, source), 0 }, +- { "flock", +- offsetof(struct lo_data, flock), 1 }, +- { "no_flock", +- offsetof(struct lo_data, flock), 0 }, +- { "xattr", +- offsetof(struct lo_data, xattr), 1 }, +- { "no_xattr", +- offsetof(struct lo_data, xattr), 0 }, +- { "timeout=%lf", +- offsetof(struct lo_data, timeout), 0 }, +- { "timeout=", +- offsetof(struct lo_data, timeout_set), 1 }, +- { "cache=never", +- offsetof(struct lo_data, cache), CACHE_NEVER }, +- { "cache=auto", +- offsetof(struct lo_data, cache), CACHE_NORMAL }, +- { "cache=always", +- offsetof(struct lo_data, cache), CACHE_ALWAYS }, +- +- FUSE_OPT_END ++ { "writeback", offsetof(struct lo_data, writeback), 1 }, ++ { "no_writeback", offsetof(struct lo_data, writeback), 0 }, ++ { "source=%s", offsetof(struct lo_data, source), 0 }, ++ { "flock", offsetof(struct lo_data, flock), 1 }, ++ { "no_flock", offsetof(struct lo_data, flock), 0 }, ++ { "xattr", offsetof(struct lo_data, xattr), 1 }, ++ { "no_xattr", offsetof(struct lo_data, xattr), 0 }, ++ { "timeout=%lf", offsetof(struct lo_data, timeout), 0 }, ++ { "timeout=", offsetof(struct lo_data, timeout_set), 1 }, ++ { "cache=never", offsetof(struct lo_data, cache), CACHE_NEVER }, ++ { "cache=auto", offsetof(struct lo_data, cache), CACHE_NORMAL }, ++ { "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS }, ++ ++ FUSE_OPT_END + }; + + static struct lo_data *lo_data(fuse_req_t req) + { +- return (struct lo_data *) fuse_req_userdata(req); ++ return (struct lo_data *)fuse_req_userdata(req); + } + + static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) + { +- if (ino == FUSE_ROOT_ID) +- return &lo_data(req)->root; +- else +- return (struct lo_inode *) (uintptr_t) ino; ++ if (ino == FUSE_ROOT_ID) { ++ return &lo_data(req)->root; ++ } else { ++ return (struct lo_inode *)(uintptr_t)ino; ++ } + } + + static int lo_fd(fuse_req_t req, fuse_ino_t ino) + { +- return lo_inode(req, ino)->fd; ++ return lo_inode(req, ino)->fd; + } + + static bool lo_debug(fuse_req_t req) + { +- return lo_data(req)->debug != 0; ++ return lo_data(req)->debug != 0; + } + +-static void lo_init(void *userdata, +- struct fuse_conn_info *conn) ++static void lo_init(void *userdata, struct fuse_conn_info *conn) + { +- struct lo_data *lo = (struct lo_data*) userdata; +- +- if(conn->capable & FUSE_CAP_EXPORT_SUPPORT) +- conn->want |= FUSE_CAP_EXPORT_SUPPORT; +- +- if (lo->writeback && +- conn->capable & FUSE_CAP_WRITEBACK_CACHE) { +- if (lo->debug) +- fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); +- conn->want |= FUSE_CAP_WRITEBACK_CACHE; +- } +- if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { +- if (lo->debug) +- fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); +- conn->want |= FUSE_CAP_FLOCK_LOCKS; +- } ++ struct lo_data *lo = (struct lo_data *)userdata; ++ ++ if (conn->capable & FUSE_CAP_EXPORT_SUPPORT) { ++ conn->want |= FUSE_CAP_EXPORT_SUPPORT; ++ } ++ ++ if (lo->writeback && conn->capable & FUSE_CAP_WRITEBACK_CACHE) { ++ if (lo->debug) { ++ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); ++ } ++ conn->want |= FUSE_CAP_WRITEBACK_CACHE; ++ } ++ if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { ++ if (lo->debug) { ++ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); ++ } ++ conn->want |= FUSE_CAP_FLOCK_LOCKS; ++ } + } + + static void lo_getattr(fuse_req_t req, fuse_ino_t ino, +- struct fuse_file_info *fi) ++ struct fuse_file_info *fi) + { +- int res; +- struct stat buf; +- struct lo_data *lo = lo_data(req); ++ int res; ++ struct stat buf; ++ struct lo_data *lo = lo_data(req); + +- (void) fi; ++ (void)fi; + +- res = fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); +- if (res == -1) +- return (void) fuse_reply_err(req, errno); ++ res = ++ fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); ++ if (res == -1) { ++ return (void)fuse_reply_err(req, errno); ++ } + +- fuse_reply_attr(req, &buf, lo->timeout); ++ fuse_reply_attr(req, &buf, lo->timeout); + } + + static int utimensat_empty_nofollow(struct lo_inode *inode, +- const struct timespec *tv) ++ const struct timespec *tv) + { +- int res; +- char procname[64]; +- +- if (inode->is_symlink) { +- res = utimensat(inode->fd, "", tv, +- AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); +- if (res == -1 && errno == EINVAL) { +- /* Sorry, no race free way to set times on symlink. */ +- errno = EPERM; +- } +- return res; +- } +- sprintf(procname, "/proc/self/fd/%i", inode->fd); +- +- return utimensat(AT_FDCWD, procname, tv, 0); ++ int res; ++ char procname[64]; ++ ++ if (inode->is_symlink) { ++ res = utimensat(inode->fd, "", tv, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); ++ if (res == -1 && errno == EINVAL) { ++ /* Sorry, no race free way to set times on symlink. */ ++ errno = EPERM; ++ } ++ return res; ++ } ++ sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ ++ return utimensat(AT_FDCWD, procname, tv, 0); + } + + static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, +- int valid, struct fuse_file_info *fi) ++ int valid, struct fuse_file_info *fi) + { +- int saverr; +- char procname[64]; +- struct lo_inode *inode = lo_inode(req, ino); +- int ifd = inode->fd; +- int res; +- +- if (valid & FUSE_SET_ATTR_MODE) { +- if (fi) { +- res = fchmod(fi->fh, attr->st_mode); +- } else { +- sprintf(procname, "/proc/self/fd/%i", ifd); +- res = chmod(procname, attr->st_mode); +- } +- if (res == -1) +- goto out_err; +- } +- if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) { +- uid_t uid = (valid & FUSE_SET_ATTR_UID) ? +- attr->st_uid : (uid_t) -1; +- gid_t gid = (valid & FUSE_SET_ATTR_GID) ? +- attr->st_gid : (gid_t) -1; +- +- res = fchownat(ifd, "", uid, gid, +- AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); +- if (res == -1) +- goto out_err; +- } +- if (valid & FUSE_SET_ATTR_SIZE) { +- if (fi) { +- res = ftruncate(fi->fh, attr->st_size); +- } else { +- sprintf(procname, "/proc/self/fd/%i", ifd); +- res = truncate(procname, attr->st_size); +- } +- if (res == -1) +- goto out_err; +- } +- if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { +- struct timespec tv[2]; +- +- tv[0].tv_sec = 0; +- tv[1].tv_sec = 0; +- tv[0].tv_nsec = UTIME_OMIT; +- tv[1].tv_nsec = UTIME_OMIT; +- +- if (valid & FUSE_SET_ATTR_ATIME_NOW) +- tv[0].tv_nsec = UTIME_NOW; +- else if (valid & FUSE_SET_ATTR_ATIME) +- tv[0] = attr->st_atim; +- +- if (valid & FUSE_SET_ATTR_MTIME_NOW) +- tv[1].tv_nsec = UTIME_NOW; +- else if (valid & FUSE_SET_ATTR_MTIME) +- tv[1] = attr->st_mtim; +- +- if (fi) +- res = futimens(fi->fh, tv); +- else +- res = utimensat_empty_nofollow(inode, tv); +- if (res == -1) +- goto out_err; +- } +- +- return lo_getattr(req, ino, fi); ++ int saverr; ++ char procname[64]; ++ struct lo_inode *inode = lo_inode(req, ino); ++ int ifd = inode->fd; ++ int res; ++ ++ if (valid & FUSE_SET_ATTR_MODE) { ++ if (fi) { ++ res = fchmod(fi->fh, attr->st_mode); ++ } else { ++ sprintf(procname, "/proc/self/fd/%i", ifd); ++ res = chmod(procname, attr->st_mode); ++ } ++ if (res == -1) { ++ goto out_err; ++ } ++ } ++ if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) { ++ uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t)-1; ++ gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t)-1; ++ ++ res = fchownat(ifd, "", uid, gid, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); ++ if (res == -1) { ++ goto out_err; ++ } ++ } ++ if (valid & FUSE_SET_ATTR_SIZE) { ++ if (fi) { ++ res = ftruncate(fi->fh, attr->st_size); ++ } else { ++ sprintf(procname, "/proc/self/fd/%i", ifd); ++ res = truncate(procname, attr->st_size); ++ } ++ if (res == -1) { ++ goto out_err; ++ } ++ } ++ if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { ++ struct timespec tv[2]; ++ ++ tv[0].tv_sec = 0; ++ tv[1].tv_sec = 0; ++ tv[0].tv_nsec = UTIME_OMIT; ++ tv[1].tv_nsec = UTIME_OMIT; ++ ++ if (valid & FUSE_SET_ATTR_ATIME_NOW) { ++ tv[0].tv_nsec = UTIME_NOW; ++ } else if (valid & FUSE_SET_ATTR_ATIME) { ++ tv[0] = attr->st_atim; ++ } ++ ++ if (valid & FUSE_SET_ATTR_MTIME_NOW) { ++ tv[1].tv_nsec = UTIME_NOW; ++ } else if (valid & FUSE_SET_ATTR_MTIME) { ++ tv[1] = attr->st_mtim; ++ } ++ ++ if (fi) { ++ res = futimens(fi->fh, tv); ++ } else { ++ res = utimensat_empty_nofollow(inode, tv); ++ } ++ if (res == -1) { ++ goto out_err; ++ } ++ } ++ ++ return lo_getattr(req, ino, fi); + + out_err: +- saverr = errno; +- fuse_reply_err(req, saverr); ++ saverr = errno; ++ fuse_reply_err(req, saverr); + } + + static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st) + { +- struct lo_inode *p; +- struct lo_inode *ret = NULL; +- +- pthread_mutex_lock(&lo->mutex); +- for (p = lo->root.next; p != &lo->root; p = p->next) { +- if (p->ino == st->st_ino && p->dev == st->st_dev) { +- assert(p->refcount > 0); +- ret = p; +- ret->refcount++; +- break; +- } +- } +- pthread_mutex_unlock(&lo->mutex); +- return ret; ++ struct lo_inode *p; ++ struct lo_inode *ret = NULL; ++ ++ pthread_mutex_lock(&lo->mutex); ++ for (p = lo->root.next; p != &lo->root; p = p->next) { ++ if (p->ino == st->st_ino && p->dev == st->st_dev) { ++ assert(p->refcount > 0); ++ ret = p; ++ ret->refcount++; ++ break; ++ } ++ } ++ pthread_mutex_unlock(&lo->mutex); ++ return ret; + } + + static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, +- struct fuse_entry_param *e) ++ struct fuse_entry_param *e) + { +- int newfd; +- int res; +- int saverr; +- struct lo_data *lo = lo_data(req); +- struct lo_inode *inode; +- +- memset(e, 0, sizeof(*e)); +- e->attr_timeout = lo->timeout; +- e->entry_timeout = lo->timeout; +- +- newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW); +- if (newfd == -1) +- goto out_err; +- +- res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); +- if (res == -1) +- goto out_err; +- +- inode = lo_find(lo_data(req), &e->attr); +- if (inode) { +- close(newfd); +- newfd = -1; +- } else { +- struct lo_inode *prev, *next; +- +- saverr = ENOMEM; +- inode = calloc(1, sizeof(struct lo_inode)); +- if (!inode) +- goto out_err; +- +- inode->is_symlink = S_ISLNK(e->attr.st_mode); +- inode->refcount = 1; +- inode->fd = newfd; +- inode->ino = e->attr.st_ino; +- inode->dev = e->attr.st_dev; +- +- pthread_mutex_lock(&lo->mutex); +- prev = &lo->root; +- next = prev->next; +- next->prev = inode; +- inode->next = next; +- inode->prev = prev; +- prev->next = inode; +- pthread_mutex_unlock(&lo->mutex); +- } +- e->ino = (uintptr_t) inode; +- +- if (lo_debug(req)) +- fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", +- (unsigned long long) parent, name, (unsigned long long) e->ino); +- +- return 0; ++ int newfd; ++ int res; ++ int saverr; ++ struct lo_data *lo = lo_data(req); ++ struct lo_inode *inode; ++ ++ memset(e, 0, sizeof(*e)); ++ e->attr_timeout = lo->timeout; ++ e->entry_timeout = lo->timeout; ++ ++ newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW); ++ if (newfd == -1) { ++ goto out_err; ++ } ++ ++ res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); ++ if (res == -1) { ++ goto out_err; ++ } ++ ++ inode = lo_find(lo_data(req), &e->attr); ++ if (inode) { ++ close(newfd); ++ newfd = -1; ++ } else { ++ struct lo_inode *prev, *next; ++ ++ saverr = ENOMEM; ++ inode = calloc(1, sizeof(struct lo_inode)); ++ if (!inode) { ++ goto out_err; ++ } ++ ++ inode->is_symlink = S_ISLNK(e->attr.st_mode); ++ inode->refcount = 1; ++ inode->fd = newfd; ++ inode->ino = e->attr.st_ino; ++ inode->dev = e->attr.st_dev; ++ ++ pthread_mutex_lock(&lo->mutex); ++ prev = &lo->root; ++ next = prev->next; ++ next->prev = inode; ++ inode->next = next; ++ inode->prev = prev; ++ prev->next = inode; ++ pthread_mutex_unlock(&lo->mutex); ++ } ++ e->ino = (uintptr_t)inode; ++ ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", ++ (unsigned long long)parent, name, (unsigned long long)e->ino); ++ } ++ ++ return 0; + + out_err: +- saverr = errno; +- if (newfd != -1) +- close(newfd); +- return saverr; ++ saverr = errno; ++ if (newfd != -1) { ++ close(newfd); ++ } ++ return saverr; + } + + static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) + { +- struct fuse_entry_param e; +- int err; +- +- if (lo_debug(req)) +- fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", +- parent, name); +- +- err = lo_do_lookup(req, parent, name, &e); +- if (err) +- fuse_reply_err(req, err); +- else +- fuse_reply_entry(req, &e); ++ struct fuse_entry_param e; ++ int err; ++ ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", ++ parent, name); ++ } ++ ++ err = lo_do_lookup(req, parent, name, &e); ++ if (err) { ++ fuse_reply_err(req, err); ++ } else { ++ fuse_reply_entry(req, &e); ++ } + } + + static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, +- const char *name, mode_t mode, dev_t rdev, +- const char *link) ++ const char *name, mode_t mode, dev_t rdev, ++ const char *link) + { +- int res; +- int saverr; +- struct lo_inode *dir = lo_inode(req, parent); +- struct fuse_entry_param e; ++ int res; ++ int saverr; ++ struct lo_inode *dir = lo_inode(req, parent); ++ struct fuse_entry_param e; + +- saverr = ENOMEM; ++ saverr = ENOMEM; + +- res = mknod_wrapper(dir->fd, name, link, mode, rdev); ++ res = mknod_wrapper(dir->fd, name, link, mode, rdev); + +- saverr = errno; +- if (res == -1) +- goto out; ++ saverr = errno; ++ if (res == -1) { ++ goto out; ++ } + +- saverr = lo_do_lookup(req, parent, name, &e); +- if (saverr) +- goto out; ++ saverr = lo_do_lookup(req, parent, name, &e); ++ if (saverr) { ++ goto out; ++ } + +- if (lo_debug(req)) +- fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", +- (unsigned long long) parent, name, (unsigned long long) e.ino); ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", ++ (unsigned long long)parent, name, (unsigned long long)e.ino); ++ } + +- fuse_reply_entry(req, &e); +- return; ++ fuse_reply_entry(req, &e); ++ return; + + out: +- fuse_reply_err(req, saverr); ++ fuse_reply_err(req, saverr); + } + +-static void lo_mknod(fuse_req_t req, fuse_ino_t parent, +- const char *name, mode_t mode, dev_t rdev) ++static void lo_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, ++ mode_t mode, dev_t rdev) + { +- lo_mknod_symlink(req, parent, name, mode, rdev, NULL); ++ lo_mknod_symlink(req, parent, name, mode, rdev, NULL); + } + + static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, +- mode_t mode) ++ mode_t mode) + { +- lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL); ++ lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL); + } + +-static void lo_symlink(fuse_req_t req, const char *link, +- fuse_ino_t parent, const char *name) ++static void lo_symlink(fuse_req_t req, const char *link, fuse_ino_t parent, ++ const char *name) + { +- lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link); ++ lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link); + } + + static int linkat_empty_nofollow(struct lo_inode *inode, int dfd, +- const char *name) ++ const char *name) + { +- int res; +- char procname[64]; ++ int res; ++ char procname[64]; + +- if (inode->is_symlink) { +- res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH); +- if (res == -1 && (errno == ENOENT || errno == EINVAL)) { +- /* Sorry, no race free way to hard-link a symlink. */ +- errno = EPERM; +- } +- return res; +- } ++ if (inode->is_symlink) { ++ res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH); ++ if (res == -1 && (errno == ENOENT || errno == EINVAL)) { ++ /* Sorry, no race free way to hard-link a symlink. */ ++ errno = EPERM; ++ } ++ return res; ++ } + +- sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ sprintf(procname, "/proc/self/fd/%i", inode->fd); + +- return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW); ++ return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW); + } + + static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, +- const char *name) ++ const char *name) + { +- int res; +- struct lo_data *lo = lo_data(req); +- struct lo_inode *inode = lo_inode(req, ino); +- struct fuse_entry_param e; +- int saverr; +- +- memset(&e, 0, sizeof(struct fuse_entry_param)); +- e.attr_timeout = lo->timeout; +- e.entry_timeout = lo->timeout; +- +- res = linkat_empty_nofollow(inode, lo_fd(req, parent), name); +- if (res == -1) +- goto out_err; +- +- res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); +- if (res == -1) +- goto out_err; +- +- pthread_mutex_lock(&lo->mutex); +- inode->refcount++; +- pthread_mutex_unlock(&lo->mutex); +- e.ino = (uintptr_t) inode; +- +- if (lo_debug(req)) +- fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", +- (unsigned long long) parent, name, +- (unsigned long long) e.ino); +- +- fuse_reply_entry(req, &e); +- return; ++ int res; ++ struct lo_data *lo = lo_data(req); ++ struct lo_inode *inode = lo_inode(req, ino); ++ struct fuse_entry_param e; ++ int saverr; ++ ++ memset(&e, 0, sizeof(struct fuse_entry_param)); ++ e.attr_timeout = lo->timeout; ++ e.entry_timeout = lo->timeout; ++ ++ res = linkat_empty_nofollow(inode, lo_fd(req, parent), name); ++ if (res == -1) { ++ goto out_err; ++ } ++ ++ res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); ++ if (res == -1) { ++ goto out_err; ++ } ++ ++ pthread_mutex_lock(&lo->mutex); ++ inode->refcount++; ++ pthread_mutex_unlock(&lo->mutex); ++ e.ino = (uintptr_t)inode; ++ ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", ++ (unsigned long long)parent, name, (unsigned long long)e.ino); ++ } ++ ++ fuse_reply_entry(req, &e); ++ return; + + out_err: +- saverr = errno; +- fuse_reply_err(req, saverr); ++ saverr = errno; ++ fuse_reply_err(req, saverr); + } + + static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) + { +- int res; ++ int res; + +- res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); ++ res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); + +- fuse_reply_err(req, res == -1 ? errno : 0); ++ fuse_reply_err(req, res == -1 ? errno : 0); + } + + static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, +- fuse_ino_t newparent, const char *newname, +- unsigned int flags) ++ fuse_ino_t newparent, const char *newname, ++ unsigned int flags) + { +- int res; ++ int res; + +- if (flags) { +- fuse_reply_err(req, EINVAL); +- return; +- } ++ if (flags) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + +- res = renameat(lo_fd(req, parent), name, +- lo_fd(req, newparent), newname); ++ res = renameat(lo_fd(req, parent), name, lo_fd(req, newparent), newname); + +- fuse_reply_err(req, res == -1 ? errno : 0); ++ fuse_reply_err(req, res == -1 ? errno : 0); + } + + static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) + { +- int res; ++ int res; + +- res = unlinkat(lo_fd(req, parent), name, 0); ++ res = unlinkat(lo_fd(req, parent), name, 0); + +- fuse_reply_err(req, res == -1 ? errno : 0); ++ fuse_reply_err(req, res == -1 ? errno : 0); + } + + static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) + { +- if (!inode) +- return; +- +- pthread_mutex_lock(&lo->mutex); +- assert(inode->refcount >= n); +- inode->refcount -= n; +- if (!inode->refcount) { +- struct lo_inode *prev, *next; +- +- prev = inode->prev; +- next = inode->next; +- next->prev = prev; +- prev->next = next; +- +- pthread_mutex_unlock(&lo->mutex); +- close(inode->fd); +- free(inode); +- +- } else { +- pthread_mutex_unlock(&lo->mutex); +- } ++ if (!inode) { ++ return; ++ } ++ ++ pthread_mutex_lock(&lo->mutex); ++ assert(inode->refcount >= n); ++ inode->refcount -= n; ++ if (!inode->refcount) { ++ struct lo_inode *prev, *next; ++ ++ prev = inode->prev; ++ next = inode->next; ++ next->prev = prev; ++ prev->next = next; ++ ++ pthread_mutex_unlock(&lo->mutex); ++ close(inode->fd); ++ free(inode); ++ ++ } else { ++ pthread_mutex_unlock(&lo->mutex); ++ } + } + + static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) + { +- struct lo_data *lo = lo_data(req); +- struct lo_inode *inode = lo_inode(req, ino); ++ struct lo_data *lo = lo_data(req); ++ struct lo_inode *inode = lo_inode(req, ino); + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", +- (unsigned long long) ino, +- (unsigned long long) inode->refcount, +- (unsigned long long) nlookup); +- } ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", ++ (unsigned long long)ino, (unsigned long long)inode->refcount, ++ (unsigned long long)nlookup); ++ } + +- unref_inode(lo, inode, nlookup); ++ unref_inode(lo, inode, nlookup); + } + + static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) + { +- lo_forget_one(req, ino, nlookup); +- fuse_reply_none(req); ++ lo_forget_one(req, ino, nlookup); ++ fuse_reply_none(req); + } + + static void lo_forget_multi(fuse_req_t req, size_t count, +- struct fuse_forget_data *forgets) ++ struct fuse_forget_data *forgets) + { +- int i; ++ int i; + +- for (i = 0; i < count; i++) +- lo_forget_one(req, forgets[i].ino, forgets[i].nlookup); +- fuse_reply_none(req); ++ for (i = 0; i < count; i++) { ++ lo_forget_one(req, forgets[i].ino, forgets[i].nlookup); ++ } ++ fuse_reply_none(req); + } + + static void lo_readlink(fuse_req_t req, fuse_ino_t ino) + { +- char buf[PATH_MAX + 1]; +- int res; ++ char buf[PATH_MAX + 1]; ++ int res; + +- res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf)); +- if (res == -1) +- return (void) fuse_reply_err(req, errno); ++ res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf)); ++ if (res == -1) { ++ return (void)fuse_reply_err(req, errno); ++ } + +- if (res == sizeof(buf)) +- return (void) fuse_reply_err(req, ENAMETOOLONG); ++ if (res == sizeof(buf)) { ++ return (void)fuse_reply_err(req, ENAMETOOLONG); ++ } + +- buf[res] = '\0'; ++ buf[res] = '\0'; + +- fuse_reply_readlink(req, buf); ++ fuse_reply_readlink(req, buf); + } + + struct lo_dirp { +- DIR *dp; +- struct dirent *entry; +- off_t offset; ++ DIR *dp; ++ struct dirent *entry; ++ off_t offset; + }; + + static struct lo_dirp *lo_dirp(struct fuse_file_info *fi) + { +- return (struct lo_dirp *) (uintptr_t) fi->fh; ++ return (struct lo_dirp *)(uintptr_t)fi->fh; + } + +-static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) ++static void lo_opendir(fuse_req_t req, fuse_ino_t ino, ++ struct fuse_file_info *fi) + { +- int error = ENOMEM; +- struct lo_data *lo = lo_data(req); +- struct lo_dirp *d; +- int fd; +- +- d = calloc(1, sizeof(struct lo_dirp)); +- if (d == NULL) +- goto out_err; +- +- fd = openat(lo_fd(req, ino), ".", O_RDONLY); +- if (fd == -1) +- goto out_errno; +- +- d->dp = fdopendir(fd); +- if (d->dp == NULL) +- goto out_errno; +- +- d->offset = 0; +- d->entry = NULL; +- +- fi->fh = (uintptr_t) d; +- if (lo->cache == CACHE_ALWAYS) +- fi->keep_cache = 1; +- fuse_reply_open(req, fi); +- return; ++ int error = ENOMEM; ++ struct lo_data *lo = lo_data(req); ++ struct lo_dirp *d; ++ int fd; ++ ++ d = calloc(1, sizeof(struct lo_dirp)); ++ if (d == NULL) { ++ goto out_err; ++ } ++ ++ fd = openat(lo_fd(req, ino), ".", O_RDONLY); ++ if (fd == -1) { ++ goto out_errno; ++ } ++ ++ d->dp = fdopendir(fd); ++ if (d->dp == NULL) { ++ goto out_errno; ++ } ++ ++ d->offset = 0; ++ d->entry = NULL; ++ ++ fi->fh = (uintptr_t)d; ++ if (lo->cache == CACHE_ALWAYS) { ++ fi->keep_cache = 1; ++ } ++ fuse_reply_open(req, fi); ++ return; + + out_errno: +- error = errno; ++ error = errno; + out_err: +- if (d) { +- if (fd != -1) +- close(fd); +- free(d); +- } +- fuse_reply_err(req, error); ++ if (d) { ++ if (fd != -1) { ++ close(fd); ++ } ++ free(d); ++ } ++ fuse_reply_err(req, error); + } + + static int is_dot_or_dotdot(const char *name) + { +- return name[0] == '.' && (name[1] == '\0' || +- (name[1] == '.' && name[2] == '\0')); ++ return name[0] == '.' && ++ (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); + } + + static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, +- off_t offset, struct fuse_file_info *fi, int plus) ++ off_t offset, struct fuse_file_info *fi, int plus) + { +- struct lo_dirp *d = lo_dirp(fi); +- char *buf; +- char *p; +- size_t rem = size; +- int err; +- +- (void) ino; +- +- buf = calloc(1, size); +- if (!buf) { +- err = ENOMEM; +- goto error; +- } +- p = buf; +- +- if (offset != d->offset) { +- seekdir(d->dp, offset); +- d->entry = NULL; +- d->offset = offset; +- } +- while (1) { +- size_t entsize; +- off_t nextoff; +- const char *name; +- +- if (!d->entry) { +- errno = 0; +- d->entry = readdir(d->dp); +- if (!d->entry) { +- if (errno) { // Error +- err = errno; +- goto error; +- } else { // End of stream +- break; +- } +- } +- } +- nextoff = d->entry->d_off; +- name = d->entry->d_name; +- fuse_ino_t entry_ino = 0; +- if (plus) { +- struct fuse_entry_param e; +- if (is_dot_or_dotdot(name)) { +- e = (struct fuse_entry_param) { +- .attr.st_ino = d->entry->d_ino, +- .attr.st_mode = d->entry->d_type << 12, +- }; +- } else { +- err = lo_do_lookup(req, ino, name, &e); +- if (err) +- goto error; +- entry_ino = e.ino; +- } +- +- entsize = fuse_add_direntry_plus(req, p, rem, name, +- &e, nextoff); +- } else { +- struct stat st = { +- .st_ino = d->entry->d_ino, +- .st_mode = d->entry->d_type << 12, +- }; +- entsize = fuse_add_direntry(req, p, rem, name, +- &st, nextoff); +- } +- if (entsize > rem) { +- if (entry_ino != 0) +- lo_forget_one(req, entry_ino, 1); +- break; +- } +- +- p += entsize; +- rem -= entsize; +- +- d->entry = NULL; +- d->offset = nextoff; +- } ++ struct lo_dirp *d = lo_dirp(fi); ++ char *buf; ++ char *p; ++ size_t rem = size; ++ int err; ++ ++ (void)ino; ++ ++ buf = calloc(1, size); ++ if (!buf) { ++ err = ENOMEM; ++ goto error; ++ } ++ p = buf; ++ ++ if (offset != d->offset) { ++ seekdir(d->dp, offset); ++ d->entry = NULL; ++ d->offset = offset; ++ } ++ while (1) { ++ size_t entsize; ++ off_t nextoff; ++ const char *name; ++ ++ if (!d->entry) { ++ errno = 0; ++ d->entry = readdir(d->dp); ++ if (!d->entry) { ++ if (errno) { /* Error */ ++ err = errno; ++ goto error; ++ } else { /* End of stream */ ++ break; ++ } ++ } ++ } ++ nextoff = d->entry->d_off; ++ name = d->entry->d_name; ++ fuse_ino_t entry_ino = 0; ++ if (plus) { ++ struct fuse_entry_param e; ++ if (is_dot_or_dotdot(name)) { ++ e = (struct fuse_entry_param){ ++ .attr.st_ino = d->entry->d_ino, ++ .attr.st_mode = d->entry->d_type << 12, ++ }; ++ } else { ++ err = lo_do_lookup(req, ino, name, &e); ++ if (err) { ++ goto error; ++ } ++ entry_ino = e.ino; ++ } ++ ++ entsize = fuse_add_direntry_plus(req, p, rem, name, &e, nextoff); ++ } else { ++ struct stat st = { ++ .st_ino = d->entry->d_ino, ++ .st_mode = d->entry->d_type << 12, ++ }; ++ entsize = fuse_add_direntry(req, p, rem, name, &st, nextoff); ++ } ++ if (entsize > rem) { ++ if (entry_ino != 0) { ++ lo_forget_one(req, entry_ino, 1); ++ } ++ break; ++ } ++ ++ p += entsize; ++ rem -= entsize; ++ ++ d->entry = NULL; ++ d->offset = nextoff; ++ } + + err = 0; + error: +- // If there's an error, we can only signal it if we haven't stored +- // any entries yet - otherwise we'd end up with wrong lookup +- // counts for the entries that are already in the buffer. So we +- // return what we've collected until that point. +- if (err && rem == size) +- fuse_reply_err(req, err); +- else +- fuse_reply_buf(req, buf, size - rem); ++ /* ++ * If there's an error, we can only signal it if we haven't stored ++ * any entries yet - otherwise we'd end up with wrong lookup ++ * counts for the entries that are already in the buffer. So we ++ * return what we've collected until that point. ++ */ ++ if (err && rem == size) { ++ fuse_reply_err(req, err); ++ } else { ++ fuse_reply_buf(req, buf, size - rem); ++ } + free(buf); + } + + static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, +- off_t offset, struct fuse_file_info *fi) ++ off_t offset, struct fuse_file_info *fi) + { +- lo_do_readdir(req, ino, size, offset, fi, 0); ++ lo_do_readdir(req, ino, size, offset, fi, 0); + } + + static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, +- off_t offset, struct fuse_file_info *fi) ++ off_t offset, struct fuse_file_info *fi) + { +- lo_do_readdir(req, ino, size, offset, fi, 1); ++ lo_do_readdir(req, ino, size, offset, fi, 1); + } + +-static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) ++static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, ++ struct fuse_file_info *fi) + { +- struct lo_dirp *d = lo_dirp(fi); +- (void) ino; +- closedir(d->dp); +- free(d); +- fuse_reply_err(req, 0); ++ struct lo_dirp *d = lo_dirp(fi); ++ (void)ino; ++ closedir(d->dp); ++ free(d); ++ fuse_reply_err(req, 0); + } + + static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, +- mode_t mode, struct fuse_file_info *fi) ++ mode_t mode, struct fuse_file_info *fi) + { +- int fd; +- struct lo_data *lo = lo_data(req); +- struct fuse_entry_param e; +- int err; +- +- if (lo_debug(req)) +- fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", +- parent, name); +- +- fd = openat(lo_fd(req, parent), name, +- (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode); +- if (fd == -1) +- return (void) fuse_reply_err(req, errno); +- +- fi->fh = fd; +- if (lo->cache == CACHE_NEVER) +- fi->direct_io = 1; +- else if (lo->cache == CACHE_ALWAYS) +- fi->keep_cache = 1; +- +- err = lo_do_lookup(req, parent, name, &e); +- if (err) +- fuse_reply_err(req, err); +- else +- fuse_reply_create(req, &e, fi); ++ int fd; ++ struct lo_data *lo = lo_data(req); ++ struct fuse_entry_param e; ++ int err; ++ ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", ++ parent, name); ++ } ++ ++ fd = openat(lo_fd(req, parent), name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, ++ mode); ++ if (fd == -1) { ++ return (void)fuse_reply_err(req, errno); ++ } ++ ++ fi->fh = fd; ++ if (lo->cache == CACHE_NEVER) { ++ fi->direct_io = 1; ++ } else if (lo->cache == CACHE_ALWAYS) { ++ fi->keep_cache = 1; ++ } ++ ++ err = lo_do_lookup(req, parent, name, &e); ++ if (err) { ++ fuse_reply_err(req, err); ++ } else { ++ fuse_reply_create(req, &e, fi); ++ } + } + + static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, +- struct fuse_file_info *fi) ++ struct fuse_file_info *fi) + { +- int res; +- int fd = dirfd(lo_dirp(fi)->dp); +- (void) ino; +- if (datasync) +- res = fdatasync(fd); +- else +- res = fsync(fd); +- fuse_reply_err(req, res == -1 ? errno : 0); ++ int res; ++ int fd = dirfd(lo_dirp(fi)->dp); ++ (void)ino; ++ if (datasync) { ++ res = fdatasync(fd); ++ } else { ++ res = fsync(fd); ++ } ++ fuse_reply_err(req, res == -1 ? errno : 0); + } + + static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) + { +- int fd; +- char buf[64]; +- struct lo_data *lo = lo_data(req); +- +- if (lo_debug(req)) +- fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", +- ino, fi->flags); +- +- /* With writeback cache, kernel may send read requests even +- when userspace opened write-only */ +- if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) { +- fi->flags &= ~O_ACCMODE; +- fi->flags |= O_RDWR; +- } +- +- /* With writeback cache, O_APPEND is handled by the kernel. +- This breaks atomicity (since the file may change in the +- underlying filesystem, so that the kernel's idea of the +- end of the file isn't accurate anymore). In this example, +- we just accept that. A more rigorous filesystem may want +- to return an error here */ +- if (lo->writeback && (fi->flags & O_APPEND)) +- fi->flags &= ~O_APPEND; +- +- sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino)); +- fd = open(buf, fi->flags & ~O_NOFOLLOW); +- if (fd == -1) +- return (void) fuse_reply_err(req, errno); +- +- fi->fh = fd; +- if (lo->cache == CACHE_NEVER) +- fi->direct_io = 1; +- else if (lo->cache == CACHE_ALWAYS) +- fi->keep_cache = 1; +- fuse_reply_open(req, fi); ++ int fd; ++ char buf[64]; ++ struct lo_data *lo = lo_data(req); ++ ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino, ++ fi->flags); ++ } ++ ++ /* ++ * With writeback cache, kernel may send read requests even ++ * when userspace opened write-only ++ */ ++ if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) { ++ fi->flags &= ~O_ACCMODE; ++ fi->flags |= O_RDWR; ++ } ++ ++ /* ++ * With writeback cache, O_APPEND is handled by the kernel. ++ * This breaks atomicity (since the file may change in the ++ * underlying filesystem, so that the kernel's idea of the ++ * end of the file isn't accurate anymore). In this example, ++ * we just accept that. A more rigorous filesystem may want ++ * to return an error here ++ */ ++ if (lo->writeback && (fi->flags & O_APPEND)) { ++ fi->flags &= ~O_APPEND; ++ } ++ ++ sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino)); ++ fd = open(buf, fi->flags & ~O_NOFOLLOW); ++ if (fd == -1) { ++ return (void)fuse_reply_err(req, errno); ++ } ++ ++ fi->fh = fd; ++ if (lo->cache == CACHE_NEVER) { ++ fi->direct_io = 1; ++ } else if (lo->cache == CACHE_ALWAYS) { ++ fi->keep_cache = 1; ++ } ++ fuse_reply_open(req, fi); + } + +-static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) ++static void lo_release(fuse_req_t req, fuse_ino_t ino, ++ struct fuse_file_info *fi) + { +- (void) ino; ++ (void)ino; + +- close(fi->fh); +- fuse_reply_err(req, 0); ++ close(fi->fh); ++ fuse_reply_err(req, 0); + } + + static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) + { +- int res; +- (void) ino; +- res = close(dup(fi->fh)); +- fuse_reply_err(req, res == -1 ? errno : 0); ++ int res; ++ (void)ino; ++ res = close(dup(fi->fh)); ++ fuse_reply_err(req, res == -1 ? errno : 0); + } + + static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, +- struct fuse_file_info *fi) ++ struct fuse_file_info *fi) + { +- int res; +- (void) ino; +- if (datasync) +- res = fdatasync(fi->fh); +- else +- res = fsync(fi->fh); +- fuse_reply_err(req, res == -1 ? errno : 0); ++ int res; ++ (void)ino; ++ if (datasync) { ++ res = fdatasync(fi->fh); ++ } else { ++ res = fsync(fi->fh); ++ } ++ fuse_reply_err(req, res == -1 ? errno : 0); + } + +-static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, +- off_t offset, struct fuse_file_info *fi) ++static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, ++ struct fuse_file_info *fi) + { +- struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); ++ struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); + +- if (lo_debug(req)) +- fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, " +- "off=%lu)\n", ino, size, (unsigned long) offset); ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, ++ "lo_read(ino=%" PRIu64 ", size=%zd, " ++ "off=%lu)\n", ++ ino, size, (unsigned long)offset); ++ } + +- buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; +- buf.buf[0].fd = fi->fh; +- buf.buf[0].pos = offset; ++ buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; ++ buf.buf[0].fd = fi->fh; ++ buf.buf[0].pos = offset; + +- fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE); ++ fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE); + } + + static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, +- struct fuse_bufvec *in_buf, off_t off, +- struct fuse_file_info *fi) ++ struct fuse_bufvec *in_buf, off_t off, ++ struct fuse_file_info *fi) + { +- (void) ino; +- ssize_t res; +- struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); +- +- out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; +- out_buf.buf[0].fd = fi->fh; +- out_buf.buf[0].pos = off; +- +- if (lo_debug(req)) +- fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n", +- ino, out_buf.buf[0].size, (unsigned long) off); +- +- res = fuse_buf_copy(&out_buf, in_buf, 0); +- if(res < 0) +- fuse_reply_err(req, -res); +- else +- fuse_reply_write(req, (size_t) res); ++ (void)ino; ++ ssize_t res; ++ struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); ++ ++ out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; ++ out_buf.buf[0].fd = fi->fh; ++ out_buf.buf[0].pos = off; ++ ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, ++ "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n", ino, ++ out_buf.buf[0].size, (unsigned long)off); ++ } ++ ++ res = fuse_buf_copy(&out_buf, in_buf, 0); ++ if (res < 0) { ++ fuse_reply_err(req, -res); ++ } else { ++ fuse_reply_write(req, (size_t)res); ++ } + } + + static void lo_statfs(fuse_req_t req, fuse_ino_t ino) + { +- int res; +- struct statvfs stbuf; +- +- res = fstatvfs(lo_fd(req, ino), &stbuf); +- if (res == -1) +- fuse_reply_err(req, errno); +- else +- fuse_reply_statfs(req, &stbuf); ++ int res; ++ struct statvfs stbuf; ++ ++ res = fstatvfs(lo_fd(req, ino), &stbuf); ++ if (res == -1) { ++ fuse_reply_err(req, errno); ++ } else { ++ fuse_reply_statfs(req, &stbuf); ++ } + } + +-static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, +- off_t offset, off_t length, struct fuse_file_info *fi) ++static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, ++ off_t length, struct fuse_file_info *fi) + { +- int err = EOPNOTSUPP; +- (void) ino; ++ int err = EOPNOTSUPP; ++ (void)ino; + + #ifdef HAVE_FALLOCATE +- err = fallocate(fi->fh, mode, offset, length); +- if (err < 0) +- err = errno; ++ err = fallocate(fi->fh, mode, offset, length); ++ if (err < 0) { ++ err = errno; ++ } + + #elif defined(HAVE_POSIX_FALLOCATE) +- if (mode) { +- fuse_reply_err(req, EOPNOTSUPP); +- return; +- } ++ if (mode) { ++ fuse_reply_err(req, EOPNOTSUPP); ++ return; ++ } + +- err = posix_fallocate(fi->fh, offset, length); ++ err = posix_fallocate(fi->fh, offset, length); + #endif + +- fuse_reply_err(req, err); ++ fuse_reply_err(req, err); + } + + static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, +- int op) ++ int op) + { +- int res; +- (void) ino; ++ int res; ++ (void)ino; + +- res = flock(fi->fh, op); ++ res = flock(fi->fh, op); + +- fuse_reply_err(req, res == -1 ? errno : 0); ++ fuse_reply_err(req, res == -1 ? errno : 0); + } + + static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, +- size_t size) ++ size_t size) + { +- char *value = NULL; +- char procname[64]; +- struct lo_inode *inode = lo_inode(req, ino); +- ssize_t ret; +- int saverr; +- +- saverr = ENOSYS; +- if (!lo_data(req)->xattr) +- goto out; +- +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", +- ino, name, size); +- } +- +- if (inode->is_symlink) { +- /* Sorry, no race free way to getxattr on symlink. */ +- saverr = EPERM; +- goto out; +- } +- +- sprintf(procname, "/proc/self/fd/%i", inode->fd); +- +- if (size) { +- value = malloc(size); +- if (!value) +- goto out_err; +- +- ret = getxattr(procname, name, value, size); +- if (ret == -1) +- goto out_err; +- saverr = 0; +- if (ret == 0) +- goto out; +- +- fuse_reply_buf(req, value, ret); +- } else { +- ret = getxattr(procname, name, NULL, 0); +- if (ret == -1) +- goto out_err; +- +- fuse_reply_xattr(req, ret); +- } ++ char *value = NULL; ++ char procname[64]; ++ struct lo_inode *inode = lo_inode(req, ino); ++ ssize_t ret; ++ int saverr; ++ ++ saverr = ENOSYS; ++ if (!lo_data(req)->xattr) { ++ goto out; ++ } ++ ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, ++ "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", ino, name, ++ size); ++ } ++ ++ if (inode->is_symlink) { ++ /* Sorry, no race free way to getxattr on symlink. */ ++ saverr = EPERM; ++ goto out; ++ } ++ ++ sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ ++ if (size) { ++ value = malloc(size); ++ if (!value) { ++ goto out_err; ++ } ++ ++ ret = getxattr(procname, name, value, size); ++ if (ret == -1) { ++ goto out_err; ++ } ++ saverr = 0; ++ if (ret == 0) { ++ goto out; ++ } ++ ++ fuse_reply_buf(req, value, ret); ++ } else { ++ ret = getxattr(procname, name, NULL, 0); ++ if (ret == -1) { ++ goto out_err; ++ } ++ ++ fuse_reply_xattr(req, ret); ++ } + out_free: +- free(value); +- return; ++ free(value); ++ return; + + out_err: +- saverr = errno; ++ saverr = errno; + out: +- fuse_reply_err(req, saverr); +- goto out_free; ++ fuse_reply_err(req, saverr); ++ goto out_free; + } + + static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) + { +- char *value = NULL; +- char procname[64]; +- struct lo_inode *inode = lo_inode(req, ino); +- ssize_t ret; +- int saverr; +- +- saverr = ENOSYS; +- if (!lo_data(req)->xattr) +- goto out; +- +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n", +- ino, size); +- } +- +- if (inode->is_symlink) { +- /* Sorry, no race free way to listxattr on symlink. */ +- saverr = EPERM; +- goto out; +- } +- +- sprintf(procname, "/proc/self/fd/%i", inode->fd); +- +- if (size) { +- value = malloc(size); +- if (!value) +- goto out_err; +- +- ret = listxattr(procname, value, size); +- if (ret == -1) +- goto out_err; +- saverr = 0; +- if (ret == 0) +- goto out; +- +- fuse_reply_buf(req, value, ret); +- } else { +- ret = listxattr(procname, NULL, 0); +- if (ret == -1) +- goto out_err; +- +- fuse_reply_xattr(req, ret); +- } ++ char *value = NULL; ++ char procname[64]; ++ struct lo_inode *inode = lo_inode(req, ino); ++ ssize_t ret; ++ int saverr; ++ ++ saverr = ENOSYS; ++ if (!lo_data(req)->xattr) { ++ goto out; ++ } ++ ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n", ++ ino, size); ++ } ++ ++ if (inode->is_symlink) { ++ /* Sorry, no race free way to listxattr on symlink. */ ++ saverr = EPERM; ++ goto out; ++ } ++ ++ sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ ++ if (size) { ++ value = malloc(size); ++ if (!value) { ++ goto out_err; ++ } ++ ++ ret = listxattr(procname, value, size); ++ if (ret == -1) { ++ goto out_err; ++ } ++ saverr = 0; ++ if (ret == 0) { ++ goto out; ++ } ++ ++ fuse_reply_buf(req, value, ret); ++ } else { ++ ret = listxattr(procname, NULL, 0); ++ if (ret == -1) { ++ goto out_err; ++ } ++ ++ fuse_reply_xattr(req, ret); ++ } + out_free: +- free(value); +- return; ++ free(value); ++ return; + + out_err: +- saverr = errno; ++ saverr = errno; + out: +- fuse_reply_err(req, saverr); +- goto out_free; ++ fuse_reply_err(req, saverr); ++ goto out_free; + } + + static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, +- const char *value, size_t size, int flags) ++ const char *value, size_t size, int flags) + { +- char procname[64]; +- struct lo_inode *inode = lo_inode(req, ino); +- ssize_t ret; +- int saverr; ++ char procname[64]; ++ struct lo_inode *inode = lo_inode(req, ino); ++ ssize_t ret; ++ int saverr; + +- saverr = ENOSYS; +- if (!lo_data(req)->xattr) +- goto out; ++ saverr = ENOSYS; ++ if (!lo_data(req)->xattr) { ++ goto out; ++ } + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n", +- ino, name, value, size); +- } ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, ++ "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n", ++ ino, name, value, size); ++ } + +- if (inode->is_symlink) { +- /* Sorry, no race free way to setxattr on symlink. */ +- saverr = EPERM; +- goto out; +- } ++ if (inode->is_symlink) { ++ /* Sorry, no race free way to setxattr on symlink. */ ++ saverr = EPERM; ++ goto out; ++ } + +- sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ sprintf(procname, "/proc/self/fd/%i", inode->fd); + +- ret = setxattr(procname, name, value, size, flags); +- saverr = ret == -1 ? errno : 0; ++ ret = setxattr(procname, name, value, size, flags); ++ saverr = ret == -1 ? errno : 0; + + out: +- fuse_reply_err(req, saverr); ++ fuse_reply_err(req, saverr); + } + + static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) + { +- char procname[64]; +- struct lo_inode *inode = lo_inode(req, ino); +- ssize_t ret; +- int saverr; ++ char procname[64]; ++ struct lo_inode *inode = lo_inode(req, ino); ++ ssize_t ret; ++ int saverr; + +- saverr = ENOSYS; +- if (!lo_data(req)->xattr) +- goto out; ++ saverr = ENOSYS; ++ if (!lo_data(req)->xattr) { ++ goto out; ++ } + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", +- ino, name); +- } ++ if (lo_debug(req)) { ++ fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", ++ ino, name); ++ } + +- if (inode->is_symlink) { +- /* Sorry, no race free way to setxattr on symlink. */ +- saverr = EPERM; +- goto out; +- } ++ if (inode->is_symlink) { ++ /* Sorry, no race free way to setxattr on symlink. */ ++ saverr = EPERM; ++ goto out; ++ } + +- sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ sprintf(procname, "/proc/self/fd/%i", inode->fd); + +- ret = removexattr(procname, name); +- saverr = ret == -1 ? errno : 0; ++ ret = removexattr(procname, name); ++ saverr = ret == -1 ? errno : 0; + + out: +- fuse_reply_err(req, saverr); ++ fuse_reply_err(req, saverr); + } + + #ifdef HAVE_COPY_FILE_RANGE + static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, +- struct fuse_file_info *fi_in, +- fuse_ino_t ino_out, off_t off_out, +- struct fuse_file_info *fi_out, size_t len, +- int flags) ++ struct fuse_file_info *fi_in, fuse_ino_t ino_out, ++ off_t off_out, struct fuse_file_info *fi_out, ++ size_t len, int flags) + { +- ssize_t res; +- +- if (lo_debug(req)) +- fuse_log(FUSE_LOG_DEBUG, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, " +- "off=%lu, ino=%" PRIu64 "/fd=%lu, " +- "off=%lu, size=%zd, flags=0x%x)\n", +- ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out, +- len, flags); +- +- res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len, +- flags); +- if (res < 0) +- fuse_reply_err(req, -errno); +- else +- fuse_reply_write(req, res); ++ ssize_t res; ++ ++ if (lo_debug(req)) ++ fuse_log(FUSE_LOG_DEBUG, ++ "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, " ++ "off=%lu, ino=%" PRIu64 "/fd=%lu, " ++ "off=%lu, size=%zd, flags=0x%x)\n", ++ ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out, len, ++ flags); ++ ++ res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len, flags); ++ if (res < 0) { ++ fuse_reply_err(req, -errno); ++ } else { ++ fuse_reply_write(req, res); ++ } + } + #endif + + static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, +- struct fuse_file_info *fi) ++ struct fuse_file_info *fi) + { +- off_t res; +- +- (void)ino; +- res = lseek(fi->fh, off, whence); +- if (res != -1) +- fuse_reply_lseek(req, res); +- else +- fuse_reply_err(req, errno); ++ off_t res; ++ ++ (void)ino; ++ res = lseek(fi->fh, off, whence); ++ if (res != -1) { ++ fuse_reply_lseek(req, res); ++ } else { ++ fuse_reply_err(req, errno); ++ } + } + + static struct fuse_lowlevel_ops lo_oper = { +- .init = lo_init, +- .lookup = lo_lookup, +- .mkdir = lo_mkdir, +- .mknod = lo_mknod, +- .symlink = lo_symlink, +- .link = lo_link, +- .unlink = lo_unlink, +- .rmdir = lo_rmdir, +- .rename = lo_rename, +- .forget = lo_forget, +- .forget_multi = lo_forget_multi, +- .getattr = lo_getattr, +- .setattr = lo_setattr, +- .readlink = lo_readlink, +- .opendir = lo_opendir, +- .readdir = lo_readdir, +- .readdirplus = lo_readdirplus, +- .releasedir = lo_releasedir, +- .fsyncdir = lo_fsyncdir, +- .create = lo_create, +- .open = lo_open, +- .release = lo_release, +- .flush = lo_flush, +- .fsync = lo_fsync, +- .read = lo_read, +- .write_buf = lo_write_buf, +- .statfs = lo_statfs, +- .fallocate = lo_fallocate, +- .flock = lo_flock, +- .getxattr = lo_getxattr, +- .listxattr = lo_listxattr, +- .setxattr = lo_setxattr, +- .removexattr = lo_removexattr, ++ .init = lo_init, ++ .lookup = lo_lookup, ++ .mkdir = lo_mkdir, ++ .mknod = lo_mknod, ++ .symlink = lo_symlink, ++ .link = lo_link, ++ .unlink = lo_unlink, ++ .rmdir = lo_rmdir, ++ .rename = lo_rename, ++ .forget = lo_forget, ++ .forget_multi = lo_forget_multi, ++ .getattr = lo_getattr, ++ .setattr = lo_setattr, ++ .readlink = lo_readlink, ++ .opendir = lo_opendir, ++ .readdir = lo_readdir, ++ .readdirplus = lo_readdirplus, ++ .releasedir = lo_releasedir, ++ .fsyncdir = lo_fsyncdir, ++ .create = lo_create, ++ .open = lo_open, ++ .release = lo_release, ++ .flush = lo_flush, ++ .fsync = lo_fsync, ++ .read = lo_read, ++ .write_buf = lo_write_buf, ++ .statfs = lo_statfs, ++ .fallocate = lo_fallocate, ++ .flock = lo_flock, ++ .getxattr = lo_getxattr, ++ .listxattr = lo_listxattr, ++ .setxattr = lo_setxattr, ++ .removexattr = lo_removexattr, + #ifdef HAVE_COPY_FILE_RANGE +- .copy_file_range = lo_copy_file_range, ++ .copy_file_range = lo_copy_file_range, + #endif +- .lseek = lo_lseek, ++ .lseek = lo_lseek, + }; + + int main(int argc, char *argv[]) + { +- struct fuse_args args = FUSE_ARGS_INIT(argc, argv); +- struct fuse_session *se; +- struct fuse_cmdline_opts opts; +- struct lo_data lo = { .debug = 0, +- .writeback = 0 }; +- int ret = -1; +- +- /* Don't mask creation mode, kernel already did that */ +- umask(0); +- +- pthread_mutex_init(&lo.mutex, NULL); +- lo.root.next = lo.root.prev = &lo.root; +- lo.root.fd = -1; +- lo.cache = CACHE_NORMAL; +- +- if (fuse_parse_cmdline(&args, &opts) != 0) +- return 1; +- if (opts.show_help) { +- printf("usage: %s [options] \n\n", argv[0]); +- fuse_cmdline_help(); +- fuse_lowlevel_help(); +- ret = 0; +- goto err_out1; +- } else if (opts.show_version) { +- fuse_lowlevel_version(); +- ret = 0; +- goto err_out1; +- } +- +- if(opts.mountpoint == NULL) { +- printf("usage: %s [options] \n", argv[0]); +- printf(" %s --help\n", argv[0]); +- ret = 1; +- goto err_out1; +- } +- +- if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1) +- return 1; +- +- lo.debug = opts.debug; +- lo.root.refcount = 2; +- if (lo.source) { +- struct stat stat; +- int res; +- +- res = lstat(lo.source, &stat); +- if (res == -1) { +- fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n", +- lo.source); +- exit(1); +- } +- if (!S_ISDIR(stat.st_mode)) { +- fuse_log(FUSE_LOG_ERR, "source is not a directory\n"); +- exit(1); +- } +- +- } else { +- lo.source = "/"; +- } +- lo.root.is_symlink = false; +- if (!lo.timeout_set) { +- switch (lo.cache) { +- case CACHE_NEVER: +- lo.timeout = 0.0; +- break; +- +- case CACHE_NORMAL: +- lo.timeout = 1.0; +- break; +- +- case CACHE_ALWAYS: +- lo.timeout = 86400.0; +- break; +- } +- } else if (lo.timeout < 0) { +- fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n", +- lo.timeout); +- exit(1); +- } +- +- lo.root.fd = open(lo.source, O_PATH); +- if (lo.root.fd == -1) { +- fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", +- lo.source); +- exit(1); +- } +- +- se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); +- if (se == NULL) +- goto err_out1; +- +- if (fuse_set_signal_handlers(se) != 0) +- goto err_out2; +- +- if (fuse_session_mount(se, opts.mountpoint) != 0) +- goto err_out3; +- +- fuse_daemonize(opts.foreground); +- +- /* Block until ctrl+c or fusermount -u */ +- if (opts.singlethread) +- ret = fuse_session_loop(se); +- else +- ret = fuse_session_loop_mt(se, opts.clone_fd); +- +- fuse_session_unmount(se); ++ struct fuse_args args = FUSE_ARGS_INIT(argc, argv); ++ struct fuse_session *se; ++ struct fuse_cmdline_opts opts; ++ struct lo_data lo = { .debug = 0, .writeback = 0 }; ++ int ret = -1; ++ ++ /* Don't mask creation mode, kernel already did that */ ++ umask(0); ++ ++ pthread_mutex_init(&lo.mutex, NULL); ++ lo.root.next = lo.root.prev = &lo.root; ++ lo.root.fd = -1; ++ lo.cache = CACHE_NORMAL; ++ ++ if (fuse_parse_cmdline(&args, &opts) != 0) { ++ return 1; ++ } ++ if (opts.show_help) { ++ printf("usage: %s [options] \n\n", argv[0]); ++ fuse_cmdline_help(); ++ fuse_lowlevel_help(); ++ ret = 0; ++ goto err_out1; ++ } else if (opts.show_version) { ++ fuse_lowlevel_version(); ++ ret = 0; ++ goto err_out1; ++ } ++ ++ if (opts.mountpoint == NULL) { ++ printf("usage: %s [options] \n", argv[0]); ++ printf(" %s --help\n", argv[0]); ++ ret = 1; ++ goto err_out1; ++ } ++ ++ if (fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1) { ++ return 1; ++ } ++ ++ lo.debug = opts.debug; ++ lo.root.refcount = 2; ++ if (lo.source) { ++ struct stat stat; ++ int res; ++ ++ res = lstat(lo.source, &stat); ++ if (res == -1) { ++ fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n", ++ lo.source); ++ exit(1); ++ } ++ if (!S_ISDIR(stat.st_mode)) { ++ fuse_log(FUSE_LOG_ERR, "source is not a directory\n"); ++ exit(1); ++ } ++ ++ } else { ++ lo.source = "/"; ++ } ++ lo.root.is_symlink = false; ++ if (!lo.timeout_set) { ++ switch (lo.cache) { ++ case CACHE_NEVER: ++ lo.timeout = 0.0; ++ break; ++ ++ case CACHE_NORMAL: ++ lo.timeout = 1.0; ++ break; ++ ++ case CACHE_ALWAYS: ++ lo.timeout = 86400.0; ++ break; ++ } ++ } else if (lo.timeout < 0) { ++ fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n", lo.timeout); ++ exit(1); ++ } ++ ++ lo.root.fd = open(lo.source, O_PATH); ++ if (lo.root.fd == -1) { ++ fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", lo.source); ++ exit(1); ++ } ++ ++ se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); ++ if (se == NULL) { ++ goto err_out1; ++ } ++ ++ if (fuse_set_signal_handlers(se) != 0) { ++ goto err_out2; ++ } ++ ++ if (fuse_session_mount(se, opts.mountpoint) != 0) { ++ goto err_out3; ++ } ++ ++ fuse_daemonize(opts.foreground); ++ ++ /* Block until ctrl+c or fusermount -u */ ++ if (opts.singlethread) { ++ ret = fuse_session_loop(se); ++ } else { ++ ret = fuse_session_loop_mt(se, opts.clone_fd); ++ } ++ ++ fuse_session_unmount(se); + err_out3: +- fuse_remove_signal_handlers(se); ++ fuse_remove_signal_handlers(se); + err_out2: +- fuse_session_destroy(se); ++ fuse_session_destroy(se); + err_out1: +- free(opts.mountpoint); +- fuse_opt_free_args(&args); ++ free(opts.mountpoint); ++ fuse_opt_free_args(&args); + +- if (lo.root.fd >= 0) +- close(lo.root.fd); ++ if (lo.root.fd >= 0) { ++ close(lo.root.fd); ++ } + +- return ret ? 1 : 0; ++ return ret ? 1 : 0; + } diff --git a/0017-virtiofsd-remove-mountpoint-dummy-argument.patch b/0017-virtiofsd-remove-mountpoint-dummy-argument.patch new file mode 100644 index 0000000..5b73fa9 --- /dev/null +++ b/0017-virtiofsd-remove-mountpoint-dummy-argument.patch @@ -0,0 +1,143 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:00:46 +0000 +Subject: [PATCH] virtiofsd: remove mountpoint dummy argument +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Classic FUSE file system daemons take a mountpoint argument but +virtiofsd exposes a vhost-user UNIX domain socket instead. The +mountpoint argument is not used by virtiofsd but the user is still +required to pass a dummy argument on the command-line. + +Remove the mountpoint argument to clean up the command-line. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 67aab02272f6cb47c56420f60b370c184961b5ca) +--- + tools/virtiofsd/fuse_lowlevel.c | 2 +- + tools/virtiofsd/fuse_lowlevel.h | 4 +--- + tools/virtiofsd/helper.c | 20 +++----------------- + tools/virtiofsd/passthrough_ll.c | 12 ++---------- + 4 files changed, 7 insertions(+), 31 deletions(-) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 5c9cb52f2a..2f32c68161 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -2455,7 +2455,7 @@ out1: + return NULL; + } + +-int fuse_session_mount(struct fuse_session *se, const char *mountpoint) ++int fuse_session_mount(struct fuse_session *se) + { + int fd; + +diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h +index adb9054bb1..8d8909b35d 100644 +--- a/tools/virtiofsd/fuse_lowlevel.h ++++ b/tools/virtiofsd/fuse_lowlevel.h +@@ -1863,7 +1863,6 @@ struct fuse_cmdline_opts { + int foreground; + int debug; + int nodefault_subtype; +- char *mountpoint; + int show_version; + int show_help; + unsigned int max_idle_threads; +@@ -1924,12 +1923,11 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, + /** + * Mount a FUSE file system. + * +- * @param mountpoint the mount point path + * @param se session object + * + * @return 0 on success, -1 on failure. + **/ +-int fuse_session_mount(struct fuse_session *se, const char *mountpoint); ++int fuse_session_mount(struct fuse_session *se); + + /** + * Enter a single threaded, blocking event loop. +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index 5711dd2660..5e6f2051a7 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -140,27 +140,13 @@ void fuse_cmdline_help(void) + static int fuse_helper_opt_proc(void *data, const char *arg, int key, + struct fuse_args *outargs) + { ++ (void)data; + (void)outargs; +- struct fuse_cmdline_opts *opts = data; + + switch (key) { + case FUSE_OPT_KEY_NONOPT: +- if (!opts->mountpoint) { +- if (fuse_mnt_parse_fuse_fd(arg) != -1) { +- return fuse_opt_add_opt(&opts->mountpoint, arg); +- } +- +- char mountpoint[PATH_MAX] = ""; +- if (realpath(arg, mountpoint) == NULL) { +- fuse_log(FUSE_LOG_ERR, "fuse: bad mount point `%s': %s\n", arg, +- strerror(errno)); +- return -1; +- } +- return fuse_opt_add_opt(&opts->mountpoint, mountpoint); +- } else { +- fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg); +- return -1; +- } ++ fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg); ++ return -1; + + default: + /* Pass through unknown options */ +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index c5850ef803..9377718d9d 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1297,7 +1297,7 @@ int main(int argc, char *argv[]) + return 1; + } + if (opts.show_help) { +- printf("usage: %s [options] \n\n", argv[0]); ++ printf("usage: %s [options]\n\n", argv[0]); + fuse_cmdline_help(); + fuse_lowlevel_help(); + ret = 0; +@@ -1308,13 +1308,6 @@ int main(int argc, char *argv[]) + goto err_out1; + } + +- if (opts.mountpoint == NULL) { +- printf("usage: %s [options] \n", argv[0]); +- printf(" %s --help\n", argv[0]); +- ret = 1; +- goto err_out1; +- } +- + if (fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1) { + return 1; + } +@@ -1374,7 +1367,7 @@ int main(int argc, char *argv[]) + goto err_out2; + } + +- if (fuse_session_mount(se, opts.mountpoint) != 0) { ++ if (fuse_session_mount(se) != 0) { + goto err_out3; + } + +@@ -1393,7 +1386,6 @@ err_out3: + err_out2: + fuse_session_destroy(se); + err_out1: +- free(opts.mountpoint); + fuse_opt_free_args(&args); + + if (lo.root.fd >= 0) { diff --git a/0018-virtiofsd-remove-unused-notify-reply-support.patch b/0018-virtiofsd-remove-unused-notify-reply-support.patch new file mode 100644 index 0000000..da2fc17 --- /dev/null +++ b/0018-virtiofsd-remove-unused-notify-reply-support.patch @@ -0,0 +1,278 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:00:47 +0000 +Subject: [PATCH] virtiofsd: remove unused notify reply support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Notify reply support is unused by virtiofsd. The code would need to be +updated to validate input buffer sizes. Remove this unused code since +changes to it are untestable. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 64c6f408a29ef03e9b8da9f5a5d8fd511b0d801e) +--- + tools/virtiofsd/fuse_lowlevel.c | 147 +------------------------------- + tools/virtiofsd/fuse_lowlevel.h | 47 ---------- + 2 files changed, 1 insertion(+), 193 deletions(-) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 2f32c68161..eb0ec49d38 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -31,12 +31,6 @@ + #define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) + #define OFFSET_MAX 0x7fffffffffffffffLL + +-#define container_of(ptr, type, member) \ +- ({ \ +- const typeof(((type *)0)->member) *__mptr = (ptr); \ +- (type *)((char *)__mptr - offsetof(type, member)); \ +- }) +- + struct fuse_pollhandle { + uint64_t kh; + struct fuse_session *se; +@@ -1862,52 +1856,6 @@ static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + send_reply_ok(req, NULL, 0); + } + +-static void list_del_nreq(struct fuse_notify_req *nreq) +-{ +- struct fuse_notify_req *prev = nreq->prev; +- struct fuse_notify_req *next = nreq->next; +- prev->next = next; +- next->prev = prev; +-} +- +-static void list_add_nreq(struct fuse_notify_req *nreq, +- struct fuse_notify_req *next) +-{ +- struct fuse_notify_req *prev = next->prev; +- nreq->next = next; +- nreq->prev = prev; +- prev->next = nreq; +- next->prev = nreq; +-} +- +-static void list_init_nreq(struct fuse_notify_req *nreq) +-{ +- nreq->next = nreq; +- nreq->prev = nreq; +-} +- +-static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid, +- const void *inarg, const struct fuse_buf *buf) +-{ +- struct fuse_session *se = req->se; +- struct fuse_notify_req *nreq; +- struct fuse_notify_req *head; +- +- pthread_mutex_lock(&se->lock); +- head = &se->notify_list; +- for (nreq = head->next; nreq != head; nreq = nreq->next) { +- if (nreq->unique == req->unique) { +- list_del_nreq(nreq); +- break; +- } +- } +- pthread_mutex_unlock(&se->lock); +- +- if (nreq != head) { +- nreq->reply(nreq, req, nodeid, inarg, buf); +- } +-} +- + static int send_notify_iov(struct fuse_session *se, int notify_code, + struct iovec *iov, int count) + { +@@ -2059,95 +2007,6 @@ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, + return res; + } + +-struct fuse_retrieve_req { +- struct fuse_notify_req nreq; +- void *cookie; +-}; +- +-static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq, fuse_req_t req, +- fuse_ino_t ino, const void *inarg, +- const struct fuse_buf *ibuf) +-{ +- struct fuse_session *se = req->se; +- struct fuse_retrieve_req *rreq = +- container_of(nreq, struct fuse_retrieve_req, nreq); +- const struct fuse_notify_retrieve_in *arg = inarg; +- struct fuse_bufvec bufv = { +- .buf[0] = *ibuf, +- .count = 1, +- }; +- +- if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) { +- bufv.buf[0].mem = PARAM(arg); +- } +- +- bufv.buf[0].size -= +- sizeof(struct fuse_in_header) + sizeof(struct fuse_notify_retrieve_in); +- +- if (bufv.buf[0].size < arg->size) { +- fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n"); +- fuse_reply_none(req); +- goto out; +- } +- bufv.buf[0].size = arg->size; +- +- if (se->op.retrieve_reply) { +- se->op.retrieve_reply(req, rreq->cookie, ino, arg->offset, &bufv); +- } else { +- fuse_reply_none(req); +- } +-out: +- free(rreq); +-} +- +-int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, +- size_t size, off_t offset, void *cookie) +-{ +- struct fuse_notify_retrieve_out outarg; +- struct iovec iov[2]; +- struct fuse_retrieve_req *rreq; +- int err; +- +- if (!se) { +- return -EINVAL; +- } +- +- if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) { +- return -ENOSYS; +- } +- +- rreq = malloc(sizeof(*rreq)); +- if (rreq == NULL) { +- return -ENOMEM; +- } +- +- pthread_mutex_lock(&se->lock); +- rreq->cookie = cookie; +- rreq->nreq.unique = se->notify_ctr++; +- rreq->nreq.reply = fuse_ll_retrieve_reply; +- list_add_nreq(&rreq->nreq, &se->notify_list); +- pthread_mutex_unlock(&se->lock); +- +- outarg.notify_unique = rreq->nreq.unique; +- outarg.nodeid = ino; +- outarg.offset = offset; +- outarg.size = size; +- outarg.padding = 0; +- +- iov[1].iov_base = &outarg; +- iov[1].iov_len = sizeof(outarg); +- +- err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2); +- if (err) { +- pthread_mutex_lock(&se->lock); +- list_del_nreq(&rreq->nreq); +- pthread_mutex_unlock(&se->lock); +- free(rreq); +- } +- +- return err; +-} +- + void *fuse_req_userdata(fuse_req_t req) + { + return req->se->userdata; +@@ -2226,7 +2085,7 @@ static struct { + [FUSE_POLL] = { do_poll, "POLL" }, + [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" }, + [FUSE_DESTROY] = { do_destroy, "DESTROY" }, +- [FUSE_NOTIFY_REPLY] = { (void *)1, "NOTIFY_REPLY" }, ++ [FUSE_NOTIFY_REPLY] = { NULL, "NOTIFY_REPLY" }, + [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" }, + [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS" }, + [FUSE_RENAME2] = { do_rename2, "RENAME2" }, +@@ -2333,8 +2192,6 @@ void fuse_session_process_buf_int(struct fuse_session *se, + inarg = (void *)&in[1]; + if (in->opcode == FUSE_WRITE && se->op.write_buf) { + do_write_buf(req, in->nodeid, inarg, buf); +- } else if (in->opcode == FUSE_NOTIFY_REPLY) { +- do_notify_reply(req, in->nodeid, inarg, buf); + } else { + fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); + } +@@ -2437,8 +2294,6 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, + + list_init_req(&se->list); + list_init_req(&se->interrupts); +- list_init_nreq(&se->notify_list); +- se->notify_ctr = 1; + fuse_mutex_init(&se->lock); + + memcpy(&se->op, op, op_size); +diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h +index 8d8909b35d..12a84b460f 100644 +--- a/tools/virtiofsd/fuse_lowlevel.h ++++ b/tools/virtiofsd/fuse_lowlevel.h +@@ -1084,21 +1084,6 @@ struct fuse_lowlevel_ops { + void (*write_buf)(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv, + off_t off, struct fuse_file_info *fi); + +- /** +- * Callback function for the retrieve request +- * +- * Valid replies: +- * fuse_reply_none +- * +- * @param req request handle +- * @param cookie user data supplied to fuse_lowlevel_notify_retrieve() +- * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve() +- * @param offset the offset supplied to fuse_lowlevel_notify_retrieve() +- * @param bufv the buffer containing the returned data +- */ +- void (*retrieve_reply)(fuse_req_t req, void *cookie, fuse_ino_t ino, +- off_t offset, struct fuse_bufvec *bufv); +- + /** + * Forget about multiple inodes + * +@@ -1726,38 +1711,6 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, + int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, + off_t offset, struct fuse_bufvec *bufv, + enum fuse_buf_copy_flags flags); +-/** +- * Retrieve data from the kernel buffers +- * +- * Retrieve data in the kernel buffers belonging to the given inode. +- * If successful then the retrieve_reply() method will be called with +- * the returned data. +- * +- * Only present pages are returned in the retrieve reply. Retrieving +- * stops when it finds a non-present page and only data prior to that +- * is returned. +- * +- * If this function returns an error, then the retrieve will not be +- * completed and no reply will be sent. +- * +- * This function doesn't change the dirty state of pages in the kernel +- * buffer. For dirty pages the write() method will be called +- * regardless of having been retrieved previously. +- * +- * Added in FUSE protocol version 7.15. If the kernel does not support +- * this (or a newer) version, the function will return -ENOSYS and do +- * nothing. +- * +- * @param se the session object +- * @param ino the inode number +- * @param size the number of bytes to retrieve +- * @param offset the starting offset into the file to retrieve from +- * @param cookie user data to supply to the reply callback +- * @return zero for success, -errno for failure +- */ +-int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, +- size_t size, off_t offset, void *cookie); +- + + /* + * Utility functions diff --git a/0019-virtiofsd-Remove-unused-enum-fuse_buf_copy_flags.patch b/0019-virtiofsd-Remove-unused-enum-fuse_buf_copy_flags.patch new file mode 100644 index 0000000..f60bab0 --- /dev/null +++ b/0019-virtiofsd-Remove-unused-enum-fuse_buf_copy_flags.patch @@ -0,0 +1,252 @@ +From: Xiao Yang +Date: Mon, 27 Jan 2020 19:00:48 +0000 +Subject: [PATCH] virtiofsd: Remove unused enum fuse_buf_copy_flags + +Signed-off-by: Xiao Yang +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 8c3fe75e0308ba2f01d160ace534b7e386cea808) +--- + tools/virtiofsd/buffer.c | 7 +++-- + tools/virtiofsd/fuse_common.h | 46 +------------------------------- + tools/virtiofsd/fuse_lowlevel.c | 13 ++++----- + tools/virtiofsd/fuse_lowlevel.h | 35 ++---------------------- + tools/virtiofsd/passthrough_ll.c | 4 +-- + 5 files changed, 13 insertions(+), 92 deletions(-) + +diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c +index 5df946c82c..4d507f3302 100644 +--- a/tools/virtiofsd/buffer.c ++++ b/tools/virtiofsd/buffer.c +@@ -171,7 +171,7 @@ static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off, + + static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off, + const struct fuse_buf *src, size_t src_off, +- size_t len, enum fuse_buf_copy_flags flags) ++ size_t len) + { + int src_is_fd = src->flags & FUSE_BUF_IS_FD; + int dst_is_fd = dst->flags & FUSE_BUF_IS_FD; +@@ -224,8 +224,7 @@ static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len) + return 1; + } + +-ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv, +- enum fuse_buf_copy_flags flags) ++ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv) + { + size_t copied = 0; + +@@ -249,7 +248,7 @@ ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv, + dst_len = dst->size - dstv->off; + len = min_size(src_len, dst_len); + +- res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags); ++ res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len); + if (res < 0) { + if (!copied) { + return res; +diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h +index bd9bf861f0..0cb33acc2f 100644 +--- a/tools/virtiofsd/fuse_common.h ++++ b/tools/virtiofsd/fuse_common.h +@@ -604,48 +604,6 @@ enum fuse_buf_flags { + FUSE_BUF_FD_RETRY = (1 << 3), + }; + +-/** +- * Buffer copy flags +- */ +-enum fuse_buf_copy_flags { +- /** +- * Don't use splice(2) +- * +- * Always fall back to using read and write instead of +- * splice(2) to copy data from one file descriptor to another. +- * +- * If this flag is not set, then only fall back if splice is +- * unavailable. +- */ +- FUSE_BUF_NO_SPLICE = (1 << 1), +- +- /** +- * Force splice +- * +- * Always use splice(2) to copy data from one file descriptor +- * to another. If splice is not available, return -EINVAL. +- */ +- FUSE_BUF_FORCE_SPLICE = (1 << 2), +- +- /** +- * Try to move data with splice. +- * +- * If splice is used, try to move pages from the source to the +- * destination instead of copying. See documentation of +- * SPLICE_F_MOVE in splice(2) man page. +- */ +- FUSE_BUF_SPLICE_MOVE = (1 << 3), +- +- /** +- * Don't block on the pipe when copying data with splice +- * +- * Makes the operations on the pipe non-blocking (if the pipe +- * is full or empty). See SPLICE_F_NONBLOCK in the splice(2) +- * man page. +- */ +- FUSE_BUF_SPLICE_NONBLOCK = (1 << 4), +-}; +- + /** + * Single data buffer + * +@@ -741,11 +699,9 @@ size_t fuse_buf_size(const struct fuse_bufvec *bufv); + * + * @param dst destination buffer vector + * @param src source buffer vector +- * @param flags flags controlling the copy + * @return actual number of bytes copied or -errno on error + */ +-ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, +- enum fuse_buf_copy_flags flags); ++ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src); + + /* + * Signal handling +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index eb0ec49d38..3da80de233 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -490,16 +490,14 @@ static int fuse_send_data_iov_fallback(struct fuse_session *se, + + static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, + struct iovec *iov, int iov_count, +- struct fuse_bufvec *buf, unsigned int flags) ++ struct fuse_bufvec *buf) + { + size_t len = fuse_buf_size(buf); +- (void)flags; + + return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); + } + +-int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, +- enum fuse_buf_copy_flags flags) ++int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv) + { + struct iovec iov[2]; + struct fuse_out_header out; +@@ -511,7 +509,7 @@ int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, + out.unique = req->unique; + out.error = 0; + +- res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags); ++ res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv); + if (res <= 0) { + fuse_free_req(req); + return res; +@@ -1969,8 +1967,7 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, + } + + int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, +- off_t offset, struct fuse_bufvec *bufv, +- enum fuse_buf_copy_flags flags) ++ off_t offset, struct fuse_bufvec *bufv) + { + struct fuse_out_header out; + struct fuse_notify_store_out outarg; +@@ -1999,7 +1996,7 @@ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, + iov[1].iov_base = &outarg; + iov[1].iov_len = sizeof(outarg); + +- res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags); ++ res = fuse_send_data_iov(se, NULL, iov, 2, bufv); + if (res > 0) { + res = -res; + } +diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h +index 12a84b460f..2fa225d40b 100644 +--- a/tools/virtiofsd/fuse_lowlevel.h ++++ b/tools/virtiofsd/fuse_lowlevel.h +@@ -1363,33 +1363,6 @@ int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size); + /** + * Reply with data copied/moved from buffer(s) + * +- * Zero copy data transfer ("splicing") will be used under +- * the following circumstances: +- * +- * 1. FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.want, and +- * 2. the kernel supports splicing from the fuse device +- * (FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.capable), and +- * 3. *flags* does not contain FUSE_BUF_NO_SPLICE +- * 4. The amount of data that is provided in file-descriptor backed +- * buffers (i.e., buffers for which bufv[n].flags == FUSE_BUF_FD) +- * is at least twice the page size. +- * +- * In order for SPLICE_F_MOVE to be used, the following additional +- * conditions have to be fulfilled: +- * +- * 1. FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.want, and +- * 2. the kernel supports it (i.e, FUSE_CAP_SPLICE_MOVE is set in +- fuse_conn_info.capable), and +- * 3. *flags* contains FUSE_BUF_SPLICE_MOVE +- * +- * Note that, if splice is used, the data is actually spliced twice: +- * once into a temporary pipe (to prepend header data), and then again +- * into the kernel. If some of the provided buffers are memory-backed, +- * the data in them is copied in step one and spliced in step two. +- * +- * The FUSE_BUF_SPLICE_FORCE_SPLICE and FUSE_BUF_SPLICE_NONBLOCK flags +- * are silently ignored. +- * + * Possible requests: + * read, readdir, getxattr, listxattr + * +@@ -1400,11 +1373,9 @@ int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size); + * + * @param req request handle + * @param bufv buffer vector +- * @param flags flags controlling the copy + * @return zero for success, -errno for failure to send reply + */ +-int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, +- enum fuse_buf_copy_flags flags); ++int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv); + + /** + * Reply with data vector +@@ -1705,12 +1676,10 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, + * @param ino the inode number + * @param offset the starting offset into the file to store to + * @param bufv buffer vector +- * @param flags flags controlling the copy + * @return zero for success, -errno for failure + */ + int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, +- off_t offset, struct fuse_bufvec *bufv, +- enum fuse_buf_copy_flags flags); ++ off_t offset, struct fuse_bufvec *bufv); + + /* + * Utility functions +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 9377718d9d..126a56ccbd 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -931,7 +931,7 @@ static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, + buf.buf[0].fd = fi->fh; + buf.buf[0].pos = offset; + +- fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE); ++ fuse_reply_data(req, &buf); + } + + static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, +@@ -952,7 +952,7 @@ static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, + out_buf.buf[0].size, (unsigned long)off); + } + +- res = fuse_buf_copy(&out_buf, in_buf, 0); ++ res = fuse_buf_copy(&out_buf, in_buf); + if (res < 0) { + fuse_reply_err(req, -res); + } else { diff --git a/0020-virtiofsd-Fix-fuse_daemonize-ignored-return-values.patch b/0020-virtiofsd-Fix-fuse_daemonize-ignored-return-values.patch new file mode 100644 index 0000000..086f244 --- /dev/null +++ b/0020-virtiofsd-Fix-fuse_daemonize-ignored-return-values.patch @@ -0,0 +1,104 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:00:49 +0000 +Subject: [PATCH] virtiofsd: Fix fuse_daemonize ignored return values +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +QEMU's compiler enables warnings/errors for ignored values +and the (void) trick used in the fuse code isn't enough. +Turn all the return values into a return value on the function. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Reviewed-by: Philippe Mathieu-Daudé +Tested-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 30d8e49760712d65697ea517c53671bd1d214fc7) +--- + tools/virtiofsd/helper.c | 33 ++++++++++++++++++++++----------- + 1 file changed, 22 insertions(+), 11 deletions(-) + +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index 5e6f2051a7..d9227d7367 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -10,12 +10,10 @@ + * See the file COPYING.LIB. + */ + +-#include "config.h" + #include "fuse_i.h" + #include "fuse_lowlevel.h" + #include "fuse_misc.h" + #include "fuse_opt.h" +-#include "mount_util.h" + + #include + #include +@@ -171,6 +169,7 @@ int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts) + + int fuse_daemonize(int foreground) + { ++ int ret = 0, rett; + if (!foreground) { + int nullfd; + int waiter[2]; +@@ -192,8 +191,8 @@ int fuse_daemonize(int foreground) + case 0: + break; + default: +- (void)read(waiter[0], &completed, sizeof(completed)); +- _exit(0); ++ _exit(read(waiter[0], &completed, ++ sizeof(completed) != sizeof(completed))); + } + + if (setsid() == -1) { +@@ -201,13 +200,22 @@ int fuse_daemonize(int foreground) + return -1; + } + +- (void)chdir("/"); ++ ret = chdir("/"); + + nullfd = open("/dev/null", O_RDWR, 0); + if (nullfd != -1) { +- (void)dup2(nullfd, 0); +- (void)dup2(nullfd, 1); +- (void)dup2(nullfd, 2); ++ rett = dup2(nullfd, 0); ++ if (!ret) { ++ ret = rett; ++ } ++ rett = dup2(nullfd, 1); ++ if (!ret) { ++ ret = rett; ++ } ++ rett = dup2(nullfd, 2); ++ if (!ret) { ++ ret = rett; ++ } + if (nullfd > 2) { + close(nullfd); + } +@@ -215,13 +223,16 @@ int fuse_daemonize(int foreground) + + /* Propagate completion of daemon initialization */ + completed = 1; +- (void)write(waiter[1], &completed, sizeof(completed)); ++ rett = write(waiter[1], &completed, sizeof(completed)); ++ if (!ret) { ++ ret = rett; ++ } + close(waiter[0]); + close(waiter[1]); + } else { +- (void)chdir("/"); ++ ret = chdir("/"); + } +- return 0; ++ return ret; + } + + void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, diff --git a/0021-virtiofsd-Fix-common-header-and-define-for-QEMU-buil.patch b/0021-virtiofsd-Fix-common-header-and-define-for-QEMU-buil.patch new file mode 100644 index 0000000..942c006 --- /dev/null +++ b/0021-virtiofsd-Fix-common-header-and-define-for-QEMU-buil.patch @@ -0,0 +1,147 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:00:50 +0000 +Subject: [PATCH] virtiofsd: Fix common header and define for QEMU builds +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +All of the fuse files include config.h and define GNU_SOURCE +where we don't have either under our build - remove them. +Fixup path to the kernel's fuse.h in the QEMUs world. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Philippe Mathieu-Daudé +Tested-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 09863ebc7e32a107235b3c815ad54d26cc64f07a) +--- + tools/virtiofsd/buffer.c | 4 +--- + tools/virtiofsd/fuse_i.h | 3 +++ + tools/virtiofsd/fuse_log.c | 1 + + tools/virtiofsd/fuse_lowlevel.c | 6 ++---- + tools/virtiofsd/fuse_opt.c | 2 +- + tools/virtiofsd/fuse_signals.c | 2 +- + tools/virtiofsd/helper.c | 1 + + tools/virtiofsd/passthrough_ll.c | 8 ++------ + 8 files changed, 12 insertions(+), 15 deletions(-) + +diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c +index 4d507f3302..772efa922d 100644 +--- a/tools/virtiofsd/buffer.c ++++ b/tools/virtiofsd/buffer.c +@@ -9,9 +9,7 @@ + * See the file COPYING.LIB + */ + +-#define _GNU_SOURCE +- +-#include "config.h" ++#include "qemu/osdep.h" + #include "fuse_i.h" + #include "fuse_lowlevel.h" + #include +diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h +index e63cb58388..bae06992e0 100644 +--- a/tools/virtiofsd/fuse_i.h ++++ b/tools/virtiofsd/fuse_i.h +@@ -6,6 +6,9 @@ + * See the file COPYING.LIB + */ + ++#define FUSE_USE_VERSION 31 ++ ++ + #include "fuse.h" + #include "fuse_lowlevel.h" + +diff --git a/tools/virtiofsd/fuse_log.c b/tools/virtiofsd/fuse_log.c +index 11345f9ec8..c301ff6da1 100644 +--- a/tools/virtiofsd/fuse_log.c ++++ b/tools/virtiofsd/fuse_log.c +@@ -8,6 +8,7 @@ + * See the file COPYING.LIB + */ + ++#include "qemu/osdep.h" + #include "fuse_log.h" + + #include +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 3da80de233..07fb8a6095 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -9,11 +9,9 @@ + * See the file COPYING.LIB + */ + +-#define _GNU_SOURCE +- +-#include "config.h" ++#include "qemu/osdep.h" + #include "fuse_i.h" +-#include "fuse_kernel.h" ++#include "standard-headers/linux/fuse.h" + #include "fuse_misc.h" + #include "fuse_opt.h" + +diff --git a/tools/virtiofsd/fuse_opt.c b/tools/virtiofsd/fuse_opt.c +index edd36f4a3b..28922361a2 100644 +--- a/tools/virtiofsd/fuse_opt.c ++++ b/tools/virtiofsd/fuse_opt.c +@@ -9,8 +9,8 @@ + * See the file COPYING.LIB + */ + ++#include "qemu/osdep.h" + #include "fuse_opt.h" +-#include "config.h" + #include "fuse_i.h" + #include "fuse_misc.h" + +diff --git a/tools/virtiofsd/fuse_signals.c b/tools/virtiofsd/fuse_signals.c +index 19d6791cb9..dc7c8ac025 100644 +--- a/tools/virtiofsd/fuse_signals.c ++++ b/tools/virtiofsd/fuse_signals.c +@@ -8,7 +8,7 @@ + * See the file COPYING.LIB + */ + +-#include "config.h" ++#include "qemu/osdep.h" + #include "fuse_i.h" + #include "fuse_lowlevel.h" + +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index d9227d7367..9333691525 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -10,6 +10,7 @@ + * See the file COPYING.LIB. + */ + ++#include "qemu/osdep.h" + #include "fuse_i.h" + #include "fuse_lowlevel.h" + #include "fuse_misc.h" +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 126a56ccbd..322a889cdf 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -35,15 +35,11 @@ + * \include passthrough_ll.c + */ + +-#define _GNU_SOURCE +-#define FUSE_USE_VERSION 31 +- +-#include "config.h" +- ++#include "qemu/osdep.h" ++#include "fuse_lowlevel.h" + #include + #include + #include +-#include + #include + #include + #include diff --git a/0022-virtiofsd-Trim-out-compatibility-code.patch b/0022-virtiofsd-Trim-out-compatibility-code.patch new file mode 100644 index 0000000..5da556a --- /dev/null +++ b/0022-virtiofsd-Trim-out-compatibility-code.patch @@ -0,0 +1,529 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:00:51 +0000 +Subject: [PATCH] virtiofsd: Trim out compatibility code +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +virtiofsd only supports major=7, minor>=31; trim out a lot of +old compatibility code. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 72c42e2d65510e073cf78fdc924d121c77fa0080) +--- + tools/virtiofsd/fuse_lowlevel.c | 330 ++++++++++++-------------------- + 1 file changed, 119 insertions(+), 211 deletions(-) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 07fb8a6095..514d79cb24 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -387,16 +387,7 @@ static void fill_open(struct fuse_open_out *arg, const struct fuse_file_info *f) + int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e) + { + struct fuse_entry_out arg; +- size_t size = req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ENTRY_OUT_SIZE : +- sizeof(arg); +- +- /* +- * before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant +- * negative entry +- */ +- if (!e->ino && req->se->conn.proto_minor < 4) { +- return fuse_reply_err(req, ENOENT); +- } ++ size_t size = sizeof(arg); + + memset(&arg, 0, sizeof(arg)); + fill_entry(&arg, e); +@@ -407,9 +398,7 @@ int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, + const struct fuse_file_info *f) + { + char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)]; +- size_t entrysize = req->se->conn.proto_minor < 9 ? +- FUSE_COMPAT_ENTRY_OUT_SIZE : +- sizeof(struct fuse_entry_out); ++ size_t entrysize = sizeof(struct fuse_entry_out); + struct fuse_entry_out *earg = (struct fuse_entry_out *)buf; + struct fuse_open_out *oarg = (struct fuse_open_out *)(buf + entrysize); + +@@ -423,8 +412,7 @@ int fuse_reply_attr(fuse_req_t req, const struct stat *attr, + double attr_timeout) + { + struct fuse_attr_out arg; +- size_t size = +- req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg); ++ size_t size = sizeof(arg); + + memset(&arg, 0, sizeof(arg)); + arg.attr_valid = calc_timeout_sec(attr_timeout); +@@ -519,8 +507,7 @@ int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv) + int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf) + { + struct fuse_statfs_out arg; +- size_t size = +- req->se->conn.proto_minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(arg); ++ size_t size = sizeof(arg); + + memset(&arg, 0, sizeof(arg)); + convert_statfs(stbuf, &arg.st); +@@ -604,45 +591,31 @@ int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, + iov[count].iov_len = sizeof(arg); + count++; + +- if (req->se->conn.proto_minor < 16) { +- if (in_count) { +- iov[count].iov_base = (void *)in_iov; +- iov[count].iov_len = sizeof(in_iov[0]) * in_count; +- count++; +- } ++ /* Can't handle non-compat 64bit ioctls on 32bit */ ++ if (sizeof(void *) == 4 && req->ioctl_64bit) { ++ res = fuse_reply_err(req, EINVAL); ++ goto out; ++ } + +- if (out_count) { +- iov[count].iov_base = (void *)out_iov; +- iov[count].iov_len = sizeof(out_iov[0]) * out_count; +- count++; ++ if (in_count) { ++ in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count); ++ if (!in_fiov) { ++ goto enomem; + } +- } else { +- /* Can't handle non-compat 64bit ioctls on 32bit */ +- if (sizeof(void *) == 4 && req->ioctl_64bit) { +- res = fuse_reply_err(req, EINVAL); +- goto out; +- } +- +- if (in_count) { +- in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count); +- if (!in_fiov) { +- goto enomem; +- } + +- iov[count].iov_base = (void *)in_fiov; +- iov[count].iov_len = sizeof(in_fiov[0]) * in_count; +- count++; ++ iov[count].iov_base = (void *)in_fiov; ++ iov[count].iov_len = sizeof(in_fiov[0]) * in_count; ++ count++; ++ } ++ if (out_count) { ++ out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count); ++ if (!out_fiov) { ++ goto enomem; + } +- if (out_count) { +- out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count); +- if (!out_fiov) { +- goto enomem; +- } + +- iov[count].iov_base = (void *)out_fiov; +- iov[count].iov_len = sizeof(out_fiov[0]) * out_count; +- count++; +- } ++ iov[count].iov_base = (void *)out_fiov; ++ iov[count].iov_len = sizeof(out_fiov[0]) * out_count; ++ count++; + } + + res = send_reply_iov(req, 0, iov, count); +@@ -784,14 +757,12 @@ static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + struct fuse_file_info *fip = NULL; + struct fuse_file_info fi; + +- if (req->se->conn.proto_minor >= 9) { +- struct fuse_getattr_in *arg = (struct fuse_getattr_in *)inarg; ++ struct fuse_getattr_in *arg = (struct fuse_getattr_in *)inarg; + +- if (arg->getattr_flags & FUSE_GETATTR_FH) { +- memset(&fi, 0, sizeof(fi)); +- fi.fh = arg->fh; +- fip = &fi; +- } ++ if (arg->getattr_flags & FUSE_GETATTR_FH) { ++ memset(&fi, 0, sizeof(fi)); ++ fi.fh = arg->fh; ++ fip = &fi; + } + + if (req->se->op.getattr) { +@@ -856,11 +827,7 @@ static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + struct fuse_mknod_in *arg = (struct fuse_mknod_in *)inarg; + char *name = PARAM(arg); + +- if (req->se->conn.proto_minor >= 12) { +- req->ctx.umask = arg->umask; +- } else { +- name = (char *)inarg + FUSE_COMPAT_MKNOD_IN_SIZE; +- } ++ req->ctx.umask = arg->umask; + + if (req->se->op.mknod) { + req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev); +@@ -873,9 +840,7 @@ static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + { + struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *)inarg; + +- if (req->se->conn.proto_minor >= 12) { +- req->ctx.umask = arg->umask; +- } ++ req->ctx.umask = arg->umask; + + if (req->se->op.mkdir) { + req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode); +@@ -967,11 +932,7 @@ static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + +- if (req->se->conn.proto_minor >= 12) { +- req->ctx.umask = arg->umask; +- } else { +- name = (char *)inarg + sizeof(struct fuse_open_in); +- } ++ req->ctx.umask = arg->umask; + + req->se->op.create(req, nodeid, name, arg->mode, &fi); + } else { +@@ -1003,10 +964,8 @@ static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; +- if (req->se->conn.proto_minor >= 9) { +- fi.lock_owner = arg->lock_owner; +- fi.flags = arg->flags; +- } ++ fi.lock_owner = arg->lock_owner; ++ fi.flags = arg->flags; + req->se->op.read(req, nodeid, arg->size, arg->offset, &fi); + } else { + fuse_reply_err(req, ENOSYS); +@@ -1023,13 +982,9 @@ static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + fi.fh = arg->fh; + fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0; + +- if (req->se->conn.proto_minor < 9) { +- param = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE; +- } else { +- fi.lock_owner = arg->lock_owner; +- fi.flags = arg->flags; +- param = PARAM(arg); +- } ++ fi.lock_owner = arg->lock_owner; ++ fi.flags = arg->flags; ++ param = PARAM(arg); + + if (req->se->op.write) { + req->se->op.write(req, nodeid, param, arg->size, arg->offset, &fi); +@@ -1053,21 +1008,14 @@ static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, + fi.fh = arg->fh; + fi.writepage = arg->write_flags & FUSE_WRITE_CACHE; + +- if (se->conn.proto_minor < 9) { +- bufv.buf[0].mem = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE; +- bufv.buf[0].size -= +- sizeof(struct fuse_in_header) + FUSE_COMPAT_WRITE_IN_SIZE; +- assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD)); +- } else { +- fi.lock_owner = arg->lock_owner; +- fi.flags = arg->flags; +- if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) { +- bufv.buf[0].mem = PARAM(arg); +- } +- +- bufv.buf[0].size -= +- sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); ++ fi.lock_owner = arg->lock_owner; ++ fi.flags = arg->flags; ++ if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) { ++ bufv.buf[0].mem = PARAM(arg); + } ++ ++ bufv.buf[0].size -= ++ sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); + if (bufv.buf[0].size < arg->size) { + fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n"); + fuse_reply_err(req, EIO); +@@ -1086,9 +1034,7 @@ static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.flush = 1; +- if (req->se->conn.proto_minor >= 7) { +- fi.lock_owner = arg->lock_owner; +- } ++ fi.lock_owner = arg->lock_owner; + + if (req->se->op.flush) { + req->se->op.flush(req, nodeid, &fi); +@@ -1105,10 +1051,8 @@ static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + fi.fh = arg->fh; +- if (req->se->conn.proto_minor >= 8) { +- fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; +- fi.lock_owner = arg->lock_owner; +- } ++ fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; ++ fi.lock_owner = arg->lock_owner; + if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) { + fi.flock_release = 1; + fi.lock_owner = arg->lock_owner; +@@ -1477,8 +1421,7 @@ static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + +- if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 && +- !(flags & FUSE_IOCTL_32BIT)) { ++ if (sizeof(void *) == 4 && !(flags & FUSE_IOCTL_32BIT)) { + req->ioctl_64bit = 1; + } + +@@ -1603,7 +1546,7 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + outarg.major = FUSE_KERNEL_VERSION; + outarg.minor = FUSE_KERNEL_MINOR_VERSION; + +- if (arg->major < 7) { ++ if (arg->major < 7 || (arg->major == 7 && arg->minor < 31)) { + fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n", + arg->major, arg->minor); + fuse_reply_err(req, EPROTO); +@@ -1616,81 +1559,71 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + return; + } + +- if (arg->minor >= 6) { +- if (arg->max_readahead < se->conn.max_readahead) { +- se->conn.max_readahead = arg->max_readahead; +- } +- if (arg->flags & FUSE_ASYNC_READ) { +- se->conn.capable |= FUSE_CAP_ASYNC_READ; +- } +- if (arg->flags & FUSE_POSIX_LOCKS) { +- se->conn.capable |= FUSE_CAP_POSIX_LOCKS; +- } +- if (arg->flags & FUSE_ATOMIC_O_TRUNC) { +- se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC; +- } +- if (arg->flags & FUSE_EXPORT_SUPPORT) { +- se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT; +- } +- if (arg->flags & FUSE_DONT_MASK) { +- se->conn.capable |= FUSE_CAP_DONT_MASK; +- } +- if (arg->flags & FUSE_FLOCK_LOCKS) { +- se->conn.capable |= FUSE_CAP_FLOCK_LOCKS; +- } +- if (arg->flags & FUSE_AUTO_INVAL_DATA) { +- se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA; +- } +- if (arg->flags & FUSE_DO_READDIRPLUS) { +- se->conn.capable |= FUSE_CAP_READDIRPLUS; +- } +- if (arg->flags & FUSE_READDIRPLUS_AUTO) { +- se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO; +- } +- if (arg->flags & FUSE_ASYNC_DIO) { +- se->conn.capable |= FUSE_CAP_ASYNC_DIO; +- } +- if (arg->flags & FUSE_WRITEBACK_CACHE) { +- se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE; +- } +- if (arg->flags & FUSE_NO_OPEN_SUPPORT) { +- se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT; +- } +- if (arg->flags & FUSE_PARALLEL_DIROPS) { +- se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS; +- } +- if (arg->flags & FUSE_POSIX_ACL) { +- se->conn.capable |= FUSE_CAP_POSIX_ACL; +- } +- if (arg->flags & FUSE_HANDLE_KILLPRIV) { +- se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV; +- } +- if (arg->flags & FUSE_NO_OPENDIR_SUPPORT) { +- se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT; +- } +- if (!(arg->flags & FUSE_MAX_PAGES)) { +- size_t max_bufsize = +- FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() + +- FUSE_BUFFER_HEADER_SIZE; +- if (bufsize > max_bufsize) { +- bufsize = max_bufsize; +- } ++ if (arg->max_readahead < se->conn.max_readahead) { ++ se->conn.max_readahead = arg->max_readahead; ++ } ++ if (arg->flags & FUSE_ASYNC_READ) { ++ se->conn.capable |= FUSE_CAP_ASYNC_READ; ++ } ++ if (arg->flags & FUSE_POSIX_LOCKS) { ++ se->conn.capable |= FUSE_CAP_POSIX_LOCKS; ++ } ++ if (arg->flags & FUSE_ATOMIC_O_TRUNC) { ++ se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC; ++ } ++ if (arg->flags & FUSE_EXPORT_SUPPORT) { ++ se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT; ++ } ++ if (arg->flags & FUSE_DONT_MASK) { ++ se->conn.capable |= FUSE_CAP_DONT_MASK; ++ } ++ if (arg->flags & FUSE_FLOCK_LOCKS) { ++ se->conn.capable |= FUSE_CAP_FLOCK_LOCKS; ++ } ++ if (arg->flags & FUSE_AUTO_INVAL_DATA) { ++ se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA; ++ } ++ if (arg->flags & FUSE_DO_READDIRPLUS) { ++ se->conn.capable |= FUSE_CAP_READDIRPLUS; ++ } ++ if (arg->flags & FUSE_READDIRPLUS_AUTO) { ++ se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO; ++ } ++ if (arg->flags & FUSE_ASYNC_DIO) { ++ se->conn.capable |= FUSE_CAP_ASYNC_DIO; ++ } ++ if (arg->flags & FUSE_WRITEBACK_CACHE) { ++ se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE; ++ } ++ if (arg->flags & FUSE_NO_OPEN_SUPPORT) { ++ se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT; ++ } ++ if (arg->flags & FUSE_PARALLEL_DIROPS) { ++ se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS; ++ } ++ if (arg->flags & FUSE_POSIX_ACL) { ++ se->conn.capable |= FUSE_CAP_POSIX_ACL; ++ } ++ if (arg->flags & FUSE_HANDLE_KILLPRIV) { ++ se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV; ++ } ++ if (arg->flags & FUSE_NO_OPENDIR_SUPPORT) { ++ se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT; ++ } ++ if (!(arg->flags & FUSE_MAX_PAGES)) { ++ size_t max_bufsize = FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() + ++ FUSE_BUFFER_HEADER_SIZE; ++ if (bufsize > max_bufsize) { ++ bufsize = max_bufsize; + } +- } else { +- se->conn.max_readahead = 0; + } +- +- if (se->conn.proto_minor >= 14) { + #ifdef HAVE_SPLICE + #ifdef HAVE_VMSPLICE +- se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; ++ se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; + #endif +- se->conn.capable |= FUSE_CAP_SPLICE_READ; ++ se->conn.capable |= FUSE_CAP_SPLICE_READ; + #endif +- } +- if (se->conn.proto_minor >= 18) { +- se->conn.capable |= FUSE_CAP_IOCTL_DIR; +- } ++ se->conn.capable |= FUSE_CAP_IOCTL_DIR; + + /* + * Default settings for modern filesystems. +@@ -1797,24 +1730,20 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + outarg.max_readahead = se->conn.max_readahead; + outarg.max_write = se->conn.max_write; +- if (se->conn.proto_minor >= 13) { +- if (se->conn.max_background >= (1 << 16)) { +- se->conn.max_background = (1 << 16) - 1; +- } +- if (se->conn.congestion_threshold > se->conn.max_background) { +- se->conn.congestion_threshold = se->conn.max_background; +- } +- if (!se->conn.congestion_threshold) { +- se->conn.congestion_threshold = se->conn.max_background * 3 / 4; +- } +- +- outarg.max_background = se->conn.max_background; +- outarg.congestion_threshold = se->conn.congestion_threshold; ++ if (se->conn.max_background >= (1 << 16)) { ++ se->conn.max_background = (1 << 16) - 1; ++ } ++ if (se->conn.congestion_threshold > se->conn.max_background) { ++ se->conn.congestion_threshold = se->conn.max_background; + } +- if (se->conn.proto_minor >= 23) { +- outarg.time_gran = se->conn.time_gran; ++ if (!se->conn.congestion_threshold) { ++ se->conn.congestion_threshold = se->conn.max_background * 3 / 4; + } + ++ outarg.max_background = se->conn.max_background; ++ outarg.congestion_threshold = se->conn.congestion_threshold; ++ outarg.time_gran = se->conn.time_gran; ++ + if (se->debug) { + fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, + outarg.minor); +@@ -1828,11 +1757,6 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + outarg.congestion_threshold); + fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", outarg.time_gran); + } +- if (arg->minor < 5) { +- outargsize = FUSE_COMPAT_INIT_OUT_SIZE; +- } else if (arg->minor < 23) { +- outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE; +- } + + send_reply_ok(req, &outarg, outargsize); + } +@@ -1896,10 +1820,6 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, + return -EINVAL; + } + +- if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) { +- return -ENOSYS; +- } +- + outarg.ino = ino; + outarg.off = off; + outarg.len = len; +@@ -1920,10 +1840,6 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, + return -EINVAL; + } + +- if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) { +- return -ENOSYS; +- } +- + outarg.parent = parent; + outarg.namelen = namelen; + outarg.padding = 0; +@@ -1947,10 +1863,6 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, + return -EINVAL; + } + +- if (se->conn.proto_major < 6 || se->conn.proto_minor < 18) { +- return -ENOSYS; +- } +- + outarg.parent = parent; + outarg.child = child; + outarg.namelen = namelen; +@@ -1977,10 +1889,6 @@ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, + return -EINVAL; + } + +- if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) { +- return -ENOSYS; +- } +- + out.unique = 0; + out.error = FUSE_NOTIFY_STORE; + diff --git a/0023-vitriofsd-passthrough_ll-fix-fallocate-ifdefs.patch b/0023-vitriofsd-passthrough_ll-fix-fallocate-ifdefs.patch new file mode 100644 index 0000000..529af3b --- /dev/null +++ b/0023-vitriofsd-passthrough_ll-fix-fallocate-ifdefs.patch @@ -0,0 +1,36 @@ +From: Xiao Yang +Date: Mon, 27 Jan 2020 19:00:52 +0000 +Subject: [PATCH] vitriofsd/passthrough_ll: fix fallocate() ifdefs + +1) Use correct CONFIG_FALLOCATE macro to check if fallocate() is supported.(i.e configure + script sets CONFIG_FALLOCATE intead of HAVE_FALLOCATE if fallocate() is supported) +2) Replace HAVE_POSIX_FALLOCATE with CONFIG_POSIX_FALLOCATE. + +Signed-off-by: Xiao Yang +Signed-off-by: Dr. David Alan Gilbert + Merged from two of Xiao Yang's patches +(cherry picked from commit 9776457ca6f05d5900e27decb1dba2ffddf95a22) +--- + tools/virtiofsd/passthrough_ll.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 322a889cdf..6c4da18075 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -975,13 +975,13 @@ static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, + int err = EOPNOTSUPP; + (void)ino; + +-#ifdef HAVE_FALLOCATE ++#ifdef CONFIG_FALLOCATE + err = fallocate(fi->fh, mode, offset, length); + if (err < 0) { + err = errno; + } + +-#elif defined(HAVE_POSIX_FALLOCATE) ++#elif defined(CONFIG_POSIX_FALLOCATE) + if (mode) { + fuse_reply_err(req, EOPNOTSUPP); + return; diff --git a/0024-virtiofsd-Make-fsync-work-even-if-only-inode-is-pass.patch b/0024-virtiofsd-Make-fsync-work-even-if-only-inode-is-pass.patch new file mode 100644 index 0000000..119bcdb --- /dev/null +++ b/0024-virtiofsd-Make-fsync-work-even-if-only-inode-is-pass.patch @@ -0,0 +1,79 @@ +From: Vivek Goyal +Date: Mon, 27 Jan 2020 19:00:53 +0000 +Subject: [PATCH] virtiofsd: Make fsync work even if only inode is passed in +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If caller has not sent file handle in request, then using inode, retrieve +the fd opened using O_PATH and use that to open file again and issue +fsync. This will be needed when dax_flush() calls fsync. At that time +we only have inode information (and not file). + +Signed-off-by: Vivek Goyal +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 1b209805f8159c3f4d89ddb9390a5f64887cebff) +--- + tools/virtiofsd/fuse_lowlevel.c | 6 +++++- + tools/virtiofsd/passthrough_ll.c | 28 ++++++++++++++++++++++++++-- + 2 files changed, 31 insertions(+), 3 deletions(-) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 514d79cb24..8552cfb8af 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -1075,7 +1075,11 @@ static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + fi.fh = arg->fh; + + if (req->se->op.fsync) { +- req->se->op.fsync(req, nodeid, datasync, &fi); ++ if (fi.fh == (uint64_t)-1) { ++ req->se->op.fsync(req, nodeid, datasync, NULL); ++ } else { ++ req->se->op.fsync(req, nodeid, datasync, &fi); ++ } + } else { + fuse_reply_err(req, ENOSYS); + } +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 6c4da18075..26ac87013b 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -903,10 +903,34 @@ static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, + { + int res; + (void)ino; ++ int fd; ++ char *buf; ++ ++ fuse_log(FUSE_LOG_DEBUG, "lo_fsync(ino=%" PRIu64 ", fi=0x%p)\n", ino, ++ (void *)fi); ++ ++ if (!fi) { ++ res = asprintf(&buf, "/proc/self/fd/%i", lo_fd(req, ino)); ++ if (res == -1) { ++ return (void)fuse_reply_err(req, errno); ++ } ++ ++ fd = open(buf, O_RDWR); ++ free(buf); ++ if (fd == -1) { ++ return (void)fuse_reply_err(req, errno); ++ } ++ } else { ++ fd = fi->fh; ++ } ++ + if (datasync) { +- res = fdatasync(fi->fh); ++ res = fdatasync(fd); + } else { +- res = fsync(fi->fh); ++ res = fsync(fd); ++ } ++ if (!fi) { ++ close(fd); + } + fuse_reply_err(req, res == -1 ? errno : 0); + } diff --git a/0025-virtiofsd-Add-options-for-virtio.patch b/0025-virtiofsd-Add-options-for-virtio.patch new file mode 100644 index 0000000..f0193f8 --- /dev/null +++ b/0025-virtiofsd-Add-options-for-virtio.patch @@ -0,0 +1,84 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:00:54 +0000 +Subject: [PATCH] virtiofsd: Add options for virtio + +Add options to specify parameters for virtio-fs paths, i.e. + + ./virtiofsd -o vhost_user_socket=/tmp/vhostqemu + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Misono Tomohiro +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 205de006aab8dcbe546a7e3a51d295c2d05e654b) +--- + tools/virtiofsd/fuse_i.h | 1 + + tools/virtiofsd/fuse_lowlevel.c | 11 ++++++++--- + tools/virtiofsd/helper.c | 14 +++++++------- + 3 files changed, 16 insertions(+), 10 deletions(-) + +diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h +index bae06992e0..26b1a7da88 100644 +--- a/tools/virtiofsd/fuse_i.h ++++ b/tools/virtiofsd/fuse_i.h +@@ -63,6 +63,7 @@ struct fuse_session { + struct fuse_notify_req notify_list; + size_t bufsize; + int error; ++ char *vu_socket_path; + }; + + struct fuse_chan { +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 8552cfb8af..17e8718283 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -2115,8 +2115,11 @@ reply_err: + } + + static const struct fuse_opt fuse_ll_opts[] = { +- LL_OPTION("debug", debug, 1), LL_OPTION("-d", debug, 1), +- LL_OPTION("--debug", debug, 1), LL_OPTION("allow_root", deny_others, 1), ++ LL_OPTION("debug", debug, 1), ++ LL_OPTION("-d", debug, 1), ++ LL_OPTION("--debug", debug, 1), ++ LL_OPTION("allow_root", deny_others, 1), ++ LL_OPTION("--socket-path=%s", vu_socket_path, 0), + FUSE_OPT_END + }; + +@@ -2132,7 +2135,9 @@ void fuse_lowlevel_help(void) + * These are not all options, but the ones that are + * potentially of interest to an end-user + */ +- printf(" -o allow_root allow access by root\n"); ++ printf( ++ " -o allow_root allow access by root\n" ++ " --socket-path=PATH path for the vhost-user socket\n"); + } + + void fuse_session_destroy(struct fuse_session *se) +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index 9333691525..676032e71f 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -127,13 +127,13 @@ static const struct fuse_opt conn_info_opt_spec[] = { + + void fuse_cmdline_help(void) + { +- printf( +- " -h --help print help\n" +- " -V --version print version\n" +- " -d -o debug enable debug output (implies -f)\n" +- " -f foreground operation\n" +- " -o max_idle_threads the maximum number of idle worker threads\n" +- " allowed (default: 10)\n"); ++ printf(" -h --help print help\n" ++ " -V --version print version\n" ++ " -d -o debug enable debug output (implies -f)\n" ++ " -f foreground operation\n" ++ " -o max_idle_threads the maximum number of idle worker " ++ "threads\n" ++ " allowed (default: 10)\n"); + } + + static int fuse_helper_opt_proc(void *data, const char *arg, int key, diff --git a/0026-virtiofsd-add-o-source-PATH-to-help-output.patch b/0026-virtiofsd-add-o-source-PATH-to-help-output.patch new file mode 100644 index 0000000..43f1c59 --- /dev/null +++ b/0026-virtiofsd-add-o-source-PATH-to-help-output.patch @@ -0,0 +1,30 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:00:55 +0000 +Subject: [PATCH] virtiofsd: add -o source=PATH to help output +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The -o source=PATH option will be used by most command-line invocations. +Let's document it! + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 4ff075f72be2f489c8998ae492ec5cdbbbd73e07) +--- + tools/virtiofsd/passthrough_ll.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 26ac87013b..fc9b264d56 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1319,6 +1319,7 @@ int main(int argc, char *argv[]) + if (opts.show_help) { + printf("usage: %s [options]\n\n", argv[0]); + fuse_cmdline_help(); ++ printf(" -o source=PATH shared directory tree\n"); + fuse_lowlevel_help(); + ret = 0; + goto err_out1; diff --git a/0027-virtiofsd-Open-vhost-connection-instead-of-mounting.patch b/0027-virtiofsd-Open-vhost-connection-instead-of-mounting.patch new file mode 100644 index 0000000..01e1514 --- /dev/null +++ b/0027-virtiofsd-Open-vhost-connection-instead-of-mounting.patch @@ -0,0 +1,241 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:00:56 +0000 +Subject: [PATCH] virtiofsd: Open vhost connection instead of mounting +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When run with vhost-user options we conect to the QEMU instead +via a socket. Start this off by creating the socket. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Reviewed-by: Misono Tomohiro +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit d14bf584dd965821e80d14c16d9292a464b1ab85) +--- + tools/virtiofsd/fuse_i.h | 7 ++- + tools/virtiofsd/fuse_lowlevel.c | 55 +++-------------------- + tools/virtiofsd/fuse_virtio.c | 79 +++++++++++++++++++++++++++++++++ + tools/virtiofsd/fuse_virtio.h | 23 ++++++++++ + 4 files changed, 114 insertions(+), 50 deletions(-) + create mode 100644 tools/virtiofsd/fuse_virtio.c + create mode 100644 tools/virtiofsd/fuse_virtio.h + +diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h +index 26b1a7da88..82d6ac7115 100644 +--- a/tools/virtiofsd/fuse_i.h ++++ b/tools/virtiofsd/fuse_i.h +@@ -6,9 +6,10 @@ + * See the file COPYING.LIB + */ + +-#define FUSE_USE_VERSION 31 +- ++#ifndef FUSE_I_H ++#define FUSE_I_H + ++#define FUSE_USE_VERSION 31 + #include "fuse.h" + #include "fuse_lowlevel.h" + +@@ -101,3 +102,5 @@ void fuse_session_process_buf_int(struct fuse_session *se, + + /* room needed in buffer to accommodate header */ + #define FUSE_BUFFER_HEADER_SIZE 0x1000 ++ ++#endif +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 17e8718283..5df124e64b 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -14,6 +14,7 @@ + #include "standard-headers/linux/fuse.h" + #include "fuse_misc.h" + #include "fuse_opt.h" ++#include "fuse_virtio.h" + + #include + #include +@@ -2202,6 +2203,11 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, + goto out4; + } + ++ if (!se->vu_socket_path) { ++ fprintf(stderr, "fuse: missing -o vhost_user_socket option\n"); ++ goto out4; ++ } ++ + se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() + FUSE_BUFFER_HEADER_SIZE; + + list_init_req(&se->list); +@@ -2224,54 +2230,7 @@ out1: + + int fuse_session_mount(struct fuse_session *se) + { +- int fd; +- +- /* +- * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos +- * would ensue. +- */ +- do { +- fd = open("/dev/null", O_RDWR); +- if (fd > 2) { +- close(fd); +- } +- } while (fd >= 0 && fd <= 2); +- +- /* +- * To allow FUSE daemons to run without privileges, the caller may open +- * /dev/fuse before launching the file system and pass on the file +- * descriptor by specifying /dev/fd/N as the mount point. Note that the +- * parent process takes care of performing the mount in this case. +- */ +- fd = fuse_mnt_parse_fuse_fd(mountpoint); +- if (fd != -1) { +- if (fcntl(fd, F_GETFD) == -1) { +- fuse_log(FUSE_LOG_ERR, "fuse: Invalid file descriptor /dev/fd/%u\n", +- fd); +- return -1; +- } +- se->fd = fd; +- return 0; +- } +- +- /* Open channel */ +- fd = fuse_kern_mount(mountpoint, se->mo); +- if (fd == -1) { +- return -1; +- } +- se->fd = fd; +- +- /* Save mountpoint */ +- se->mountpoint = strdup(mountpoint); +- if (se->mountpoint == NULL) { +- goto error_out; +- } +- +- return 0; +- +-error_out: +- fuse_kern_unmount(mountpoint, fd); +- return -1; ++ return virtio_session_mount(se); + } + + int fuse_session_fd(struct fuse_session *se) +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +new file mode 100644 +index 0000000000..cbef6ffdda +--- /dev/null ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -0,0 +1,79 @@ ++/* ++ * virtio-fs glue for FUSE ++ * Copyright (C) 2018 Red Hat, Inc. and/or its affiliates ++ * ++ * Authors: ++ * Dave Gilbert ++ * ++ * Implements the glue between libfuse and libvhost-user ++ * ++ * This program can be distributed under the terms of the GNU LGPLv2. ++ * See the file COPYING.LIB ++ */ ++ ++#include "fuse_i.h" ++#include "standard-headers/linux/fuse.h" ++#include "fuse_misc.h" ++#include "fuse_opt.h" ++#include "fuse_virtio.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* From spec */ ++struct virtio_fs_config { ++ char tag[36]; ++ uint32_t num_queues; ++}; ++ ++int virtio_session_mount(struct fuse_session *se) ++{ ++ struct sockaddr_un un; ++ mode_t old_umask; ++ ++ if (strlen(se->vu_socket_path) >= sizeof(un.sun_path)) { ++ fuse_log(FUSE_LOG_ERR, "Socket path too long\n"); ++ return -1; ++ } ++ ++ se->fd = -1; ++ ++ /* ++ * Create the Unix socket to communicate with qemu ++ * based on QEMU's vhost-user-bridge ++ */ ++ unlink(se->vu_socket_path); ++ strcpy(un.sun_path, se->vu_socket_path); ++ size_t addr_len = sizeof(un); ++ ++ int listen_sock = socket(AF_UNIX, SOCK_STREAM, 0); ++ if (listen_sock == -1) { ++ fuse_log(FUSE_LOG_ERR, "vhost socket creation: %m\n"); ++ return -1; ++ } ++ un.sun_family = AF_UNIX; ++ ++ /* ++ * Unfortunately bind doesn't let you set the mask on the socket, ++ * so set umask to 077 and restore it later. ++ */ ++ old_umask = umask(0077); ++ if (bind(listen_sock, (struct sockaddr *)&un, addr_len) == -1) { ++ fuse_log(FUSE_LOG_ERR, "vhost socket bind: %m\n"); ++ umask(old_umask); ++ return -1; ++ } ++ umask(old_umask); ++ ++ if (listen(listen_sock, 1) == -1) { ++ fuse_log(FUSE_LOG_ERR, "vhost socket listen: %m\n"); ++ return -1; ++ } ++ ++ return -1; ++} +diff --git a/tools/virtiofsd/fuse_virtio.h b/tools/virtiofsd/fuse_virtio.h +new file mode 100644 +index 0000000000..8f2edb69ca +--- /dev/null ++++ b/tools/virtiofsd/fuse_virtio.h +@@ -0,0 +1,23 @@ ++/* ++ * virtio-fs glue for FUSE ++ * Copyright (C) 2018 Red Hat, Inc. and/or its affiliates ++ * ++ * Authors: ++ * Dave Gilbert ++ * ++ * Implements the glue between libfuse and libvhost-user ++ * ++ * This program can be distributed under the terms of the GNU LGPLv2. ++ * See the file COPYING.LIB ++ */ ++ ++#ifndef FUSE_VIRTIO_H ++#define FUSE_VIRTIO_H ++ ++#include "fuse_i.h" ++ ++struct fuse_session; ++ ++int virtio_session_mount(struct fuse_session *se); ++ ++#endif diff --git a/0028-virtiofsd-Start-wiring-up-vhost-user.patch b/0028-virtiofsd-Start-wiring-up-vhost-user.patch new file mode 100644 index 0000000..1fdd8a4 --- /dev/null +++ b/0028-virtiofsd-Start-wiring-up-vhost-user.patch @@ -0,0 +1,231 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:00:57 +0000 +Subject: [PATCH] virtiofsd: Start wiring up vhost-user +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Listen on our unix socket for the connection from QEMU, when we get it +initialise vhost-user and dive into our own loop variant (currently +dummy). + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit f6f3573c6f271af5ded63ce28589a113f7205c72) +--- + tools/virtiofsd/fuse_i.h | 4 ++ + tools/virtiofsd/fuse_lowlevel.c | 5 ++ + tools/virtiofsd/fuse_lowlevel.h | 7 +++ + tools/virtiofsd/fuse_virtio.c | 87 +++++++++++++++++++++++++++++++- + tools/virtiofsd/fuse_virtio.h | 2 + + tools/virtiofsd/passthrough_ll.c | 7 +-- + 6 files changed, 106 insertions(+), 6 deletions(-) + +diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h +index 82d6ac7115..ec04449069 100644 +--- a/tools/virtiofsd/fuse_i.h ++++ b/tools/virtiofsd/fuse_i.h +@@ -13,6 +13,8 @@ + #include "fuse.h" + #include "fuse_lowlevel.h" + ++struct fv_VuDev; ++ + struct fuse_req { + struct fuse_session *se; + uint64_t unique; +@@ -65,6 +67,8 @@ struct fuse_session { + size_t bufsize; + int error; + char *vu_socket_path; ++ int vu_socketfd; ++ struct fv_VuDev *virtio_dev; + }; + + struct fuse_chan { +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 5df124e64b..af09fa2b94 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -2242,6 +2242,11 @@ void fuse_session_unmount(struct fuse_session *se) + { + } + ++int fuse_lowlevel_is_virtio(struct fuse_session *se) ++{ ++ return se->vu_socket_path != NULL; ++} ++ + #ifdef linux + int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) + { +diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h +index 2fa225d40b..f6b34700af 100644 +--- a/tools/virtiofsd/fuse_lowlevel.h ++++ b/tools/virtiofsd/fuse_lowlevel.h +@@ -1755,6 +1755,13 @@ void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, + */ + int fuse_req_interrupted(fuse_req_t req); + ++/** ++ * Check if the session is connected via virtio ++ * ++ * @param se session object ++ * @return 1 if the session is a virtio session ++ */ ++int fuse_lowlevel_is_virtio(struct fuse_session *se); + + /* + * Inquiry functions +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index cbef6ffdda..2ae3c764dd 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -19,18 +19,78 @@ + + #include + #include ++#include + #include + #include + #include + #include + #include + ++#include "contrib/libvhost-user/libvhost-user.h" ++ ++/* ++ * We pass the dev element into libvhost-user ++ * and then use it to get back to the outer ++ * container for other data. ++ */ ++struct fv_VuDev { ++ VuDev dev; ++ struct fuse_session *se; ++}; ++ + /* From spec */ + struct virtio_fs_config { + char tag[36]; + uint32_t num_queues; + }; + ++/* ++ * Callback from libvhost-user if there's a new fd we're supposed to listen ++ * to, typically a queue kick? ++ */ ++static void fv_set_watch(VuDev *dev, int fd, int condition, vu_watch_cb cb, ++ void *data) ++{ ++ fuse_log(FUSE_LOG_WARNING, "%s: TODO! fd=%d\n", __func__, fd); ++} ++ ++/* ++ * Callback from libvhost-user if we're no longer supposed to listen on an fd ++ */ ++static void fv_remove_watch(VuDev *dev, int fd) ++{ ++ fuse_log(FUSE_LOG_WARNING, "%s: TODO! fd=%d\n", __func__, fd); ++} ++ ++/* Callback from libvhost-user to panic */ ++static void fv_panic(VuDev *dev, const char *err) ++{ ++ fuse_log(FUSE_LOG_ERR, "%s: libvhost-user: %s\n", __func__, err); ++ /* TODO: Allow reconnects?? */ ++ exit(EXIT_FAILURE); ++} ++ ++static bool fv_queue_order(VuDev *dev, int qidx) ++{ ++ return false; ++} ++ ++static const VuDevIface fv_iface = { ++ /* TODO: Add other callbacks */ ++ .queue_is_processed_in_order = fv_queue_order, ++}; ++ ++int virtio_loop(struct fuse_session *se) ++{ ++ fuse_log(FUSE_LOG_INFO, "%s: Entry\n", __func__); ++ ++ while (1) { ++ /* TODO: Add stuffing */ ++ } ++ ++ fuse_log(FUSE_LOG_INFO, "%s: Exit\n", __func__); ++} ++ + int virtio_session_mount(struct fuse_session *se) + { + struct sockaddr_un un; +@@ -75,5 +135,30 @@ int virtio_session_mount(struct fuse_session *se) + return -1; + } + +- return -1; ++ fuse_log(FUSE_LOG_INFO, "%s: Waiting for vhost-user socket connection...\n", ++ __func__); ++ int data_sock = accept(listen_sock, NULL, NULL); ++ if (data_sock == -1) { ++ fuse_log(FUSE_LOG_ERR, "vhost socket accept: %m\n"); ++ close(listen_sock); ++ return -1; ++ } ++ close(listen_sock); ++ fuse_log(FUSE_LOG_INFO, "%s: Received vhost-user socket connection\n", ++ __func__); ++ ++ /* TODO: Some cleanup/deallocation! */ ++ se->virtio_dev = calloc(sizeof(struct fv_VuDev), 1); ++ if (!se->virtio_dev) { ++ fuse_log(FUSE_LOG_ERR, "%s: virtio_dev calloc failed\n", __func__); ++ close(data_sock); ++ return -1; ++ } ++ ++ se->vu_socketfd = data_sock; ++ se->virtio_dev->se = se; ++ vu_init(&se->virtio_dev->dev, 2, se->vu_socketfd, fv_panic, fv_set_watch, ++ fv_remove_watch, &fv_iface); ++ ++ return 0; + } +diff --git a/tools/virtiofsd/fuse_virtio.h b/tools/virtiofsd/fuse_virtio.h +index 8f2edb69ca..23026d6e4c 100644 +--- a/tools/virtiofsd/fuse_virtio.h ++++ b/tools/virtiofsd/fuse_virtio.h +@@ -20,4 +20,6 @@ struct fuse_session; + + int virtio_session_mount(struct fuse_session *se); + ++int virtio_loop(struct fuse_session *se); ++ + #endif +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index fc9b264d56..037c5d7b26 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -36,6 +36,7 @@ + */ + + #include "qemu/osdep.h" ++#include "fuse_virtio.h" + #include "fuse_lowlevel.h" + #include + #include +@@ -1395,11 +1396,7 @@ int main(int argc, char *argv[]) + fuse_daemonize(opts.foreground); + + /* Block until ctrl+c or fusermount -u */ +- if (opts.singlethread) { +- ret = fuse_session_loop(se); +- } else { +- ret = fuse_session_loop_mt(se, opts.clone_fd); +- } ++ ret = virtio_loop(se); + + fuse_session_unmount(se); + err_out3: diff --git a/0029-virtiofsd-Add-main-virtio-loop.patch b/0029-virtiofsd-Add-main-virtio-loop.patch new file mode 100644 index 0000000..d8217c4 --- /dev/null +++ b/0029-virtiofsd-Add-main-virtio-loop.patch @@ -0,0 +1,89 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:00:58 +0000 +Subject: [PATCH] virtiofsd: Add main virtio loop +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Processes incoming requests on the vhost-user fd. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 204d8ae57b3c57098642c79b3c03d42495149c09) +--- + tools/virtiofsd/fuse_virtio.c | 42 ++++++++++++++++++++++++++++++++--- + 1 file changed, 39 insertions(+), 3 deletions(-) + +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index 2ae3c764dd..1928a2025c 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -11,12 +11,14 @@ + * See the file COPYING.LIB + */ + ++#include "fuse_virtio.h" + #include "fuse_i.h" + #include "standard-headers/linux/fuse.h" + #include "fuse_misc.h" + #include "fuse_opt.h" +-#include "fuse_virtio.h" + ++#include ++#include + #include + #include + #include +@@ -80,15 +82,49 @@ static const VuDevIface fv_iface = { + .queue_is_processed_in_order = fv_queue_order, + }; + ++/* ++ * Main loop; this mostly deals with events on the vhost-user ++ * socket itself, and not actual fuse data. ++ */ + int virtio_loop(struct fuse_session *se) + { + fuse_log(FUSE_LOG_INFO, "%s: Entry\n", __func__); + +- while (1) { +- /* TODO: Add stuffing */ ++ while (!fuse_session_exited(se)) { ++ struct pollfd pf[1]; ++ pf[0].fd = se->vu_socketfd; ++ pf[0].events = POLLIN; ++ pf[0].revents = 0; ++ ++ fuse_log(FUSE_LOG_DEBUG, "%s: Waiting for VU event\n", __func__); ++ int poll_res = ppoll(pf, 1, NULL, NULL); ++ ++ if (poll_res == -1) { ++ if (errno == EINTR) { ++ fuse_log(FUSE_LOG_INFO, "%s: ppoll interrupted, going around\n", ++ __func__); ++ continue; ++ } ++ fuse_log(FUSE_LOG_ERR, "virtio_loop ppoll: %m\n"); ++ break; ++ } ++ assert(poll_res == 1); ++ if (pf[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { ++ fuse_log(FUSE_LOG_ERR, "%s: Unexpected poll revents %x\n", __func__, ++ pf[0].revents); ++ break; ++ } ++ assert(pf[0].revents & POLLIN); ++ fuse_log(FUSE_LOG_DEBUG, "%s: Got VU event\n", __func__); ++ if (!vu_dispatch(&se->virtio_dev->dev)) { ++ fuse_log(FUSE_LOG_ERR, "%s: vu_dispatch failed\n", __func__); ++ break; ++ } + } + + fuse_log(FUSE_LOG_INFO, "%s: Exit\n", __func__); ++ ++ return 0; + } + + int virtio_session_mount(struct fuse_session *se) diff --git a/0030-virtiofsd-get-set-features-callbacks.patch b/0030-virtiofsd-get-set-features-callbacks.patch new file mode 100644 index 0000000..cd07092 --- /dev/null +++ b/0030-virtiofsd-get-set-features-callbacks.patch @@ -0,0 +1,50 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:00:59 +0000 +Subject: [PATCH] virtiofsd: get/set features callbacks +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add the get/set features callbacks. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit f2cef5fb9ae20136ca18d16328787b69b3abfa18) +--- + tools/virtiofsd/fuse_virtio.c | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index 1928a2025c..4819e56568 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -46,6 +46,17 @@ struct virtio_fs_config { + uint32_t num_queues; + }; + ++/* Callback from libvhost-user */ ++static uint64_t fv_get_features(VuDev *dev) ++{ ++ return 1ULL << VIRTIO_F_VERSION_1; ++} ++ ++/* Callback from libvhost-user */ ++static void fv_set_features(VuDev *dev, uint64_t features) ++{ ++} ++ + /* + * Callback from libvhost-user if there's a new fd we're supposed to listen + * to, typically a queue kick? +@@ -78,7 +89,9 @@ static bool fv_queue_order(VuDev *dev, int qidx) + } + + static const VuDevIface fv_iface = { +- /* TODO: Add other callbacks */ ++ .get_features = fv_get_features, ++ .set_features = fv_set_features, ++ + .queue_is_processed_in_order = fv_queue_order, + }; + diff --git a/0031-virtiofsd-Start-queue-threads.patch b/0031-virtiofsd-Start-queue-threads.patch new file mode 100644 index 0000000..f463a47 --- /dev/null +++ b/0031-virtiofsd-Start-queue-threads.patch @@ -0,0 +1,148 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:00 +0000 +Subject: [PATCH] virtiofsd: Start queue threads +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Start a thread for each queue when we get notified it's been started. + +Signed-off-by: Dr. David Alan Gilbert +fix by: +Signed-off-by: Jun Piao +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit e4c55a3c144493b436e40031e2eed61a84eca47b) +--- + tools/virtiofsd/fuse_virtio.c | 89 +++++++++++++++++++++++++++++++++++ + 1 file changed, 89 insertions(+) + +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index 4819e56568..2a94bb3cca 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -11,6 +11,7 @@ + * See the file COPYING.LIB + */ + ++#include "qemu/osdep.h" + #include "fuse_virtio.h" + #include "fuse_i.h" + #include "standard-headers/linux/fuse.h" +@@ -30,6 +31,15 @@ + + #include "contrib/libvhost-user/libvhost-user.h" + ++struct fv_QueueInfo { ++ pthread_t thread; ++ struct fv_VuDev *virtio_dev; ++ ++ /* Our queue index, corresponds to array position */ ++ int qidx; ++ int kick_fd; ++}; ++ + /* + * We pass the dev element into libvhost-user + * and then use it to get back to the outer +@@ -38,6 +48,13 @@ + struct fv_VuDev { + VuDev dev; + struct fuse_session *se; ++ ++ /* ++ * The following pair of fields are only accessed in the main ++ * virtio_loop ++ */ ++ size_t nqueues; ++ struct fv_QueueInfo **qi; + }; + + /* From spec */ +@@ -83,6 +100,75 @@ static void fv_panic(VuDev *dev, const char *err) + exit(EXIT_FAILURE); + } + ++static void *fv_queue_thread(void *opaque) ++{ ++ struct fv_QueueInfo *qi = opaque; ++ fuse_log(FUSE_LOG_INFO, "%s: Start for queue %d kick_fd %d\n", __func__, ++ qi->qidx, qi->kick_fd); ++ while (1) { ++ /* TODO */ ++ } ++ ++ return NULL; ++} ++ ++/* Callback from libvhost-user on start or stop of a queue */ ++static void fv_queue_set_started(VuDev *dev, int qidx, bool started) ++{ ++ struct fv_VuDev *vud = container_of(dev, struct fv_VuDev, dev); ++ struct fv_QueueInfo *ourqi; ++ ++ fuse_log(FUSE_LOG_INFO, "%s: qidx=%d started=%d\n", __func__, qidx, ++ started); ++ assert(qidx >= 0); ++ ++ /* ++ * Ignore additional request queues for now. passthrough_ll.c must be ++ * audited for thread-safety issues first. It was written with a ++ * well-behaved client in mind and may not protect against all types of ++ * races yet. ++ */ ++ if (qidx > 1) { ++ fuse_log(FUSE_LOG_ERR, ++ "%s: multiple request queues not yet implemented, please only " ++ "configure 1 request queue\n", ++ __func__); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (started) { ++ /* Fire up a thread to watch this queue */ ++ if (qidx >= vud->nqueues) { ++ vud->qi = realloc(vud->qi, (qidx + 1) * sizeof(vud->qi[0])); ++ assert(vud->qi); ++ memset(vud->qi + vud->nqueues, 0, ++ sizeof(vud->qi[0]) * (1 + (qidx - vud->nqueues))); ++ vud->nqueues = qidx + 1; ++ } ++ if (!vud->qi[qidx]) { ++ vud->qi[qidx] = calloc(sizeof(struct fv_QueueInfo), 1); ++ assert(vud->qi[qidx]); ++ vud->qi[qidx]->virtio_dev = vud; ++ vud->qi[qidx]->qidx = qidx; ++ } else { ++ /* Shouldn't have been started */ ++ assert(vud->qi[qidx]->kick_fd == -1); ++ } ++ ourqi = vud->qi[qidx]; ++ ourqi->kick_fd = dev->vq[qidx].kick_fd; ++ if (pthread_create(&ourqi->thread, NULL, fv_queue_thread, ourqi)) { ++ fuse_log(FUSE_LOG_ERR, "%s: Failed to create thread for queue %d\n", ++ __func__, qidx); ++ assert(0); ++ } ++ } else { ++ /* TODO: Kill the thread */ ++ assert(qidx < vud->nqueues); ++ ourqi = vud->qi[qidx]; ++ ourqi->kick_fd = -1; ++ } ++} ++ + static bool fv_queue_order(VuDev *dev, int qidx) + { + return false; +@@ -92,6 +178,9 @@ static const VuDevIface fv_iface = { + .get_features = fv_get_features, + .set_features = fv_set_features, + ++ /* Don't need process message, we've not got any at vhost-user level */ ++ .queue_set_started = fv_queue_set_started, ++ + .queue_is_processed_in_order = fv_queue_order, + }; + diff --git a/0032-virtiofsd-Poll-kick_fd-for-queue.patch b/0032-virtiofsd-Poll-kick_fd-for-queue.patch new file mode 100644 index 0000000..085ca83 --- /dev/null +++ b/0032-virtiofsd-Poll-kick_fd-for-queue.patch @@ -0,0 +1,81 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:01 +0000 +Subject: [PATCH] virtiofsd: Poll kick_fd for queue +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In the queue thread poll the kick_fd we're passed. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 5dcd1f56141378226d33dc3df68ec57913e0aa04) +--- + tools/virtiofsd/fuse_virtio.c | 40 ++++++++++++++++++++++++++++++++++- + 1 file changed, 39 insertions(+), 1 deletion(-) + +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index 2a94bb3cca..05e7258712 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -100,13 +101,50 @@ static void fv_panic(VuDev *dev, const char *err) + exit(EXIT_FAILURE); + } + ++/* Thread function for individual queues, created when a queue is 'started' */ + static void *fv_queue_thread(void *opaque) + { + struct fv_QueueInfo *qi = opaque; + fuse_log(FUSE_LOG_INFO, "%s: Start for queue %d kick_fd %d\n", __func__, + qi->qidx, qi->kick_fd); + while (1) { +- /* TODO */ ++ struct pollfd pf[1]; ++ pf[0].fd = qi->kick_fd; ++ pf[0].events = POLLIN; ++ pf[0].revents = 0; ++ ++ fuse_log(FUSE_LOG_DEBUG, "%s: Waiting for Queue %d event\n", __func__, ++ qi->qidx); ++ int poll_res = ppoll(pf, 1, NULL, NULL); ++ ++ if (poll_res == -1) { ++ if (errno == EINTR) { ++ fuse_log(FUSE_LOG_INFO, "%s: ppoll interrupted, going around\n", ++ __func__); ++ continue; ++ } ++ fuse_log(FUSE_LOG_ERR, "fv_queue_thread ppoll: %m\n"); ++ break; ++ } ++ assert(poll_res == 1); ++ if (pf[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { ++ fuse_log(FUSE_LOG_ERR, "%s: Unexpected poll revents %x Queue %d\n", ++ __func__, pf[0].revents, qi->qidx); ++ break; ++ } ++ assert(pf[0].revents & POLLIN); ++ fuse_log(FUSE_LOG_DEBUG, "%s: Got queue event on Queue %d\n", __func__, ++ qi->qidx); ++ ++ eventfd_t evalue; ++ if (eventfd_read(qi->kick_fd, &evalue)) { ++ fuse_log(FUSE_LOG_ERR, "Eventfd_read for queue: %m\n"); ++ break; ++ } ++ if (qi->virtio_dev->se->debug) { ++ fprintf(stderr, "%s: Queue %d gave evalue: %zx\n", __func__, ++ qi->qidx, (size_t)evalue); ++ } + } + + return NULL; diff --git a/0033-virtiofsd-Start-reading-commands-from-queue.patch b/0033-virtiofsd-Start-reading-commands-from-queue.patch new file mode 100644 index 0000000..8b652e5 --- /dev/null +++ b/0033-virtiofsd-Start-reading-commands-from-queue.patch @@ -0,0 +1,184 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:02 +0000 +Subject: [PATCH] virtiofsd: Start reading commands from queue +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Pop queue elements off queues, copy the data from them and +pass that to fuse. + + Note: 'out' in a VuVirtqElement is from QEMU + 'in' in libfuse is into the daemon + + So we read from the out iov's to get a fuse_in_header + +When we get a kick we've got to read all the elements until the queue +is empty. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit b509e1228b3e5eb83c14819045988999fc2dbd1b) +--- + tools/virtiofsd/fuse_i.h | 2 + + tools/virtiofsd/fuse_virtio.c | 99 +++++++++++++++++++++++++++++++++-- + 2 files changed, 98 insertions(+), 3 deletions(-) + +diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h +index ec04449069..1126723d18 100644 +--- a/tools/virtiofsd/fuse_i.h ++++ b/tools/virtiofsd/fuse_i.h +@@ -14,6 +14,7 @@ + #include "fuse_lowlevel.h" + + struct fv_VuDev; ++struct fv_QueueInfo; + + struct fuse_req { + struct fuse_session *se; +@@ -75,6 +76,7 @@ struct fuse_chan { + pthread_mutex_t lock; + int ctr; + int fd; ++ struct fv_QueueInfo *qi; + }; + + /** +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index 05e7258712..3841b20129 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -12,6 +12,7 @@ + */ + + #include "qemu/osdep.h" ++#include "qemu/iov.h" + #include "fuse_virtio.h" + #include "fuse_i.h" + #include "standard-headers/linux/fuse.h" +@@ -32,6 +33,7 @@ + + #include "contrib/libvhost-user/libvhost-user.h" + ++struct fv_VuDev; + struct fv_QueueInfo { + pthread_t thread; + struct fv_VuDev *virtio_dev; +@@ -101,10 +103,41 @@ static void fv_panic(VuDev *dev, const char *err) + exit(EXIT_FAILURE); + } + ++/* ++ * Copy from an iovec into a fuse_buf (memory only) ++ * Caller must ensure there is space ++ */ ++static void copy_from_iov(struct fuse_buf *buf, size_t out_num, ++ const struct iovec *out_sg) ++{ ++ void *dest = buf->mem; ++ ++ while (out_num) { ++ size_t onelen = out_sg->iov_len; ++ memcpy(dest, out_sg->iov_base, onelen); ++ dest += onelen; ++ out_sg++; ++ out_num--; ++ } ++} ++ + /* Thread function for individual queues, created when a queue is 'started' */ + static void *fv_queue_thread(void *opaque) + { + struct fv_QueueInfo *qi = opaque; ++ struct VuDev *dev = &qi->virtio_dev->dev; ++ struct VuVirtq *q = vu_get_queue(dev, qi->qidx); ++ struct fuse_session *se = qi->virtio_dev->se; ++ struct fuse_chan ch; ++ struct fuse_buf fbuf; ++ ++ fbuf.mem = NULL; ++ fbuf.flags = 0; ++ ++ fuse_mutex_init(&ch.lock); ++ ch.fd = (int)0xdaff0d111; ++ ch.qi = qi; ++ + fuse_log(FUSE_LOG_INFO, "%s: Start for queue %d kick_fd %d\n", __func__, + qi->qidx, qi->kick_fd); + while (1) { +@@ -141,11 +174,71 @@ static void *fv_queue_thread(void *opaque) + fuse_log(FUSE_LOG_ERR, "Eventfd_read for queue: %m\n"); + break; + } +- if (qi->virtio_dev->se->debug) { +- fprintf(stderr, "%s: Queue %d gave evalue: %zx\n", __func__, +- qi->qidx, (size_t)evalue); ++ /* out is from guest, in is too guest */ ++ unsigned int in_bytes, out_bytes; ++ vu_queue_get_avail_bytes(dev, q, &in_bytes, &out_bytes, ~0, ~0); ++ ++ fuse_log(FUSE_LOG_DEBUG, ++ "%s: Queue %d gave evalue: %zx available: in: %u out: %u\n", ++ __func__, qi->qidx, (size_t)evalue, in_bytes, out_bytes); ++ ++ while (1) { ++ /* ++ * An element contains one request and the space to send our ++ * response They're spread over multiple descriptors in a ++ * scatter/gather set and we can't trust the guest to keep them ++ * still; so copy in/out. ++ */ ++ VuVirtqElement *elem = vu_queue_pop(dev, q, sizeof(VuVirtqElement)); ++ if (!elem) { ++ break; ++ } ++ ++ if (!fbuf.mem) { ++ fbuf.mem = malloc(se->bufsize); ++ assert(fbuf.mem); ++ assert(se->bufsize > sizeof(struct fuse_in_header)); ++ } ++ /* The 'out' part of the elem is from qemu */ ++ unsigned int out_num = elem->out_num; ++ struct iovec *out_sg = elem->out_sg; ++ size_t out_len = iov_size(out_sg, out_num); ++ fuse_log(FUSE_LOG_DEBUG, ++ "%s: elem %d: with %d out desc of length %zd\n", __func__, ++ elem->index, out_num, out_len); ++ ++ /* ++ * The elem should contain a 'fuse_in_header' (in to fuse) ++ * plus the data based on the len in the header. ++ */ ++ if (out_len < sizeof(struct fuse_in_header)) { ++ fuse_log(FUSE_LOG_ERR, "%s: elem %d too short for in_header\n", ++ __func__, elem->index); ++ assert(0); /* TODO */ ++ } ++ if (out_len > se->bufsize) { ++ fuse_log(FUSE_LOG_ERR, "%s: elem %d too large for buffer\n", ++ __func__, elem->index); ++ assert(0); /* TODO */ ++ } ++ copy_from_iov(&fbuf, out_num, out_sg); ++ fbuf.size = out_len; ++ ++ /* TODO! Endianness of header */ ++ ++ /* TODO: Fixup fuse_send_msg */ ++ /* TODO: Add checks for fuse_session_exited */ ++ fuse_session_process_buf_int(se, &fbuf, &ch); ++ ++ /* TODO: vu_queue_push(dev, q, elem, qi->write_count); */ ++ vu_queue_notify(dev, q); ++ ++ free(elem); ++ elem = NULL; + } + } ++ pthread_mutex_destroy(&ch.lock); ++ free(fbuf.mem); + + return NULL; + } diff --git a/0034-virtiofsd-Send-replies-to-messages.patch b/0034-virtiofsd-Send-replies-to-messages.patch new file mode 100644 index 0000000..66b9376 --- /dev/null +++ b/0034-virtiofsd-Send-replies-to-messages.patch @@ -0,0 +1,183 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:03 +0000 +Subject: [PATCH] virtiofsd: Send replies to messages +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Route fuse out messages back through the same queue elements +that had the command that triggered the request. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit df57ba919ec3edef9cc208d35685095e6e92713e) +--- + tools/virtiofsd/fuse_lowlevel.c | 4 ++ + tools/virtiofsd/fuse_virtio.c | 107 ++++++++++++++++++++++++++++++-- + tools/virtiofsd/fuse_virtio.h | 4 ++ + 3 files changed, 111 insertions(+), 4 deletions(-) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index af09fa2b94..380d93bd01 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -171,6 +171,10 @@ static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, + } + } + ++ if (fuse_lowlevel_is_virtio(se)) { ++ return virtio_send_msg(se, ch, iov, count); ++ } ++ + abort(); /* virtio should have taken it before here */ + return 0; + } +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index 3841b20129..05d0e29f12 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -41,6 +41,9 @@ struct fv_QueueInfo { + /* Our queue index, corresponds to array position */ + int qidx; + int kick_fd; ++ ++ /* The element for the command currently being processed */ ++ VuVirtqElement *qe; + }; + + /* +@@ -121,6 +124,105 @@ static void copy_from_iov(struct fuse_buf *buf, size_t out_num, + } + } + ++/* ++ * Copy from one iov to another, the given number of bytes ++ * The caller must have checked sizes. ++ */ ++static void copy_iov(struct iovec *src_iov, int src_count, ++ struct iovec *dst_iov, int dst_count, size_t to_copy) ++{ ++ size_t dst_offset = 0; ++ /* Outer loop copies 'src' elements */ ++ while (to_copy) { ++ assert(src_count); ++ size_t src_len = src_iov[0].iov_len; ++ size_t src_offset = 0; ++ ++ if (src_len > to_copy) { ++ src_len = to_copy; ++ } ++ /* Inner loop copies contents of one 'src' to maybe multiple dst. */ ++ while (src_len) { ++ assert(dst_count); ++ size_t dst_len = dst_iov[0].iov_len - dst_offset; ++ if (dst_len > src_len) { ++ dst_len = src_len; ++ } ++ ++ memcpy(dst_iov[0].iov_base + dst_offset, ++ src_iov[0].iov_base + src_offset, dst_len); ++ src_len -= dst_len; ++ to_copy -= dst_len; ++ src_offset += dst_len; ++ dst_offset += dst_len; ++ ++ assert(dst_offset <= dst_iov[0].iov_len); ++ if (dst_offset == dst_iov[0].iov_len) { ++ dst_offset = 0; ++ dst_iov++; ++ dst_count--; ++ } ++ } ++ src_iov++; ++ src_count--; ++ } ++} ++ ++/* ++ * Called back by ll whenever it wants to send a reply/message back ++ * The 1st element of the iov starts with the fuse_out_header ++ * 'unique'==0 means it's a notify message. ++ */ ++int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, ++ struct iovec *iov, int count) ++{ ++ VuVirtqElement *elem; ++ VuVirtq *q; ++ ++ assert(count >= 1); ++ assert(iov[0].iov_len >= sizeof(struct fuse_out_header)); ++ ++ struct fuse_out_header *out = iov[0].iov_base; ++ /* TODO: Endianness! */ ++ ++ size_t tosend_len = iov_size(iov, count); ++ ++ /* unique == 0 is notification, which we don't support */ ++ assert(out->unique); ++ /* For virtio we always have ch */ ++ assert(ch); ++ elem = ch->qi->qe; ++ q = &ch->qi->virtio_dev->dev.vq[ch->qi->qidx]; ++ ++ /* The 'in' part of the elem is to qemu */ ++ unsigned int in_num = elem->in_num; ++ struct iovec *in_sg = elem->in_sg; ++ size_t in_len = iov_size(in_sg, in_num); ++ fuse_log(FUSE_LOG_DEBUG, "%s: elem %d: with %d in desc of length %zd\n", ++ __func__, elem->index, in_num, in_len); ++ ++ /* ++ * The elem should have room for a 'fuse_out_header' (out from fuse) ++ * plus the data based on the len in the header. ++ */ ++ if (in_len < sizeof(struct fuse_out_header)) { ++ fuse_log(FUSE_LOG_ERR, "%s: elem %d too short for out_header\n", ++ __func__, elem->index); ++ return -E2BIG; ++ } ++ if (in_len < tosend_len) { ++ fuse_log(FUSE_LOG_ERR, "%s: elem %d too small for data len %zd\n", ++ __func__, elem->index, tosend_len); ++ return -E2BIG; ++ } ++ ++ copy_iov(iov, count, in_sg, in_num, tosend_len); ++ vu_queue_push(&se->virtio_dev->dev, q, elem, tosend_len); ++ vu_queue_notify(&se->virtio_dev->dev, q); ++ ++ return 0; ++} ++ + /* Thread function for individual queues, created when a queue is 'started' */ + static void *fv_queue_thread(void *opaque) + { +@@ -226,13 +328,10 @@ static void *fv_queue_thread(void *opaque) + + /* TODO! Endianness of header */ + +- /* TODO: Fixup fuse_send_msg */ + /* TODO: Add checks for fuse_session_exited */ + fuse_session_process_buf_int(se, &fbuf, &ch); + +- /* TODO: vu_queue_push(dev, q, elem, qi->write_count); */ +- vu_queue_notify(dev, q); +- ++ qi->qe = NULL; + free(elem); + elem = NULL; + } +diff --git a/tools/virtiofsd/fuse_virtio.h b/tools/virtiofsd/fuse_virtio.h +index 23026d6e4c..135a14875a 100644 +--- a/tools/virtiofsd/fuse_virtio.h ++++ b/tools/virtiofsd/fuse_virtio.h +@@ -22,4 +22,8 @@ int virtio_session_mount(struct fuse_session *se); + + int virtio_loop(struct fuse_session *se); + ++ ++int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, ++ struct iovec *iov, int count); ++ + #endif diff --git a/0035-virtiofsd-Keep-track-of-replies.patch b/0035-virtiofsd-Keep-track-of-replies.patch new file mode 100644 index 0000000..874a16d --- /dev/null +++ b/0035-virtiofsd-Keep-track-of-replies.patch @@ -0,0 +1,100 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:04 +0000 +Subject: [PATCH] virtiofsd: Keep track of replies +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Keep track of whether we sent a reply to a request; this is a bit +paranoid but it means: + a) We should always recycle an element even if there was an error + in the request + b) Never try and send two replies on one queue element + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 2f65e69a7f22da8d20c747f34f339ebb40a0634f) +--- + tools/virtiofsd/fuse_virtio.c | 23 ++++++++++++++++++++--- + 1 file changed, 20 insertions(+), 3 deletions(-) + +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index 05d0e29f12..f1adeb6345 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -44,6 +44,7 @@ struct fv_QueueInfo { + + /* The element for the command currently being processed */ + VuVirtqElement *qe; ++ bool reply_sent; + }; + + /* +@@ -178,6 +179,7 @@ int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, + { + VuVirtqElement *elem; + VuVirtq *q; ++ int ret = 0; + + assert(count >= 1); + assert(iov[0].iov_len >= sizeof(struct fuse_out_header)); +@@ -191,6 +193,7 @@ int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, + assert(out->unique); + /* For virtio we always have ch */ + assert(ch); ++ assert(!ch->qi->reply_sent); + elem = ch->qi->qe; + q = &ch->qi->virtio_dev->dev.vq[ch->qi->qidx]; + +@@ -208,19 +211,23 @@ int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, + if (in_len < sizeof(struct fuse_out_header)) { + fuse_log(FUSE_LOG_ERR, "%s: elem %d too short for out_header\n", + __func__, elem->index); +- return -E2BIG; ++ ret = -E2BIG; ++ goto err; + } + if (in_len < tosend_len) { + fuse_log(FUSE_LOG_ERR, "%s: elem %d too small for data len %zd\n", + __func__, elem->index, tosend_len); +- return -E2BIG; ++ ret = -E2BIG; ++ goto err; + } + + copy_iov(iov, count, in_sg, in_num, tosend_len); + vu_queue_push(&se->virtio_dev->dev, q, elem, tosend_len); + vu_queue_notify(&se->virtio_dev->dev, q); ++ ch->qi->reply_sent = true; + +- return 0; ++err: ++ return ret; + } + + /* Thread function for individual queues, created when a queue is 'started' */ +@@ -296,6 +303,9 @@ static void *fv_queue_thread(void *opaque) + break; + } + ++ qi->qe = elem; ++ qi->reply_sent = false; ++ + if (!fbuf.mem) { + fbuf.mem = malloc(se->bufsize); + assert(fbuf.mem); +@@ -331,6 +341,13 @@ static void *fv_queue_thread(void *opaque) + /* TODO: Add checks for fuse_session_exited */ + fuse_session_process_buf_int(se, &fbuf, &ch); + ++ if (!qi->reply_sent) { ++ fuse_log(FUSE_LOG_DEBUG, "%s: elem %d no reply sent\n", ++ __func__, elem->index); ++ /* I think we've still got to recycle the element */ ++ vu_queue_push(dev, q, elem, 0); ++ vu_queue_notify(dev, q); ++ } + qi->qe = NULL; + free(elem); + elem = NULL; diff --git a/0036-virtiofsd-Add-Makefile-wiring-for-virtiofsd-contrib.patch b/0036-virtiofsd-Add-Makefile-wiring-for-virtiofsd-contrib.patch new file mode 100644 index 0000000..a52d5dd --- /dev/null +++ b/0036-virtiofsd-Add-Makefile-wiring-for-virtiofsd-contrib.patch @@ -0,0 +1,90 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:05 +0000 +Subject: [PATCH] virtiofsd: Add Makefile wiring for virtiofsd contrib +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Wire up the building of the virtiofsd in tools. + +virtiofsd relies on Linux-specific system calls and seccomp. Anyone +wishing to port it to other host operating systems should do so +carefully and without reducing security. + +Only allow building on Linux hosts. + +Signed-off-by: Dr. David Alan Gilbert +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Liam Merwick +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 81bfc42dcf473bc8d3790622633410da72d8e622) +--- + Makefile | 10 ++++++++++ + Makefile.objs | 1 + + tools/virtiofsd/Makefile.objs | 9 +++++++++ + 3 files changed, 20 insertions(+) + create mode 100644 tools/virtiofsd/Makefile.objs + +diff --git a/Makefile b/Makefile +index b437a346d7..10fd31e705 100644 +--- a/Makefile ++++ b/Makefile +@@ -330,6 +330,10 @@ endif + endif + endif + ++ifdef CONFIG_LINUX ++HELPERS-y += virtiofsd$(EXESUF) ++endif ++ + # Sphinx does not allow building manuals into the same directory as + # the source files, so if we're doing an in-tree QEMU build we must + # build the manuals into a subdirectory (and then install them from +@@ -430,6 +434,7 @@ dummy := $(call unnest-vars,, \ + elf2dmp-obj-y \ + ivshmem-client-obj-y \ + ivshmem-server-obj-y \ ++ virtiofsd-obj-y \ + rdmacm-mux-obj-y \ + libvhost-user-obj-y \ + vhost-user-scsi-obj-y \ +@@ -674,6 +679,11 @@ rdmacm-mux$(EXESUF): LIBS += "-libumad" + rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) + $(call LINK, $^) + ++ifdef CONFIG_LINUX # relies on Linux-specific syscalls ++virtiofsd$(EXESUF): $(virtiofsd-obj-y) libvhost-user.a $(COMMON_LDADDS) ++ $(call LINK, $^) ++endif ++ + vhost-user-gpu$(EXESUF): $(vhost-user-gpu-obj-y) $(libvhost-user-obj-y) libqemuutil.a libqemustub.a + $(call LINK, $^) + +diff --git a/Makefile.objs b/Makefile.objs +index 11ba1a36bd..b5f667a4ba 100644 +--- a/Makefile.objs ++++ b/Makefile.objs +@@ -125,6 +125,7 @@ vhost-user-blk-obj-y = contrib/vhost-user-blk/ + rdmacm-mux-obj-y = contrib/rdmacm-mux/ + vhost-user-input-obj-y = contrib/vhost-user-input/ + vhost-user-gpu-obj-y = contrib/vhost-user-gpu/ ++virtiofsd-obj-y = tools/virtiofsd/ + + ###################################################################### + trace-events-subdirs = +diff --git a/tools/virtiofsd/Makefile.objs b/tools/virtiofsd/Makefile.objs +new file mode 100644 +index 0000000000..45a807500d +--- /dev/null ++++ b/tools/virtiofsd/Makefile.objs +@@ -0,0 +1,9 @@ ++virtiofsd-obj-y = buffer.o \ ++ fuse_opt.o \ ++ fuse_log.o \ ++ fuse_lowlevel.o \ ++ fuse_signals.o \ ++ fuse_virtio.o \ ++ helper.o \ ++ passthrough_ll.o ++ diff --git a/0037-virtiofsd-Fast-path-for-virtio-read.patch b/0037-virtiofsd-Fast-path-for-virtio-read.patch new file mode 100644 index 0000000..200f1da --- /dev/null +++ b/0037-virtiofsd-Fast-path-for-virtio-read.patch @@ -0,0 +1,220 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:06 +0000 +Subject: [PATCH] virtiofsd: Fast path for virtio read + +Readv the data straight into the guests buffer. + +Signed-off-by: Dr. David Alan Gilbert +With fix by: +Signed-off-by: Eryu Guan +Reviewed-by: Masayoshi Mizuma +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit eb49d187ef5134483a34c970bbfece28aaa686a7) +--- + tools/virtiofsd/fuse_lowlevel.c | 5 + + tools/virtiofsd/fuse_virtio.c | 162 ++++++++++++++++++++++++++++++++ + tools/virtiofsd/fuse_virtio.h | 4 + + 3 files changed, 171 insertions(+) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 380d93bd01..4f4684d942 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -475,6 +475,11 @@ static int fuse_send_data_iov_fallback(struct fuse_session *se, + return fuse_send_msg(se, ch, iov, iov_count); + } + ++ if (fuse_lowlevel_is_virtio(se) && buf->count == 1 && ++ buf->buf[0].flags == (FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK)) { ++ return virtio_send_data_iov(se, ch, iov, iov_count, buf, len); ++ } ++ + abort(); /* Will have taken vhost path */ + return 0; + } +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index f1adeb6345..7e2711b504 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -230,6 +230,168 @@ err: + return ret; + } + ++/* ++ * Callback from fuse_send_data_iov_* when it's virtio and the buffer ++ * is a single FD with FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK ++ * We need send the iov and then the buffer. ++ * Return 0 on success ++ */ ++int virtio_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, ++ struct iovec *iov, int count, struct fuse_bufvec *buf, ++ size_t len) ++{ ++ int ret = 0; ++ VuVirtqElement *elem; ++ VuVirtq *q; ++ ++ assert(count >= 1); ++ assert(iov[0].iov_len >= sizeof(struct fuse_out_header)); ++ ++ struct fuse_out_header *out = iov[0].iov_base; ++ /* TODO: Endianness! */ ++ ++ size_t iov_len = iov_size(iov, count); ++ size_t tosend_len = iov_len + len; ++ ++ out->len = tosend_len; ++ ++ fuse_log(FUSE_LOG_DEBUG, "%s: count=%d len=%zd iov_len=%zd\n", __func__, ++ count, len, iov_len); ++ ++ /* unique == 0 is notification which we don't support */ ++ assert(out->unique); ++ ++ /* For virtio we always have ch */ ++ assert(ch); ++ assert(!ch->qi->reply_sent); ++ elem = ch->qi->qe; ++ q = &ch->qi->virtio_dev->dev.vq[ch->qi->qidx]; ++ ++ /* The 'in' part of the elem is to qemu */ ++ unsigned int in_num = elem->in_num; ++ struct iovec *in_sg = elem->in_sg; ++ size_t in_len = iov_size(in_sg, in_num); ++ fuse_log(FUSE_LOG_DEBUG, "%s: elem %d: with %d in desc of length %zd\n", ++ __func__, elem->index, in_num, in_len); ++ ++ /* ++ * The elem should have room for a 'fuse_out_header' (out from fuse) ++ * plus the data based on the len in the header. ++ */ ++ if (in_len < sizeof(struct fuse_out_header)) { ++ fuse_log(FUSE_LOG_ERR, "%s: elem %d too short for out_header\n", ++ __func__, elem->index); ++ ret = E2BIG; ++ goto err; ++ } ++ if (in_len < tosend_len) { ++ fuse_log(FUSE_LOG_ERR, "%s: elem %d too small for data len %zd\n", ++ __func__, elem->index, tosend_len); ++ ret = E2BIG; ++ goto err; ++ } ++ ++ /* TODO: Limit to 'len' */ ++ ++ /* First copy the header data from iov->in_sg */ ++ copy_iov(iov, count, in_sg, in_num, iov_len); ++ ++ /* ++ * Build a copy of the the in_sg iov so we can skip bits in it, ++ * including changing the offsets ++ */ ++ struct iovec *in_sg_cpy = calloc(sizeof(struct iovec), in_num); ++ assert(in_sg_cpy); ++ memcpy(in_sg_cpy, in_sg, sizeof(struct iovec) * in_num); ++ /* These get updated as we skip */ ++ struct iovec *in_sg_ptr = in_sg_cpy; ++ int in_sg_cpy_count = in_num; ++ ++ /* skip over parts of in_sg that contained the header iov */ ++ size_t skip_size = iov_len; ++ ++ size_t in_sg_left = 0; ++ do { ++ while (skip_size != 0 && in_sg_cpy_count) { ++ if (skip_size >= in_sg_ptr[0].iov_len) { ++ skip_size -= in_sg_ptr[0].iov_len; ++ in_sg_ptr++; ++ in_sg_cpy_count--; ++ } else { ++ in_sg_ptr[0].iov_len -= skip_size; ++ in_sg_ptr[0].iov_base += skip_size; ++ break; ++ } ++ } ++ ++ int i; ++ for (i = 0, in_sg_left = 0; i < in_sg_cpy_count; i++) { ++ in_sg_left += in_sg_ptr[i].iov_len; ++ } ++ fuse_log(FUSE_LOG_DEBUG, ++ "%s: after skip skip_size=%zd in_sg_cpy_count=%d " ++ "in_sg_left=%zd\n", ++ __func__, skip_size, in_sg_cpy_count, in_sg_left); ++ ret = preadv(buf->buf[0].fd, in_sg_ptr, in_sg_cpy_count, ++ buf->buf[0].pos); ++ ++ if (ret == -1) { ++ ret = errno; ++ fuse_log(FUSE_LOG_DEBUG, "%s: preadv failed (%m) len=%zd\n", ++ __func__, len); ++ free(in_sg_cpy); ++ goto err; ++ } ++ fuse_log(FUSE_LOG_DEBUG, "%s: preadv ret=%d len=%zd\n", __func__, ++ ret, len); ++ if (ret < len && ret) { ++ fuse_log(FUSE_LOG_DEBUG, "%s: ret < len\n", __func__); ++ /* Skip over this much next time around */ ++ skip_size = ret; ++ buf->buf[0].pos += ret; ++ len -= ret; ++ ++ /* Lets do another read */ ++ continue; ++ } ++ if (!ret) { ++ /* EOF case? */ ++ fuse_log(FUSE_LOG_DEBUG, "%s: !ret in_sg_left=%zd\n", __func__, ++ in_sg_left); ++ break; ++ } ++ if (ret != len) { ++ fuse_log(FUSE_LOG_DEBUG, "%s: ret!=len\n", __func__); ++ ret = EIO; ++ free(in_sg_cpy); ++ goto err; ++ } ++ in_sg_left -= ret; ++ len -= ret; ++ } while (in_sg_left); ++ free(in_sg_cpy); ++ ++ /* Need to fix out->len on EOF */ ++ if (len) { ++ struct fuse_out_header *out_sg = in_sg[0].iov_base; ++ ++ tosend_len -= len; ++ out_sg->len = tosend_len; ++ } ++ ++ ret = 0; ++ ++ vu_queue_push(&se->virtio_dev->dev, q, elem, tosend_len); ++ vu_queue_notify(&se->virtio_dev->dev, q); ++ ++err: ++ if (ret == 0) { ++ ch->qi->reply_sent = true; ++ } ++ ++ return ret; ++} ++ + /* Thread function for individual queues, created when a queue is 'started' */ + static void *fv_queue_thread(void *opaque) + { +diff --git a/tools/virtiofsd/fuse_virtio.h b/tools/virtiofsd/fuse_virtio.h +index 135a14875a..cc676b9193 100644 +--- a/tools/virtiofsd/fuse_virtio.h ++++ b/tools/virtiofsd/fuse_virtio.h +@@ -26,4 +26,8 @@ int virtio_loop(struct fuse_session *se); + int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, + struct iovec *iov, int count); + ++int virtio_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, ++ struct iovec *iov, int count, ++ struct fuse_bufvec *buf, size_t len); ++ + #endif diff --git a/0038-virtiofsd-add-fd-FDNUM-fd-passing-option.patch b/0038-virtiofsd-add-fd-FDNUM-fd-passing-option.patch new file mode 100644 index 0000000..2947b73 --- /dev/null +++ b/0038-virtiofsd-add-fd-FDNUM-fd-passing-option.patch @@ -0,0 +1,154 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:07 +0000 +Subject: [PATCH] virtiofsd: add --fd=FDNUM fd passing option +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Although --socket-path=PATH is useful for manual invocations, management +tools typically create the UNIX domain socket themselves and pass it to +the vhost-user device backend. This way QEMU can be launched +immediately with a valid socket. No waiting for the vhost-user device +backend is required when fd passing is used. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit cee8e35d4386e34bf79c3ca2aab7f7b1bb48cf8d) +--- + tools/virtiofsd/fuse_i.h | 1 + + tools/virtiofsd/fuse_lowlevel.c | 16 ++++++++++++---- + tools/virtiofsd/fuse_virtio.c | 31 +++++++++++++++++++++++++------ + 3 files changed, 38 insertions(+), 10 deletions(-) + +diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h +index 1126723d18..45995f3246 100644 +--- a/tools/virtiofsd/fuse_i.h ++++ b/tools/virtiofsd/fuse_i.h +@@ -68,6 +68,7 @@ struct fuse_session { + size_t bufsize; + int error; + char *vu_socket_path; ++ int vu_listen_fd; + int vu_socketfd; + struct fv_VuDev *virtio_dev; + }; +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 4f4684d942..95f4db8fcf 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -2130,6 +2130,7 @@ static const struct fuse_opt fuse_ll_opts[] = { + LL_OPTION("--debug", debug, 1), + LL_OPTION("allow_root", deny_others, 1), + LL_OPTION("--socket-path=%s", vu_socket_path, 0), ++ LL_OPTION("--fd=%d", vu_listen_fd, 0), + FUSE_OPT_END + }; + +@@ -2147,7 +2148,8 @@ void fuse_lowlevel_help(void) + */ + printf( + " -o allow_root allow access by root\n" +- " --socket-path=PATH path for the vhost-user socket\n"); ++ " --socket-path=PATH path for the vhost-user socket\n" ++ " --fd=FDNUM fd number of vhost-user socket\n"); + } + + void fuse_session_destroy(struct fuse_session *se) +@@ -2191,6 +2193,7 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, + goto out1; + } + se->fd = -1; ++ se->vu_listen_fd = -1; + se->conn.max_write = UINT_MAX; + se->conn.max_readahead = UINT_MAX; + +@@ -2212,8 +2215,13 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, + goto out4; + } + +- if (!se->vu_socket_path) { +- fprintf(stderr, "fuse: missing -o vhost_user_socket option\n"); ++ if (!se->vu_socket_path && se->vu_listen_fd < 0) { ++ fuse_log(FUSE_LOG_ERR, "fuse: missing --socket-path or --fd option\n"); ++ goto out4; ++ } ++ if (se->vu_socket_path && se->vu_listen_fd >= 0) { ++ fuse_log(FUSE_LOG_ERR, ++ "fuse: --socket-path and --fd cannot be given together\n"); + goto out4; + } + +@@ -2253,7 +2261,7 @@ void fuse_session_unmount(struct fuse_session *se) + + int fuse_lowlevel_is_virtio(struct fuse_session *se) + { +- return se->vu_socket_path != NULL; ++ return !!se->virtio_dev; + } + + #ifdef linux +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index 7e2711b504..635f87756a 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -638,18 +638,21 @@ int virtio_loop(struct fuse_session *se) + return 0; + } + +-int virtio_session_mount(struct fuse_session *se) ++static int fv_create_listen_socket(struct fuse_session *se) + { + struct sockaddr_un un; + mode_t old_umask; + ++ /* Nothing to do if fd is already initialized */ ++ if (se->vu_listen_fd >= 0) { ++ return 0; ++ } ++ + if (strlen(se->vu_socket_path) >= sizeof(un.sun_path)) { + fuse_log(FUSE_LOG_ERR, "Socket path too long\n"); + return -1; + } + +- se->fd = -1; +- + /* + * Create the Unix socket to communicate with qemu + * based on QEMU's vhost-user-bridge +@@ -682,15 +685,31 @@ int virtio_session_mount(struct fuse_session *se) + return -1; + } + ++ se->vu_listen_fd = listen_sock; ++ return 0; ++} ++ ++int virtio_session_mount(struct fuse_session *se) ++{ ++ int ret; ++ ++ ret = fv_create_listen_socket(se); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ se->fd = -1; ++ + fuse_log(FUSE_LOG_INFO, "%s: Waiting for vhost-user socket connection...\n", + __func__); +- int data_sock = accept(listen_sock, NULL, NULL); ++ int data_sock = accept(se->vu_listen_fd, NULL, NULL); + if (data_sock == -1) { + fuse_log(FUSE_LOG_ERR, "vhost socket accept: %m\n"); +- close(listen_sock); ++ close(se->vu_listen_fd); + return -1; + } +- close(listen_sock); ++ close(se->vu_listen_fd); ++ se->vu_listen_fd = -1; + fuse_log(FUSE_LOG_INFO, "%s: Received vhost-user socket connection\n", + __func__); + diff --git a/0039-virtiofsd-make-f-foreground-the-default.patch b/0039-virtiofsd-make-f-foreground-the-default.patch new file mode 100644 index 0000000..2712afc --- /dev/null +++ b/0039-virtiofsd-make-f-foreground-the-default.patch @@ -0,0 +1,60 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:08 +0000 +Subject: [PATCH] virtiofsd: make -f (foreground) the default +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +According to vhost-user.rst "Backend program conventions", backend +programs should run in the foregound by default. Follow the +conventions so libvirt and other management tools can control virtiofsd +in a standard way. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 0bbd31753714ac2899efda0f0de31e353e965789) +--- + tools/virtiofsd/helper.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index 676032e71f..a3645fc807 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -29,6 +29,11 @@ + { \ + t, offsetof(struct fuse_cmdline_opts, p), 1 \ + } ++#define FUSE_HELPER_OPT_VALUE(t, p, v) \ ++ { \ ++ t, offsetof(struct fuse_cmdline_opts, p), v \ ++ } ++ + + static const struct fuse_opt fuse_helper_opts[] = { + FUSE_HELPER_OPT("-h", show_help), +@@ -42,6 +47,7 @@ static const struct fuse_opt fuse_helper_opts[] = { + FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), + FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), + FUSE_HELPER_OPT("-f", foreground), ++ FUSE_HELPER_OPT_VALUE("--daemonize", foreground, 0), + FUSE_HELPER_OPT("fsname=", nodefault_subtype), + FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), + FUSE_HELPER_OPT("subtype=", nodefault_subtype), +@@ -131,6 +137,7 @@ void fuse_cmdline_help(void) + " -V --version print version\n" + " -d -o debug enable debug output (implies -f)\n" + " -f foreground operation\n" ++ " --daemonize run in background\n" + " -o max_idle_threads the maximum number of idle worker " + "threads\n" + " allowed (default: 10)\n"); +@@ -158,6 +165,7 @@ int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts) + memset(opts, 0, sizeof(struct fuse_cmdline_opts)); + + opts->max_idle_threads = 10; ++ opts->foreground = 1; + + if (fuse_opt_parse(args, opts, fuse_helper_opts, fuse_helper_opt_proc) == + -1) { diff --git a/0040-virtiofsd-add-vhost-user.json-file.patch b/0040-virtiofsd-add-vhost-user.json-file.patch new file mode 100644 index 0000000..0af1555 --- /dev/null +++ b/0040-virtiofsd-add-vhost-user.json-file.patch @@ -0,0 +1,57 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:09 +0000 +Subject: [PATCH] virtiofsd: add vhost-user.json file +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Install a vhost-user.json file describing virtiofsd. This allows +libvirt and other management tools to enumerate vhost-user backend +programs. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 315616ed50ba15a5d7236ade8a402a93898202de) +--- + .gitignore | 1 + + Makefile | 1 + + tools/virtiofsd/50-qemu-virtiofsd.json.in | 5 +++++ + 3 files changed, 7 insertions(+) + create mode 100644 tools/virtiofsd/50-qemu-virtiofsd.json.in + +diff --git a/.gitignore b/.gitignore +index 7de868d1ea..c56ec1d122 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -6,6 +6,7 @@ + /config-target.* + /config.status + /config-temp ++/tools/virtiofsd/50-qemu-virtiofsd.json + /elf2dmp + /trace-events-all + /trace/generated-events.h +diff --git a/Makefile b/Makefile +index 10fd31e705..aebb57aed8 100644 +--- a/Makefile ++++ b/Makefile +@@ -332,6 +332,7 @@ endif + + ifdef CONFIG_LINUX + HELPERS-y += virtiofsd$(EXESUF) ++vhost-user-json-y += tools/virtiofsd/50-qemu-virtiofsd.json + endif + + # Sphinx does not allow building manuals into the same directory as +diff --git a/tools/virtiofsd/50-qemu-virtiofsd.json.in b/tools/virtiofsd/50-qemu-virtiofsd.json.in +new file mode 100644 +index 0000000000..9bcd86f8dc +--- /dev/null ++++ b/tools/virtiofsd/50-qemu-virtiofsd.json.in +@@ -0,0 +1,5 @@ ++{ ++ "description": "QEMU virtiofsd vhost-user-fs", ++ "type": "fs", ++ "binary": "@libexecdir@/virtiofsd" ++} diff --git a/0041-virtiofsd-add-print-capabilities-option.patch b/0041-virtiofsd-add-print-capabilities-option.patch new file mode 100644 index 0000000..f7bd267 --- /dev/null +++ b/0041-virtiofsd-add-print-capabilities-option.patch @@ -0,0 +1,105 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:10 +0000 +Subject: [PATCH] virtiofsd: add --print-capabilities option +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add the --print-capabilities option as per vhost-user.rst "Backend +programs conventions". Currently there are no advertised features. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 45018fbb0a73ce66fd3dd87ecd2872b45658add4) +--- + docs/interop/vhost-user.json | 4 +++- + tools/virtiofsd/fuse_lowlevel.h | 1 + + tools/virtiofsd/helper.c | 2 ++ + tools/virtiofsd/passthrough_ll.c | 12 ++++++++++++ + 4 files changed, 18 insertions(+), 1 deletion(-) + +diff --git a/docs/interop/vhost-user.json b/docs/interop/vhost-user.json +index da6aaf51c8..d4ea1f7ac5 100644 +--- a/docs/interop/vhost-user.json ++++ b/docs/interop/vhost-user.json +@@ -31,6 +31,7 @@ + # @rproc-serial: virtio remoteproc serial link + # @scsi: virtio scsi + # @vsock: virtio vsock transport ++# @fs: virtio fs (since 4.2) + # + # Since: 4.0 + ## +@@ -50,7 +51,8 @@ + 'rpmsg', + 'rproc-serial', + 'scsi', +- 'vsock' ++ 'vsock', ++ 'fs' + ] + } + +diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h +index f6b34700af..0d61df8110 100644 +--- a/tools/virtiofsd/fuse_lowlevel.h ++++ b/tools/virtiofsd/fuse_lowlevel.h +@@ -1794,6 +1794,7 @@ struct fuse_cmdline_opts { + int nodefault_subtype; + int show_version; + int show_help; ++ int print_capabilities; + unsigned int max_idle_threads; + }; + +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index a3645fc807..b8ec5ac8dc 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -40,6 +40,7 @@ static const struct fuse_opt fuse_helper_opts[] = { + FUSE_HELPER_OPT("--help", show_help), + FUSE_HELPER_OPT("-V", show_version), + FUSE_HELPER_OPT("--version", show_version), ++ FUSE_HELPER_OPT("--print-capabilities", print_capabilities), + FUSE_HELPER_OPT("-d", debug), + FUSE_HELPER_OPT("debug", debug), + FUSE_HELPER_OPT("-d", foreground), +@@ -135,6 +136,7 @@ void fuse_cmdline_help(void) + { + printf(" -h --help print help\n" + " -V --version print version\n" ++ " --print-capabilities print vhost-user.json\n" + " -d -o debug enable debug output (implies -f)\n" + " -f foreground operation\n" + " --daemonize run in background\n" +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 037c5d7b26..cd27c09f59 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1298,6 +1298,14 @@ static struct fuse_lowlevel_ops lo_oper = { + .lseek = lo_lseek, + }; + ++/* Print vhost-user.json backend program capabilities */ ++static void print_capabilities(void) ++{ ++ printf("{\n"); ++ printf(" \"type\": \"fs\"\n"); ++ printf("}\n"); ++} ++ + int main(int argc, char *argv[]) + { + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); +@@ -1328,6 +1336,10 @@ int main(int argc, char *argv[]) + fuse_lowlevel_version(); + ret = 0; + goto err_out1; ++ } else if (opts.print_capabilities) { ++ print_capabilities(); ++ ret = 0; ++ goto err_out1; + } + + if (fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1) { diff --git a/0042-virtiofs-Add-maintainers-entry.patch b/0042-virtiofs-Add-maintainers-entry.patch new file mode 100644 index 0000000..224e64b --- /dev/null +++ b/0042-virtiofs-Add-maintainers-entry.patch @@ -0,0 +1,36 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:11 +0000 +Subject: [PATCH] virtiofs: Add maintainers entry +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Reviewed-by: Philippe Mathieu-Daudé +Tested-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit bad7d2c3ad1af9344df035aedaf8e0967a543070) +--- + MAINTAINERS | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/MAINTAINERS b/MAINTAINERS +index 5e5e3e52d6..d1b3e262d2 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -1575,6 +1575,14 @@ T: git https://github.com/cohuck/qemu.git s390-next + T: git https://github.com/borntraeger/qemu.git s390-next + L: qemu-s390x@nongnu.org + ++virtiofs ++M: Dr. David Alan Gilbert ++M: Stefan Hajnoczi ++S: Supported ++F: tools/virtiofsd/* ++F: hw/virtio/vhost-user-fs* ++F: include/hw/virtio/vhost-user-fs.h ++ + virtio-input + M: Gerd Hoffmann + S: Maintained diff --git a/0043-virtiofsd-passthrough_ll-create-new-files-in-caller-.patch b/0043-virtiofsd-passthrough_ll-create-new-files-in-caller-.patch new file mode 100644 index 0000000..9412335 --- /dev/null +++ b/0043-virtiofsd-passthrough_ll-create-new-files-in-caller-.patch @@ -0,0 +1,179 @@ +From: Vivek Goyal +Date: Mon, 27 Jan 2020 19:01:12 +0000 +Subject: [PATCH] virtiofsd: passthrough_ll: create new files in caller's + context + +We need to create files in the caller's context. Otherwise after +creating a file, the caller might not be able to do file operations on +that file. + +Changed effective uid/gid to caller's uid/gid, create file and then +switch back to uid/gid 0. + +Use syscall(setresuid, ...) otherwise glibc does some magic to change EUID +in all threads, which is not what we want. + +Signed-off-by: Vivek Goyal +Signed-off-by: Miklos Szeredi +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 929cfb7a9a1b101cdfc9ac19807ecab4c81a13e4) +--- + tools/virtiofsd/passthrough_ll.c | 96 ++++++++++++++++++++++++++++++-- + 1 file changed, 91 insertions(+), 5 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index cd27c09f59..5e061797d4 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -50,6 +50,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -83,6 +84,11 @@ struct lo_inode { + uint64_t refcount; /* protected by lo->mutex */ + }; + ++struct lo_cred { ++ uid_t euid; ++ gid_t egid; ++}; ++ + enum { + CACHE_NEVER, + CACHE_NORMAL, +@@ -383,6 +389,69 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) + } + } + ++/* ++ * On some archs, setres*id is limited to 2^16 but they ++ * provide setres*id32 variants that allow 2^32. ++ * Others just let setres*id do 2^32 anyway. ++ */ ++#ifdef SYS_setresgid32 ++#define OURSYS_setresgid SYS_setresgid32 ++#else ++#define OURSYS_setresgid SYS_setresgid ++#endif ++ ++#ifdef SYS_setresuid32 ++#define OURSYS_setresuid SYS_setresuid32 ++#else ++#define OURSYS_setresuid SYS_setresuid ++#endif ++ ++/* ++ * Change to uid/gid of caller so that file is created with ++ * ownership of caller. ++ * TODO: What about selinux context? ++ */ ++static int lo_change_cred(fuse_req_t req, struct lo_cred *old) ++{ ++ int res; ++ ++ old->euid = geteuid(); ++ old->egid = getegid(); ++ ++ res = syscall(OURSYS_setresgid, -1, fuse_req_ctx(req)->gid, -1); ++ if (res == -1) { ++ return errno; ++ } ++ ++ res = syscall(OURSYS_setresuid, -1, fuse_req_ctx(req)->uid, -1); ++ if (res == -1) { ++ int errno_save = errno; ++ ++ syscall(OURSYS_setresgid, -1, old->egid, -1); ++ return errno_save; ++ } ++ ++ return 0; ++} ++ ++/* Regain Privileges */ ++static void lo_restore_cred(struct lo_cred *old) ++{ ++ int res; ++ ++ res = syscall(OURSYS_setresuid, -1, old->euid, -1); ++ if (res == -1) { ++ fuse_log(FUSE_LOG_ERR, "seteuid(%u): %m\n", old->euid); ++ exit(1); ++ } ++ ++ res = syscall(OURSYS_setresgid, -1, old->egid, -1); ++ if (res == -1) { ++ fuse_log(FUSE_LOG_ERR, "setegid(%u): %m\n", old->egid); ++ exit(1); ++ } ++} ++ + static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, + const char *name, mode_t mode, dev_t rdev, + const char *link) +@@ -391,12 +460,21 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, + int saverr; + struct lo_inode *dir = lo_inode(req, parent); + struct fuse_entry_param e; ++ struct lo_cred old = {}; + + saverr = ENOMEM; + ++ saverr = lo_change_cred(req, &old); ++ if (saverr) { ++ goto out; ++ } ++ + res = mknod_wrapper(dir->fd, name, link, mode, rdev); + + saverr = errno; ++ ++ lo_restore_cred(&old); ++ + if (res == -1) { + goto out; + } +@@ -794,26 +872,34 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, + struct lo_data *lo = lo_data(req); + struct fuse_entry_param e; + int err; ++ struct lo_cred old = {}; + + if (lo_debug(req)) { + fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", + parent, name); + } + ++ err = lo_change_cred(req, &old); ++ if (err) { ++ goto out; ++ } ++ + fd = openat(lo_fd(req, parent), name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, + mode); +- if (fd == -1) { +- return (void)fuse_reply_err(req, errno); +- } ++ err = fd == -1 ? errno : 0; ++ lo_restore_cred(&old); + +- fi->fh = fd; ++ if (!err) { ++ fi->fh = fd; ++ err = lo_do_lookup(req, parent, name, &e); ++ } + if (lo->cache == CACHE_NEVER) { + fi->direct_io = 1; + } else if (lo->cache == CACHE_ALWAYS) { + fi->keep_cache = 1; + } + +- err = lo_do_lookup(req, parent, name, &e); ++out: + if (err) { + fuse_reply_err(req, err); + } else { diff --git a/0044-virtiofsd-passthrough_ll-add-lo_map-for-ino-fh-indir.patch b/0044-virtiofsd-passthrough_ll-add-lo_map-for-ino-fh-indir.patch new file mode 100644 index 0000000..43ee290 --- /dev/null +++ b/0044-virtiofsd-passthrough_ll-add-lo_map-for-ino-fh-indir.patch @@ -0,0 +1,162 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:13 +0000 +Subject: [PATCH] virtiofsd: passthrough_ll: add lo_map for ino/fh indirection + +A layer of indirection is needed because passthrough_ll cannot expose +pointers or file descriptor numbers to untrusted clients. Malicious +clients could send invalid pointers or file descriptors in order to +crash or exploit the file system daemon. + +lo_map provides an integer key->value mapping. This will be used for +ino and fh fields in the patches that follow. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Masayoshi Mizuma +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 25c135727b08dca90f00094e522a69170b13dfac) +--- + tools/virtiofsd/passthrough_ll.c | 124 +++++++++++++++++++++++++++++++ + 1 file changed, 124 insertions(+) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 5e061797d4..e83a976587 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -74,6 +74,21 @@ struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { + }; + #endif + ++struct lo_map_elem { ++ union { ++ /* Element values will go here... */ ++ ssize_t freelist; ++ }; ++ bool in_use; ++}; ++ ++/* Maps FUSE fh or ino values to internal objects */ ++struct lo_map { ++ struct lo_map_elem *elems; ++ size_t nelems; ++ ssize_t freelist; ++}; ++ + struct lo_inode { + struct lo_inode *next; /* protected by lo->mutex */ + struct lo_inode *prev; /* protected by lo->mutex */ +@@ -130,6 +145,115 @@ static struct lo_data *lo_data(fuse_req_t req) + return (struct lo_data *)fuse_req_userdata(req); + } + ++__attribute__((unused)) static void lo_map_init(struct lo_map *map) ++{ ++ map->elems = NULL; ++ map->nelems = 0; ++ map->freelist = -1; ++} ++ ++__attribute__((unused)) static void lo_map_destroy(struct lo_map *map) ++{ ++ free(map->elems); ++} ++ ++static int lo_map_grow(struct lo_map *map, size_t new_nelems) ++{ ++ struct lo_map_elem *new_elems; ++ size_t i; ++ ++ if (new_nelems <= map->nelems) { ++ return 1; ++ } ++ ++ new_elems = realloc(map->elems, sizeof(map->elems[0]) * new_nelems); ++ if (!new_elems) { ++ return 0; ++ } ++ ++ for (i = map->nelems; i < new_nelems; i++) { ++ new_elems[i].freelist = i + 1; ++ new_elems[i].in_use = false; ++ } ++ new_elems[new_nelems - 1].freelist = -1; ++ ++ map->elems = new_elems; ++ map->freelist = map->nelems; ++ map->nelems = new_nelems; ++ return 1; ++} ++ ++__attribute__((unused)) static struct lo_map_elem * ++lo_map_alloc_elem(struct lo_map *map) ++{ ++ struct lo_map_elem *elem; ++ ++ if (map->freelist == -1 && !lo_map_grow(map, map->nelems + 256)) { ++ return NULL; ++ } ++ ++ elem = &map->elems[map->freelist]; ++ map->freelist = elem->freelist; ++ ++ elem->in_use = true; ++ ++ return elem; ++} ++ ++__attribute__((unused)) static struct lo_map_elem * ++lo_map_reserve(struct lo_map *map, size_t key) ++{ ++ ssize_t *prev; ++ ++ if (!lo_map_grow(map, key + 1)) { ++ return NULL; ++ } ++ ++ for (prev = &map->freelist; *prev != -1; ++ prev = &map->elems[*prev].freelist) { ++ if (*prev == key) { ++ struct lo_map_elem *elem = &map->elems[key]; ++ ++ *prev = elem->freelist; ++ elem->in_use = true; ++ return elem; ++ } ++ } ++ return NULL; ++} ++ ++__attribute__((unused)) static struct lo_map_elem * ++lo_map_get(struct lo_map *map, size_t key) ++{ ++ if (key >= map->nelems) { ++ return NULL; ++ } ++ if (!map->elems[key].in_use) { ++ return NULL; ++ } ++ return &map->elems[key]; ++} ++ ++__attribute__((unused)) static void lo_map_remove(struct lo_map *map, ++ size_t key) ++{ ++ struct lo_map_elem *elem; ++ ++ if (key >= map->nelems) { ++ return; ++ } ++ ++ elem = &map->elems[key]; ++ if (!elem->in_use) { ++ return; ++ } ++ ++ elem->in_use = false; ++ ++ elem->freelist = map->freelist; ++ map->freelist = key; ++} ++ + static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) + { + if (ino == FUSE_ROOT_ID) { diff --git a/0045-virtiofsd-passthrough_ll-add-ino_map-to-hide-lo_inod.patch b/0045-virtiofsd-passthrough_ll-add-ino_map-to-hide-lo_inod.patch new file mode 100644 index 0000000..9fb606a --- /dev/null +++ b/0045-virtiofsd-passthrough_ll-add-ino_map-to-hide-lo_inod.patch @@ -0,0 +1,376 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:14 +0000 +Subject: [PATCH] virtiofsd: passthrough_ll: add ino_map to hide lo_inode + pointers + +Do not expose lo_inode pointers to clients. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Masayoshi Mizuma +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 92fb57b83cdbfc4bf53c0c46a3d0bcbc36e64126) +--- + tools/virtiofsd/passthrough_ll.c | 144 ++++++++++++++++++++++++------- + 1 file changed, 114 insertions(+), 30 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index e83a976587..a3ebf74eab 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -57,8 +57,8 @@ + #include "passthrough_helpers.h" + + /* +- * We are re-using pointers to our `struct lo_inode` and `struct +- * lo_dirp` elements as inodes. This means that we must be able to ++ * We are re-using pointers to our `struct lo_inode` ++ * elements as inodes. This means that we must be able to + * store uintptr_t values in a fuse_ino_t variable. The following + * incantation checks this condition at compile time. + */ +@@ -76,7 +76,7 @@ struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { + + struct lo_map_elem { + union { +- /* Element values will go here... */ ++ struct lo_inode *inode; + ssize_t freelist; + }; + bool in_use; +@@ -97,6 +97,7 @@ struct lo_inode { + ino_t ino; + dev_t dev; + uint64_t refcount; /* protected by lo->mutex */ ++ fuse_ino_t fuse_ino; + }; + + struct lo_cred { +@@ -121,6 +122,7 @@ struct lo_data { + int cache; + int timeout_set; + struct lo_inode root; /* protected by lo->mutex */ ++ struct lo_map ino_map; /* protected by lo->mutex */ + }; + + static const struct fuse_opt lo_opts[] = { +@@ -145,14 +147,14 @@ static struct lo_data *lo_data(fuse_req_t req) + return (struct lo_data *)fuse_req_userdata(req); + } + +-__attribute__((unused)) static void lo_map_init(struct lo_map *map) ++static void lo_map_init(struct lo_map *map) + { + map->elems = NULL; + map->nelems = 0; + map->freelist = -1; + } + +-__attribute__((unused)) static void lo_map_destroy(struct lo_map *map) ++static void lo_map_destroy(struct lo_map *map) + { + free(map->elems); + } +@@ -183,8 +185,7 @@ static int lo_map_grow(struct lo_map *map, size_t new_nelems) + return 1; + } + +-__attribute__((unused)) static struct lo_map_elem * +-lo_map_alloc_elem(struct lo_map *map) ++static struct lo_map_elem *lo_map_alloc_elem(struct lo_map *map) + { + struct lo_map_elem *elem; + +@@ -200,8 +201,7 @@ lo_map_alloc_elem(struct lo_map *map) + return elem; + } + +-__attribute__((unused)) static struct lo_map_elem * +-lo_map_reserve(struct lo_map *map, size_t key) ++static struct lo_map_elem *lo_map_reserve(struct lo_map *map, size_t key) + { + ssize_t *prev; + +@@ -222,8 +222,7 @@ lo_map_reserve(struct lo_map *map, size_t key) + return NULL; + } + +-__attribute__((unused)) static struct lo_map_elem * +-lo_map_get(struct lo_map *map, size_t key) ++static struct lo_map_elem *lo_map_get(struct lo_map *map, size_t key) + { + if (key >= map->nelems) { + return NULL; +@@ -234,8 +233,7 @@ lo_map_get(struct lo_map *map, size_t key) + return &map->elems[key]; + } + +-__attribute__((unused)) static void lo_map_remove(struct lo_map *map, +- size_t key) ++static void lo_map_remove(struct lo_map *map, size_t key) + { + struct lo_map_elem *elem; + +@@ -254,18 +252,40 @@ __attribute__((unused)) static void lo_map_remove(struct lo_map *map, + map->freelist = key; + } + ++/* Assumes lo->mutex is held */ ++static ssize_t lo_add_inode_mapping(fuse_req_t req, struct lo_inode *inode) ++{ ++ struct lo_map_elem *elem; ++ ++ elem = lo_map_alloc_elem(&lo_data(req)->ino_map); ++ if (!elem) { ++ return -1; ++ } ++ ++ elem->inode = inode; ++ return elem - lo_data(req)->ino_map.elems; ++} ++ + static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) + { +- if (ino == FUSE_ROOT_ID) { +- return &lo_data(req)->root; +- } else { +- return (struct lo_inode *)(uintptr_t)ino; ++ struct lo_data *lo = lo_data(req); ++ struct lo_map_elem *elem; ++ ++ pthread_mutex_lock(&lo->mutex); ++ elem = lo_map_get(&lo->ino_map, ino); ++ pthread_mutex_unlock(&lo->mutex); ++ ++ if (!elem) { ++ return NULL; + } ++ ++ return elem->inode; + } + + static int lo_fd(fuse_req_t req, fuse_ino_t ino) + { +- return lo_inode(req, ino)->fd; ++ struct lo_inode *inode = lo_inode(req, ino); ++ return inode ? inode->fd : -1; + } + + static bool lo_debug(fuse_req_t req) +@@ -337,10 +357,18 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + { + int saverr; + char procname[64]; +- struct lo_inode *inode = lo_inode(req, ino); +- int ifd = inode->fd; ++ struct lo_inode *inode; ++ int ifd; + int res; + ++ inode = lo_inode(req, ino); ++ if (!inode) { ++ fuse_reply_err(req, EBADF); ++ return; ++ } ++ ++ ifd = inode->fd; ++ + if (valid & FUSE_SET_ATTR_MODE) { + if (fi) { + res = fchmod(fi->fh, attr->st_mode); +@@ -470,6 +498,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + inode->dev = e->attr.st_dev; + + pthread_mutex_lock(&lo->mutex); ++ inode->fuse_ino = lo_add_inode_mapping(req, inode); + prev = &lo->root; + next = prev->next; + next->prev = inode; +@@ -478,7 +507,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + prev->next = inode; + pthread_mutex_unlock(&lo->mutex); + } +- e->ino = (uintptr_t)inode; ++ e->ino = inode->fuse_ino; + + if (lo_debug(req)) { + fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", +@@ -582,10 +611,16 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, + { + int res; + int saverr; +- struct lo_inode *dir = lo_inode(req, parent); ++ struct lo_inode *dir; + struct fuse_entry_param e; + struct lo_cred old = {}; + ++ dir = lo_inode(req, parent); ++ if (!dir) { ++ fuse_reply_err(req, EBADF); ++ return; ++ } ++ + saverr = ENOMEM; + + saverr = lo_change_cred(req, &old); +@@ -663,10 +698,16 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, + { + int res; + struct lo_data *lo = lo_data(req); +- struct lo_inode *inode = lo_inode(req, ino); ++ struct lo_inode *inode; + struct fuse_entry_param e; + int saverr; + ++ inode = lo_inode(req, ino); ++ if (!inode) { ++ fuse_reply_err(req, EBADF); ++ return; ++ } ++ + memset(&e, 0, sizeof(struct fuse_entry_param)); + e.attr_timeout = lo->timeout; + e.entry_timeout = lo->timeout; +@@ -684,7 +725,7 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, + pthread_mutex_lock(&lo->mutex); + inode->refcount++; + pthread_mutex_unlock(&lo->mutex); +- e.ino = (uintptr_t)inode; ++ e.ino = inode->fuse_ino; + + if (lo_debug(req)) { + fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", +@@ -750,10 +791,10 @@ static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) + next->prev = prev; + prev->next = next; + ++ lo_map_remove(&lo->ino_map, inode->fuse_ino); + pthread_mutex_unlock(&lo->mutex); + close(inode->fd); + free(inode); +- + } else { + pthread_mutex_unlock(&lo->mutex); + } +@@ -762,7 +803,12 @@ static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) + static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) + { + struct lo_data *lo = lo_data(req); +- struct lo_inode *inode = lo_inode(req, ino); ++ struct lo_inode *inode; ++ ++ inode = lo_inode(req, ino); ++ if (!inode) { ++ return; ++ } + + if (lo_debug(req)) { + fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", +@@ -1244,10 +1290,16 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + { + char *value = NULL; + char procname[64]; +- struct lo_inode *inode = lo_inode(req, ino); ++ struct lo_inode *inode; + ssize_t ret; + int saverr; + ++ inode = lo_inode(req, ino); ++ if (!inode) { ++ fuse_reply_err(req, EBADF); ++ return; ++ } ++ + saverr = ENOSYS; + if (!lo_data(req)->xattr) { + goto out; +@@ -1306,10 +1358,16 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) + { + char *value = NULL; + char procname[64]; +- struct lo_inode *inode = lo_inode(req, ino); ++ struct lo_inode *inode; + ssize_t ret; + int saverr; + ++ inode = lo_inode(req, ino); ++ if (!inode) { ++ fuse_reply_err(req, EBADF); ++ return; ++ } ++ + saverr = ENOSYS; + if (!lo_data(req)->xattr) { + goto out; +@@ -1367,10 +1425,16 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags) + { + char procname[64]; +- struct lo_inode *inode = lo_inode(req, ino); ++ struct lo_inode *inode; + ssize_t ret; + int saverr; + ++ inode = lo_inode(req, ino); ++ if (!inode) { ++ fuse_reply_err(req, EBADF); ++ return; ++ } ++ + saverr = ENOSYS; + if (!lo_data(req)->xattr) { + goto out; +@@ -1400,10 +1464,16 @@ out: + static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) + { + char procname[64]; +- struct lo_inode *inode = lo_inode(req, ino); ++ struct lo_inode *inode; + ssize_t ret; + int saverr; + ++ inode = lo_inode(req, ino); ++ if (!inode) { ++ fuse_reply_err(req, EBADF); ++ return; ++ } ++ + saverr = ENOSYS; + if (!lo_data(req)->xattr) { + goto out; +@@ -1522,6 +1592,7 @@ int main(int argc, char *argv[]) + struct fuse_session *se; + struct fuse_cmdline_opts opts; + struct lo_data lo = { .debug = 0, .writeback = 0 }; ++ struct lo_map_elem *root_elem; + int ret = -1; + + /* Don't mask creation mode, kernel already did that */ +@@ -1530,8 +1601,19 @@ int main(int argc, char *argv[]) + pthread_mutex_init(&lo.mutex, NULL); + lo.root.next = lo.root.prev = &lo.root; + lo.root.fd = -1; ++ lo.root.fuse_ino = FUSE_ROOT_ID; + lo.cache = CACHE_NORMAL; + ++ /* ++ * Set up the ino map like this: ++ * [0] Reserved (will not be used) ++ * [1] Root inode ++ */ ++ lo_map_init(&lo.ino_map); ++ lo_map_reserve(&lo.ino_map, 0)->in_use = false; ++ root_elem = lo_map_reserve(&lo.ino_map, lo.root.fuse_ino); ++ root_elem->inode = &lo.root; ++ + if (fuse_parse_cmdline(&args, &opts) != 0) { + return 1; + } +@@ -1628,6 +1710,8 @@ err_out2: + err_out1: + fuse_opt_free_args(&args); + ++ lo_map_destroy(&lo.ino_map); ++ + if (lo.root.fd >= 0) { + close(lo.root.fd); + } diff --git a/0046-virtiofsd-passthrough_ll-add-dirp_map-to-hide-lo_dir.patch b/0046-virtiofsd-passthrough_ll-add-dirp_map-to-hide-lo_dir.patch new file mode 100644 index 0000000..1e4ed85 --- /dev/null +++ b/0046-virtiofsd-passthrough_ll-add-dirp_map-to-hide-lo_dir.patch @@ -0,0 +1,222 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:15 +0000 +Subject: [PATCH] virtiofsd: passthrough_ll: add dirp_map to hide lo_dirp + pointers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Do not expose lo_dirp pointers to clients. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit b39bce121bfad8757eec0ee41f14607b883935d3) +--- + tools/virtiofsd/passthrough_ll.c | 103 +++++++++++++++++++++++-------- + 1 file changed, 76 insertions(+), 27 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index a3ebf74eab..5f5a72fdbb 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -56,27 +56,10 @@ + + #include "passthrough_helpers.h" + +-/* +- * We are re-using pointers to our `struct lo_inode` +- * elements as inodes. This means that we must be able to +- * store uintptr_t values in a fuse_ino_t variable. The following +- * incantation checks this condition at compile time. +- */ +-#if defined(__GNUC__) && \ +- (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && \ +- !defined __cplusplus +-_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t), +- "fuse_ino_t too small to hold uintptr_t values!"); +-#else +-struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { +- unsigned _uintptr_to_must_hold_fuse_ino_t +- : ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); +-}; +-#endif +- + struct lo_map_elem { + union { + struct lo_inode *inode; ++ struct lo_dirp *dirp; + ssize_t freelist; + }; + bool in_use; +@@ -123,6 +106,7 @@ struct lo_data { + int timeout_set; + struct lo_inode root; /* protected by lo->mutex */ + struct lo_map ino_map; /* protected by lo->mutex */ ++ struct lo_map dirp_map; /* protected by lo->mutex */ + }; + + static const struct fuse_opt lo_opts[] = { +@@ -252,6 +236,20 @@ static void lo_map_remove(struct lo_map *map, size_t key) + map->freelist = key; + } + ++/* Assumes lo->mutex is held */ ++static ssize_t lo_add_dirp_mapping(fuse_req_t req, struct lo_dirp *dirp) ++{ ++ struct lo_map_elem *elem; ++ ++ elem = lo_map_alloc_elem(&lo_data(req)->dirp_map); ++ if (!elem) { ++ return -1; ++ } ++ ++ elem->dirp = dirp; ++ return elem - lo_data(req)->dirp_map.elems; ++} ++ + /* Assumes lo->mutex is held */ + static ssize_t lo_add_inode_mapping(fuse_req_t req, struct lo_inode *inode) + { +@@ -861,9 +859,19 @@ struct lo_dirp { + off_t offset; + }; + +-static struct lo_dirp *lo_dirp(struct fuse_file_info *fi) ++static struct lo_dirp *lo_dirp(fuse_req_t req, struct fuse_file_info *fi) + { +- return (struct lo_dirp *)(uintptr_t)fi->fh; ++ struct lo_data *lo = lo_data(req); ++ struct lo_map_elem *elem; ++ ++ pthread_mutex_lock(&lo->mutex); ++ elem = lo_map_get(&lo->dirp_map, fi->fh); ++ pthread_mutex_unlock(&lo->mutex); ++ if (!elem) { ++ return NULL; ++ } ++ ++ return elem->dirp; + } + + static void lo_opendir(fuse_req_t req, fuse_ino_t ino, +@@ -873,6 +881,7 @@ static void lo_opendir(fuse_req_t req, fuse_ino_t ino, + struct lo_data *lo = lo_data(req); + struct lo_dirp *d; + int fd; ++ ssize_t fh; + + d = calloc(1, sizeof(struct lo_dirp)); + if (d == NULL) { +@@ -892,7 +901,14 @@ static void lo_opendir(fuse_req_t req, fuse_ino_t ino, + d->offset = 0; + d->entry = NULL; + +- fi->fh = (uintptr_t)d; ++ pthread_mutex_lock(&lo->mutex); ++ fh = lo_add_dirp_mapping(req, d); ++ pthread_mutex_unlock(&lo->mutex); ++ if (fh == -1) { ++ goto out_err; ++ } ++ ++ fi->fh = fh; + if (lo->cache == CACHE_ALWAYS) { + fi->keep_cache = 1; + } +@@ -903,6 +919,9 @@ out_errno: + error = errno; + out_err: + if (d) { ++ if (d->dp) { ++ closedir(d->dp); ++ } + if (fd != -1) { + close(fd); + } +@@ -920,17 +939,21 @@ static int is_dot_or_dotdot(const char *name) + static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, struct fuse_file_info *fi, int plus) + { +- struct lo_dirp *d = lo_dirp(fi); +- char *buf; ++ struct lo_dirp *d; ++ char *buf = NULL; + char *p; + size_t rem = size; +- int err; ++ int err = ENOMEM; + + (void)ino; + ++ d = lo_dirp(req, fi); ++ if (!d) { ++ goto error; ++ } ++ + buf = calloc(1, size); + if (!buf) { +- err = ENOMEM; + goto error; + } + p = buf; +@@ -1028,8 +1051,21 @@ static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, + static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) + { +- struct lo_dirp *d = lo_dirp(fi); ++ struct lo_data *lo = lo_data(req); ++ struct lo_dirp *d; ++ + (void)ino; ++ ++ d = lo_dirp(req, fi); ++ if (!d) { ++ fuse_reply_err(req, EBADF); ++ return; ++ } ++ ++ pthread_mutex_lock(&lo->mutex); ++ lo_map_remove(&lo->dirp_map, fi->fh); ++ pthread_mutex_unlock(&lo->mutex); ++ + closedir(d->dp); + free(d); + fuse_reply_err(req, 0); +@@ -1081,8 +1117,18 @@ static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi) + { + int res; +- int fd = dirfd(lo_dirp(fi)->dp); ++ struct lo_dirp *d; ++ int fd; ++ + (void)ino; ++ ++ d = lo_dirp(req, fi); ++ if (!d) { ++ fuse_reply_err(req, EBADF); ++ return; ++ } ++ ++ fd = dirfd(d->dp); + if (datasync) { + res = fdatasync(fd); + } else { +@@ -1614,6 +1660,8 @@ int main(int argc, char *argv[]) + root_elem = lo_map_reserve(&lo.ino_map, lo.root.fuse_ino); + root_elem->inode = &lo.root; + ++ lo_map_init(&lo.dirp_map); ++ + if (fuse_parse_cmdline(&args, &opts) != 0) { + return 1; + } +@@ -1710,6 +1758,7 @@ err_out2: + err_out1: + fuse_opt_free_args(&args); + ++ lo_map_destroy(&lo.dirp_map); + lo_map_destroy(&lo.ino_map); + + if (lo.root.fd >= 0) { diff --git a/0047-virtiofsd-passthrough_ll-add-fd_map-to-hide-file-des.patch b/0047-virtiofsd-passthrough_ll-add-fd_map-to-hide-file-des.patch new file mode 100644 index 0000000..485a73b --- /dev/null +++ b/0047-virtiofsd-passthrough_ll-add-fd_map-to-hide-file-des.patch @@ -0,0 +1,308 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:16 +0000 +Subject: [PATCH] virtiofsd: passthrough_ll: add fd_map to hide file + descriptors + +Do not expose file descriptor numbers to clients. This prevents the +abuse of internal file descriptors (like stdin/stdout). + +Signed-off-by: Stefan Hajnoczi +Fix from: +Signed-off-by: Xiao Yang +dgilbert: + Added lseek +Reviewed-by: Masayoshi Mizuma +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 73b4d19dfc4248a74c1f3e511cfa934681d9c602) +--- + tools/virtiofsd/passthrough_ll.c | 116 +++++++++++++++++++++++++------ + 1 file changed, 94 insertions(+), 22 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 5f5a72fdbb..9815bfa5c5 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -60,6 +60,7 @@ struct lo_map_elem { + union { + struct lo_inode *inode; + struct lo_dirp *dirp; ++ int fd; + ssize_t freelist; + }; + bool in_use; +@@ -107,6 +108,7 @@ struct lo_data { + struct lo_inode root; /* protected by lo->mutex */ + struct lo_map ino_map; /* protected by lo->mutex */ + struct lo_map dirp_map; /* protected by lo->mutex */ ++ struct lo_map fd_map; /* protected by lo->mutex */ + }; + + static const struct fuse_opt lo_opts[] = { +@@ -236,6 +238,20 @@ static void lo_map_remove(struct lo_map *map, size_t key) + map->freelist = key; + } + ++/* Assumes lo->mutex is held */ ++static ssize_t lo_add_fd_mapping(fuse_req_t req, int fd) ++{ ++ struct lo_map_elem *elem; ++ ++ elem = lo_map_alloc_elem(&lo_data(req)->fd_map); ++ if (!elem) { ++ return -1; ++ } ++ ++ elem->fd = fd; ++ return elem - lo_data(req)->fd_map.elems; ++} ++ + /* Assumes lo->mutex is held */ + static ssize_t lo_add_dirp_mapping(fuse_req_t req, struct lo_dirp *dirp) + { +@@ -350,6 +366,22 @@ static int utimensat_empty_nofollow(struct lo_inode *inode, + return utimensat(AT_FDCWD, procname, tv, 0); + } + ++static int lo_fi_fd(fuse_req_t req, struct fuse_file_info *fi) ++{ ++ struct lo_data *lo = lo_data(req); ++ struct lo_map_elem *elem; ++ ++ pthread_mutex_lock(&lo->mutex); ++ elem = lo_map_get(&lo->fd_map, fi->fh); ++ pthread_mutex_unlock(&lo->mutex); ++ ++ if (!elem) { ++ return -1; ++ } ++ ++ return elem->fd; ++} ++ + static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + int valid, struct fuse_file_info *fi) + { +@@ -358,6 +390,7 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + struct lo_inode *inode; + int ifd; + int res; ++ int fd; + + inode = lo_inode(req, ino); + if (!inode) { +@@ -367,9 +400,14 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + + ifd = inode->fd; + ++ /* If fi->fh is invalid we'll report EBADF later */ ++ if (fi) { ++ fd = lo_fi_fd(req, fi); ++ } ++ + if (valid & FUSE_SET_ATTR_MODE) { + if (fi) { +- res = fchmod(fi->fh, attr->st_mode); ++ res = fchmod(fd, attr->st_mode); + } else { + sprintf(procname, "/proc/self/fd/%i", ifd); + res = chmod(procname, attr->st_mode); +@@ -389,7 +427,7 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + } + if (valid & FUSE_SET_ATTR_SIZE) { + if (fi) { +- res = ftruncate(fi->fh, attr->st_size); ++ res = ftruncate(fd, attr->st_size); + } else { + sprintf(procname, "/proc/self/fd/%i", ifd); + res = truncate(procname, attr->st_size); +@@ -419,7 +457,7 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + } + + if (fi) { +- res = futimens(fi->fh, tv); ++ res = futimens(fd, tv); + } else { + res = utimensat_empty_nofollow(inode, tv); + } +@@ -1096,7 +1134,18 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, + lo_restore_cred(&old); + + if (!err) { +- fi->fh = fd; ++ ssize_t fh; ++ ++ pthread_mutex_lock(&lo->mutex); ++ fh = lo_add_fd_mapping(req, fd); ++ pthread_mutex_unlock(&lo->mutex); ++ if (fh == -1) { ++ close(fd); ++ fuse_reply_err(req, ENOMEM); ++ return; ++ } ++ ++ fi->fh = fh; + err = lo_do_lookup(req, parent, name, &e); + } + if (lo->cache == CACHE_NEVER) { +@@ -1140,6 +1189,7 @@ static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, + static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) + { + int fd; ++ ssize_t fh; + char buf[64]; + struct lo_data *lo = lo_data(req); + +@@ -1175,7 +1225,16 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) + return (void)fuse_reply_err(req, errno); + } + +- fi->fh = fd; ++ pthread_mutex_lock(&lo->mutex); ++ fh = lo_add_fd_mapping(req, fd); ++ pthread_mutex_unlock(&lo->mutex); ++ if (fh == -1) { ++ close(fd); ++ fuse_reply_err(req, ENOMEM); ++ return; ++ } ++ ++ fi->fh = fh; + if (lo->cache == CACHE_NEVER) { + fi->direct_io = 1; + } else if (lo->cache == CACHE_ALWAYS) { +@@ -1187,9 +1246,18 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) + static void lo_release(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) + { ++ struct lo_data *lo = lo_data(req); ++ int fd; ++ + (void)ino; + +- close(fi->fh); ++ fd = lo_fi_fd(req, fi); ++ ++ pthread_mutex_lock(&lo->mutex); ++ lo_map_remove(&lo->fd_map, fi->fh); ++ pthread_mutex_unlock(&lo->mutex); ++ ++ close(fd); + fuse_reply_err(req, 0); + } + +@@ -1197,7 +1265,7 @@ static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) + { + int res; + (void)ino; +- res = close(dup(fi->fh)); ++ res = close(dup(lo_fi_fd(req, fi))); + fuse_reply_err(req, res == -1 ? errno : 0); + } + +@@ -1224,7 +1292,7 @@ static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, + return (void)fuse_reply_err(req, errno); + } + } else { +- fd = fi->fh; ++ fd = lo_fi_fd(req, fi); + } + + if (datasync) { +@@ -1251,7 +1319,7 @@ static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, + } + + buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; +- buf.buf[0].fd = fi->fh; ++ buf.buf[0].fd = lo_fi_fd(req, fi); + buf.buf[0].pos = offset; + + fuse_reply_data(req, &buf); +@@ -1266,7 +1334,7 @@ static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, + struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); + + out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; +- out_buf.buf[0].fd = fi->fh; ++ out_buf.buf[0].fd = lo_fi_fd(req, fi); + out_buf.buf[0].pos = off; + + if (lo_debug(req)) { +@@ -1303,7 +1371,7 @@ static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, + (void)ino; + + #ifdef CONFIG_FALLOCATE +- err = fallocate(fi->fh, mode, offset, length); ++ err = fallocate(lo_fi_fd(req, fi), mode, offset, length); + if (err < 0) { + err = errno; + } +@@ -1314,7 +1382,7 @@ static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, + return; + } + +- err = posix_fallocate(fi->fh, offset, length); ++ err = posix_fallocate(lo_fi_fd(req, fi), offset, length); + #endif + + fuse_reply_err(req, err); +@@ -1326,7 +1394,7 @@ static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + int res; + (void)ino; + +- res = flock(fi->fh, op); ++ res = flock(lo_fi_fd(req, fi), op); + + fuse_reply_err(req, res == -1 ? errno : 0); + } +@@ -1551,17 +1619,19 @@ static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, + off_t off_out, struct fuse_file_info *fi_out, + size_t len, int flags) + { ++ int in_fd, out_fd; + ssize_t res; + +- if (lo_debug(req)) +- fuse_log(FUSE_LOG_DEBUG, +- "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, " +- "off=%lu, ino=%" PRIu64 "/fd=%lu, " +- "off=%lu, size=%zd, flags=0x%x)\n", +- ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out, len, +- flags); ++ in_fd = lo_fi_fd(req, fi_in); ++ out_fd = lo_fi_fd(req, fi_out); ++ ++ fuse_log(FUSE_LOG_DEBUG, ++ "lo_copy_file_range(ino=%" PRIu64 "/fd=%d, " ++ "off=%lu, ino=%" PRIu64 "/fd=%d, " ++ "off=%lu, size=%zd, flags=0x%x)\n", ++ ino_in, in_fd, off_in, ino_out, out_fd, off_out, len, flags); + +- res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len, flags); ++ res = copy_file_range(in_fd, &off_in, out_fd, &off_out, len, flags); + if (res < 0) { + fuse_reply_err(req, -errno); + } else { +@@ -1576,7 +1646,7 @@ static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, + off_t res; + + (void)ino; +- res = lseek(fi->fh, off, whence); ++ res = lseek(lo_fi_fd(req, fi), off, whence); + if (res != -1) { + fuse_reply_lseek(req, res); + } else { +@@ -1661,6 +1731,7 @@ int main(int argc, char *argv[]) + root_elem->inode = &lo.root; + + lo_map_init(&lo.dirp_map); ++ lo_map_init(&lo.fd_map); + + if (fuse_parse_cmdline(&args, &opts) != 0) { + return 1; +@@ -1758,6 +1829,7 @@ err_out2: + err_out1: + fuse_opt_free_args(&args); + ++ lo_map_destroy(&lo.fd_map); + lo_map_destroy(&lo.dirp_map); + lo_map_destroy(&lo.ino_map); + diff --git a/0048-virtiofsd-passthrough_ll-add-fallback-for-racy-ops.patch b/0048-virtiofsd-passthrough_ll-add-fallback-for-racy-ops.patch new file mode 100644 index 0000000..f843056 --- /dev/null +++ b/0048-virtiofsd-passthrough_ll-add-fallback-for-racy-ops.patch @@ -0,0 +1,284 @@ +From: Miklos Szeredi +Date: Mon, 27 Jan 2020 19:01:17 +0000 +Subject: [PATCH] virtiofsd: passthrough_ll: add fallback for racy ops + +We have two operations that cannot be done race-free on a symlink in +certain cases: utimes and link. + +Add racy fallback for these if the race-free method doesn't work. We do +our best to avoid races even in this case: + + - get absolute path by reading /proc/self/fd/NN symlink + + - lookup parent directory: after this we are safe against renames in + ancestors + + - lookup name in parent directory, and verify that we got to the original + inode, if not retry the whole thing + +Both utimes(2) and link(2) hold i_lock on the inode across the operation, +so a racing rename/delete by this fuse instance is not possible, only from +other entities changing the filesystem. + +If the "norace" option is given, then disable the racy fallbacks. + +Signed-off-by: Miklos Szeredi +Reviewed-by: Masayoshi Mizuma +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 5fe319a7b19c9c328e6e061bffcf1ff6cc8b89ce) +--- + tools/virtiofsd/helper.c | 5 +- + tools/virtiofsd/passthrough_ll.c | 157 +++++++++++++++++++++++++++---- + 2 files changed, 145 insertions(+), 17 deletions(-) + +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index b8ec5ac8dc..5531425223 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -142,7 +142,10 @@ void fuse_cmdline_help(void) + " --daemonize run in background\n" + " -o max_idle_threads the maximum number of idle worker " + "threads\n" +- " allowed (default: 10)\n"); ++ " allowed (default: 10)\n" ++ " -o norace disable racy fallback\n" ++ " default: false\n" ++ ); + } + + static int fuse_helper_opt_proc(void *data, const char *arg, int key, +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 9815bfa5c5..ac380efcb1 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -98,6 +98,7 @@ enum { + struct lo_data { + pthread_mutex_t mutex; + int debug; ++ int norace; + int writeback; + int flock; + int xattr; +@@ -124,10 +125,15 @@ static const struct fuse_opt lo_opts[] = { + { "cache=never", offsetof(struct lo_data, cache), CACHE_NEVER }, + { "cache=auto", offsetof(struct lo_data, cache), CACHE_NORMAL }, + { "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS }, +- ++ { "norace", offsetof(struct lo_data, norace), 1 }, + FUSE_OPT_END + }; + ++static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n); ++ ++static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st); ++ ++ + static struct lo_data *lo_data(fuse_req_t req) + { + return (struct lo_data *)fuse_req_userdata(req); +@@ -347,23 +353,127 @@ static void lo_getattr(fuse_req_t req, fuse_ino_t ino, + fuse_reply_attr(req, &buf, lo->timeout); + } + +-static int utimensat_empty_nofollow(struct lo_inode *inode, +- const struct timespec *tv) ++static int lo_parent_and_name(struct lo_data *lo, struct lo_inode *inode, ++ char path[PATH_MAX], struct lo_inode **parent) + { +- int res; + char procname[64]; ++ char *last; ++ struct stat stat; ++ struct lo_inode *p; ++ int retries = 2; ++ int res; ++ ++retry: ++ sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ ++ res = readlink(procname, path, PATH_MAX); ++ if (res < 0) { ++ fuse_log(FUSE_LOG_WARNING, "%s: readlink failed: %m\n", __func__); ++ goto fail_noretry; ++ } ++ ++ if (res >= PATH_MAX) { ++ fuse_log(FUSE_LOG_WARNING, "%s: readlink overflowed\n", __func__); ++ goto fail_noretry; ++ } ++ path[res] = '\0'; ++ ++ last = strrchr(path, '/'); ++ if (last == NULL) { ++ /* Shouldn't happen */ ++ fuse_log( ++ FUSE_LOG_WARNING, ++ "%s: INTERNAL ERROR: bad path read from proc\n", __func__); ++ goto fail_noretry; ++ } ++ if (last == path) { ++ p = &lo->root; ++ pthread_mutex_lock(&lo->mutex); ++ p->refcount++; ++ pthread_mutex_unlock(&lo->mutex); ++ } else { ++ *last = '\0'; ++ res = fstatat(AT_FDCWD, last == path ? "/" : path, &stat, 0); ++ if (res == -1) { ++ if (!retries) { ++ fuse_log(FUSE_LOG_WARNING, ++ "%s: failed to stat parent: %m\n", __func__); ++ } ++ goto fail; ++ } ++ p = lo_find(lo, &stat); ++ if (p == NULL) { ++ if (!retries) { ++ fuse_log(FUSE_LOG_WARNING, ++ "%s: failed to find parent\n", __func__); ++ } ++ goto fail; ++ } ++ } ++ last++; ++ res = fstatat(p->fd, last, &stat, AT_SYMLINK_NOFOLLOW); ++ if (res == -1) { ++ if (!retries) { ++ fuse_log(FUSE_LOG_WARNING, ++ "%s: failed to stat last\n", __func__); ++ } ++ goto fail_unref; ++ } ++ if (stat.st_dev != inode->dev || stat.st_ino != inode->ino) { ++ if (!retries) { ++ fuse_log(FUSE_LOG_WARNING, ++ "%s: failed to match last\n", __func__); ++ } ++ goto fail_unref; ++ } ++ *parent = p; ++ memmove(path, last, strlen(last) + 1); ++ ++ return 0; ++ ++fail_unref: ++ unref_inode(lo, p, 1); ++fail: ++ if (retries) { ++ retries--; ++ goto retry; ++ } ++fail_noretry: ++ errno = EIO; ++ return -1; ++} ++ ++static int utimensat_empty(struct lo_data *lo, struct lo_inode *inode, ++ const struct timespec *tv) ++{ ++ int res; ++ struct lo_inode *parent; ++ char path[PATH_MAX]; + + if (inode->is_symlink) { +- res = utimensat(inode->fd, "", tv, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); ++ res = utimensat(inode->fd, "", tv, AT_EMPTY_PATH); + if (res == -1 && errno == EINVAL) { + /* Sorry, no race free way to set times on symlink. */ +- errno = EPERM; ++ if (lo->norace) { ++ errno = EPERM; ++ } else { ++ goto fallback; ++ } + } + return res; + } +- sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ sprintf(path, "/proc/self/fd/%i", inode->fd); + +- return utimensat(AT_FDCWD, procname, tv, 0); ++ return utimensat(AT_FDCWD, path, tv, 0); ++ ++fallback: ++ res = lo_parent_and_name(lo, inode, path, &parent); ++ if (res != -1) { ++ res = utimensat(parent->fd, path, tv, AT_SYMLINK_NOFOLLOW); ++ unref_inode(lo, parent, 1); ++ } ++ ++ return res; + } + + static int lo_fi_fd(fuse_req_t req, struct fuse_file_info *fi) +@@ -387,6 +497,7 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + { + int saverr; + char procname[64]; ++ struct lo_data *lo = lo_data(req); + struct lo_inode *inode; + int ifd; + int res; +@@ -459,7 +570,7 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + if (fi) { + res = futimens(fd, tv); + } else { +- res = utimensat_empty_nofollow(inode, tv); ++ res = utimensat_empty(lo, inode, tv); + } + if (res == -1) { + goto out_err; +@@ -709,24 +820,38 @@ static void lo_symlink(fuse_req_t req, const char *link, fuse_ino_t parent, + lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link); + } + +-static int linkat_empty_nofollow(struct lo_inode *inode, int dfd, +- const char *name) ++static int linkat_empty_nofollow(struct lo_data *lo, struct lo_inode *inode, ++ int dfd, const char *name) + { + int res; +- char procname[64]; ++ struct lo_inode *parent; ++ char path[PATH_MAX]; + + if (inode->is_symlink) { + res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH); + if (res == -1 && (errno == ENOENT || errno == EINVAL)) { + /* Sorry, no race free way to hard-link a symlink. */ +- errno = EPERM; ++ if (lo->norace) { ++ errno = EPERM; ++ } else { ++ goto fallback; ++ } + } + return res; + } + +- sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ sprintf(path, "/proc/self/fd/%i", inode->fd); ++ ++ return linkat(AT_FDCWD, path, dfd, name, AT_SYMLINK_FOLLOW); ++ ++fallback: ++ res = lo_parent_and_name(lo, inode, path, &parent); ++ if (res != -1) { ++ res = linkat(parent->fd, path, dfd, name, 0); ++ unref_inode(lo, parent, 1); ++ } + +- return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW); ++ return res; + } + + static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, +@@ -748,7 +873,7 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, + e.attr_timeout = lo->timeout; + e.entry_timeout = lo->timeout; + +- res = linkat_empty_nofollow(inode, lo_fd(req, parent), name); ++ res = linkat_empty_nofollow(lo, inode, lo_fd(req, parent), name); + if (res == -1) { + goto out_err; + } diff --git a/0049-virtiofsd-validate-path-components.patch b/0049-virtiofsd-validate-path-components.patch new file mode 100644 index 0000000..1e5bd2b --- /dev/null +++ b/0049-virtiofsd-validate-path-components.patch @@ -0,0 +1,148 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:18 +0000 +Subject: [PATCH] virtiofsd: validate path components +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Several FUSE requests contain single path components. A correct FUSE +client sends well-formed path components but there is currently no input +validation in case something went wrong or the client is malicious. + +Refuse ".", "..", and paths containing '/' when we expect a path +component. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 25dae28c58d7e706b5d5db99042c9db3cef2e657) +--- + tools/virtiofsd/passthrough_ll.c | 59 ++++++++++++++++++++++++++++---- + 1 file changed, 53 insertions(+), 6 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index ac380efcb1..e375406160 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -133,6 +133,21 @@ static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n); + + static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st); + ++static int is_dot_or_dotdot(const char *name) ++{ ++ return name[0] == '.' && ++ (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); ++} ++ ++/* Is `path` a single path component that is not "." or ".."? */ ++static int is_safe_path_component(const char *path) ++{ ++ if (strchr(path, '/')) { ++ return 0; ++ } ++ ++ return !is_dot_or_dotdot(path); ++} + + static struct lo_data *lo_data(fuse_req_t req) + { +@@ -681,6 +696,15 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) + parent, name); + } + ++ /* ++ * Don't use is_safe_path_component(), allow "." and ".." for NFS export ++ * support. ++ */ ++ if (strchr(name, '/')) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + err = lo_do_lookup(req, parent, name, &e); + if (err) { + fuse_reply_err(req, err); +@@ -762,6 +786,11 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, + struct fuse_entry_param e; + struct lo_cred old = {}; + ++ if (!is_safe_path_component(name)) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + dir = lo_inode(req, parent); + if (!dir) { + fuse_reply_err(req, EBADF); +@@ -863,6 +892,11 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, + struct fuse_entry_param e; + int saverr; + ++ if (!is_safe_path_component(name)) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + inode = lo_inode(req, ino); + if (!inode) { + fuse_reply_err(req, EBADF); +@@ -904,6 +938,10 @@ out_err: + static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) + { + int res; ++ if (!is_safe_path_component(name)) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); + +@@ -916,6 +954,11 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, + { + int res; + ++ if (!is_safe_path_component(name) || !is_safe_path_component(newname)) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + if (flags) { + fuse_reply_err(req, EINVAL); + return; +@@ -930,6 +973,11 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) + { + int res; + ++ if (!is_safe_path_component(name)) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + res = unlinkat(lo_fd(req, parent), name, 0); + + fuse_reply_err(req, res == -1 ? errno : 0); +@@ -1093,12 +1141,6 @@ out_err: + fuse_reply_err(req, error); + } + +-static int is_dot_or_dotdot(const char *name) +-{ +- return name[0] == '.' && +- (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); +-} +- + static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, struct fuse_file_info *fi, int plus) + { +@@ -1248,6 +1290,11 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, + parent, name); + } + ++ if (!is_safe_path_component(name)) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + err = lo_change_cred(req, &old); + if (err) { + goto out; diff --git a/0050-virtiofsd-Plumb-fuse_bufvec-through-to-do_write_buf.patch b/0050-virtiofsd-Plumb-fuse_bufvec-through-to-do_write_buf.patch new file mode 100644 index 0000000..451e3b9 --- /dev/null +++ b/0050-virtiofsd-Plumb-fuse_bufvec-through-to-do_write_buf.patch @@ -0,0 +1,149 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:19 +0000 +Subject: [PATCH] virtiofsd: Plumb fuse_bufvec through to do_write_buf + +Let fuse_session_process_buf_int take a fuse_bufvec * instead of a +fuse_buf; and then through to do_write_buf - where in the best +case it can pass that straight through to op.write_buf without copying +(other than skipping a header). + +Signed-off-by: Dr. David Alan Gilbert +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Masayoshi Mizuma +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 469f9d2fc405b0508e6cf1b4b5bbcadfc82064e5) +--- + tools/virtiofsd/fuse_i.h | 2 +- + tools/virtiofsd/fuse_lowlevel.c | 61 ++++++++++++++++++++++----------- + tools/virtiofsd/fuse_virtio.c | 3 +- + 3 files changed, 44 insertions(+), 22 deletions(-) + +diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h +index 45995f3246..a20854f1c4 100644 +--- a/tools/virtiofsd/fuse_i.h ++++ b/tools/virtiofsd/fuse_i.h +@@ -100,7 +100,7 @@ int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, + void fuse_free_req(fuse_req_t req); + + void fuse_session_process_buf_int(struct fuse_session *se, +- const struct fuse_buf *buf, ++ struct fuse_bufvec *bufv, + struct fuse_chan *ch); + + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 95f4db8fcf..7e10995adc 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -1004,11 +1004,12 @@ static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + + static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, +- const struct fuse_buf *ibuf) ++ struct fuse_bufvec *ibufv) + { + struct fuse_session *se = req->se; +- struct fuse_bufvec bufv = { +- .buf[0] = *ibuf, ++ struct fuse_bufvec *pbufv = ibufv; ++ struct fuse_bufvec tmpbufv = { ++ .buf[0] = ibufv->buf[0], + .count = 1, + }; + struct fuse_write_in *arg = (struct fuse_write_in *)inarg; +@@ -1018,22 +1019,31 @@ static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, + fi.fh = arg->fh; + fi.writepage = arg->write_flags & FUSE_WRITE_CACHE; + +- fi.lock_owner = arg->lock_owner; +- fi.flags = arg->flags; +- if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) { +- bufv.buf[0].mem = PARAM(arg); +- } +- +- bufv.buf[0].size -= +- sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); +- if (bufv.buf[0].size < arg->size) { +- fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n"); +- fuse_reply_err(req, EIO); +- return; ++ if (ibufv->count == 1) { ++ fi.lock_owner = arg->lock_owner; ++ fi.flags = arg->flags; ++ if (!(tmpbufv.buf[0].flags & FUSE_BUF_IS_FD)) { ++ tmpbufv.buf[0].mem = PARAM(arg); ++ } ++ tmpbufv.buf[0].size -= ++ sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); ++ if (tmpbufv.buf[0].size < arg->size) { ++ fuse_log(FUSE_LOG_ERR, ++ "fuse: do_write_buf: buffer size too small\n"); ++ fuse_reply_err(req, EIO); ++ return; ++ } ++ tmpbufv.buf[0].size = arg->size; ++ pbufv = &tmpbufv; ++ } else { ++ /* ++ * Input bufv contains the headers in the first element ++ * and the data in the rest, we need to skip that first element ++ */ ++ ibufv->buf[0].size = 0; + } +- bufv.buf[0].size = arg->size; + +- se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi); ++ se->op.write_buf(req, nodeid, pbufv, arg->offset, &fi); + } + + static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) +@@ -2024,13 +2034,24 @@ static const char *opname(enum fuse_opcode opcode) + void fuse_session_process_buf(struct fuse_session *se, + const struct fuse_buf *buf) + { +- fuse_session_process_buf_int(se, buf, NULL); ++ struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 }; ++ fuse_session_process_buf_int(se, &bufv, NULL); + } + ++/* ++ * Restriction: ++ * bufv is normally a single entry buffer, except for a write ++ * where (if it's in memory) then the bufv may be multiple entries, ++ * where the first entry contains all headers and subsequent entries ++ * contain data ++ * bufv shall not use any offsets etc to make the data anything ++ * other than contiguous starting from 0. ++ */ + void fuse_session_process_buf_int(struct fuse_session *se, +- const struct fuse_buf *buf, ++ struct fuse_bufvec *bufv, + struct fuse_chan *ch) + { ++ const struct fuse_buf *buf = bufv->buf; + struct fuse_in_header *in; + const void *inarg; + struct fuse_req *req; +@@ -2108,7 +2129,7 @@ void fuse_session_process_buf_int(struct fuse_session *se, + + inarg = (void *)&in[1]; + if (in->opcode == FUSE_WRITE && se->op.write_buf) { +- do_write_buf(req, in->nodeid, inarg, buf); ++ do_write_buf(req, in->nodeid, inarg, bufv); + } else { + fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); + } +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index 635f87756a..fd588a4829 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -501,7 +501,8 @@ static void *fv_queue_thread(void *opaque) + /* TODO! Endianness of header */ + + /* TODO: Add checks for fuse_session_exited */ +- fuse_session_process_buf_int(se, &fbuf, &ch); ++ struct fuse_bufvec bufv = { .buf[0] = fbuf, .count = 1 }; ++ fuse_session_process_buf_int(se, &bufv, &ch); + + if (!qi->reply_sent) { + fuse_log(FUSE_LOG_DEBUG, "%s: elem %d no reply sent\n", diff --git a/0051-virtiofsd-Pass-write-iov-s-all-the-way-through.patch b/0051-virtiofsd-Pass-write-iov-s-all-the-way-through.patch new file mode 100644 index 0000000..d32681d --- /dev/null +++ b/0051-virtiofsd-Pass-write-iov-s-all-the-way-through.patch @@ -0,0 +1,121 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:20 +0000 +Subject: [PATCH] virtiofsd: Pass write iov's all the way through + +Pass the write iov pointing to guest RAM all the way through rather +than copying the data. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Xiao Yang +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit e17f7a580e2c599330ad3a6946be615ca2fe97d9) +--- + tools/virtiofsd/fuse_virtio.c | 79 ++++++++++++++++++++++++++++++++--- + 1 file changed, 73 insertions(+), 6 deletions(-) + +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index fd588a4829..872968f2c8 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -454,6 +454,10 @@ static void *fv_queue_thread(void *opaque) + __func__, qi->qidx, (size_t)evalue, in_bytes, out_bytes); + + while (1) { ++ bool allocated_bufv = false; ++ struct fuse_bufvec bufv; ++ struct fuse_bufvec *pbufv; ++ + /* + * An element contains one request and the space to send our + * response They're spread over multiple descriptors in a +@@ -495,14 +499,76 @@ static void *fv_queue_thread(void *opaque) + __func__, elem->index); + assert(0); /* TODO */ + } +- copy_from_iov(&fbuf, out_num, out_sg); +- fbuf.size = out_len; ++ /* Copy just the first element and look at it */ ++ copy_from_iov(&fbuf, 1, out_sg); ++ ++ if (out_num > 2 && ++ out_sg[0].iov_len == sizeof(struct fuse_in_header) && ++ ((struct fuse_in_header *)fbuf.mem)->opcode == FUSE_WRITE && ++ out_sg[1].iov_len == sizeof(struct fuse_write_in)) { ++ /* ++ * For a write we don't actually need to copy the ++ * data, we can just do it straight out of guest memory ++ * but we must still copy the headers in case the guest ++ * was nasty and changed them while we were using them. ++ */ ++ fuse_log(FUSE_LOG_DEBUG, "%s: Write special case\n", __func__); ++ ++ /* copy the fuse_write_in header after the fuse_in_header */ ++ fbuf.mem += out_sg->iov_len; ++ copy_from_iov(&fbuf, 1, out_sg + 1); ++ fbuf.mem -= out_sg->iov_len; ++ fbuf.size = out_sg[0].iov_len + out_sg[1].iov_len; ++ ++ /* Allocate the bufv, with space for the rest of the iov */ ++ allocated_bufv = true; ++ pbufv = malloc(sizeof(struct fuse_bufvec) + ++ sizeof(struct fuse_buf) * (out_num - 2)); ++ if (!pbufv) { ++ vu_queue_unpop(dev, q, elem, 0); ++ free(elem); ++ fuse_log(FUSE_LOG_ERR, "%s: pbufv malloc failed\n", ++ __func__); ++ goto out; ++ } ++ ++ pbufv->count = 1; ++ pbufv->buf[0] = fbuf; ++ ++ size_t iovindex, pbufvindex; ++ iovindex = 2; /* 2 headers, separate iovs */ ++ pbufvindex = 1; /* 2 headers, 1 fusebuf */ ++ ++ for (; iovindex < out_num; iovindex++, pbufvindex++) { ++ pbufv->count++; ++ pbufv->buf[pbufvindex].pos = ~0; /* Dummy */ ++ pbufv->buf[pbufvindex].flags = 0; ++ pbufv->buf[pbufvindex].mem = out_sg[iovindex].iov_base; ++ pbufv->buf[pbufvindex].size = out_sg[iovindex].iov_len; ++ } ++ } else { ++ /* Normal (non fast write) path */ ++ ++ /* Copy the rest of the buffer */ ++ fbuf.mem += out_sg->iov_len; ++ copy_from_iov(&fbuf, out_num - 1, out_sg + 1); ++ fbuf.mem -= out_sg->iov_len; ++ fbuf.size = out_len; + +- /* TODO! Endianness of header */ ++ /* TODO! Endianness of header */ + +- /* TODO: Add checks for fuse_session_exited */ +- struct fuse_bufvec bufv = { .buf[0] = fbuf, .count = 1 }; +- fuse_session_process_buf_int(se, &bufv, &ch); ++ /* TODO: Add checks for fuse_session_exited */ ++ bufv.buf[0] = fbuf; ++ bufv.count = 1; ++ pbufv = &bufv; ++ } ++ pbufv->idx = 0; ++ pbufv->off = 0; ++ fuse_session_process_buf_int(se, pbufv, &ch); ++ ++ if (allocated_bufv) { ++ free(pbufv); ++ } + + if (!qi->reply_sent) { + fuse_log(FUSE_LOG_DEBUG, "%s: elem %d no reply sent\n", +@@ -516,6 +582,7 @@ static void *fv_queue_thread(void *opaque) + elem = NULL; + } + } ++out: + pthread_mutex_destroy(&ch.lock); + free(fbuf.mem); + diff --git a/0052-virtiofsd-add-fuse_mbuf_iter-API.patch b/0052-virtiofsd-add-fuse_mbuf_iter-API.patch new file mode 100644 index 0000000..1358aa8 --- /dev/null +++ b/0052-virtiofsd-add-fuse_mbuf_iter-API.patch @@ -0,0 +1,115 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:21 +0000 +Subject: [PATCH] virtiofsd: add fuse_mbuf_iter API + +Introduce an API for consuming bytes from a buffer with size checks. +All FUSE operations will be converted to use this safe API instead of +void *inarg. + +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit dad157e880416ab3a0e45beaa0e81977516568bc) +--- + tools/virtiofsd/buffer.c | 28 ++++++++++++++++++++ + tools/virtiofsd/fuse_common.h | 49 ++++++++++++++++++++++++++++++++++- + 2 files changed, 76 insertions(+), 1 deletion(-) + +diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c +index 772efa922d..42a608f6bd 100644 +--- a/tools/virtiofsd/buffer.c ++++ b/tools/virtiofsd/buffer.c +@@ -267,3 +267,31 @@ ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv) + + return copied; + } ++ ++void *fuse_mbuf_iter_advance(struct fuse_mbuf_iter *iter, size_t len) ++{ ++ void *ptr; ++ ++ if (len > iter->size - iter->pos) { ++ return NULL; ++ } ++ ++ ptr = iter->mem + iter->pos; ++ iter->pos += len; ++ return ptr; ++} ++ ++const char *fuse_mbuf_iter_advance_str(struct fuse_mbuf_iter *iter) ++{ ++ const char *str = iter->mem + iter->pos; ++ size_t remaining = iter->size - iter->pos; ++ size_t i; ++ ++ for (i = 0; i < remaining; i++) { ++ if (str[i] == '\0') { ++ iter->pos += i + 1; ++ return str; ++ } ++ } ++ return NULL; ++} +diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h +index 0cb33acc2f..f8f6433743 100644 +--- a/tools/virtiofsd/fuse_common.h ++++ b/tools/virtiofsd/fuse_common.h +@@ -703,10 +703,57 @@ size_t fuse_buf_size(const struct fuse_bufvec *bufv); + */ + ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src); + ++/** ++ * Memory buffer iterator ++ * ++ */ ++struct fuse_mbuf_iter { ++ /** ++ * Data pointer ++ */ ++ void *mem; ++ ++ /** ++ * Total length, in bytes ++ */ ++ size_t size; ++ ++ /** ++ * Offset from start of buffer ++ */ ++ size_t pos; ++}; ++ ++/* Initialize memory buffer iterator from a fuse_buf */ ++#define FUSE_MBUF_ITER_INIT(fbuf) \ ++ ((struct fuse_mbuf_iter){ \ ++ .mem = fbuf->mem, \ ++ .size = fbuf->size, \ ++ .pos = 0, \ ++ }) ++ ++/** ++ * Consume bytes from a memory buffer iterator ++ * ++ * @param iter memory buffer iterator ++ * @param len number of bytes to consume ++ * @return pointer to start of consumed bytes or ++ * NULL if advancing beyond end of buffer ++ */ ++void *fuse_mbuf_iter_advance(struct fuse_mbuf_iter *iter, size_t len); ++ ++/** ++ * Consume a NUL-terminated string from a memory buffer iterator ++ * ++ * @param iter memory buffer iterator ++ * @return pointer to the string or ++ * NULL if advancing beyond end of buffer or there is no NUL-terminator ++ */ ++const char *fuse_mbuf_iter_advance_str(struct fuse_mbuf_iter *iter); ++ + /* + * Signal handling + */ +- + /** + * Exit session on HUP, TERM and INT signals and ignore PIPE signal + * diff --git a/0053-virtiofsd-validate-input-buffer-sizes-in-do_write_bu.patch b/0053-virtiofsd-validate-input-buffer-sizes-in-do_write_bu.patch new file mode 100644 index 0000000..d5ad1dd --- /dev/null +++ b/0053-virtiofsd-validate-input-buffer-sizes-in-do_write_bu.patch @@ -0,0 +1,117 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:22 +0000 +Subject: [PATCH] virtiofsd: validate input buffer sizes in do_write_buf() + +There is a small change in behavior: if fuse_write_in->size doesn't +match the input buffer size then the request is failed. Previously +write requests with 1 fuse_buf element would truncate to +fuse_write_in->size. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Sergio Lopez +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 0ba8c3c6fce8fe949d59c1fd84d98d220ef9e759) +--- + tools/virtiofsd/fuse_lowlevel.c | 49 ++++++++++++++++++++------------- + 1 file changed, 30 insertions(+), 19 deletions(-) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 7e10995adc..611e8b0354 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -1003,8 +1003,8 @@ static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, +- struct fuse_bufvec *ibufv) ++static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter, struct fuse_bufvec *ibufv) + { + struct fuse_session *se = req->se; + struct fuse_bufvec *pbufv = ibufv; +@@ -1012,28 +1012,27 @@ static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, + .buf[0] = ibufv->buf[0], + .count = 1, + }; +- struct fuse_write_in *arg = (struct fuse_write_in *)inarg; ++ struct fuse_write_in *arg; ++ size_t arg_size = sizeof(*arg); + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); ++ ++ arg = fuse_mbuf_iter_advance(iter, arg_size); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ ++ fi.lock_owner = arg->lock_owner; ++ fi.flags = arg->flags; + fi.fh = arg->fh; + fi.writepage = arg->write_flags & FUSE_WRITE_CACHE; + + if (ibufv->count == 1) { +- fi.lock_owner = arg->lock_owner; +- fi.flags = arg->flags; +- if (!(tmpbufv.buf[0].flags & FUSE_BUF_IS_FD)) { +- tmpbufv.buf[0].mem = PARAM(arg); +- } +- tmpbufv.buf[0].size -= +- sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); +- if (tmpbufv.buf[0].size < arg->size) { +- fuse_log(FUSE_LOG_ERR, +- "fuse: do_write_buf: buffer size too small\n"); +- fuse_reply_err(req, EIO); +- return; +- } +- tmpbufv.buf[0].size = arg->size; ++ assert(!(tmpbufv.buf[0].flags & FUSE_BUF_IS_FD)); ++ tmpbufv.buf[0].mem = ((char *)arg) + arg_size; ++ tmpbufv.buf[0].size -= sizeof(struct fuse_in_header) + arg_size; + pbufv = &tmpbufv; + } else { + /* +@@ -1043,6 +1042,13 @@ static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, + ibufv->buf[0].size = 0; + } + ++ if (fuse_buf_size(pbufv) != arg->size) { ++ fuse_log(FUSE_LOG_ERR, ++ "fuse: do_write_buf: buffer size doesn't match arg->size\n"); ++ fuse_reply_err(req, EIO); ++ return; ++ } ++ + se->op.write_buf(req, nodeid, pbufv, arg->offset, &fi); + } + +@@ -2052,12 +2058,17 @@ void fuse_session_process_buf_int(struct fuse_session *se, + struct fuse_chan *ch) + { + const struct fuse_buf *buf = bufv->buf; ++ struct fuse_mbuf_iter iter = FUSE_MBUF_ITER_INIT(buf); + struct fuse_in_header *in; + const void *inarg; + struct fuse_req *req; + int err; + +- in = buf->mem; ++ /* The first buffer must be a memory buffer */ ++ assert(!(buf->flags & FUSE_BUF_IS_FD)); ++ ++ in = fuse_mbuf_iter_advance(&iter, sizeof(*in)); ++ assert(in); /* caller guarantees the input buffer is large enough */ + + if (se->debug) { + fuse_log(FUSE_LOG_DEBUG, +@@ -2129,7 +2140,7 @@ void fuse_session_process_buf_int(struct fuse_session *se, + + inarg = (void *)&in[1]; + if (in->opcode == FUSE_WRITE && se->op.write_buf) { +- do_write_buf(req, in->nodeid, inarg, bufv); ++ do_write_buf(req, in->nodeid, &iter, bufv); + } else { + fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); + } diff --git a/0054-virtiofsd-check-input-buffer-size-in-fuse_lowlevel.c.patch b/0054-virtiofsd-check-input-buffer-size-in-fuse_lowlevel.c.patch new file mode 100644 index 0000000..6746752 --- /dev/null +++ b/0054-virtiofsd-check-input-buffer-size-in-fuse_lowlevel.c.patch @@ -0,0 +1,1091 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:23 +0000 +Subject: [PATCH] virtiofsd: check input buffer size in fuse_lowlevel.c ops + +Each FUSE operation involves parsing the input buffer. Currently the +code assumes the input buffer is large enough for the expected +arguments. This patch uses fuse_mbuf_iter to check the size. + +Most operations are simple to convert. Some are more complicated due to +variable-length inputs or different sizes depending on the protocol +version. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Sergio Lopez +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 70995754416eb4491c31607fe380a83cfd25a087) +--- + tools/virtiofsd/fuse_lowlevel.c | 581 +++++++++++++++++++++++++------- + 1 file changed, 456 insertions(+), 125 deletions(-) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 611e8b0354..02e1d83038 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -27,7 +28,6 @@ + #include + + +-#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) + #define OFFSET_MAX 0x7fffffffffffffffLL + + struct fuse_pollhandle { +@@ -706,9 +706,14 @@ int fuse_reply_lseek(fuse_req_t req, off_t off) + return send_reply_ok(req, &arg, sizeof(arg)); + } + +-static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- char *name = (char *)inarg; ++ const char *name = fuse_mbuf_iter_advance_str(iter); ++ if (!name) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + if (req->se->op.lookup) { + req->se->op.lookup(req, nodeid, name); +@@ -717,9 +722,16 @@ static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_forget(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_forget_in *arg = (struct fuse_forget_in *)inarg; ++ struct fuse_forget_in *arg; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + if (req->se->op.forget) { + req->se->op.forget(req, nodeid, arg->nlookup); +@@ -729,20 +741,48 @@ static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + + static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid, +- const void *inarg) ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_batch_forget_in *arg = (void *)inarg; +- struct fuse_forget_one *param = (void *)PARAM(arg); +- unsigned int i; ++ struct fuse_batch_forget_in *arg; ++ struct fuse_forget_data *forgets; ++ size_t scount; + + (void)nodeid; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_none(req); ++ return; ++ } ++ ++ /* ++ * Prevent integer overflow. The compiler emits the following warning ++ * unless we use the scount local variable: ++ * ++ * error: comparison is always false due to limited range of data type ++ * [-Werror=type-limits] ++ * ++ * This may be true on 64-bit hosts but we need this check for 32-bit ++ * hosts. ++ */ ++ scount = arg->count; ++ if (scount > SIZE_MAX / sizeof(forgets[0])) { ++ fuse_reply_none(req); ++ return; ++ } ++ ++ forgets = fuse_mbuf_iter_advance(iter, arg->count * sizeof(forgets[0])); ++ if (!forgets) { ++ fuse_reply_none(req); ++ return; ++ } ++ + if (req->se->op.forget_multi) { +- req->se->op.forget_multi(req, arg->count, +- (struct fuse_forget_data *)param); ++ req->se->op.forget_multi(req, arg->count, forgets); + } else if (req->se->op.forget) { ++ unsigned int i; ++ + for (i = 0; i < arg->count; i++) { +- struct fuse_forget_one *forget = ¶m[i]; + struct fuse_req *dummy_req; + + dummy_req = fuse_ll_alloc_req(req->se); +@@ -754,7 +794,7 @@ static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid, + dummy_req->ctx = req->ctx; + dummy_req->ch = NULL; + +- req->se->op.forget(dummy_req, forget->nodeid, forget->nlookup); ++ req->se->op.forget(dummy_req, forgets[i].ino, forgets[i].nlookup); + } + fuse_reply_none(req); + } else { +@@ -762,12 +802,19 @@ static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid, + } + } + +-static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { + struct fuse_file_info *fip = NULL; + struct fuse_file_info fi; + +- struct fuse_getattr_in *arg = (struct fuse_getattr_in *)inarg; ++ struct fuse_getattr_in *arg; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + if (arg->getattr_flags & FUSE_GETATTR_FH) { + memset(&fi, 0, sizeof(fi)); +@@ -782,14 +829,21 @@ static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_setattr_in *arg = (struct fuse_setattr_in *)inarg; +- + if (req->se->op.setattr) { ++ struct fuse_setattr_in *arg; + struct fuse_file_info *fi = NULL; + struct fuse_file_info fi_store; + struct stat stbuf; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + memset(&stbuf, 0, sizeof(stbuf)); + convert_attr(arg, &stbuf); + if (arg->valid & FATTR_FH) { +@@ -810,9 +864,16 @@ static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_access(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_access_in *arg = (struct fuse_access_in *)inarg; ++ struct fuse_access_in *arg; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + if (req->se->op.access) { + req->se->op.access(req, nodeid, arg->mask); +@@ -821,9 +882,10 @@ static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- (void)inarg; ++ (void)iter; + + if (req->se->op.readlink) { + req->se->op.readlink(req, nodeid); +@@ -832,10 +894,18 @@ static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_mknod_in *arg = (struct fuse_mknod_in *)inarg; +- char *name = PARAM(arg); ++ struct fuse_mknod_in *arg; ++ const char *name; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ name = fuse_mbuf_iter_advance_str(iter); ++ if (!arg || !name) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + req->ctx.umask = arg->umask; + +@@ -846,22 +916,37 @@ static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *)inarg; ++ struct fuse_mkdir_in *arg; ++ const char *name; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ name = fuse_mbuf_iter_advance_str(iter); ++ if (!arg || !name) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + req->ctx.umask = arg->umask; + + if (req->se->op.mkdir) { +- req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode); ++ req->se->op.mkdir(req, nodeid, name, arg->mode); + } else { + fuse_reply_err(req, ENOSYS); + } + } + +-static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- char *name = (char *)inarg; ++ const char *name = fuse_mbuf_iter_advance_str(iter); ++ ++ if (!name) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + if (req->se->op.unlink) { + req->se->op.unlink(req, nodeid, name); +@@ -870,9 +955,15 @@ static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- char *name = (char *)inarg; ++ const char *name = fuse_mbuf_iter_advance_str(iter); ++ ++ if (!name) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + if (req->se->op.rmdir) { + req->se->op.rmdir(req, nodeid, name); +@@ -881,10 +972,16 @@ static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- char *name = (char *)inarg; +- char *linkname = ((char *)inarg) + strlen((char *)inarg) + 1; ++ const char *name = fuse_mbuf_iter_advance_str(iter); ++ const char *linkname = fuse_mbuf_iter_advance_str(iter); ++ ++ if (!name || !linkname) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + if (req->se->op.symlink) { + req->se->op.symlink(req, linkname, nodeid, name); +@@ -893,11 +990,20 @@ static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_rename(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_rename_in *arg = (struct fuse_rename_in *)inarg; +- char *oldname = PARAM(arg); +- char *newname = oldname + strlen(oldname) + 1; ++ struct fuse_rename_in *arg; ++ const char *oldname; ++ const char *newname; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ oldname = fuse_mbuf_iter_advance_str(iter); ++ newname = fuse_mbuf_iter_advance_str(iter); ++ if (!arg || !oldname || !newname) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + if (req->se->op.rename) { + req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, 0); +@@ -906,11 +1012,20 @@ static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_rename2_in *arg = (struct fuse_rename2_in *)inarg; +- char *oldname = PARAM(arg); +- char *newname = oldname + strlen(oldname) + 1; ++ struct fuse_rename2_in *arg; ++ const char *oldname; ++ const char *newname; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ oldname = fuse_mbuf_iter_advance_str(iter); ++ newname = fuse_mbuf_iter_advance_str(iter); ++ if (!arg || !oldname || !newname) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + if (req->se->op.rename) { + req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, +@@ -920,24 +1035,38 @@ static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_link(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_link_in *arg = (struct fuse_link_in *)inarg; ++ struct fuse_link_in *arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ const char *name = fuse_mbuf_iter_advance_str(iter); ++ ++ if (!arg || !name) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + if (req->se->op.link) { +- req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg)); ++ req->se->op.link(req, arg->oldnodeid, nodeid, name); + } else { + fuse_reply_err(req, ENOSYS); + } + } + +-static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_create(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_create_in *arg = (struct fuse_create_in *)inarg; +- + if (req->se->op.create) { ++ struct fuse_create_in *arg; + struct fuse_file_info fi; +- char *name = PARAM(arg); ++ const char *name; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ name = fuse_mbuf_iter_advance_str(iter); ++ if (!arg || !name) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; +@@ -950,11 +1079,18 @@ static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_open(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_open_in *arg = (struct fuse_open_in *)inarg; ++ struct fuse_open_in *arg; + struct fuse_file_info fi; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + +@@ -965,13 +1101,15 @@ static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_read(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_read_in *arg = (struct fuse_read_in *)inarg; +- + if (req->se->op.read) { ++ struct fuse_read_in *arg; + struct fuse_file_info fi; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.lock_owner = arg->lock_owner; +@@ -982,11 +1120,24 @@ static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_write(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_write_in *arg = (struct fuse_write_in *)inarg; ++ struct fuse_write_in *arg; + struct fuse_file_info fi; +- char *param; ++ const char *param; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ ++ param = fuse_mbuf_iter_advance(iter, arg->size); ++ if (!param) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; +@@ -994,7 +1145,6 @@ static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + + fi.lock_owner = arg->lock_owner; + fi.flags = arg->flags; +- param = PARAM(arg); + + if (req->se->op.write) { + req->se->op.write(req, nodeid, param, arg->size, arg->offset, &fi); +@@ -1052,11 +1202,18 @@ static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, + se->op.write_buf(req, nodeid, pbufv, arg->offset, &fi); + } + +-static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_flush(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_flush_in *arg = (struct fuse_flush_in *)inarg; ++ struct fuse_flush_in *arg; + struct fuse_file_info fi; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.flush = 1; +@@ -1069,19 +1226,26 @@ static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_release(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_release_in *arg = (struct fuse_release_in *)inarg; ++ struct fuse_release_in *arg; + struct fuse_file_info fi; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + fi.fh = arg->fh; + fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; + fi.lock_owner = arg->lock_owner; ++ + if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) { + fi.flock_release = 1; +- fi.lock_owner = arg->lock_owner; + } + + if (req->se->op.release) { +@@ -1091,11 +1255,19 @@ static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_fsync_in *arg = (struct fuse_fsync_in *)inarg; ++ struct fuse_fsync_in *arg; + struct fuse_file_info fi; +- int datasync = arg->fsync_flags & 1; ++ int datasync; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ datasync = arg->fsync_flags & 1; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; +@@ -1111,11 +1283,18 @@ static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_open_in *arg = (struct fuse_open_in *)inarg; ++ struct fuse_open_in *arg; + struct fuse_file_info fi; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + +@@ -1126,11 +1305,18 @@ static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_read_in *arg = (struct fuse_read_in *)inarg; ++ struct fuse_read_in *arg; + struct fuse_file_info fi; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + +@@ -1141,11 +1327,18 @@ static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_read_in *arg = (struct fuse_read_in *)inarg; ++ struct fuse_read_in *arg; + struct fuse_file_info fi; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + +@@ -1156,11 +1349,18 @@ static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_release_in *arg = (struct fuse_release_in *)inarg; ++ struct fuse_release_in *arg; + struct fuse_file_info fi; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + fi.fh = arg->fh; +@@ -1172,11 +1372,19 @@ static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_fsync_in *arg = (struct fuse_fsync_in *)inarg; ++ struct fuse_fsync_in *arg; + struct fuse_file_info fi; +- int datasync = arg->fsync_flags & 1; ++ int datasync; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ datasync = arg->fsync_flags & 1; + + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; +@@ -1188,10 +1396,11 @@ static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { + (void)nodeid; +- (void)inarg; ++ (void)iter; + + if (req->se->op.statfs) { + req->se->op.statfs(req, nodeid); +@@ -1204,11 +1413,25 @@ static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *)inarg; +- char *name = PARAM(arg); +- char *value = name + strlen(name) + 1; ++ struct fuse_setxattr_in *arg; ++ const char *name; ++ const char *value; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ name = fuse_mbuf_iter_advance_str(iter); ++ if (!arg || !name) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ ++ value = fuse_mbuf_iter_advance(iter, arg->size); ++ if (!value) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + if (req->se->op.setxattr) { + req->se->op.setxattr(req, nodeid, name, value, arg->size, arg->flags); +@@ -1217,20 +1440,36 @@ static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *)inarg; ++ struct fuse_getxattr_in *arg; ++ const char *name; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ name = fuse_mbuf_iter_advance_str(iter); ++ if (!arg || !name) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + if (req->se->op.getxattr) { +- req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size); ++ req->se->op.getxattr(req, nodeid, name, arg->size); + } else { + fuse_reply_err(req, ENOSYS); + } + } + +-static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *)inarg; ++ struct fuse_getxattr_in *arg; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + if (req->se->op.listxattr) { + req->se->op.listxattr(req, nodeid, arg->size); +@@ -1239,9 +1478,15 @@ static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- char *name = (char *)inarg; ++ const char *name = fuse_mbuf_iter_advance_str(iter); ++ ++ if (!name) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + if (req->se->op.removexattr) { + req->se->op.removexattr(req, nodeid, name); +@@ -1265,12 +1510,19 @@ static void convert_fuse_file_lock(struct fuse_file_lock *fl, + flock->l_pid = fl->pid; + } + +-static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_lk_in *arg = (struct fuse_lk_in *)inarg; ++ struct fuse_lk_in *arg; + struct fuse_file_info fi; + struct flock flock; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.lock_owner = arg->owner; +@@ -1284,12 +1536,18 @@ static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + + static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, +- const void *inarg, int sleep) ++ struct fuse_mbuf_iter *iter, int sleep) + { +- struct fuse_lk_in *arg = (struct fuse_lk_in *)inarg; ++ struct fuse_lk_in *arg; + struct fuse_file_info fi; + struct flock flock; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.lock_owner = arg->owner; +@@ -1327,14 +1585,16 @@ static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, + } + } + +-static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- do_setlk_common(req, nodeid, inarg, 0); ++ do_setlk_common(req, nodeid, iter, 0); + } + +-static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- do_setlk_common(req, nodeid, inarg, 1); ++ do_setlk_common(req, nodeid, iter, 1); + } + + static int find_interrupted(struct fuse_session *se, struct fuse_req *req) +@@ -1379,12 +1639,20 @@ static int find_interrupted(struct fuse_session *se, struct fuse_req *req) + return 0; + } + +-static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *)inarg; ++ struct fuse_interrupt_in *arg; + struct fuse_session *se = req->se; + + (void)nodeid; ++ ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + if (se->debug) { + fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n", + (unsigned long long)arg->unique); +@@ -1425,9 +1693,15 @@ static struct fuse_req *check_interrupt(struct fuse_session *se, + } + } + +-static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_bmap_in *arg = (struct fuse_bmap_in *)inarg; ++ struct fuse_bmap_in *arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + + if (req->se->op.bmap) { + req->se->op.bmap(req, nodeid, arg->blocksize, arg->block); +@@ -1436,18 +1710,34 @@ static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *)inarg; +- unsigned int flags = arg->flags; +- void *in_buf = arg->in_size ? PARAM(arg) : NULL; ++ struct fuse_ioctl_in *arg; ++ unsigned int flags; ++ void *in_buf = NULL; + struct fuse_file_info fi; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ ++ flags = arg->flags; + if (flags & FUSE_IOCTL_DIR && !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) { + fuse_reply_err(req, ENOTTY); + return; + } + ++ if (arg->in_size) { ++ in_buf = fuse_mbuf_iter_advance(iter, arg->in_size); ++ if (!in_buf) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ } ++ + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + +@@ -1468,11 +1758,18 @@ void fuse_pollhandle_destroy(struct fuse_pollhandle *ph) + free(ph); + } + +-static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_poll(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_poll_in *arg = (struct fuse_poll_in *)inarg; ++ struct fuse_poll_in *arg; + struct fuse_file_info fi; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.poll_events = arg->events; +@@ -1496,11 +1793,18 @@ static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *)inarg; ++ struct fuse_fallocate_in *arg; + struct fuse_file_info fi; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + +@@ -1513,12 +1817,17 @@ static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + + static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, +- const void *inarg) ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_copy_file_range_in *arg = +- (struct fuse_copy_file_range_in *)inarg; ++ struct fuse_copy_file_range_in *arg; + struct fuse_file_info fi_in, fi_out; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ + memset(&fi_in, 0, sizeof(fi_in)); + fi_in.fh = arg->fh_in; + +@@ -1535,11 +1844,17 @@ static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, + } + } + +-static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_lseek_in *arg = (struct fuse_lseek_in *)inarg; ++ struct fuse_lseek_in *arg; + struct fuse_file_info fi; + ++ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + +@@ -1550,15 +1865,33 @@ static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + } + } + +-static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_init(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { +- struct fuse_init_in *arg = (struct fuse_init_in *)inarg; ++ size_t compat_size = offsetof(struct fuse_init_in, max_readahead); ++ struct fuse_init_in *arg; + struct fuse_init_out outarg; + struct fuse_session *se = req->se; + size_t bufsize = se->bufsize; + size_t outargsize = sizeof(outarg); + + (void)nodeid; ++ ++ /* First consume the old fields... */ ++ arg = fuse_mbuf_iter_advance(iter, compat_size); ++ if (!arg) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ ++ /* ...and now consume the new fields. */ ++ if (arg->major == 7 && arg->minor >= 6) { ++ if (!fuse_mbuf_iter_advance(iter, sizeof(*arg) - compat_size)) { ++ fuse_reply_err(req, EINVAL); ++ return; ++ } ++ } ++ + if (se->debug) { + fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor); + if (arg->major == 7 && arg->minor >= 6) { +@@ -1791,12 +2124,13 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) + send_reply_ok(req, &outarg, outargsize); + } + +-static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) ++static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, ++ struct fuse_mbuf_iter *iter) + { + struct fuse_session *se = req->se; + + (void)nodeid; +- (void)inarg; ++ (void)iter; + + se->got_destroy = 1; + if (se->op.destroy) { +@@ -1976,7 +2310,7 @@ int fuse_req_interrupted(fuse_req_t req) + } + + static struct { +- void (*func)(fuse_req_t, fuse_ino_t, const void *); ++ void (*func)(fuse_req_t, fuse_ino_t, struct fuse_mbuf_iter *); + const char *name; + } fuse_ll_ops[] = { + [FUSE_LOOKUP] = { do_lookup, "LOOKUP" }, +@@ -2060,7 +2394,6 @@ void fuse_session_process_buf_int(struct fuse_session *se, + const struct fuse_buf *buf = bufv->buf; + struct fuse_mbuf_iter iter = FUSE_MBUF_ITER_INIT(buf); + struct fuse_in_header *in; +- const void *inarg; + struct fuse_req *req; + int err; + +@@ -2138,13 +2471,11 @@ void fuse_session_process_buf_int(struct fuse_session *se, + } + } + +- inarg = (void *)&in[1]; + if (in->opcode == FUSE_WRITE && se->op.write_buf) { + do_write_buf(req, in->nodeid, &iter, bufv); + } else { +- fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); ++ fuse_ll_ops[in->opcode].func(req, in->nodeid, &iter); + } +- + return; + + reply_err: diff --git a/0055-virtiofsd-prevent-.-escape-in-lo_do_lookup.patch b/0055-virtiofsd-prevent-.-escape-in-lo_do_lookup.patch new file mode 100644 index 0000000..00894ad --- /dev/null +++ b/0055-virtiofsd-prevent-.-escape-in-lo_do_lookup.patch @@ -0,0 +1,35 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:24 +0000 +Subject: [PATCH] virtiofsd: prevent ".." escape in lo_do_lookup() + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Sergio Lopez +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 854684bc0b3d63eb90b3abdfe471c2e4271ef176) +--- + tools/virtiofsd/passthrough_ll.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index e375406160..79d5966eea 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -624,12 +624,17 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + int res; + int saverr; + struct lo_data *lo = lo_data(req); +- struct lo_inode *inode; ++ struct lo_inode *inode, *dir = lo_inode(req, parent); + + memset(e, 0, sizeof(*e)); + e->attr_timeout = lo->timeout; + e->entry_timeout = lo->timeout; + ++ /* Do not allow escaping root directory */ ++ if (dir == &lo->root && strcmp(name, "..") == 0) { ++ name = "."; ++ } ++ + newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW); + if (newfd == -1) { + goto out_err; diff --git a/0056-virtiofsd-prevent-.-escape-in-lo_do_readdir.patch b/0056-virtiofsd-prevent-.-escape-in-lo_do_readdir.patch new file mode 100644 index 0000000..47845ba --- /dev/null +++ b/0056-virtiofsd-prevent-.-escape-in-lo_do_readdir.patch @@ -0,0 +1,89 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:25 +0000 +Subject: [PATCH] virtiofsd: prevent ".." escape in lo_do_readdir() + +Construct a fake dirent for the root directory's ".." entry. This hides +the parent directory from the FUSE client. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Sergio Lopez +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 752272da2b68a2312f0e11fc5303015a6c3ee1ac) +--- + tools/virtiofsd/passthrough_ll.c | 36 +++++++++++++++++++------------- + 1 file changed, 22 insertions(+), 14 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 79d5966eea..e3d65c3676 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1149,19 +1149,25 @@ out_err: + static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, struct fuse_file_info *fi, int plus) + { ++ struct lo_data *lo = lo_data(req); + struct lo_dirp *d; ++ struct lo_inode *dinode; + char *buf = NULL; + char *p; + size_t rem = size; +- int err = ENOMEM; ++ int err = EBADF; + +- (void)ino; ++ dinode = lo_inode(req, ino); ++ if (!dinode) { ++ goto error; ++ } + + d = lo_dirp(req, fi); + if (!d) { + goto error; + } + ++ err = ENOMEM; + buf = calloc(1, size); + if (!buf) { + goto error; +@@ -1192,15 +1198,21 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + } + nextoff = d->entry->d_off; + name = d->entry->d_name; ++ + fuse_ino_t entry_ino = 0; ++ struct fuse_entry_param e = (struct fuse_entry_param){ ++ .attr.st_ino = d->entry->d_ino, ++ .attr.st_mode = d->entry->d_type << 12, ++ }; ++ ++ /* Hide root's parent directory */ ++ if (dinode == &lo->root && strcmp(name, "..") == 0) { ++ e.attr.st_ino = lo->root.ino; ++ e.attr.st_mode = DT_DIR << 12; ++ } ++ + if (plus) { +- struct fuse_entry_param e; +- if (is_dot_or_dotdot(name)) { +- e = (struct fuse_entry_param){ +- .attr.st_ino = d->entry->d_ino, +- .attr.st_mode = d->entry->d_type << 12, +- }; +- } else { ++ if (!is_dot_or_dotdot(name)) { + err = lo_do_lookup(req, ino, name, &e); + if (err) { + goto error; +@@ -1210,11 +1222,7 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + + entsize = fuse_add_direntry_plus(req, p, rem, name, &e, nextoff); + } else { +- struct stat st = { +- .st_ino = d->entry->d_ino, +- .st_mode = d->entry->d_type << 12, +- }; +- entsize = fuse_add_direntry(req, p, rem, name, &st, nextoff); ++ entsize = fuse_add_direntry(req, p, rem, name, &e.attr, nextoff); + } + if (entsize > rem) { + if (entry_ino != 0) { diff --git a/0057-virtiofsd-use-proc-self-fd-O_PATH-file-descriptor.patch b/0057-virtiofsd-use-proc-self-fd-O_PATH-file-descriptor.patch new file mode 100644 index 0000000..d3473e0 --- /dev/null +++ b/0057-virtiofsd-use-proc-self-fd-O_PATH-file-descriptor.patch @@ -0,0 +1,374 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:26 +0000 +Subject: [PATCH] virtiofsd: use /proc/self/fd/ O_PATH file descriptor +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Sandboxing will remove /proc from the mount namespace so we can no +longer build string paths into "/proc/self/fd/...". + +Keep an O_PATH file descriptor so we can still re-open fds via +/proc/self/fd. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 9f59d175e2ca96f0b87f534dba69ea547dd35945) +--- + tools/virtiofsd/passthrough_ll.c | 130 ++++++++++++++++++++++++------- + 1 file changed, 103 insertions(+), 27 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index e3d65c3676..e2e2211ea1 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -110,6 +110,9 @@ struct lo_data { + struct lo_map ino_map; /* protected by lo->mutex */ + struct lo_map dirp_map; /* protected by lo->mutex */ + struct lo_map fd_map; /* protected by lo->mutex */ ++ ++ /* An O_PATH file descriptor to /proc/self/fd/ */ ++ int proc_self_fd; + }; + + static const struct fuse_opt lo_opts[] = { +@@ -379,9 +382,9 @@ static int lo_parent_and_name(struct lo_data *lo, struct lo_inode *inode, + int res; + + retry: +- sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ sprintf(procname, "%i", inode->fd); + +- res = readlink(procname, path, PATH_MAX); ++ res = readlinkat(lo->proc_self_fd, procname, path, PATH_MAX); + if (res < 0) { + fuse_log(FUSE_LOG_WARNING, "%s: readlink failed: %m\n", __func__); + goto fail_noretry; +@@ -477,9 +480,9 @@ static int utimensat_empty(struct lo_data *lo, struct lo_inode *inode, + } + return res; + } +- sprintf(path, "/proc/self/fd/%i", inode->fd); ++ sprintf(path, "%i", inode->fd); + +- return utimensat(AT_FDCWD, path, tv, 0); ++ return utimensat(lo->proc_self_fd, path, tv, 0); + + fallback: + res = lo_parent_and_name(lo, inode, path, &parent); +@@ -535,8 +538,8 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + if (fi) { + res = fchmod(fd, attr->st_mode); + } else { +- sprintf(procname, "/proc/self/fd/%i", ifd); +- res = chmod(procname, attr->st_mode); ++ sprintf(procname, "%i", ifd); ++ res = fchmodat(lo->proc_self_fd, procname, attr->st_mode, 0); + } + if (res == -1) { + goto out_err; +@@ -552,11 +555,23 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + } + } + if (valid & FUSE_SET_ATTR_SIZE) { ++ int truncfd; ++ + if (fi) { +- res = ftruncate(fd, attr->st_size); ++ truncfd = fd; + } else { +- sprintf(procname, "/proc/self/fd/%i", ifd); +- res = truncate(procname, attr->st_size); ++ sprintf(procname, "%i", ifd); ++ truncfd = openat(lo->proc_self_fd, procname, O_RDWR); ++ if (truncfd < 0) { ++ goto out_err; ++ } ++ } ++ ++ res = ftruncate(truncfd, attr->st_size); ++ if (!fi) { ++ saverr = errno; ++ close(truncfd); ++ errno = saverr; + } + if (res == -1) { + goto out_err; +@@ -874,9 +889,9 @@ static int linkat_empty_nofollow(struct lo_data *lo, struct lo_inode *inode, + return res; + } + +- sprintf(path, "/proc/self/fd/%i", inode->fd); ++ sprintf(path, "%i", inode->fd); + +- return linkat(AT_FDCWD, path, dfd, name, AT_SYMLINK_FOLLOW); ++ return linkat(lo->proc_self_fd, path, dfd, name, AT_SYMLINK_FOLLOW); + + fallback: + res = lo_parent_and_name(lo, inode, path, &parent); +@@ -1404,8 +1419,8 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) + fi->flags &= ~O_APPEND; + } + +- sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino)); +- fd = open(buf, fi->flags & ~O_NOFOLLOW); ++ sprintf(buf, "%i", lo_fd(req, ino)); ++ fd = openat(lo->proc_self_fd, buf, fi->flags & ~O_NOFOLLOW); + if (fd == -1) { + return (void)fuse_reply_err(req, errno); + } +@@ -1458,7 +1473,6 @@ static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi) + { + int res; +- (void)ino; + int fd; + char *buf; + +@@ -1466,12 +1480,14 @@ static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, + (void *)fi); + + if (!fi) { +- res = asprintf(&buf, "/proc/self/fd/%i", lo_fd(req, ino)); ++ struct lo_data *lo = lo_data(req); ++ ++ res = asprintf(&buf, "%i", lo_fd(req, ino)); + if (res == -1) { + return (void)fuse_reply_err(req, errno); + } + +- fd = open(buf, O_RDWR); ++ fd = openat(lo->proc_self_fd, buf, O_RDWR); + free(buf); + if (fd == -1) { + return (void)fuse_reply_err(req, errno); +@@ -1587,11 +1603,13 @@ static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + size_t size) + { ++ struct lo_data *lo = lo_data(req); + char *value = NULL; + char procname[64]; + struct lo_inode *inode; + ssize_t ret; + int saverr; ++ int fd = -1; + + inode = lo_inode(req, ino); + if (!inode) { +@@ -1616,7 +1634,11 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + goto out; + } + +- sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ sprintf(procname, "%i", inode->fd); ++ fd = openat(lo->proc_self_fd, procname, O_RDONLY); ++ if (fd < 0) { ++ goto out_err; ++ } + + if (size) { + value = malloc(size); +@@ -1624,7 +1646,7 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + goto out_err; + } + +- ret = getxattr(procname, name, value, size); ++ ret = fgetxattr(fd, name, value, size); + if (ret == -1) { + goto out_err; + } +@@ -1635,7 +1657,7 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + + fuse_reply_buf(req, value, ret); + } else { +- ret = getxattr(procname, name, NULL, 0); ++ ret = fgetxattr(fd, name, NULL, 0); + if (ret == -1) { + goto out_err; + } +@@ -1644,6 +1666,10 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + } + out_free: + free(value); ++ ++ if (fd >= 0) { ++ close(fd); ++ } + return; + + out_err: +@@ -1655,11 +1681,13 @@ out: + + static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) + { ++ struct lo_data *lo = lo_data(req); + char *value = NULL; + char procname[64]; + struct lo_inode *inode; + ssize_t ret; + int saverr; ++ int fd = -1; + + inode = lo_inode(req, ino); + if (!inode) { +@@ -1683,7 +1711,11 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) + goto out; + } + +- sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ sprintf(procname, "%i", inode->fd); ++ fd = openat(lo->proc_self_fd, procname, O_RDONLY); ++ if (fd < 0) { ++ goto out_err; ++ } + + if (size) { + value = malloc(size); +@@ -1691,7 +1723,7 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) + goto out_err; + } + +- ret = listxattr(procname, value, size); ++ ret = flistxattr(fd, value, size); + if (ret == -1) { + goto out_err; + } +@@ -1702,7 +1734,7 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) + + fuse_reply_buf(req, value, ret); + } else { +- ret = listxattr(procname, NULL, 0); ++ ret = flistxattr(fd, NULL, 0); + if (ret == -1) { + goto out_err; + } +@@ -1711,6 +1743,10 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) + } + out_free: + free(value); ++ ++ if (fd >= 0) { ++ close(fd); ++ } + return; + + out_err: +@@ -1724,9 +1760,11 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + const char *value, size_t size, int flags) + { + char procname[64]; ++ struct lo_data *lo = lo_data(req); + struct lo_inode *inode; + ssize_t ret; + int saverr; ++ int fd = -1; + + inode = lo_inode(req, ino); + if (!inode) { +@@ -1751,21 +1789,31 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + goto out; + } + +- sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ sprintf(procname, "%i", inode->fd); ++ fd = openat(lo->proc_self_fd, procname, O_RDWR); ++ if (fd < 0) { ++ saverr = errno; ++ goto out; ++ } + +- ret = setxattr(procname, name, value, size, flags); ++ ret = fsetxattr(fd, name, value, size, flags); + saverr = ret == -1 ? errno : 0; + + out: ++ if (fd >= 0) { ++ close(fd); ++ } + fuse_reply_err(req, saverr); + } + + static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) + { + char procname[64]; ++ struct lo_data *lo = lo_data(req); + struct lo_inode *inode; + ssize_t ret; + int saverr; ++ int fd = -1; + + inode = lo_inode(req, ino); + if (!inode) { +@@ -1789,12 +1837,20 @@ static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) + goto out; + } + +- sprintf(procname, "/proc/self/fd/%i", inode->fd); ++ sprintf(procname, "%i", inode->fd); ++ fd = openat(lo->proc_self_fd, procname, O_RDWR); ++ if (fd < 0) { ++ saverr = errno; ++ goto out; ++ } + +- ret = removexattr(procname, name); ++ ret = fremovexattr(fd, name); + saverr = ret == -1 ? errno : 0; + + out: ++ if (fd >= 0) { ++ close(fd); ++ } + fuse_reply_err(req, saverr); + } + +@@ -1887,12 +1943,25 @@ static void print_capabilities(void) + printf("}\n"); + } + ++static void setup_proc_self_fd(struct lo_data *lo) ++{ ++ lo->proc_self_fd = open("/proc/self/fd", O_PATH); ++ if (lo->proc_self_fd == -1) { ++ fuse_log(FUSE_LOG_ERR, "open(/proc/self/fd, O_PATH): %m\n"); ++ exit(1); ++ } ++} ++ + int main(int argc, char *argv[]) + { + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); + struct fuse_session *se; + struct fuse_cmdline_opts opts; +- struct lo_data lo = { .debug = 0, .writeback = 0 }; ++ struct lo_data lo = { ++ .debug = 0, ++ .writeback = 0, ++ .proc_self_fd = -1, ++ }; + struct lo_map_elem *root_elem; + int ret = -1; + +@@ -2003,6 +2072,9 @@ int main(int argc, char *argv[]) + + fuse_daemonize(opts.foreground); + ++ /* Must be after daemonize to get the right /proc/self/fd */ ++ setup_proc_self_fd(&lo); ++ + /* Block until ctrl+c or fusermount -u */ + ret = virtio_loop(se); + +@@ -2018,6 +2090,10 @@ err_out1: + lo_map_destroy(&lo.dirp_map); + lo_map_destroy(&lo.ino_map); + ++ if (lo.proc_self_fd >= 0) { ++ close(lo.proc_self_fd); ++ } ++ + if (lo.root.fd >= 0) { + close(lo.root.fd); + } diff --git a/0058-virtiofsd-sandbox-mount-namespace.patch b/0058-virtiofsd-sandbox-mount-namespace.patch new file mode 100644 index 0000000..76fb892 --- /dev/null +++ b/0058-virtiofsd-sandbox-mount-namespace.patch @@ -0,0 +1,150 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:27 +0000 +Subject: [PATCH] virtiofsd: sandbox mount namespace +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Use a mount namespace with the shared directory tree mounted at "/" and +no other mounts. + +This prevents symlink escape attacks because symlink targets are +resolved only against the shared directory and cannot go outside it. + +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Peng Tao +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 5baa3b8e95064c2434bd9e2f312edd5e9ae275dc) +--- + tools/virtiofsd/passthrough_ll.c | 89 ++++++++++++++++++++++++++++++++ + 1 file changed, 89 insertions(+) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index e2e2211ea1..0570453eef 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -50,6 +50,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1943,6 +1944,58 @@ static void print_capabilities(void) + printf("}\n"); + } + ++/* This magic is based on lxc's lxc_pivot_root() */ ++static void setup_pivot_root(const char *source) ++{ ++ int oldroot; ++ int newroot; ++ ++ oldroot = open("/", O_DIRECTORY | O_RDONLY | O_CLOEXEC); ++ if (oldroot < 0) { ++ fuse_log(FUSE_LOG_ERR, "open(/): %m\n"); ++ exit(1); ++ } ++ ++ newroot = open(source, O_DIRECTORY | O_RDONLY | O_CLOEXEC); ++ if (newroot < 0) { ++ fuse_log(FUSE_LOG_ERR, "open(%s): %m\n", source); ++ exit(1); ++ } ++ ++ if (fchdir(newroot) < 0) { ++ fuse_log(FUSE_LOG_ERR, "fchdir(newroot): %m\n"); ++ exit(1); ++ } ++ ++ if (syscall(__NR_pivot_root, ".", ".") < 0) { ++ fuse_log(FUSE_LOG_ERR, "pivot_root(., .): %m\n"); ++ exit(1); ++ } ++ ++ if (fchdir(oldroot) < 0) { ++ fuse_log(FUSE_LOG_ERR, "fchdir(oldroot): %m\n"); ++ exit(1); ++ } ++ ++ if (mount("", ".", "", MS_SLAVE | MS_REC, NULL) < 0) { ++ fuse_log(FUSE_LOG_ERR, "mount(., MS_SLAVE | MS_REC): %m\n"); ++ exit(1); ++ } ++ ++ if (umount2(".", MNT_DETACH) < 0) { ++ fuse_log(FUSE_LOG_ERR, "umount2(., MNT_DETACH): %m\n"); ++ exit(1); ++ } ++ ++ if (fchdir(newroot) < 0) { ++ fuse_log(FUSE_LOG_ERR, "fchdir(newroot): %m\n"); ++ exit(1); ++ } ++ ++ close(newroot); ++ close(oldroot); ++} ++ + static void setup_proc_self_fd(struct lo_data *lo) + { + lo->proc_self_fd = open("/proc/self/fd", O_PATH); +@@ -1952,6 +2005,39 @@ static void setup_proc_self_fd(struct lo_data *lo) + } + } + ++/* ++ * Make the source directory our root so symlinks cannot escape and no other ++ * files are accessible. ++ */ ++static void setup_mount_namespace(const char *source) ++{ ++ if (unshare(CLONE_NEWNS) != 0) { ++ fuse_log(FUSE_LOG_ERR, "unshare(CLONE_NEWNS): %m\n"); ++ exit(1); ++ } ++ ++ if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) < 0) { ++ fuse_log(FUSE_LOG_ERR, "mount(/, MS_REC|MS_PRIVATE): %m\n"); ++ exit(1); ++ } ++ ++ if (mount(source, source, NULL, MS_BIND, NULL) < 0) { ++ fuse_log(FUSE_LOG_ERR, "mount(%s, %s, MS_BIND): %m\n", source, source); ++ exit(1); ++ } ++ ++ setup_pivot_root(source); ++} ++ ++/* ++ * Lock down this process to prevent access to other processes or files outside ++ * source directory. This reduces the impact of arbitrary code execution bugs. ++ */ ++static void setup_sandbox(struct lo_data *lo) ++{ ++ setup_mount_namespace(lo->source); ++} ++ + int main(int argc, char *argv[]) + { + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); +@@ -2052,6 +2138,7 @@ int main(int argc, char *argv[]) + } + + lo.root.fd = open(lo.source, O_PATH); ++ + if (lo.root.fd == -1) { + fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", lo.source); + exit(1); +@@ -2075,6 +2162,8 @@ int main(int argc, char *argv[]) + /* Must be after daemonize to get the right /proc/self/fd */ + setup_proc_self_fd(&lo); + ++ setup_sandbox(&lo); ++ + /* Block until ctrl+c or fusermount -u */ + ret = virtio_loop(se); + diff --git a/0059-virtiofsd-move-to-an-empty-network-namespace.patch b/0059-virtiofsd-move-to-an-empty-network-namespace.patch new file mode 100644 index 0000000..043aa5f --- /dev/null +++ b/0059-virtiofsd-move-to-an-empty-network-namespace.patch @@ -0,0 +1,50 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:28 +0000 +Subject: [PATCH] virtiofsd: move to an empty network namespace +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If the process is compromised there should be no network access. Use an +empty network namespace to sandbox networking. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit d74830d12ae233186ff74ddf64c552d26bb39e50) +--- + tools/virtiofsd/passthrough_ll.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 0570453eef..27ab328722 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1944,6 +1944,19 @@ static void print_capabilities(void) + printf("}\n"); + } + ++/* ++ * Called after our UNIX domain sockets have been created, now we can move to ++ * an empty network namespace to prevent TCP/IP and other network activity in ++ * case this process is compromised. ++ */ ++static void setup_net_namespace(void) ++{ ++ if (unshare(CLONE_NEWNET) != 0) { ++ fuse_log(FUSE_LOG_ERR, "unshare(CLONE_NEWNET): %m\n"); ++ exit(1); ++ } ++} ++ + /* This magic is based on lxc's lxc_pivot_root() */ + static void setup_pivot_root(const char *source) + { +@@ -2035,6 +2048,7 @@ static void setup_mount_namespace(const char *source) + */ + static void setup_sandbox(struct lo_data *lo) + { ++ setup_net_namespace(); + setup_mount_namespace(lo->source); + } + diff --git a/0060-virtiofsd-move-to-a-new-pid-namespace.patch b/0060-virtiofsd-move-to-a-new-pid-namespace.patch new file mode 100644 index 0000000..043f962 --- /dev/null +++ b/0060-virtiofsd-move-to-a-new-pid-namespace.patch @@ -0,0 +1,207 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:29 +0000 +Subject: [PATCH] virtiofsd: move to a new pid namespace +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +virtiofsd needs access to /proc/self/fd. Let's move to a new pid +namespace so that a compromised process cannot see another other +processes running on the system. + +One wrinkle in this approach: unshare(CLONE_NEWPID) affects *child* +processes and not the current process. Therefore we need to fork the +pid 1 process that will actually run virtiofsd and leave a parent in +waitpid(2). This is not the same thing as daemonization and parent +processes should not notice a difference. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 8e1d4ef231d8327be219f7aea7aa15d181375bbc) +--- + tools/virtiofsd/passthrough_ll.c | 134 ++++++++++++++++++++----------- + 1 file changed, 86 insertions(+), 48 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 27ab328722..0947d14e5b 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -51,7 +51,10 @@ + #include + #include + #include ++#include + #include ++#include ++#include + #include + #include + +@@ -1945,24 +1948,95 @@ static void print_capabilities(void) + } + + /* +- * Called after our UNIX domain sockets have been created, now we can move to +- * an empty network namespace to prevent TCP/IP and other network activity in +- * case this process is compromised. ++ * Move to a new mount, net, and pid namespaces to isolate this process. + */ +-static void setup_net_namespace(void) ++static void setup_namespaces(struct lo_data *lo, struct fuse_session *se) + { +- if (unshare(CLONE_NEWNET) != 0) { +- fuse_log(FUSE_LOG_ERR, "unshare(CLONE_NEWNET): %m\n"); ++ pid_t child; ++ ++ /* ++ * Create a new pid namespace for *child* processes. We'll have to ++ * fork in order to enter the new pid namespace. A new mount namespace ++ * is also needed so that we can remount /proc for the new pid ++ * namespace. ++ * ++ * Our UNIX domain sockets have been created. Now we can move to ++ * an empty network namespace to prevent TCP/IP and other network ++ * activity in case this process is compromised. ++ */ ++ if (unshare(CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET) != 0) { ++ fuse_log(FUSE_LOG_ERR, "unshare(CLONE_NEWPID | CLONE_NEWNS): %m\n"); ++ exit(1); ++ } ++ ++ child = fork(); ++ if (child < 0) { ++ fuse_log(FUSE_LOG_ERR, "fork() failed: %m\n"); ++ exit(1); ++ } ++ if (child > 0) { ++ pid_t waited; ++ int wstatus; ++ ++ /* The parent waits for the child */ ++ do { ++ waited = waitpid(child, &wstatus, 0); ++ } while (waited < 0 && errno == EINTR && !se->exited); ++ ++ /* We were terminated by a signal, see fuse_signals.c */ ++ if (se->exited) { ++ exit(0); ++ } ++ ++ if (WIFEXITED(wstatus)) { ++ exit(WEXITSTATUS(wstatus)); ++ } ++ ++ exit(1); ++ } ++ ++ /* Send us SIGTERM when the parent thread terminates, see prctl(2) */ ++ prctl(PR_SET_PDEATHSIG, SIGTERM); ++ ++ /* ++ * If the mounts have shared propagation then we want to opt out so our ++ * mount changes don't affect the parent mount namespace. ++ */ ++ if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) < 0) { ++ fuse_log(FUSE_LOG_ERR, "mount(/, MS_REC|MS_SLAVE): %m\n"); ++ exit(1); ++ } ++ ++ /* The child must remount /proc to use the new pid namespace */ ++ if (mount("proc", "/proc", "proc", ++ MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_RELATIME, NULL) < 0) { ++ fuse_log(FUSE_LOG_ERR, "mount(/proc): %m\n"); ++ exit(1); ++ } ++ ++ /* Now we can get our /proc/self/fd directory file descriptor */ ++ lo->proc_self_fd = open("/proc/self/fd", O_PATH); ++ if (lo->proc_self_fd == -1) { ++ fuse_log(FUSE_LOG_ERR, "open(/proc/self/fd, O_PATH): %m\n"); + exit(1); + } + } + +-/* This magic is based on lxc's lxc_pivot_root() */ +-static void setup_pivot_root(const char *source) ++/* ++ * Make the source directory our root so symlinks cannot escape and no other ++ * files are accessible. Assumes unshare(CLONE_NEWNS) was already called. ++ */ ++static void setup_mounts(const char *source) + { + int oldroot; + int newroot; + ++ if (mount(source, source, NULL, MS_BIND, NULL) < 0) { ++ fuse_log(FUSE_LOG_ERR, "mount(%s, %s, MS_BIND): %m\n", source, source); ++ exit(1); ++ } ++ ++ /* This magic is based on lxc's lxc_pivot_root() */ + oldroot = open("/", O_DIRECTORY | O_RDONLY | O_CLOEXEC); + if (oldroot < 0) { + fuse_log(FUSE_LOG_ERR, "open(/): %m\n"); +@@ -2009,47 +2083,14 @@ static void setup_pivot_root(const char *source) + close(oldroot); + } + +-static void setup_proc_self_fd(struct lo_data *lo) +-{ +- lo->proc_self_fd = open("/proc/self/fd", O_PATH); +- if (lo->proc_self_fd == -1) { +- fuse_log(FUSE_LOG_ERR, "open(/proc/self/fd, O_PATH): %m\n"); +- exit(1); +- } +-} +- +-/* +- * Make the source directory our root so symlinks cannot escape and no other +- * files are accessible. +- */ +-static void setup_mount_namespace(const char *source) +-{ +- if (unshare(CLONE_NEWNS) != 0) { +- fuse_log(FUSE_LOG_ERR, "unshare(CLONE_NEWNS): %m\n"); +- exit(1); +- } +- +- if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) < 0) { +- fuse_log(FUSE_LOG_ERR, "mount(/, MS_REC|MS_PRIVATE): %m\n"); +- exit(1); +- } +- +- if (mount(source, source, NULL, MS_BIND, NULL) < 0) { +- fuse_log(FUSE_LOG_ERR, "mount(%s, %s, MS_BIND): %m\n", source, source); +- exit(1); +- } +- +- setup_pivot_root(source); +-} +- + /* + * Lock down this process to prevent access to other processes or files outside + * source directory. This reduces the impact of arbitrary code execution bugs. + */ +-static void setup_sandbox(struct lo_data *lo) ++static void setup_sandbox(struct lo_data *lo, struct fuse_session *se) + { +- setup_net_namespace(); +- setup_mount_namespace(lo->source); ++ setup_namespaces(lo, se); ++ setup_mounts(lo->source); + } + + int main(int argc, char *argv[]) +@@ -2173,10 +2214,7 @@ int main(int argc, char *argv[]) + + fuse_daemonize(opts.foreground); + +- /* Must be after daemonize to get the right /proc/self/fd */ +- setup_proc_self_fd(&lo); +- +- setup_sandbox(&lo); ++ setup_sandbox(&lo, se); + + /* Block until ctrl+c or fusermount -u */ + ret = virtio_loop(se); diff --git a/0061-virtiofsd-add-seccomp-whitelist.patch b/0061-virtiofsd-add-seccomp-whitelist.patch new file mode 100644 index 0000000..124cd4e --- /dev/null +++ b/0061-virtiofsd-add-seccomp-whitelist.patch @@ -0,0 +1,265 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:30 +0000 +Subject: [PATCH] virtiofsd: add seccomp whitelist + +Only allow system calls that are needed by virtiofsd. All other system +calls cause SIGSYS to be directed at the thread and the process will +coredump. + +Restricting system calls reduces the kernel attack surface and limits +what the process can do when compromised. + +Signed-off-by: Stefan Hajnoczi +with additional entries by: +Signed-off-by: Ganesh Maharaj Mahalingam +Signed-off-by: Masayoshi Mizuma +Signed-off-by: Misono Tomohiro +Signed-off-by: piaojun +Signed-off-by: Vivek Goyal +Signed-off-by: Eric Ren +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 4f8bde99c175ffd86b5125098a4707d43f5e80c6) +--- + Makefile | 5 +- + tools/virtiofsd/Makefile.objs | 5 +- + tools/virtiofsd/passthrough_ll.c | 2 + + tools/virtiofsd/seccomp.c | 151 +++++++++++++++++++++++++++++++ + tools/virtiofsd/seccomp.h | 14 +++ + 5 files changed, 174 insertions(+), 3 deletions(-) + create mode 100644 tools/virtiofsd/seccomp.c + create mode 100644 tools/virtiofsd/seccomp.h + +diff --git a/Makefile b/Makefile +index aebb57aed8..9a17e34603 100644 +--- a/Makefile ++++ b/Makefile +@@ -330,7 +330,7 @@ endif + endif + endif + +-ifdef CONFIG_LINUX ++ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP),yy) + HELPERS-y += virtiofsd$(EXESUF) + vhost-user-json-y += tools/virtiofsd/50-qemu-virtiofsd.json + endif +@@ -680,7 +680,8 @@ rdmacm-mux$(EXESUF): LIBS += "-libumad" + rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) + $(call LINK, $^) + +-ifdef CONFIG_LINUX # relies on Linux-specific syscalls ++# relies on Linux-specific syscalls ++ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP),yy) + virtiofsd$(EXESUF): $(virtiofsd-obj-y) libvhost-user.a $(COMMON_LDADDS) + $(call LINK, $^) + endif +diff --git a/tools/virtiofsd/Makefile.objs b/tools/virtiofsd/Makefile.objs +index 45a807500d..076f667e46 100644 +--- a/tools/virtiofsd/Makefile.objs ++++ b/tools/virtiofsd/Makefile.objs +@@ -5,5 +5,8 @@ virtiofsd-obj-y = buffer.o \ + fuse_signals.o \ + fuse_virtio.o \ + helper.o \ +- passthrough_ll.o ++ passthrough_ll.o \ ++ seccomp.o + ++seccomp.o-cflags := $(SECCOMP_CFLAGS) ++seccomp.o-libs := $(SECCOMP_LIBS) +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 0947d14e5b..bd8925bd83 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -59,6 +59,7 @@ + #include + + #include "passthrough_helpers.h" ++#include "seccomp.h" + + struct lo_map_elem { + union { +@@ -2091,6 +2092,7 @@ static void setup_sandbox(struct lo_data *lo, struct fuse_session *se) + { + setup_namespaces(lo, se); + setup_mounts(lo->source); ++ setup_seccomp(); + } + + int main(int argc, char *argv[]) +diff --git a/tools/virtiofsd/seccomp.c b/tools/virtiofsd/seccomp.c +new file mode 100644 +index 0000000000..691fb63dea +--- /dev/null ++++ b/tools/virtiofsd/seccomp.c +@@ -0,0 +1,151 @@ ++/* ++ * Seccomp sandboxing for virtiofsd ++ * ++ * Copyright (C) 2019 Red Hat, Inc. ++ * ++ * SPDX-License-Identifier: GPL-2.0-or-later ++ */ ++ ++#include "qemu/osdep.h" ++#include "seccomp.h" ++#include "fuse_i.h" ++#include "fuse_log.h" ++#include ++#include ++#include ++#include ++ ++/* Bodge for libseccomp 2.4.2 which broke ppoll */ ++#if !defined(__SNR_ppoll) && defined(__SNR_brk) ++#ifdef __NR_ppoll ++#define __SNR_ppoll __NR_ppoll ++#else ++#define __SNR_ppoll __PNR_ppoll ++#endif ++#endif ++ ++static const int syscall_whitelist[] = { ++ /* TODO ireg sem*() syscalls */ ++ SCMP_SYS(brk), ++ SCMP_SYS(capget), /* For CAP_FSETID */ ++ SCMP_SYS(capset), ++ SCMP_SYS(clock_gettime), ++ SCMP_SYS(clone), ++#ifdef __NR_clone3 ++ SCMP_SYS(clone3), ++#endif ++ SCMP_SYS(close), ++ SCMP_SYS(copy_file_range), ++ SCMP_SYS(dup), ++ SCMP_SYS(eventfd2), ++ SCMP_SYS(exit), ++ SCMP_SYS(exit_group), ++ SCMP_SYS(fallocate), ++ SCMP_SYS(fchmodat), ++ SCMP_SYS(fchownat), ++ SCMP_SYS(fcntl), ++ SCMP_SYS(fdatasync), ++ SCMP_SYS(fgetxattr), ++ SCMP_SYS(flistxattr), ++ SCMP_SYS(flock), ++ SCMP_SYS(fremovexattr), ++ SCMP_SYS(fsetxattr), ++ SCMP_SYS(fstat), ++ SCMP_SYS(fstatfs), ++ SCMP_SYS(fsync), ++ SCMP_SYS(ftruncate), ++ SCMP_SYS(futex), ++ SCMP_SYS(getdents), ++ SCMP_SYS(getdents64), ++ SCMP_SYS(getegid), ++ SCMP_SYS(geteuid), ++ SCMP_SYS(getpid), ++ SCMP_SYS(gettid), ++ SCMP_SYS(gettimeofday), ++ SCMP_SYS(linkat), ++ SCMP_SYS(lseek), ++ SCMP_SYS(madvise), ++ SCMP_SYS(mkdirat), ++ SCMP_SYS(mknodat), ++ SCMP_SYS(mmap), ++ SCMP_SYS(mprotect), ++ SCMP_SYS(mremap), ++ SCMP_SYS(munmap), ++ SCMP_SYS(newfstatat), ++ SCMP_SYS(open), ++ SCMP_SYS(openat), ++ SCMP_SYS(ppoll), ++ SCMP_SYS(prctl), /* TODO restrict to just PR_SET_NAME? */ ++ SCMP_SYS(preadv), ++ SCMP_SYS(pread64), ++ SCMP_SYS(pwritev), ++ SCMP_SYS(pwrite64), ++ SCMP_SYS(read), ++ SCMP_SYS(readlinkat), ++ SCMP_SYS(recvmsg), ++ SCMP_SYS(renameat), ++ SCMP_SYS(renameat2), ++ SCMP_SYS(rt_sigaction), ++ SCMP_SYS(rt_sigprocmask), ++ SCMP_SYS(rt_sigreturn), ++ SCMP_SYS(sendmsg), ++ SCMP_SYS(setresgid), ++ SCMP_SYS(setresuid), ++#ifdef __NR_setresgid32 ++ SCMP_SYS(setresgid32), ++#endif ++#ifdef __NR_setresuid32 ++ SCMP_SYS(setresuid32), ++#endif ++ SCMP_SYS(set_robust_list), ++ SCMP_SYS(symlinkat), ++ SCMP_SYS(time), /* Rarely needed, except on static builds */ ++ SCMP_SYS(tgkill), ++ SCMP_SYS(unlinkat), ++ SCMP_SYS(utimensat), ++ SCMP_SYS(write), ++ SCMP_SYS(writev), ++}; ++ ++void setup_seccomp(void) ++{ ++ scmp_filter_ctx ctx; ++ size_t i; ++ ++#ifdef SCMP_ACT_KILL_PROCESS ++ ctx = seccomp_init(SCMP_ACT_KILL_PROCESS); ++ /* Handle a newer libseccomp but an older kernel */ ++ if (!ctx && errno == EOPNOTSUPP) { ++ ctx = seccomp_init(SCMP_ACT_TRAP); ++ } ++#else ++ ctx = seccomp_init(SCMP_ACT_TRAP); ++#endif ++ if (!ctx) { ++ fuse_log(FUSE_LOG_ERR, "seccomp_init() failed\n"); ++ exit(1); ++ } ++ ++ for (i = 0; i < G_N_ELEMENTS(syscall_whitelist); i++) { ++ if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, ++ syscall_whitelist[i], 0) != 0) { ++ fuse_log(FUSE_LOG_ERR, "seccomp_rule_add syscall %d", ++ syscall_whitelist[i]); ++ exit(1); ++ } ++ } ++ ++ /* libvhost-user calls this for post-copy migration, we don't need it */ ++ if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOSYS), ++ SCMP_SYS(userfaultfd), 0) != 0) { ++ fuse_log(FUSE_LOG_ERR, "seccomp_rule_add userfaultfd failed\n"); ++ exit(1); ++ } ++ ++ if (seccomp_load(ctx) < 0) { ++ fuse_log(FUSE_LOG_ERR, "seccomp_load() failed\n"); ++ exit(1); ++ } ++ ++ seccomp_release(ctx); ++} +diff --git a/tools/virtiofsd/seccomp.h b/tools/virtiofsd/seccomp.h +new file mode 100644 +index 0000000000..86bce72652 +--- /dev/null ++++ b/tools/virtiofsd/seccomp.h +@@ -0,0 +1,14 @@ ++/* ++ * Seccomp sandboxing for virtiofsd ++ * ++ * Copyright (C) 2019 Red Hat, Inc. ++ * ++ * SPDX-License-Identifier: GPL-2.0-or-later ++ */ ++ ++#ifndef VIRTIOFSD_SECCOMP_H ++#define VIRTIOFSD_SECCOMP_H ++ ++void setup_seccomp(void); ++ ++#endif /* VIRTIOFSD_SECCOMP_H */ diff --git a/0062-virtiofsd-Parse-flag-FUSE_WRITE_KILL_PRIV.patch b/0062-virtiofsd-Parse-flag-FUSE_WRITE_KILL_PRIV.patch new file mode 100644 index 0000000..1670c83 --- /dev/null +++ b/0062-virtiofsd-Parse-flag-FUSE_WRITE_KILL_PRIV.patch @@ -0,0 +1,57 @@ +From: Vivek Goyal +Date: Mon, 27 Jan 2020 19:01:31 +0000 +Subject: [PATCH] virtiofsd: Parse flag FUSE_WRITE_KILL_PRIV + +Caller can set FUSE_WRITE_KILL_PRIV in write_flags. Parse it and pass it +to the filesystem. + +Signed-off-by: Vivek Goyal +Reviewed-by: Misono Tomohiro +Reviewed-by: Sergio Lopez +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit f779bc5265e7e7abb13a03d4bfbc74151afc15c2) +--- + tools/virtiofsd/fuse_common.h | 6 +++++- + tools/virtiofsd/fuse_lowlevel.c | 4 +++- + 2 files changed, 8 insertions(+), 2 deletions(-) + +diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h +index f8f6433743..686c42c0a5 100644 +--- a/tools/virtiofsd/fuse_common.h ++++ b/tools/virtiofsd/fuse_common.h +@@ -93,8 +93,12 @@ struct fuse_file_info { + */ + unsigned int cache_readdir:1; + ++ /* Indicates that suid/sgid bits should be removed upon write */ ++ unsigned int kill_priv:1; ++ ++ + /** Padding. Reserved for future use*/ +- unsigned int padding:25; ++ unsigned int padding:24; + unsigned int padding2:32; + + /* +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 02e1d83038..2d6dc5a680 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -1142,6 +1142,7 @@ static void do_write(fuse_req_t req, fuse_ino_t nodeid, + memset(&fi, 0, sizeof(fi)); + fi.fh = arg->fh; + fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0; ++ fi.kill_priv = !!(arg->write_flags & FUSE_WRITE_KILL_PRIV); + + fi.lock_owner = arg->lock_owner; + fi.flags = arg->flags; +@@ -1177,7 +1178,8 @@ static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, + fi.lock_owner = arg->lock_owner; + fi.flags = arg->flags; + fi.fh = arg->fh; +- fi.writepage = arg->write_flags & FUSE_WRITE_CACHE; ++ fi.writepage = !!(arg->write_flags & FUSE_WRITE_CACHE); ++ fi.kill_priv = !!(arg->write_flags & FUSE_WRITE_KILL_PRIV); + + if (ibufv->count == 1) { + assert(!(tmpbufv.buf[0].flags & FUSE_BUF_IS_FD)); diff --git a/0063-virtiofsd-cap-ng-helpers.patch b/0063-virtiofsd-cap-ng-helpers.patch new file mode 100644 index 0000000..7bc8ca9 --- /dev/null +++ b/0063-virtiofsd-cap-ng-helpers.patch @@ -0,0 +1,159 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:32 +0000 +Subject: [PATCH] virtiofsd: cap-ng helpers +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +libcap-ng reads /proc during capng_get_caps_process, and virtiofsd's +sandboxing doesn't have /proc mounted; thus we have to do the +caps read before we sandbox it and save/restore the state. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 2405f3c0d19eb4d516a88aa4e5c54e5f9c6bbea3) +--- + Makefile | 4 +- + tools/virtiofsd/passthrough_ll.c | 72 ++++++++++++++++++++++++++++++++ + 2 files changed, 74 insertions(+), 2 deletions(-) + +diff --git a/Makefile b/Makefile +index 9a17e34603..14793cad11 100644 +--- a/Makefile ++++ b/Makefile +@@ -330,7 +330,7 @@ endif + endif + endif + +-ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP),yy) ++ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy) + HELPERS-y += virtiofsd$(EXESUF) + vhost-user-json-y += tools/virtiofsd/50-qemu-virtiofsd.json + endif +@@ -681,7 +681,7 @@ rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) + $(call LINK, $^) + + # relies on Linux-specific syscalls +-ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP),yy) ++ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy) + virtiofsd$(EXESUF): $(virtiofsd-obj-y) libvhost-user.a $(COMMON_LDADDS) + $(call LINK, $^) + endif +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index bd8925bd83..97e7c75667 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -39,6 +39,7 @@ + #include "fuse_virtio.h" + #include "fuse_lowlevel.h" + #include ++#include + #include + #include + #include +@@ -139,6 +140,13 @@ static const struct fuse_opt lo_opts[] = { + + static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n); + ++static struct { ++ pthread_mutex_t mutex; ++ void *saved; ++} cap; ++/* That we loaded cap-ng in the current thread from the saved */ ++static __thread bool cap_loaded = 0; ++ + static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st); + + static int is_dot_or_dotdot(const char *name) +@@ -162,6 +170,37 @@ static struct lo_data *lo_data(fuse_req_t req) + return (struct lo_data *)fuse_req_userdata(req); + } + ++/* ++ * Load capng's state from our saved state if the current thread ++ * hadn't previously been loaded. ++ * returns 0 on success ++ */ ++static int load_capng(void) ++{ ++ if (!cap_loaded) { ++ pthread_mutex_lock(&cap.mutex); ++ capng_restore_state(&cap.saved); ++ /* ++ * restore_state free's the saved copy ++ * so make another. ++ */ ++ cap.saved = capng_save_state(); ++ if (!cap.saved) { ++ fuse_log(FUSE_LOG_ERR, "capng_save_state (thread)\n"); ++ return -EINVAL; ++ } ++ pthread_mutex_unlock(&cap.mutex); ++ ++ /* ++ * We want to use the loaded state for our pid, ++ * not the original ++ */ ++ capng_setpid(syscall(SYS_gettid)); ++ cap_loaded = true; ++ } ++ return 0; ++} ++ + static void lo_map_init(struct lo_map *map) + { + map->elems = NULL; +@@ -2023,6 +2062,35 @@ static void setup_namespaces(struct lo_data *lo, struct fuse_session *se) + } + } + ++/* ++ * Capture the capability state, we'll need to restore this for individual ++ * threads later; see load_capng. ++ */ ++static void setup_capng(void) ++{ ++ /* Note this accesses /proc so has to happen before the sandbox */ ++ if (capng_get_caps_process()) { ++ fuse_log(FUSE_LOG_ERR, "capng_get_caps_process\n"); ++ exit(1); ++ } ++ pthread_mutex_init(&cap.mutex, NULL); ++ pthread_mutex_lock(&cap.mutex); ++ cap.saved = capng_save_state(); ++ if (!cap.saved) { ++ fuse_log(FUSE_LOG_ERR, "capng_save_state\n"); ++ exit(1); ++ } ++ pthread_mutex_unlock(&cap.mutex); ++} ++ ++static void cleanup_capng(void) ++{ ++ free(cap.saved); ++ cap.saved = NULL; ++ pthread_mutex_destroy(&cap.mutex); ++} ++ ++ + /* + * Make the source directory our root so symlinks cannot escape and no other + * files are accessible. Assumes unshare(CLONE_NEWNS) was already called. +@@ -2216,12 +2284,16 @@ int main(int argc, char *argv[]) + + fuse_daemonize(opts.foreground); + ++ /* Must be before sandbox since it wants /proc */ ++ setup_capng(); ++ + setup_sandbox(&lo, se); + + /* Block until ctrl+c or fusermount -u */ + ret = virtio_loop(se); + + fuse_session_unmount(se); ++ cleanup_capng(); + err_out3: + fuse_remove_signal_handlers(se); + err_out2: diff --git a/0064-virtiofsd-Drop-CAP_FSETID-if-client-asked-for-it.patch b/0064-virtiofsd-Drop-CAP_FSETID-if-client-asked-for-it.patch new file mode 100644 index 0000000..13438cc --- /dev/null +++ b/0064-virtiofsd-Drop-CAP_FSETID-if-client-asked-for-it.patch @@ -0,0 +1,156 @@ +From: Vivek Goyal +Date: Mon, 27 Jan 2020 19:01:33 +0000 +Subject: [PATCH] virtiofsd: Drop CAP_FSETID if client asked for it + +If client requested killing setuid/setgid bits on file being written, drop +CAP_FSETID capability so that setuid/setgid bits are cleared upon write +automatically. + +pjdfstest chown/12.t needs this. + +Signed-off-by: Vivek Goyal + dgilbert: reworked for libcap-ng +Reviewed-by: Misono Tomohiro +Reviewed-by: Sergio Lopez +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit ee88465224b3aed2596049caa28f86cbe0d5a3d0) +--- + tools/virtiofsd/passthrough_ll.c | 105 +++++++++++++++++++++++++++++++ + 1 file changed, 105 insertions(+) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 97e7c75667..d53cb1e005 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -201,6 +201,91 @@ static int load_capng(void) + return 0; + } + ++/* ++ * Helpers for dropping and regaining effective capabilities. Returns 0 ++ * on success, error otherwise ++ */ ++static int drop_effective_cap(const char *cap_name, bool *cap_dropped) ++{ ++ int cap, ret; ++ ++ cap = capng_name_to_capability(cap_name); ++ if (cap < 0) { ++ ret = errno; ++ fuse_log(FUSE_LOG_ERR, "capng_name_to_capability(%s) failed:%s\n", ++ cap_name, strerror(errno)); ++ goto out; ++ } ++ ++ if (load_capng()) { ++ ret = errno; ++ fuse_log(FUSE_LOG_ERR, "load_capng() failed\n"); ++ goto out; ++ } ++ ++ /* We dont have this capability in effective set already. */ ++ if (!capng_have_capability(CAPNG_EFFECTIVE, cap)) { ++ ret = 0; ++ goto out; ++ } ++ ++ if (capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, cap)) { ++ ret = errno; ++ fuse_log(FUSE_LOG_ERR, "capng_update(DROP,) failed\n"); ++ goto out; ++ } ++ ++ if (capng_apply(CAPNG_SELECT_CAPS)) { ++ ret = errno; ++ fuse_log(FUSE_LOG_ERR, "drop:capng_apply() failed\n"); ++ goto out; ++ } ++ ++ ret = 0; ++ if (cap_dropped) { ++ *cap_dropped = true; ++ } ++ ++out: ++ return ret; ++} ++ ++static int gain_effective_cap(const char *cap_name) ++{ ++ int cap; ++ int ret = 0; ++ ++ cap = capng_name_to_capability(cap_name); ++ if (cap < 0) { ++ ret = errno; ++ fuse_log(FUSE_LOG_ERR, "capng_name_to_capability(%s) failed:%s\n", ++ cap_name, strerror(errno)); ++ goto out; ++ } ++ ++ if (load_capng()) { ++ ret = errno; ++ fuse_log(FUSE_LOG_ERR, "load_capng() failed\n"); ++ goto out; ++ } ++ ++ if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, cap)) { ++ ret = errno; ++ fuse_log(FUSE_LOG_ERR, "capng_update(ADD,) failed\n"); ++ goto out; ++ } ++ ++ if (capng_apply(CAPNG_SELECT_CAPS)) { ++ ret = errno; ++ fuse_log(FUSE_LOG_ERR, "gain:capng_apply() failed\n"); ++ goto out; ++ } ++ ret = 0; ++ ++out: ++ return ret; ++} ++ + static void lo_map_init(struct lo_map *map) + { + map->elems = NULL; +@@ -1577,6 +1662,7 @@ static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, + (void)ino; + ssize_t res; + struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); ++ bool cap_fsetid_dropped = false; + + out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; + out_buf.buf[0].fd = lo_fi_fd(req, fi); +@@ -1588,12 +1674,31 @@ static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, + out_buf.buf[0].size, (unsigned long)off); + } + ++ /* ++ * If kill_priv is set, drop CAP_FSETID which should lead to kernel ++ * clearing setuid/setgid on file. ++ */ ++ if (fi->kill_priv) { ++ res = drop_effective_cap("FSETID", &cap_fsetid_dropped); ++ if (res != 0) { ++ fuse_reply_err(req, res); ++ return; ++ } ++ } ++ + res = fuse_buf_copy(&out_buf, in_buf); + if (res < 0) { + fuse_reply_err(req, -res); + } else { + fuse_reply_write(req, (size_t)res); + } ++ ++ if (cap_fsetid_dropped) { ++ res = gain_effective_cap("FSETID"); ++ if (res) { ++ fuse_log(FUSE_LOG_ERR, "Failed to gain CAP_FSETID\n"); ++ } ++ } + } + + static void lo_statfs(fuse_req_t req, fuse_ino_t ino) diff --git a/0065-virtiofsd-set-maximum-RLIMIT_NOFILE-limit.patch b/0065-virtiofsd-set-maximum-RLIMIT_NOFILE-limit.patch new file mode 100644 index 0000000..9782dc5 --- /dev/null +++ b/0065-virtiofsd-set-maximum-RLIMIT_NOFILE-limit.patch @@ -0,0 +1,77 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:34 +0000 +Subject: [PATCH] virtiofsd: set maximum RLIMIT_NOFILE limit +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +virtiofsd can exceed the default open file descriptor limit easily on +most systems. Take advantage of the fact that it runs as root to raise +the limit. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 01a6dc95ec7f71eeff9963fe3cb03d85225fba3e) +--- + tools/virtiofsd/passthrough_ll.c | 32 ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index d53cb1e005..c281d817af 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -53,6 +53,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -2268,6 +2269,35 @@ static void setup_sandbox(struct lo_data *lo, struct fuse_session *se) + setup_seccomp(); + } + ++/* Raise the maximum number of open file descriptors */ ++static void setup_nofile_rlimit(void) ++{ ++ const rlim_t max_fds = 1000000; ++ struct rlimit rlim; ++ ++ if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { ++ fuse_log(FUSE_LOG_ERR, "getrlimit(RLIMIT_NOFILE): %m\n"); ++ exit(1); ++ } ++ ++ if (rlim.rlim_cur >= max_fds) { ++ return; /* nothing to do */ ++ } ++ ++ rlim.rlim_cur = max_fds; ++ rlim.rlim_max = max_fds; ++ ++ if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) { ++ /* Ignore SELinux denials */ ++ if (errno == EPERM) { ++ return; ++ } ++ ++ fuse_log(FUSE_LOG_ERR, "setrlimit(RLIMIT_NOFILE): %m\n"); ++ exit(1); ++ } ++} ++ + int main(int argc, char *argv[]) + { + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); +@@ -2389,6 +2419,8 @@ int main(int argc, char *argv[]) + + fuse_daemonize(opts.foreground); + ++ setup_nofile_rlimit(); ++ + /* Must be before sandbox since it wants /proc */ + setup_capng(); + diff --git a/0066-virtiofsd-fix-libfuse-information-leaks.patch b/0066-virtiofsd-fix-libfuse-information-leaks.patch new file mode 100644 index 0000000..8a5b630 --- /dev/null +++ b/0066-virtiofsd-fix-libfuse-information-leaks.patch @@ -0,0 +1,306 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:35 +0000 +Subject: [PATCH] virtiofsd: fix libfuse information leaks +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Some FUSE message replies contain padding fields that are not +initialized by libfuse. This is fine in traditional FUSE applications +because the kernel is trusted. virtiofsd does not trust the guest and +must not expose uninitialized memory. + +Use C struct initializers to automatically zero out memory. Not all of +these code changes are strictly necessary but they will prevent future +information leaks if the structs are extended. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 3db2876a0153ac7103c077c53090e020faffb3ea) +--- + tools/virtiofsd/fuse_lowlevel.c | 150 ++++++++++++++++---------------- + 1 file changed, 76 insertions(+), 74 deletions(-) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 2d6dc5a680..6ceb33d913 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -44,21 +44,23 @@ static __attribute__((constructor)) void fuse_ll_init_pagesize(void) + + static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr) + { +- attr->ino = stbuf->st_ino; +- attr->mode = stbuf->st_mode; +- attr->nlink = stbuf->st_nlink; +- attr->uid = stbuf->st_uid; +- attr->gid = stbuf->st_gid; +- attr->rdev = stbuf->st_rdev; +- attr->size = stbuf->st_size; +- attr->blksize = stbuf->st_blksize; +- attr->blocks = stbuf->st_blocks; +- attr->atime = stbuf->st_atime; +- attr->mtime = stbuf->st_mtime; +- attr->ctime = stbuf->st_ctime; +- attr->atimensec = ST_ATIM_NSEC(stbuf); +- attr->mtimensec = ST_MTIM_NSEC(stbuf); +- attr->ctimensec = ST_CTIM_NSEC(stbuf); ++ *attr = (struct fuse_attr){ ++ .ino = stbuf->st_ino, ++ .mode = stbuf->st_mode, ++ .nlink = stbuf->st_nlink, ++ .uid = stbuf->st_uid, ++ .gid = stbuf->st_gid, ++ .rdev = stbuf->st_rdev, ++ .size = stbuf->st_size, ++ .blksize = stbuf->st_blksize, ++ .blocks = stbuf->st_blocks, ++ .atime = stbuf->st_atime, ++ .mtime = stbuf->st_mtime, ++ .ctime = stbuf->st_ctime, ++ .atimensec = ST_ATIM_NSEC(stbuf), ++ .mtimensec = ST_MTIM_NSEC(stbuf), ++ .ctimensec = ST_CTIM_NSEC(stbuf), ++ }; + } + + static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf) +@@ -183,16 +185,16 @@ static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, + int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, + int count) + { +- struct fuse_out_header out; ++ struct fuse_out_header out = { ++ .unique = req->unique, ++ .error = error, ++ }; + + if (error <= -1000 || error > 0) { + fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error); + error = -ERANGE; + } + +- out.unique = req->unique; +- out.error = error; +- + iov[0].iov_base = &out; + iov[0].iov_len = sizeof(struct fuse_out_header); + +@@ -277,14 +279,16 @@ size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, + static void convert_statfs(const struct statvfs *stbuf, + struct fuse_kstatfs *kstatfs) + { +- kstatfs->bsize = stbuf->f_bsize; +- kstatfs->frsize = stbuf->f_frsize; +- kstatfs->blocks = stbuf->f_blocks; +- kstatfs->bfree = stbuf->f_bfree; +- kstatfs->bavail = stbuf->f_bavail; +- kstatfs->files = stbuf->f_files; +- kstatfs->ffree = stbuf->f_ffree; +- kstatfs->namelen = stbuf->f_namemax; ++ *kstatfs = (struct fuse_kstatfs){ ++ .bsize = stbuf->f_bsize, ++ .frsize = stbuf->f_frsize, ++ .blocks = stbuf->f_blocks, ++ .bfree = stbuf->f_bfree, ++ .bavail = stbuf->f_bavail, ++ .files = stbuf->f_files, ++ .ffree = stbuf->f_ffree, ++ .namelen = stbuf->f_namemax, ++ }; + } + + static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize) +@@ -328,12 +332,14 @@ static unsigned int calc_timeout_nsec(double t) + static void fill_entry(struct fuse_entry_out *arg, + const struct fuse_entry_param *e) + { +- arg->nodeid = e->ino; +- arg->generation = e->generation; +- arg->entry_valid = calc_timeout_sec(e->entry_timeout); +- arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout); +- arg->attr_valid = calc_timeout_sec(e->attr_timeout); +- arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout); ++ *arg = (struct fuse_entry_out){ ++ .nodeid = e->ino, ++ .generation = e->generation, ++ .entry_valid = calc_timeout_sec(e->entry_timeout), ++ .entry_valid_nsec = calc_timeout_nsec(e->entry_timeout), ++ .attr_valid = calc_timeout_sec(e->attr_timeout), ++ .attr_valid_nsec = calc_timeout_nsec(e->attr_timeout), ++ }; + convert_stat(&e->attr, &arg->attr); + } + +@@ -362,10 +368,12 @@ size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, + fill_entry(&dp->entry_out, e); + + struct fuse_dirent *dirent = &dp->dirent; +- dirent->ino = e->attr.st_ino; +- dirent->off = off; +- dirent->namelen = namelen; +- dirent->type = (e->attr.st_mode & S_IFMT) >> 12; ++ *dirent = (struct fuse_dirent){ ++ .ino = e->attr.st_ino, ++ .off = off, ++ .namelen = namelen, ++ .type = (e->attr.st_mode & S_IFMT) >> 12, ++ }; + memcpy(dirent->name, name, namelen); + memset(dirent->name + namelen, 0, entlen_padded - entlen); + +@@ -496,15 +504,14 @@ static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, + int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv) + { + struct iovec iov[2]; +- struct fuse_out_header out; ++ struct fuse_out_header out = { ++ .unique = req->unique, ++ }; + int res; + + iov[0].iov_base = &out; + iov[0].iov_len = sizeof(struct fuse_out_header); + +- out.unique = req->unique; +- out.error = 0; +- + res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv); + if (res <= 0) { + fuse_free_req(req); +@@ -2145,14 +2152,14 @@ static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, + static int send_notify_iov(struct fuse_session *se, int notify_code, + struct iovec *iov, int count) + { +- struct fuse_out_header out; ++ struct fuse_out_header out = { ++ .error = notify_code, ++ }; + + if (!se->got_init) { + return -ENOTCONN; + } + +- out.unique = 0; +- out.error = notify_code; + iov[0].iov_base = &out; + iov[0].iov_len = sizeof(struct fuse_out_header); + +@@ -2162,11 +2169,11 @@ static int send_notify_iov(struct fuse_session *se, int notify_code, + int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph) + { + if (ph != NULL) { +- struct fuse_notify_poll_wakeup_out outarg; ++ struct fuse_notify_poll_wakeup_out outarg = { ++ .kh = ph->kh, ++ }; + struct iovec iov[2]; + +- outarg.kh = ph->kh; +- + iov[1].iov_base = &outarg; + iov[1].iov_len = sizeof(outarg); + +@@ -2179,17 +2186,17 @@ int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph) + int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, + off_t off, off_t len) + { +- struct fuse_notify_inval_inode_out outarg; ++ struct fuse_notify_inval_inode_out outarg = { ++ .ino = ino, ++ .off = off, ++ .len = len, ++ }; + struct iovec iov[2]; + + if (!se) { + return -EINVAL; + } + +- outarg.ino = ino; +- outarg.off = off; +- outarg.len = len; +- + iov[1].iov_base = &outarg; + iov[1].iov_len = sizeof(outarg); + +@@ -2199,17 +2206,16 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, + int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, + const char *name, size_t namelen) + { +- struct fuse_notify_inval_entry_out outarg; ++ struct fuse_notify_inval_entry_out outarg = { ++ .parent = parent, ++ .namelen = namelen, ++ }; + struct iovec iov[3]; + + if (!se) { + return -EINVAL; + } + +- outarg.parent = parent; +- outarg.namelen = namelen; +- outarg.padding = 0; +- + iov[1].iov_base = &outarg; + iov[1].iov_len = sizeof(outarg); + iov[2].iov_base = (void *)name; +@@ -2222,18 +2228,17 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, + fuse_ino_t child, const char *name, + size_t namelen) + { +- struct fuse_notify_delete_out outarg; ++ struct fuse_notify_delete_out outarg = { ++ .parent = parent, ++ .child = child, ++ .namelen = namelen, ++ }; + struct iovec iov[3]; + + if (!se) { + return -EINVAL; + } + +- outarg.parent = parent; +- outarg.child = child; +- outarg.namelen = namelen; +- outarg.padding = 0; +- + iov[1].iov_base = &outarg; + iov[1].iov_len = sizeof(outarg); + iov[2].iov_base = (void *)name; +@@ -2245,24 +2250,21 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, + int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, + off_t offset, struct fuse_bufvec *bufv) + { +- struct fuse_out_header out; +- struct fuse_notify_store_out outarg; ++ struct fuse_out_header out = { ++ .error = FUSE_NOTIFY_STORE, ++ }; ++ struct fuse_notify_store_out outarg = { ++ .nodeid = ino, ++ .offset = offset, ++ .size = fuse_buf_size(bufv), ++ }; + struct iovec iov[3]; +- size_t size = fuse_buf_size(bufv); + int res; + + if (!se) { + return -EINVAL; + } + +- out.unique = 0; +- out.error = FUSE_NOTIFY_STORE; +- +- outarg.nodeid = ino; +- outarg.offset = offset; +- outarg.size = size; +- outarg.padding = 0; +- + iov[0].iov_base = &out; + iov[0].iov_len = sizeof(out); + iov[1].iov_base = &outarg; diff --git a/0067-virtiofsd-add-syslog-command-line-option.patch b/0067-virtiofsd-add-syslog-command-line-option.patch new file mode 100644 index 0000000..756de55 --- /dev/null +++ b/0067-virtiofsd-add-syslog-command-line-option.patch @@ -0,0 +1,223 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:01:36 +0000 +Subject: [PATCH] virtiofsd: add --syslog command-line option +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Sometimes collecting output from stderr is inconvenient or does not fit +within the overall logging architecture. Add syslog(3) support for +cases where stderr cannot be used. + +Signed-off-by: Stefan Hajnoczi +dgilbert: Reworked as a logging function +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit f185621d41f03a23b55795b89e6584253fa23505) +--- + tools/virtiofsd/fuse_lowlevel.h | 1 + + tools/virtiofsd/helper.c | 2 ++ + tools/virtiofsd/passthrough_ll.c | 50 ++++++++++++++++++++++++++++++-- + tools/virtiofsd/seccomp.c | 32 ++++++++++++++------ + tools/virtiofsd/seccomp.h | 4 ++- + 5 files changed, 76 insertions(+), 13 deletions(-) + +diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h +index 0d61df8110..f2750bc189 100644 +--- a/tools/virtiofsd/fuse_lowlevel.h ++++ b/tools/virtiofsd/fuse_lowlevel.h +@@ -1795,6 +1795,7 @@ struct fuse_cmdline_opts { + int show_version; + int show_help; + int print_capabilities; ++ int syslog; + unsigned int max_idle_threads; + }; + +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index 5531425223..9692ef9f1f 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -54,6 +54,7 @@ static const struct fuse_opt fuse_helper_opts[] = { + FUSE_HELPER_OPT("subtype=", nodefault_subtype), + FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), + FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads), ++ FUSE_HELPER_OPT("--syslog", syslog), + FUSE_OPT_END + }; + +@@ -138,6 +139,7 @@ void fuse_cmdline_help(void) + " -V --version print version\n" + " --print-capabilities print vhost-user.json\n" + " -d -o debug enable debug output (implies -f)\n" ++ " --syslog log to syslog (default stderr)\n" + " -f foreground operation\n" + " --daemonize run in background\n" + " -o max_idle_threads the maximum number of idle worker " +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index c281d817af..0372aca143 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -58,6 +58,7 @@ + #include + #include + #include ++#include + #include + + #include "passthrough_helpers.h" +@@ -138,6 +139,7 @@ static const struct fuse_opt lo_opts[] = { + { "norace", offsetof(struct lo_data, norace), 1 }, + FUSE_OPT_END + }; ++static bool use_syslog = false; + + static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n); + +@@ -2262,11 +2264,12 @@ static void setup_mounts(const char *source) + * Lock down this process to prevent access to other processes or files outside + * source directory. This reduces the impact of arbitrary code execution bugs. + */ +-static void setup_sandbox(struct lo_data *lo, struct fuse_session *se) ++static void setup_sandbox(struct lo_data *lo, struct fuse_session *se, ++ bool enable_syslog) + { + setup_namespaces(lo, se); + setup_mounts(lo->source); +- setup_seccomp(); ++ setup_seccomp(enable_syslog); + } + + /* Raise the maximum number of open file descriptors */ +@@ -2298,6 +2301,42 @@ static void setup_nofile_rlimit(void) + } + } + ++static void log_func(enum fuse_log_level level, const char *fmt, va_list ap) ++{ ++ if (use_syslog) { ++ int priority = LOG_ERR; ++ switch (level) { ++ case FUSE_LOG_EMERG: ++ priority = LOG_EMERG; ++ break; ++ case FUSE_LOG_ALERT: ++ priority = LOG_ALERT; ++ break; ++ case FUSE_LOG_CRIT: ++ priority = LOG_CRIT; ++ break; ++ case FUSE_LOG_ERR: ++ priority = LOG_ERR; ++ break; ++ case FUSE_LOG_WARNING: ++ priority = LOG_WARNING; ++ break; ++ case FUSE_LOG_NOTICE: ++ priority = LOG_NOTICE; ++ break; ++ case FUSE_LOG_INFO: ++ priority = LOG_INFO; ++ break; ++ case FUSE_LOG_DEBUG: ++ priority = LOG_DEBUG; ++ break; ++ } ++ vsyslog(priority, fmt, ap); ++ } else { ++ vfprintf(stderr, fmt, ap); ++ } ++} ++ + int main(int argc, char *argv[]) + { + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); +@@ -2336,6 +2375,11 @@ int main(int argc, char *argv[]) + if (fuse_parse_cmdline(&args, &opts) != 0) { + return 1; + } ++ fuse_set_log_func(log_func); ++ use_syslog = opts.syslog; ++ if (use_syslog) { ++ openlog("virtiofsd", LOG_PID, LOG_DAEMON); ++ } + if (opts.show_help) { + printf("usage: %s [options]\n\n", argv[0]); + fuse_cmdline_help(); +@@ -2424,7 +2468,7 @@ int main(int argc, char *argv[]) + /* Must be before sandbox since it wants /proc */ + setup_capng(); + +- setup_sandbox(&lo, se); ++ setup_sandbox(&lo, se, opts.syslog); + + /* Block until ctrl+c or fusermount -u */ + ret = virtio_loop(se); +diff --git a/tools/virtiofsd/seccomp.c b/tools/virtiofsd/seccomp.c +index 691fb63dea..2d9d4a7ec0 100644 +--- a/tools/virtiofsd/seccomp.c ++++ b/tools/virtiofsd/seccomp.c +@@ -107,11 +107,28 @@ static const int syscall_whitelist[] = { + SCMP_SYS(writev), + }; + +-void setup_seccomp(void) ++/* Syscalls used when --syslog is enabled */ ++static const int syscall_whitelist_syslog[] = { ++ SCMP_SYS(sendto), ++}; ++ ++static void add_whitelist(scmp_filter_ctx ctx, const int syscalls[], size_t len) + { +- scmp_filter_ctx ctx; + size_t i; + ++ for (i = 0; i < len; i++) { ++ if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls[i], 0) != 0) { ++ fuse_log(FUSE_LOG_ERR, "seccomp_rule_add syscall %d failed\n", ++ syscalls[i]); ++ exit(1); ++ } ++ } ++} ++ ++void setup_seccomp(bool enable_syslog) ++{ ++ scmp_filter_ctx ctx; ++ + #ifdef SCMP_ACT_KILL_PROCESS + ctx = seccomp_init(SCMP_ACT_KILL_PROCESS); + /* Handle a newer libseccomp but an older kernel */ +@@ -126,13 +143,10 @@ void setup_seccomp(void) + exit(1); + } + +- for (i = 0; i < G_N_ELEMENTS(syscall_whitelist); i++) { +- if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, +- syscall_whitelist[i], 0) != 0) { +- fuse_log(FUSE_LOG_ERR, "seccomp_rule_add syscall %d", +- syscall_whitelist[i]); +- exit(1); +- } ++ add_whitelist(ctx, syscall_whitelist, G_N_ELEMENTS(syscall_whitelist)); ++ if (enable_syslog) { ++ add_whitelist(ctx, syscall_whitelist_syslog, ++ G_N_ELEMENTS(syscall_whitelist_syslog)); + } + + /* libvhost-user calls this for post-copy migration, we don't need it */ +diff --git a/tools/virtiofsd/seccomp.h b/tools/virtiofsd/seccomp.h +index 86bce72652..d47c8eade6 100644 +--- a/tools/virtiofsd/seccomp.h ++++ b/tools/virtiofsd/seccomp.h +@@ -9,6 +9,8 @@ + #ifndef VIRTIOFSD_SECCOMP_H + #define VIRTIOFSD_SECCOMP_H + +-void setup_seccomp(void); ++#include ++ ++void setup_seccomp(bool enable_syslog); + + #endif /* VIRTIOFSD_SECCOMP_H */ diff --git a/0068-virtiofsd-print-log-only-when-priority-is-high-enoug.patch b/0068-virtiofsd-print-log-only-when-priority-is-high-enoug.patch new file mode 100644 index 0000000..fa679f4 --- /dev/null +++ b/0068-virtiofsd-print-log-only-when-priority-is-high-enoug.patch @@ -0,0 +1,451 @@ +From: Eryu Guan +Date: Mon, 27 Jan 2020 19:01:37 +0000 +Subject: [PATCH] virtiofsd: print log only when priority is high enough +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Introduce "-o log_level=" command line option to specify current log +level (priority), valid values are "debug info warn err", e.g. + + ./virtiofsd -o log_level=debug ... + +So only log priority higher than "debug" will be printed to +stderr/syslog. And the default level is info. + +The "-o debug"/"-d" options are kept, and imply debug log level. + +Signed-off-by: Eryu Guan +dgilbert: Reworked for libfuse's log_func +Signed-off-by: Dr. David Alan Gilbert +with fix by: +Signed-off-by: Xiao Yang +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit d240314a1a18a1d914af1b5763fe8c9a572e6409) +--- + tools/virtiofsd/fuse_lowlevel.c | 75 ++++++++------------ + tools/virtiofsd/fuse_lowlevel.h | 1 + + tools/virtiofsd/helper.c | 8 ++- + tools/virtiofsd/passthrough_ll.c | 118 +++++++++++++------------------ + 4 files changed, 87 insertions(+), 115 deletions(-) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 6ceb33d913..a7a19685b5 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -158,19 +158,17 @@ static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, + struct fuse_out_header *out = iov[0].iov_base; + + out->len = iov_length(iov, count); +- if (se->debug) { +- if (out->unique == 0) { +- fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n", out->error, +- out->len); +- } else if (out->error) { +- fuse_log(FUSE_LOG_DEBUG, +- " unique: %llu, error: %i (%s), outsize: %i\n", +- (unsigned long long)out->unique, out->error, +- strerror(-out->error), out->len); +- } else { +- fuse_log(FUSE_LOG_DEBUG, " unique: %llu, success, outsize: %i\n", +- (unsigned long long)out->unique, out->len); +- } ++ if (out->unique == 0) { ++ fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n", out->error, ++ out->len); ++ } else if (out->error) { ++ fuse_log(FUSE_LOG_DEBUG, ++ " unique: %llu, error: %i (%s), outsize: %i\n", ++ (unsigned long long)out->unique, out->error, ++ strerror(-out->error), out->len); ++ } else { ++ fuse_log(FUSE_LOG_DEBUG, " unique: %llu, success, outsize: %i\n", ++ (unsigned long long)out->unique, out->len); + } + + if (fuse_lowlevel_is_virtio(se)) { +@@ -1662,10 +1660,8 @@ static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, + return; + } + +- if (se->debug) { +- fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n", +- (unsigned long long)arg->unique); +- } ++ fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n", ++ (unsigned long long)arg->unique); + + req->u.i.unique = arg->unique; + +@@ -1901,13 +1897,10 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, + } + } + +- if (se->debug) { +- fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor); +- if (arg->major == 7 && arg->minor >= 6) { +- fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags); +- fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n", +- arg->max_readahead); +- } ++ fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor); ++ if (arg->major == 7 && arg->minor >= 6) { ++ fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags); ++ fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n", arg->max_readahead); + } + se->conn.proto_major = arg->major; + se->conn.proto_minor = arg->minor; +@@ -2116,19 +2109,14 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, + outarg.congestion_threshold = se->conn.congestion_threshold; + outarg.time_gran = se->conn.time_gran; + +- if (se->debug) { +- fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, +- outarg.minor); +- fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); +- fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n", +- outarg.max_readahead); +- fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write); +- fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n", +- outarg.max_background); +- fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n", +- outarg.congestion_threshold); +- fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", outarg.time_gran); +- } ++ fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor); ++ fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); ++ fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n", outarg.max_readahead); ++ fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write); ++ fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n", outarg.max_background); ++ fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n", ++ outarg.congestion_threshold); ++ fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", outarg.time_gran); + + send_reply_ok(req, &outarg, outargsize); + } +@@ -2407,14 +2395,11 @@ void fuse_session_process_buf_int(struct fuse_session *se, + in = fuse_mbuf_iter_advance(&iter, sizeof(*in)); + assert(in); /* caller guarantees the input buffer is large enough */ + +- if (se->debug) { +- fuse_log(FUSE_LOG_DEBUG, +- "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, " +- "pid: %u\n", +- (unsigned long long)in->unique, +- opname((enum fuse_opcode)in->opcode), in->opcode, +- (unsigned long long)in->nodeid, buf->size, in->pid); +- } ++ fuse_log( ++ FUSE_LOG_DEBUG, ++ "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n", ++ (unsigned long long)in->unique, opname((enum fuse_opcode)in->opcode), ++ in->opcode, (unsigned long long)in->nodeid, buf->size, in->pid); + + req = fuse_ll_alloc_req(se); + if (req == NULL) { +diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h +index f2750bc189..138041e5f1 100644 +--- a/tools/virtiofsd/fuse_lowlevel.h ++++ b/tools/virtiofsd/fuse_lowlevel.h +@@ -1796,6 +1796,7 @@ struct fuse_cmdline_opts { + int show_help; + int print_capabilities; + int syslog; ++ int log_level; + unsigned int max_idle_threads; + }; + +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index 9692ef9f1f..6d50a46a7e 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -34,7 +34,6 @@ + t, offsetof(struct fuse_cmdline_opts, p), v \ + } + +- + static const struct fuse_opt fuse_helper_opts[] = { + FUSE_HELPER_OPT("-h", show_help), + FUSE_HELPER_OPT("--help", show_help), +@@ -55,6 +54,10 @@ static const struct fuse_opt fuse_helper_opts[] = { + FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), + FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads), + FUSE_HELPER_OPT("--syslog", syslog), ++ FUSE_HELPER_OPT_VALUE("log_level=debug", log_level, FUSE_LOG_DEBUG), ++ FUSE_HELPER_OPT_VALUE("log_level=info", log_level, FUSE_LOG_INFO), ++ FUSE_HELPER_OPT_VALUE("log_level=warn", log_level, FUSE_LOG_WARNING), ++ FUSE_HELPER_OPT_VALUE("log_level=err", log_level, FUSE_LOG_ERR), + FUSE_OPT_END + }; + +@@ -142,6 +145,9 @@ void fuse_cmdline_help(void) + " --syslog log to syslog (default stderr)\n" + " -f foreground operation\n" + " --daemonize run in background\n" ++ " -o log_level= log level, default to \"info\"\n" ++ " level could be one of \"debug, " ++ "info, warn, err\"\n" + " -o max_idle_threads the maximum number of idle worker " + "threads\n" + " allowed (default: 10)\n" +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 0372aca143..ff6910fd73 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -37,6 +37,7 @@ + + #include "qemu/osdep.h" + #include "fuse_virtio.h" ++#include "fuse_log.h" + #include "fuse_lowlevel.h" + #include + #include +@@ -140,6 +141,7 @@ static const struct fuse_opt lo_opts[] = { + FUSE_OPT_END + }; + static bool use_syslog = false; ++static int current_log_level; + + static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n); + +@@ -458,11 +460,6 @@ static int lo_fd(fuse_req_t req, fuse_ino_t ino) + return inode ? inode->fd : -1; + } + +-static bool lo_debug(fuse_req_t req) +-{ +- return lo_data(req)->debug != 0; +-} +- + static void lo_init(void *userdata, struct fuse_conn_info *conn) + { + struct lo_data *lo = (struct lo_data *)userdata; +@@ -472,15 +469,11 @@ static void lo_init(void *userdata, struct fuse_conn_info *conn) + } + + if (lo->writeback && conn->capable & FUSE_CAP_WRITEBACK_CACHE) { +- if (lo->debug) { +- fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); +- } ++ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); + conn->want |= FUSE_CAP_WRITEBACK_CACHE; + } + if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { +- if (lo->debug) { +- fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); +- } ++ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); + conn->want |= FUSE_CAP_FLOCK_LOCKS; + } + } +@@ -823,10 +816,8 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + } + e->ino = inode->fuse_ino; + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", +- (unsigned long long)parent, name, (unsigned long long)e->ino); +- } ++ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long)parent, ++ name, (unsigned long long)e->ino); + + return 0; + +@@ -843,10 +834,8 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) + struct fuse_entry_param e; + int err; + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", +- parent, name); +- } ++ fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", parent, ++ name); + + /* + * Don't use is_safe_path_component(), allow "." and ".." for NFS export +@@ -971,10 +960,8 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, + goto out; + } + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", +- (unsigned long long)parent, name, (unsigned long long)e.ino); +- } ++ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long)parent, ++ name, (unsigned long long)e.ino); + + fuse_reply_entry(req, &e); + return; +@@ -1074,10 +1061,8 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, + pthread_mutex_unlock(&lo->mutex); + e.ino = inode->fuse_ino; + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", +- (unsigned long long)parent, name, (unsigned long long)e.ino); +- } ++ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long)parent, ++ name, (unsigned long long)e.ino); + + fuse_reply_entry(req, &e); + return; +@@ -1171,11 +1156,9 @@ static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) + return; + } + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", +- (unsigned long long)ino, (unsigned long long)inode->refcount, +- (unsigned long long)nlookup); +- } ++ fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", ++ (unsigned long long)ino, (unsigned long long)inode->refcount, ++ (unsigned long long)nlookup); + + unref_inode(lo, inode, nlookup); + } +@@ -1445,10 +1428,8 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, + int err; + struct lo_cred old = {}; + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", +- parent, name); +- } ++ fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", parent, ++ name); + + if (!is_safe_path_component(name)) { + fuse_reply_err(req, EINVAL); +@@ -1525,10 +1506,8 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) + char buf[64]; + struct lo_data *lo = lo_data(req); + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino, +- fi->flags); +- } ++ fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino, ++ fi->flags); + + /* + * With writeback cache, kernel may send read requests even +@@ -1644,12 +1623,10 @@ static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, + { + struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, +- "lo_read(ino=%" PRIu64 ", size=%zd, " +- "off=%lu)\n", +- ino, size, (unsigned long)offset); +- } ++ fuse_log(FUSE_LOG_DEBUG, ++ "lo_read(ino=%" PRIu64 ", size=%zd, " ++ "off=%lu)\n", ++ ino, size, (unsigned long)offset); + + buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; + buf.buf[0].fd = lo_fi_fd(req, fi); +@@ -1671,11 +1648,9 @@ static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, + out_buf.buf[0].fd = lo_fi_fd(req, fi); + out_buf.buf[0].pos = off; + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, +- "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n", ino, +- out_buf.buf[0].size, (unsigned long)off); +- } ++ fuse_log(FUSE_LOG_DEBUG, ++ "lo_write_buf(ino=%" PRIu64 ", size=%zd, off=%lu)\n", ino, ++ out_buf.buf[0].size, (unsigned long)off); + + /* + * If kill_priv is set, drop CAP_FSETID which should lead to kernel +@@ -1774,11 +1749,8 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + goto out; + } + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, +- "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", ino, name, +- size); +- } ++ fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", ++ ino, name, size); + + if (inode->is_symlink) { + /* Sorry, no race free way to getxattr on symlink. */ +@@ -1852,10 +1824,8 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) + goto out; + } + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n", +- ino, size); +- } ++ fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n", ino, ++ size); + + if (inode->is_symlink) { + /* Sorry, no race free way to listxattr on symlink. */ +@@ -1929,11 +1899,8 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, + goto out; + } + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, +- "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n", +- ino, name, value, size); +- } ++ fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ++ ", name=%s value=%s size=%zd)\n", ino, name, value, size); + + if (inode->is_symlink) { + /* Sorry, no race free way to setxattr on symlink. */ +@@ -1978,10 +1945,8 @@ static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) + goto out; + } + +- if (lo_debug(req)) { +- fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", +- ino, name); +- } ++ fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", ino, ++ name); + + if (inode->is_symlink) { + /* Sorry, no race free way to setxattr on symlink. */ +@@ -2303,6 +2268,10 @@ static void setup_nofile_rlimit(void) + + static void log_func(enum fuse_log_level level, const char *fmt, va_list ap) + { ++ if (current_log_level < level) { ++ return; ++ } ++ + if (use_syslog) { + int priority = LOG_ERR; + switch (level) { +@@ -2401,8 +2370,19 @@ int main(int argc, char *argv[]) + return 1; + } + ++ /* ++ * log_level is 0 if not configured via cmd options (0 is LOG_EMERG, ++ * and we don't use this log level). ++ */ ++ if (opts.log_level != 0) { ++ current_log_level = opts.log_level; ++ } + lo.debug = opts.debug; ++ if (lo.debug) { ++ current_log_level = FUSE_LOG_DEBUG; ++ } + lo.root.refcount = 2; ++ + if (lo.source) { + struct stat stat; + int res; diff --git a/0069-virtiofsd-Add-ID-to-the-log-with-FUSE_LOG_DEBUG-leve.patch b/0069-virtiofsd-Add-ID-to-the-log-with-FUSE_LOG_DEBUG-leve.patch new file mode 100644 index 0000000..e3aaef9 --- /dev/null +++ b/0069-virtiofsd-Add-ID-to-the-log-with-FUSE_LOG_DEBUG-leve.patch @@ -0,0 +1,68 @@ +From: Masayoshi Mizuma +Date: Mon, 27 Jan 2020 19:01:38 +0000 +Subject: [PATCH] virtiofsd: Add ID to the log with FUSE_LOG_DEBUG level +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +virtiofsd has some threads, so we see a lot of logs with debug option. +It would be useful for debugging if we can identify the specific thread +from the log. + +Add ID, which is got by gettid(), to the log with FUSE_LOG_DEBUG level +so that we can grep the specific thread. + +The log is like as: + + ]# ./virtiofsd -d -o vhost_user_socket=/tmp/vhostqemu0 -o source=/tmp/share0 -o cache=auto + ... + [ID: 00000097] unique: 12696, success, outsize: 120 + [ID: 00000097] virtio_send_msg: elem 18: with 2 in desc of length 120 + [ID: 00000003] fv_queue_thread: Got queue event on Queue 1 + [ID: 00000003] fv_queue_thread: Queue 1 gave evalue: 1 available: in: 65552 out: 80 + [ID: 00000003] fv_queue_thread: Waiting for Queue 1 event + [ID: 00000071] fv_queue_worker: elem 33: with 2 out desc of length 80 bad_in_num=0 bad_out_num=0 + [ID: 00000071] unique: 12694, opcode: READ (15), nodeid: 2, insize: 80, pid: 2014 + [ID: 00000071] lo_read(ino=2, size=65536, off=131072) + +Signed-off-by: Masayoshi Mizuma + +Signed-off-by: Dr. David Alan Gilbert + added rework as suggested by Daniel P. Berrangé during review +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 36f3846902bd41413f6c0bf797dee509028c29f4) +--- + tools/virtiofsd/passthrough_ll.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index ff6910fd73..f08324f000 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -43,6 +43,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -2268,10 +2269,17 @@ static void setup_nofile_rlimit(void) + + static void log_func(enum fuse_log_level level, const char *fmt, va_list ap) + { ++ g_autofree char *localfmt = NULL; ++ + if (current_log_level < level) { + return; + } + ++ if (current_log_level == FUSE_LOG_DEBUG) { ++ localfmt = g_strdup_printf("[ID: %08ld] %s", syscall(__NR_gettid), fmt); ++ fmt = localfmt; ++ } ++ + if (use_syslog) { + int priority = LOG_ERR; + switch (level) { diff --git a/0070-virtiofsd-Add-timestamp-to-the-log-with-FUSE_LOG_DEB.patch b/0070-virtiofsd-Add-timestamp-to-the-log-with-FUSE_LOG_DEB.patch new file mode 100644 index 0000000..b58a8c2 --- /dev/null +++ b/0070-virtiofsd-Add-timestamp-to-the-log-with-FUSE_LOG_DEB.patch @@ -0,0 +1,56 @@ +From: Masayoshi Mizuma +Date: Mon, 27 Jan 2020 19:01:39 +0000 +Subject: [PATCH] virtiofsd: Add timestamp to the log with FUSE_LOG_DEBUG level +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +virtiofsd has some threads, so we see a lot of logs with debug option. +It would be useful for debugging if we can see the timestamp. + +Add nano second timestamp, which got by get_clock(), to the log with +FUSE_LOG_DEBUG level if the syslog option isn't set. + +The log is like as: + + # ./virtiofsd -d -o vhost_user_socket=/tmp/vhostqemu0 -o source=/tmp/share0 -o cache=auto + ... + [5365943125463727] [ID: 00000002] fv_queue_thread: Start for queue 0 kick_fd 9 + [5365943125568644] [ID: 00000002] fv_queue_thread: Waiting for Queue 0 event + [5365943125573561] [ID: 00000002] fv_queue_thread: Got queue event on Queue 0 + +Signed-off-by: Masayoshi Mizuma +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 50fb955aa0e6ede929422146936cf68bf1ca876f) +--- + tools/virtiofsd/passthrough_ll.c | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index f08324f000..98114a3f4a 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -36,6 +36,7 @@ + */ + + #include "qemu/osdep.h" ++#include "qemu/timer.h" + #include "fuse_virtio.h" + #include "fuse_log.h" + #include "fuse_lowlevel.h" +@@ -2276,7 +2277,13 @@ static void log_func(enum fuse_log_level level, const char *fmt, va_list ap) + } + + if (current_log_level == FUSE_LOG_DEBUG) { +- localfmt = g_strdup_printf("[ID: %08ld] %s", syscall(__NR_gettid), fmt); ++ if (!use_syslog) { ++ localfmt = g_strdup_printf("[%" PRId64 "] [ID: %08ld] %s", ++ get_clock(), syscall(__NR_gettid), fmt); ++ } else { ++ localfmt = g_strdup_printf("[ID: %08ld] %s", syscall(__NR_gettid), ++ fmt); ++ } + fmt = localfmt; + } + diff --git a/0071-virtiofsd-Handle-reinit.patch b/0071-virtiofsd-Handle-reinit.patch new file mode 100644 index 0000000..88c91c7 --- /dev/null +++ b/0071-virtiofsd-Handle-reinit.patch @@ -0,0 +1,37 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:40 +0000 +Subject: [PATCH] virtiofsd: Handle reinit +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Allow init->destroy->init for mount->umount->mount + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit c806d6435fe95fd54b379920aca2f4e3ea1f3258) +--- + tools/virtiofsd/fuse_lowlevel.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index a7a19685b5..7d742b5a72 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -2028,6 +2028,7 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, + } + + se->got_init = 1; ++ se->got_destroy = 0; + if (se->op.init) { + se->op.init(se->userdata, &se->conn); + } +@@ -2130,6 +2131,7 @@ static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, + (void)iter; + + se->got_destroy = 1; ++ se->got_init = 0; + if (se->op.destroy) { + se->op.destroy(se->userdata); + } diff --git a/0072-virtiofsd-Handle-hard-reboot.patch b/0072-virtiofsd-Handle-hard-reboot.patch new file mode 100644 index 0000000..c5e2dfd --- /dev/null +++ b/0072-virtiofsd-Handle-hard-reboot.patch @@ -0,0 +1,49 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:41 +0000 +Subject: [PATCH] virtiofsd: Handle hard reboot +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Handle a + mount + hard reboot (without unmount) + mount + +we get another 'init' which FUSE doesn't normally expect. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit e8556f49098b5d95634e592d79a97f761b76c96e) +--- + tools/virtiofsd/fuse_lowlevel.c | 16 +++++++++++++++- + 1 file changed, 15 insertions(+), 1 deletion(-) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 7d742b5a72..65f91dabae 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -2433,7 +2433,21 @@ void fuse_session_process_buf_int(struct fuse_session *se, + goto reply_err; + } + } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT) { +- goto reply_err; ++ if (fuse_lowlevel_is_virtio(se)) { ++ /* ++ * TODO: This is after a hard reboot typically, we need to do ++ * a destroy, but we can't reply to this request yet so ++ * we can't use do_destroy ++ */ ++ fuse_log(FUSE_LOG_DEBUG, "%s: reinit\n", __func__); ++ se->got_destroy = 1; ++ se->got_init = 0; ++ if (se->op.destroy) { ++ se->op.destroy(se->userdata); ++ } ++ } else { ++ goto reply_err; ++ } + } + + err = EACCES; diff --git a/0073-virtiofsd-Kill-threads-when-queues-are-stopped.patch b/0073-virtiofsd-Kill-threads-when-queues-are-stopped.patch new file mode 100644 index 0000000..70d3e7a --- /dev/null +++ b/0073-virtiofsd-Kill-threads-when-queues-are-stopped.patch @@ -0,0 +1,126 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:42 +0000 +Subject: [PATCH] virtiofsd: Kill threads when queues are stopped +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Kill the threads we've started when the queues get stopped. + +Signed-off-by: Dr. David Alan Gilbert +With improvements by: +Signed-off-by: Eryu Guan +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 10477ac47fc57d00a84802ff97c15450cd8021c1) +--- + tools/virtiofsd/fuse_virtio.c | 51 ++++++++++++++++++++++++++++++----- + 1 file changed, 44 insertions(+), 7 deletions(-) + +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index 872968f2c8..7a8774a3ee 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -41,6 +41,7 @@ struct fv_QueueInfo { + /* Our queue index, corresponds to array position */ + int qidx; + int kick_fd; ++ int kill_fd; /* For killing the thread */ + + /* The element for the command currently being processed */ + VuVirtqElement *qe; +@@ -412,14 +413,17 @@ static void *fv_queue_thread(void *opaque) + fuse_log(FUSE_LOG_INFO, "%s: Start for queue %d kick_fd %d\n", __func__, + qi->qidx, qi->kick_fd); + while (1) { +- struct pollfd pf[1]; ++ struct pollfd pf[2]; + pf[0].fd = qi->kick_fd; + pf[0].events = POLLIN; + pf[0].revents = 0; ++ pf[1].fd = qi->kill_fd; ++ pf[1].events = POLLIN; ++ pf[1].revents = 0; + + fuse_log(FUSE_LOG_DEBUG, "%s: Waiting for Queue %d event\n", __func__, + qi->qidx); +- int poll_res = ppoll(pf, 1, NULL, NULL); ++ int poll_res = ppoll(pf, 2, NULL, NULL); + + if (poll_res == -1) { + if (errno == EINTR) { +@@ -430,12 +434,23 @@ static void *fv_queue_thread(void *opaque) + fuse_log(FUSE_LOG_ERR, "fv_queue_thread ppoll: %m\n"); + break; + } +- assert(poll_res == 1); ++ assert(poll_res >= 1); + if (pf[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { + fuse_log(FUSE_LOG_ERR, "%s: Unexpected poll revents %x Queue %d\n", + __func__, pf[0].revents, qi->qidx); + break; + } ++ if (pf[1].revents & (POLLERR | POLLHUP | POLLNVAL)) { ++ fuse_log(FUSE_LOG_ERR, ++ "%s: Unexpected poll revents %x Queue %d killfd\n", ++ __func__, pf[1].revents, qi->qidx); ++ break; ++ } ++ if (pf[1].revents) { ++ fuse_log(FUSE_LOG_INFO, "%s: kill event on queue %d - quitting\n", ++ __func__, qi->qidx); ++ break; ++ } + assert(pf[0].revents & POLLIN); + fuse_log(FUSE_LOG_DEBUG, "%s: Got queue event on Queue %d\n", __func__, + qi->qidx); +@@ -589,6 +604,28 @@ out: + return NULL; + } + ++static void fv_queue_cleanup_thread(struct fv_VuDev *vud, int qidx) ++{ ++ int ret; ++ struct fv_QueueInfo *ourqi; ++ ++ assert(qidx < vud->nqueues); ++ ourqi = vud->qi[qidx]; ++ ++ /* Kill the thread */ ++ if (eventfd_write(ourqi->kill_fd, 1)) { ++ fuse_log(FUSE_LOG_ERR, "Eventfd_write for queue %d: %s\n", ++ qidx, strerror(errno)); ++ } ++ ret = pthread_join(ourqi->thread, NULL); ++ if (ret) { ++ fuse_log(FUSE_LOG_ERR, "%s: Failed to join thread idx %d err %d\n", ++ __func__, qidx, ret); ++ } ++ close(ourqi->kill_fd); ++ ourqi->kick_fd = -1; ++} ++ + /* Callback from libvhost-user on start or stop of a queue */ + static void fv_queue_set_started(VuDev *dev, int qidx, bool started) + { +@@ -633,16 +670,16 @@ static void fv_queue_set_started(VuDev *dev, int qidx, bool started) + } + ourqi = vud->qi[qidx]; + ourqi->kick_fd = dev->vq[qidx].kick_fd; ++ ++ ourqi->kill_fd = eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE); ++ assert(ourqi->kill_fd != -1); + if (pthread_create(&ourqi->thread, NULL, fv_queue_thread, ourqi)) { + fuse_log(FUSE_LOG_ERR, "%s: Failed to create thread for queue %d\n", + __func__, qidx); + assert(0); + } + } else { +- /* TODO: Kill the thread */ +- assert(qidx < vud->nqueues); +- ourqi = vud->qi[qidx]; +- ourqi->kick_fd = -1; ++ fv_queue_cleanup_thread(vud, qidx); + } + } + diff --git a/0074-vhost-user-Print-unexpected-slave-message-types.patch b/0074-vhost-user-Print-unexpected-slave-message-types.patch new file mode 100644 index 0000000..cead453 --- /dev/null +++ b/0074-vhost-user-Print-unexpected-slave-message-types.patch @@ -0,0 +1,32 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:43 +0000 +Subject: [PATCH] vhost-user: Print unexpected slave message types +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When we receive an unexpected message type on the slave fd, print +the type. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 0fdc465d7d5aafeae127eba488f247ac6f58df4c) +--- + hw/virtio/vhost-user.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c +index 02a9b25199..e4f46ecf88 100644 +--- a/hw/virtio/vhost-user.c ++++ b/hw/virtio/vhost-user.c +@@ -1055,7 +1055,7 @@ static void slave_read(void *opaque) + fd[0]); + break; + default: +- error_report("Received unexpected msg type."); ++ error_report("Received unexpected msg type: %d.", hdr.request); + ret = -EINVAL; + } + diff --git a/0075-contrib-libvhost-user-Protect-slave-fd-with-mutex.patch b/0075-contrib-libvhost-user-Protect-slave-fd-with-mutex.patch new file mode 100644 index 0000000..2e05ece --- /dev/null +++ b/0075-contrib-libvhost-user-Protect-slave-fd-with-mutex.patch @@ -0,0 +1,118 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:44 +0000 +Subject: [PATCH] contrib/libvhost-user: Protect slave fd with mutex +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +In future patches we'll be performing commands on the slave-fd driven +by commands on queues, since those queues will be driven by individual +threads we need to make sure they don't attempt to use the slave-fd +for multiple commands in parallel. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit c25c02b9e6a196be87a818f459c426556b24770d) +--- + contrib/libvhost-user/libvhost-user.c | 24 ++++++++++++++++++++---- + contrib/libvhost-user/libvhost-user.h | 3 +++ + 2 files changed, 23 insertions(+), 4 deletions(-) + +diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c +index ec27b78ff1..63e41062a4 100644 +--- a/contrib/libvhost-user/libvhost-user.c ++++ b/contrib/libvhost-user/libvhost-user.c +@@ -392,26 +392,37 @@ vu_send_reply(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) + return vu_message_write(dev, conn_fd, vmsg); + } + ++/* ++ * Processes a reply on the slave channel. ++ * Entered with slave_mutex held and releases it before exit. ++ * Returns true on success. ++ */ + static bool + vu_process_message_reply(VuDev *dev, const VhostUserMsg *vmsg) + { + VhostUserMsg msg_reply; ++ bool result = false; + + if ((vmsg->flags & VHOST_USER_NEED_REPLY_MASK) == 0) { +- return true; ++ result = true; ++ goto out; + } + + if (!vu_message_read(dev, dev->slave_fd, &msg_reply)) { +- return false; ++ goto out; + } + + if (msg_reply.request != vmsg->request) { + DPRINT("Received unexpected msg type. Expected %d received %d", + vmsg->request, msg_reply.request); +- return false; ++ goto out; + } + +- return msg_reply.payload.u64 == 0; ++ result = msg_reply.payload.u64 == 0; ++ ++out: ++ pthread_mutex_unlock(&dev->slave_mutex); ++ return result; + } + + /* Kick the log_call_fd if required. */ +@@ -1105,10 +1116,13 @@ bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd, + return false; + } + ++ pthread_mutex_lock(&dev->slave_mutex); + if (!vu_message_write(dev, dev->slave_fd, &vmsg)) { ++ pthread_mutex_unlock(&dev->slave_mutex); + return false; + } + ++ /* Also unlocks the slave_mutex */ + return vu_process_message_reply(dev, &vmsg); + } + +@@ -1628,6 +1642,7 @@ vu_deinit(VuDev *dev) + close(dev->slave_fd); + dev->slave_fd = -1; + } ++ pthread_mutex_destroy(&dev->slave_mutex); + + if (dev->sock != -1) { + close(dev->sock); +@@ -1663,6 +1678,7 @@ vu_init(VuDev *dev, + dev->remove_watch = remove_watch; + dev->iface = iface; + dev->log_call_fd = -1; ++ pthread_mutex_init(&dev->slave_mutex, NULL); + dev->slave_fd = -1; + dev->max_queues = max_queues; + +diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h +index 46b600799b..1844b6f8d4 100644 +--- a/contrib/libvhost-user/libvhost-user.h ++++ b/contrib/libvhost-user/libvhost-user.h +@@ -19,6 +19,7 @@ + #include + #include + #include ++#include + #include "standard-headers/linux/virtio_ring.h" + + /* Based on qemu/hw/virtio/vhost-user.c */ +@@ -355,6 +356,8 @@ struct VuDev { + VuVirtq *vq; + VuDevInflightInfo inflight_info; + int log_call_fd; ++ /* Must be held while using slave_fd */ ++ pthread_mutex_t slave_mutex; + int slave_fd; + uint64_t log_size; + uint8_t *log_table; diff --git a/0076-virtiofsd-passthrough_ll-add-renameat2-support.patch b/0076-virtiofsd-passthrough_ll-add-renameat2-support.patch new file mode 100644 index 0000000..e375310 --- /dev/null +++ b/0076-virtiofsd-passthrough_ll-add-renameat2-support.patch @@ -0,0 +1,33 @@ +From: Miklos Szeredi +Date: Mon, 27 Jan 2020 19:01:45 +0000 +Subject: [PATCH] virtiofsd: passthrough_ll: add renameat2 support + +Signed-off-by: Miklos Szeredi +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit f0ab7d6f78a7d3c1c19fd81a91c9b1199f56c4f6) +--- + tools/virtiofsd/passthrough_ll.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 98114a3f4a..18d69abcbc 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1099,7 +1099,17 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, + } + + if (flags) { ++#ifndef SYS_renameat2 + fuse_reply_err(req, EINVAL); ++#else ++ res = syscall(SYS_renameat2, lo_fd(req, parent), name, ++ lo_fd(req, newparent), newname, flags); ++ if (res == -1 && errno == ENOSYS) { ++ fuse_reply_err(req, EINVAL); ++ } else { ++ fuse_reply_err(req, res == -1 ? errno : 0); ++ } ++#endif + return; + } + diff --git a/0077-virtiofsd-passthrough_ll-disable-readdirplus-on-cach.patch b/0077-virtiofsd-passthrough_ll-disable-readdirplus-on-cach.patch new file mode 100644 index 0000000..d4dbb58 --- /dev/null +++ b/0077-virtiofsd-passthrough_ll-disable-readdirplus-on-cach.patch @@ -0,0 +1,33 @@ +From: Miklos Szeredi +Date: Mon, 27 Jan 2020 19:01:46 +0000 +Subject: [PATCH] virtiofsd: passthrough_ll: disable readdirplus on cache=never +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +...because the attributes sent in the READDIRPLUS reply would be discarded +anyway. + +Signed-off-by: Miklos Szeredi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit ddcbabcb0ea177be3ec3500726b699c7c26ffd93) +--- + tools/virtiofsd/passthrough_ll.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 18d69abcbc..6480c517dc 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -478,6 +478,10 @@ static void lo_init(void *userdata, struct fuse_conn_info *conn) + fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); + conn->want |= FUSE_CAP_FLOCK_LOCKS; + } ++ if (lo->cache == CACHE_NEVER) { ++ fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling readdirplus\n"); ++ conn->want &= ~FUSE_CAP_READDIRPLUS; ++ } + } + + static void lo_getattr(fuse_req_t req, fuse_ino_t ino, diff --git a/0078-virtiofsd-passthrough_ll-control-readdirplus.patch b/0078-virtiofsd-passthrough_ll-control-readdirplus.patch new file mode 100644 index 0000000..d40c6f6 --- /dev/null +++ b/0078-virtiofsd-passthrough_ll-control-readdirplus.patch @@ -0,0 +1,60 @@ +From: Miklos Szeredi +Date: Mon, 27 Jan 2020 19:01:47 +0000 +Subject: [PATCH] virtiofsd: passthrough_ll: control readdirplus + +Signed-off-by: Miklos Szeredi +Reviewed-by: Misono Tomohiro +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 59aef494be2d8d91055ff3f3a8eb13d9f32873d8) +--- + tools/virtiofsd/helper.c | 4 ++++ + tools/virtiofsd/passthrough_ll.c | 7 ++++++- + 2 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index 6d50a46a7e..14f5d70c10 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -153,6 +153,10 @@ void fuse_cmdline_help(void) + " allowed (default: 10)\n" + " -o norace disable racy fallback\n" + " default: false\n" ++ " -o readdirplus|no_readdirplus\n" ++ " enable/disable readirplus\n" ++ " default: readdirplus except with " ++ "cache=never\n" + ); + } + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 6480c517dc..8b1784ff7b 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -117,6 +117,8 @@ struct lo_data { + double timeout; + int cache; + int timeout_set; ++ int readdirplus_set; ++ int readdirplus_clear; + struct lo_inode root; /* protected by lo->mutex */ + struct lo_map ino_map; /* protected by lo->mutex */ + struct lo_map dirp_map; /* protected by lo->mutex */ +@@ -140,6 +142,8 @@ static const struct fuse_opt lo_opts[] = { + { "cache=auto", offsetof(struct lo_data, cache), CACHE_NORMAL }, + { "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS }, + { "norace", offsetof(struct lo_data, norace), 1 }, ++ { "readdirplus", offsetof(struct lo_data, readdirplus_set), 1 }, ++ { "no_readdirplus", offsetof(struct lo_data, readdirplus_clear), 1 }, + FUSE_OPT_END + }; + static bool use_syslog = false; +@@ -478,7 +482,8 @@ static void lo_init(void *userdata, struct fuse_conn_info *conn) + fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); + conn->want |= FUSE_CAP_FLOCK_LOCKS; + } +- if (lo->cache == CACHE_NEVER) { ++ if ((lo->cache == CACHE_NEVER && !lo->readdirplus_set) || ++ lo->readdirplus_clear) { + fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling readdirplus\n"); + conn->want &= ~FUSE_CAP_READDIRPLUS; + } diff --git a/0079-virtiofsd-rename-unref_inode-to-unref_inode_lolocked.patch b/0079-virtiofsd-rename-unref_inode-to-unref_inode_lolocked.patch new file mode 100644 index 0000000..90fc806 --- /dev/null +++ b/0079-virtiofsd-rename-unref_inode-to-unref_inode_lolocked.patch @@ -0,0 +1,77 @@ +From: Miklos Szeredi +Date: Mon, 27 Jan 2020 19:01:48 +0000 +Subject: [PATCH] virtiofsd: rename unref_inode() to unref_inode_lolocked() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Miklos Szeredi +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 95d2715791c60b5dc2d22e4eb7b83217273296fa) +--- + tools/virtiofsd/passthrough_ll.c | 15 ++++++++------- + 1 file changed, 8 insertions(+), 7 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 8b1784ff7b..de12e75a9e 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -148,8 +148,8 @@ static const struct fuse_opt lo_opts[] = { + }; + static bool use_syslog = false; + static int current_log_level; +- +-static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n); ++static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, ++ uint64_t n); + + static struct { + pthread_mutex_t mutex; +@@ -586,7 +586,7 @@ retry: + return 0; + + fail_unref: +- unref_inode(lo, p, 1); ++ unref_inode_lolocked(lo, p, 1); + fail: + if (retries) { + retries--; +@@ -624,7 +624,7 @@ fallback: + res = lo_parent_and_name(lo, inode, path, &parent); + if (res != -1) { + res = utimensat(parent->fd, path, tv, AT_SYMLINK_NOFOLLOW); +- unref_inode(lo, parent, 1); ++ unref_inode_lolocked(lo, parent, 1); + } + + return res; +@@ -1027,7 +1027,7 @@ fallback: + res = lo_parent_and_name(lo, inode, path, &parent); + if (res != -1) { + res = linkat(parent->fd, path, dfd, name, 0); +- unref_inode(lo, parent, 1); ++ unref_inode_lolocked(lo, parent, 1); + } + + return res; +@@ -1141,7 +1141,8 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) + fuse_reply_err(req, res == -1 ? errno : 0); + } + +-static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) ++static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, ++ uint64_t n) + { + if (!inode) { + return; +@@ -1181,7 +1182,7 @@ static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) + (unsigned long long)ino, (unsigned long long)inode->refcount, + (unsigned long long)nlookup); + +- unref_inode(lo, inode, nlookup); ++ unref_inode_lolocked(lo, inode, nlookup); + } + + static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) diff --git a/0080-virtiofsd-fail-when-parent-inode-isn-t-known-in-lo_d.patch b/0080-virtiofsd-fail-when-parent-inode-isn-t-known-in-lo_d.patch new file mode 100644 index 0000000..cc729ce --- /dev/null +++ b/0080-virtiofsd-fail-when-parent-inode-isn-t-known-in-lo_d.patch @@ -0,0 +1,66 @@ +From: Miklos Szeredi +Date: Mon, 27 Jan 2020 19:01:49 +0000 +Subject: [PATCH] virtiofsd: fail when parent inode isn't known in + lo_do_lookup() + +The Linux file handle APIs (struct export_operations) can access inodes +that are not attached to parents because path name traversal is not +performed. Refuse if there is no parent in lo_do_lookup(). + +Also clean up lo_do_lookup() while we're here. + +Signed-off-by: Miklos Szeredi +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Misono Tomohiro +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 9de4fab5995d115f8ebfb41d8d94a866d80a1708) +--- + tools/virtiofsd/passthrough_ll.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index de12e75a9e..33bfb4d1f4 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -777,6 +777,15 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + struct lo_data *lo = lo_data(req); + struct lo_inode *inode, *dir = lo_inode(req, parent); + ++ /* ++ * name_to_handle_at() and open_by_handle_at() can reach here with fuse ++ * mount point in guest, but we don't have its inode info in the ++ * ino_map. ++ */ ++ if (!dir) { ++ return ENOENT; ++ } ++ + memset(e, 0, sizeof(*e)); + e->attr_timeout = lo->timeout; + e->entry_timeout = lo->timeout; +@@ -786,7 +795,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + name = "."; + } + +- newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW); ++ newfd = openat(dir->fd, name, O_PATH | O_NOFOLLOW); + if (newfd == -1) { + goto out_err; + } +@@ -796,7 +805,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + goto out_err; + } + +- inode = lo_find(lo_data(req), &e->attr); ++ inode = lo_find(lo, &e->attr); + if (inode) { + close(newfd); + newfd = -1; +@@ -812,6 +821,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + inode->is_symlink = S_ISLNK(e->attr.st_mode); + inode->refcount = 1; + inode->fd = newfd; ++ newfd = -1; + inode->ino = e->attr.st_ino; + inode->dev = e->attr.st_dev; + diff --git a/0081-virtiofsd-extract-root-inode-init-into-setup_root.patch b/0081-virtiofsd-extract-root-inode-init-into-setup_root.patch new file mode 100644 index 0000000..f88349d --- /dev/null +++ b/0081-virtiofsd-extract-root-inode-init-into-setup_root.patch @@ -0,0 +1,91 @@ +From: Miklos Szeredi +Date: Mon, 27 Jan 2020 19:01:50 +0000 +Subject: [PATCH] virtiofsd: extract root inode init into setup_root() + +Inititialize the root inode in a single place. + +Signed-off-by: Miklos Szeredi +Signed-off-by: Stefan Hajnoczi +dgilbert: +with fix suggested by Misono Tomohiro +Reviewed-by: Misono Tomohiro +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 3ca8a2b1c83eb185c232a4e87abbb65495263756) +--- + tools/virtiofsd/passthrough_ll.c | 35 +++++++++++++++++++++++--------- + 1 file changed, 25 insertions(+), 10 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 33bfb4d1f4..9e7191eb75 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -2351,6 +2351,30 @@ static void log_func(enum fuse_log_level level, const char *fmt, va_list ap) + } + } + ++static void setup_root(struct lo_data *lo, struct lo_inode *root) ++{ ++ int fd, res; ++ struct stat stat; ++ ++ fd = open("/", O_PATH); ++ if (fd == -1) { ++ fuse_log(FUSE_LOG_ERR, "open(%s, O_PATH): %m\n", lo->source); ++ exit(1); ++ } ++ ++ res = fstatat(fd, "", &stat, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); ++ if (res == -1) { ++ fuse_log(FUSE_LOG_ERR, "fstatat(%s): %m\n", lo->source); ++ exit(1); ++ } ++ ++ root->is_symlink = false; ++ root->fd = fd; ++ root->ino = stat.st_ino; ++ root->dev = stat.st_dev; ++ root->refcount = 2; ++} ++ + int main(int argc, char *argv[]) + { + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); +@@ -2426,8 +2450,6 @@ int main(int argc, char *argv[]) + if (lo.debug) { + current_log_level = FUSE_LOG_DEBUG; + } +- lo.root.refcount = 2; +- + if (lo.source) { + struct stat stat; + int res; +@@ -2446,7 +2468,6 @@ int main(int argc, char *argv[]) + } else { + lo.source = "/"; + } +- lo.root.is_symlink = false; + if (!lo.timeout_set) { + switch (lo.cache) { + case CACHE_NEVER: +@@ -2466,13 +2487,6 @@ int main(int argc, char *argv[]) + exit(1); + } + +- lo.root.fd = open(lo.source, O_PATH); +- +- if (lo.root.fd == -1) { +- fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", lo.source); +- exit(1); +- } +- + se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); + if (se == NULL) { + goto err_out1; +@@ -2495,6 +2509,7 @@ int main(int argc, char *argv[]) + + setup_sandbox(&lo, se, opts.syslog); + ++ setup_root(&lo, &lo.root); + /* Block until ctrl+c or fusermount -u */ + ret = virtio_loop(se); + diff --git a/0082-virtiofsd-passthrough_ll-clean-up-cache-related-opti.patch b/0082-virtiofsd-passthrough_ll-clean-up-cache-related-opti.patch new file mode 100644 index 0000000..57a2381 --- /dev/null +++ b/0082-virtiofsd-passthrough_ll-clean-up-cache-related-opti.patch @@ -0,0 +1,121 @@ +From: Miklos Szeredi +Date: Mon, 27 Jan 2020 19:01:51 +0000 +Subject: [PATCH] virtiofsd: passthrough_ll: clean up cache related options +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + + - Rename "cache=never" to "cache=none" to match 9p's similar option. + + - Rename CACHE_NORMAL constant to CACHE_AUTO to match the "cache=auto" + option. + +Signed-off-by: Miklos Szeredi +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 230e777b5e250759ee0480fcc0e9ccfa2b082fba) +--- + tools/virtiofsd/helper.c | 5 ++++- + tools/virtiofsd/passthrough_ll.c | 20 ++++++++++---------- + 2 files changed, 14 insertions(+), 11 deletions(-) + +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index 14f5d70c10..567202444a 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -145,6 +145,9 @@ void fuse_cmdline_help(void) + " --syslog log to syslog (default stderr)\n" + " -f foreground operation\n" + " --daemonize run in background\n" ++ " -o cache= cache mode. could be one of \"auto, " ++ "always, none\"\n" ++ " default: auto\n" + " -o log_level= log level, default to \"info\"\n" + " level could be one of \"debug, " + "info, warn, err\"\n" +@@ -156,7 +159,7 @@ void fuse_cmdline_help(void) + " -o readdirplus|no_readdirplus\n" + " enable/disable readirplus\n" + " default: readdirplus except with " +- "cache=never\n" ++ "cache=none\n" + ); + } + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 9e7191eb75..b40f2874a7 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -101,8 +101,8 @@ struct lo_cred { + }; + + enum { +- CACHE_NEVER, +- CACHE_NORMAL, ++ CACHE_NONE, ++ CACHE_AUTO, + CACHE_ALWAYS, + }; + +@@ -138,8 +138,8 @@ static const struct fuse_opt lo_opts[] = { + { "no_xattr", offsetof(struct lo_data, xattr), 0 }, + { "timeout=%lf", offsetof(struct lo_data, timeout), 0 }, + { "timeout=", offsetof(struct lo_data, timeout_set), 1 }, +- { "cache=never", offsetof(struct lo_data, cache), CACHE_NEVER }, +- { "cache=auto", offsetof(struct lo_data, cache), CACHE_NORMAL }, ++ { "cache=none", offsetof(struct lo_data, cache), CACHE_NONE }, ++ { "cache=auto", offsetof(struct lo_data, cache), CACHE_AUTO }, + { "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS }, + { "norace", offsetof(struct lo_data, norace), 1 }, + { "readdirplus", offsetof(struct lo_data, readdirplus_set), 1 }, +@@ -482,7 +482,7 @@ static void lo_init(void *userdata, struct fuse_conn_info *conn) + fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); + conn->want |= FUSE_CAP_FLOCK_LOCKS; + } +- if ((lo->cache == CACHE_NEVER && !lo->readdirplus_set) || ++ if ((lo->cache == CACHE_NONE && !lo->readdirplus_set) || + lo->readdirplus_clear) { + fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling readdirplus\n"); + conn->want &= ~FUSE_CAP_READDIRPLUS; +@@ -1493,7 +1493,7 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, + fi->fh = fh; + err = lo_do_lookup(req, parent, name, &e); + } +- if (lo->cache == CACHE_NEVER) { ++ if (lo->cache == CACHE_NONE) { + fi->direct_io = 1; + } else if (lo->cache == CACHE_ALWAYS) { + fi->keep_cache = 1; +@@ -1578,7 +1578,7 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) + } + + fi->fh = fh; +- if (lo->cache == CACHE_NEVER) { ++ if (lo->cache == CACHE_NONE) { + fi->direct_io = 1; + } else if (lo->cache == CACHE_ALWAYS) { + fi->keep_cache = 1; +@@ -2395,7 +2395,7 @@ int main(int argc, char *argv[]) + lo.root.next = lo.root.prev = &lo.root; + lo.root.fd = -1; + lo.root.fuse_ino = FUSE_ROOT_ID; +- lo.cache = CACHE_NORMAL; ++ lo.cache = CACHE_AUTO; + + /* + * Set up the ino map like this: +@@ -2470,11 +2470,11 @@ int main(int argc, char *argv[]) + } + if (!lo.timeout_set) { + switch (lo.cache) { +- case CACHE_NEVER: ++ case CACHE_NONE: + lo.timeout = 0.0; + break; + +- case CACHE_NORMAL: ++ case CACHE_AUTO: + lo.timeout = 1.0; + break; + diff --git a/0083-virtiofsd-passthrough_ll-use-hashtable.patch b/0083-virtiofsd-passthrough_ll-use-hashtable.patch new file mode 100644 index 0000000..0780206 --- /dev/null +++ b/0083-virtiofsd-passthrough_ll-use-hashtable.patch @@ -0,0 +1,195 @@ +From: Miklos Szeredi +Date: Mon, 27 Jan 2020 19:01:52 +0000 +Subject: [PATCH] virtiofsd: passthrough_ll: use hashtable +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Improve performance of inode lookup by using a hash table. + +Signed-off-by: Miklos Szeredi +Signed-off-by: Dr. David Alan Gilbert +Signed-off-by: Liu Bo +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit bfc50a6e06b10b2f9dbaf6c1a89dd523322e016f) +--- + tools/virtiofsd/passthrough_ll.c | 81 ++++++++++++++++++-------------- + 1 file changed, 45 insertions(+), 36 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index b40f2874a7..b176a314d6 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -84,13 +84,15 @@ struct lo_map { + ssize_t freelist; + }; + ++struct lo_key { ++ ino_t ino; ++ dev_t dev; ++}; ++ + struct lo_inode { +- struct lo_inode *next; /* protected by lo->mutex */ +- struct lo_inode *prev; /* protected by lo->mutex */ + int fd; + bool is_symlink; +- ino_t ino; +- dev_t dev; ++ struct lo_key key; + uint64_t refcount; /* protected by lo->mutex */ + fuse_ino_t fuse_ino; + }; +@@ -119,7 +121,8 @@ struct lo_data { + int timeout_set; + int readdirplus_set; + int readdirplus_clear; +- struct lo_inode root; /* protected by lo->mutex */ ++ struct lo_inode root; ++ GHashTable *inodes; /* protected by lo->mutex */ + struct lo_map ino_map; /* protected by lo->mutex */ + struct lo_map dirp_map; /* protected by lo->mutex */ + struct lo_map fd_map; /* protected by lo->mutex */ +@@ -573,7 +576,7 @@ retry: + } + goto fail_unref; + } +- if (stat.st_dev != inode->dev || stat.st_ino != inode->ino) { ++ if (stat.st_dev != inode->key.dev || stat.st_ino != inode->key.ino) { + if (!retries) { + fuse_log(FUSE_LOG_WARNING, + "%s: failed to match last\n", __func__); +@@ -753,19 +756,20 @@ out_err: + static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st) + { + struct lo_inode *p; +- struct lo_inode *ret = NULL; ++ struct lo_key key = { ++ .ino = st->st_ino, ++ .dev = st->st_dev, ++ }; + + pthread_mutex_lock(&lo->mutex); +- for (p = lo->root.next; p != &lo->root; p = p->next) { +- if (p->ino == st->st_ino && p->dev == st->st_dev) { +- assert(p->refcount > 0); +- ret = p; +- ret->refcount++; +- break; +- } ++ p = g_hash_table_lookup(lo->inodes, &key); ++ if (p) { ++ assert(p->refcount > 0); ++ p->refcount++; + } + pthread_mutex_unlock(&lo->mutex); +- return ret; ++ ++ return p; + } + + static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, +@@ -810,8 +814,6 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + close(newfd); + newfd = -1; + } else { +- struct lo_inode *prev, *next; +- + saverr = ENOMEM; + inode = calloc(1, sizeof(struct lo_inode)); + if (!inode) { +@@ -822,17 +824,12 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + inode->refcount = 1; + inode->fd = newfd; + newfd = -1; +- inode->ino = e->attr.st_ino; +- inode->dev = e->attr.st_dev; ++ inode->key.ino = e->attr.st_ino; ++ inode->key.dev = e->attr.st_dev; + + pthread_mutex_lock(&lo->mutex); + inode->fuse_ino = lo_add_inode_mapping(req, inode); +- prev = &lo->root; +- next = prev->next; +- next->prev = inode; +- inode->next = next; +- inode->prev = prev; +- prev->next = inode; ++ g_hash_table_insert(lo->inodes, &inode->key, inode); + pthread_mutex_unlock(&lo->mutex); + } + e->ino = inode->fuse_ino; +@@ -1162,14 +1159,8 @@ static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, + assert(inode->refcount >= n); + inode->refcount -= n; + if (!inode->refcount) { +- struct lo_inode *prev, *next; +- +- prev = inode->prev; +- next = inode->next; +- next->prev = prev; +- prev->next = next; +- + lo_map_remove(&lo->ino_map, inode->fuse_ino); ++ g_hash_table_remove(lo->inodes, &inode->key); + pthread_mutex_unlock(&lo->mutex); + close(inode->fd); + free(inode); +@@ -1369,7 +1360,7 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + + /* Hide root's parent directory */ + if (dinode == &lo->root && strcmp(name, "..") == 0) { +- e.attr.st_ino = lo->root.ino; ++ e.attr.st_ino = lo->root.key.ino; + e.attr.st_mode = DT_DIR << 12; + } + +@@ -2370,11 +2361,26 @@ static void setup_root(struct lo_data *lo, struct lo_inode *root) + + root->is_symlink = false; + root->fd = fd; +- root->ino = stat.st_ino; +- root->dev = stat.st_dev; ++ root->key.ino = stat.st_ino; ++ root->key.dev = stat.st_dev; + root->refcount = 2; + } + ++static guint lo_key_hash(gconstpointer key) ++{ ++ const struct lo_key *lkey = key; ++ ++ return (guint)lkey->ino + (guint)lkey->dev; ++} ++ ++static gboolean lo_key_equal(gconstpointer a, gconstpointer b) ++{ ++ const struct lo_key *la = a; ++ const struct lo_key *lb = b; ++ ++ return la->ino == lb->ino && la->dev == lb->dev; ++} ++ + int main(int argc, char *argv[]) + { + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); +@@ -2392,7 +2398,7 @@ int main(int argc, char *argv[]) + umask(0); + + pthread_mutex_init(&lo.mutex, NULL); +- lo.root.next = lo.root.prev = &lo.root; ++ lo.inodes = g_hash_table_new(lo_key_hash, lo_key_equal); + lo.root.fd = -1; + lo.root.fuse_ino = FUSE_ROOT_ID; + lo.cache = CACHE_AUTO; +@@ -2522,6 +2528,9 @@ err_out2: + err_out1: + fuse_opt_free_args(&args); + ++ if (lo.inodes) { ++ g_hash_table_destroy(lo.inodes); ++ } + lo_map_destroy(&lo.fd_map); + lo_map_destroy(&lo.dirp_map); + lo_map_destroy(&lo.ino_map); diff --git a/0084-virtiofsd-Clean-up-inodes-on-destroy.patch b/0084-virtiofsd-Clean-up-inodes-on-destroy.patch new file mode 100644 index 0000000..48b65d5 --- /dev/null +++ b/0084-virtiofsd-Clean-up-inodes-on-destroy.patch @@ -0,0 +1,69 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:01:53 +0000 +Subject: [PATCH] virtiofsd: Clean up inodes on destroy +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Clear out our inodes and fd's on a 'destroy' - so we get rid +of them if we reboot the guest. + +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 771b01eb76ff480fee984bd1d21727147cc3e702) +--- + tools/virtiofsd/passthrough_ll.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index b176a314d6..9ed77a17fd 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1169,6 +1169,25 @@ static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, + } + } + ++static int unref_all_inodes_cb(gpointer key, gpointer value, gpointer user_data) ++{ ++ struct lo_inode *inode = value; ++ struct lo_data *lo = user_data; ++ ++ inode->refcount = 0; ++ lo_map_remove(&lo->ino_map, inode->fuse_ino); ++ close(inode->fd); ++ ++ return TRUE; ++} ++ ++static void unref_all_inodes(struct lo_data *lo) ++{ ++ pthread_mutex_lock(&lo->mutex); ++ g_hash_table_foreach_remove(lo->inodes, unref_all_inodes_cb, lo); ++ pthread_mutex_unlock(&lo->mutex); ++} ++ + static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) + { + struct lo_data *lo = lo_data(req); +@@ -2035,6 +2054,12 @@ static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, + } + } + ++static void lo_destroy(void *userdata) ++{ ++ struct lo_data *lo = (struct lo_data *)userdata; ++ unref_all_inodes(lo); ++} ++ + static struct fuse_lowlevel_ops lo_oper = { + .init = lo_init, + .lookup = lo_lookup, +@@ -2073,6 +2098,7 @@ static struct fuse_lowlevel_ops lo_oper = { + .copy_file_range = lo_copy_file_range, + #endif + .lseek = lo_lseek, ++ .destroy = lo_destroy, + }; + + /* Print vhost-user.json backend program capabilities */ diff --git a/0085-virtiofsd-support-nanosecond-resolution-for-file-tim.patch b/0085-virtiofsd-support-nanosecond-resolution-for-file-tim.patch new file mode 100644 index 0000000..59996ef --- /dev/null +++ b/0085-virtiofsd-support-nanosecond-resolution-for-file-tim.patch @@ -0,0 +1,66 @@ +From: Jiufei Xue +Date: Mon, 27 Jan 2020 19:01:54 +0000 +Subject: [PATCH] virtiofsd: support nanosecond resolution for file timestamp +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Define HAVE_STRUCT_STAT_ST_ATIM to 1 if `st_atim' is member of `struct +stat' which means support nanosecond resolution for the file timestamp +fields. + +Signed-off-by: Jiufei Xue +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 8a792b034d4b315251fd842bb4c73a133aa1368f) +--- + configure | 16 ++++++++++++++++ + tools/virtiofsd/fuse_misc.h | 1 + + 2 files changed, 17 insertions(+) + +diff --git a/configure b/configure +index afe9393f04..dd50b03b01 100755 +--- a/configure ++++ b/configure +@@ -5217,6 +5217,19 @@ if compile_prog "" "" ; then + strchrnul=yes + fi + ++######################################### ++# check if we have st_atim ++ ++st_atim=no ++cat > $TMPC << EOF ++#include ++#include ++int main(void) { return offsetof(struct stat, st_atim); } ++EOF ++if compile_prog "" "" ; then ++ st_atim=yes ++fi ++ + ########################################## + # check if trace backend exists + +@@ -6918,6 +6931,9 @@ fi + if test "$strchrnul" = "yes" ; then + echo "HAVE_STRCHRNUL=y" >> $config_host_mak + fi ++if test "$st_atim" = "yes" ; then ++ echo "HAVE_STRUCT_STAT_ST_ATIM=y" >> $config_host_mak ++fi + if test "$byteswap_h" = "yes" ; then + echo "CONFIG_BYTESWAP_H=y" >> $config_host_mak + fi +diff --git a/tools/virtiofsd/fuse_misc.h b/tools/virtiofsd/fuse_misc.h +index f252baa752..5c618ce21f 100644 +--- a/tools/virtiofsd/fuse_misc.h ++++ b/tools/virtiofsd/fuse_misc.h +@@ -7,6 +7,7 @@ + */ + + #include ++#include "config-host.h" + + /* + * Versioned symbols cannot be used in some cases because it diff --git a/0086-virtiofsd-fix-error-handling-in-main.patch b/0086-virtiofsd-fix-error-handling-in-main.patch new file mode 100644 index 0000000..3828cef --- /dev/null +++ b/0086-virtiofsd-fix-error-handling-in-main.patch @@ -0,0 +1,47 @@ +From: Liu Bo +Date: Mon, 27 Jan 2020 19:01:55 +0000 +Subject: [PATCH] virtiofsd: fix error handling in main() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Neither fuse_parse_cmdline() nor fuse_opt_parse() goes to the right place +to do cleanup. + +Signed-off-by: Liu Bo +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit c6de804670f2255ce776263124c37f3370dc5ac1) +--- + tools/virtiofsd/passthrough_ll.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 9ed77a17fd..af050c6d97 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -2443,13 +2443,14 @@ int main(int argc, char *argv[]) + lo_map_init(&lo.fd_map); + + if (fuse_parse_cmdline(&args, &opts) != 0) { +- return 1; ++ goto err_out1; + } + fuse_set_log_func(log_func); + use_syslog = opts.syslog; + if (use_syslog) { + openlog("virtiofsd", LOG_PID, LOG_DAEMON); + } ++ + if (opts.show_help) { + printf("usage: %s [options]\n\n", argv[0]); + fuse_cmdline_help(); +@@ -2468,7 +2469,7 @@ int main(int argc, char *argv[]) + } + + if (fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1) { +- return 1; ++ goto err_out1; + } + + /* diff --git a/0087-virtiofsd-cleanup-allocated-resource-in-se.patch b/0087-virtiofsd-cleanup-allocated-resource-in-se.patch new file mode 100644 index 0000000..cf1ddb1 --- /dev/null +++ b/0087-virtiofsd-cleanup-allocated-resource-in-se.patch @@ -0,0 +1,66 @@ +From: Liu Bo +Date: Mon, 27 Jan 2020 19:01:56 +0000 +Subject: [PATCH] virtiofsd: cleanup allocated resource in se +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This cleans up unfreed resources in se on quiting, including +se->virtio_dev, se->vu_socket_path, se->vu_socketfd. + +Signed-off-by: Liu Bo +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 61cfc44982e566c33b9d5df17858e4d5ae373873) +--- + tools/virtiofsd/fuse_lowlevel.c | 7 +++++++ + tools/virtiofsd/fuse_virtio.c | 7 +++++++ + tools/virtiofsd/fuse_virtio.h | 2 +- + 3 files changed, 15 insertions(+), 1 deletion(-) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 65f91dabae..440508a6ec 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -2532,6 +2532,13 @@ void fuse_session_destroy(struct fuse_session *se) + if (se->fd != -1) { + close(se->fd); + } ++ ++ if (se->vu_socket_path) { ++ virtio_session_close(se); ++ free(se->vu_socket_path); ++ se->vu_socket_path = NULL; ++ } ++ + free(se); + } + +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index 7a8774a3ee..e7bd772805 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -833,3 +833,10 @@ int virtio_session_mount(struct fuse_session *se) + + return 0; + } ++ ++void virtio_session_close(struct fuse_session *se) ++{ ++ close(se->vu_socketfd); ++ free(se->virtio_dev); ++ se->virtio_dev = NULL; ++} +diff --git a/tools/virtiofsd/fuse_virtio.h b/tools/virtiofsd/fuse_virtio.h +index cc676b9193..111684032c 100644 +--- a/tools/virtiofsd/fuse_virtio.h ++++ b/tools/virtiofsd/fuse_virtio.h +@@ -19,7 +19,7 @@ + struct fuse_session; + + int virtio_session_mount(struct fuse_session *se); +- ++void virtio_session_close(struct fuse_session *se); + int virtio_loop(struct fuse_session *se); + + diff --git a/0088-virtiofsd-fix-memory-leak-on-lo.source.patch b/0088-virtiofsd-fix-memory-leak-on-lo.source.patch new file mode 100644 index 0000000..3c509a7 --- /dev/null +++ b/0088-virtiofsd-fix-memory-leak-on-lo.source.patch @@ -0,0 +1,47 @@ +From: Liu Bo +Date: Mon, 27 Jan 2020 19:01:57 +0000 +Subject: [PATCH] virtiofsd: fix memory leak on lo.source + +valgrind reported that lo.source is leaked on quiting, but it was defined +as (const char*) as it may point to a const string "/". + +Signed-off-by: Liu Bo +Reviewed-by: Misono Tomohiro +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit eb68a33b5fc5dde87bd9b99b94e7c33a5d8ea82e) +--- + tools/virtiofsd/passthrough_ll.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index af050c6d97..056ebe8556 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -115,7 +115,7 @@ struct lo_data { + int writeback; + int flock; + int xattr; +- const char *source; ++ char *source; + double timeout; + int cache; + int timeout_set; +@@ -2497,9 +2497,8 @@ int main(int argc, char *argv[]) + fuse_log(FUSE_LOG_ERR, "source is not a directory\n"); + exit(1); + } +- + } else { +- lo.source = "/"; ++ lo.source = strdup("/"); + } + if (!lo.timeout_set) { + switch (lo.cache) { +@@ -2570,5 +2569,7 @@ err_out1: + close(lo.root.fd); + } + ++ free(lo.source); ++ + return ret ? 1 : 0; + } diff --git a/0089-virtiofsd-add-helper-for-lo_data-cleanup.patch b/0089-virtiofsd-add-helper-for-lo_data-cleanup.patch new file mode 100644 index 0000000..2cdf880 --- /dev/null +++ b/0089-virtiofsd-add-helper-for-lo_data-cleanup.patch @@ -0,0 +1,72 @@ +From: Liu Bo +Date: Mon, 27 Jan 2020 19:01:58 +0000 +Subject: [PATCH] virtiofsd: add helper for lo_data cleanup +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This offers an helper function for lo_data's cleanup. + +Signed-off-by: Liu Bo +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 18a69cbbb6a4caa7c2040c6db4a33b044a32be7e) +--- + tools/virtiofsd/passthrough_ll.c | 37 ++++++++++++++++++-------------- + 1 file changed, 21 insertions(+), 16 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 056ebe8556..e8dc5c7320 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -2407,6 +2407,26 @@ static gboolean lo_key_equal(gconstpointer a, gconstpointer b) + return la->ino == lb->ino && la->dev == lb->dev; + } + ++static void fuse_lo_data_cleanup(struct lo_data *lo) ++{ ++ if (lo->inodes) { ++ g_hash_table_destroy(lo->inodes); ++ } ++ lo_map_destroy(&lo->fd_map); ++ lo_map_destroy(&lo->dirp_map); ++ lo_map_destroy(&lo->ino_map); ++ ++ if (lo->proc_self_fd >= 0) { ++ close(lo->proc_self_fd); ++ } ++ ++ if (lo->root.fd >= 0) { ++ close(lo->root.fd); ++ } ++ ++ free(lo->source); ++} ++ + int main(int argc, char *argv[]) + { + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); +@@ -2554,22 +2574,7 @@ err_out2: + err_out1: + fuse_opt_free_args(&args); + +- if (lo.inodes) { +- g_hash_table_destroy(lo.inodes); +- } +- lo_map_destroy(&lo.fd_map); +- lo_map_destroy(&lo.dirp_map); +- lo_map_destroy(&lo.ino_map); +- +- if (lo.proc_self_fd >= 0) { +- close(lo.proc_self_fd); +- } +- +- if (lo.root.fd >= 0) { +- close(lo.root.fd); +- } +- +- free(lo.source); ++ fuse_lo_data_cleanup(&lo); + + return ret ? 1 : 0; + } diff --git a/0090-virtiofsd-Prevent-multiply-running-with-same-vhost_u.patch b/0090-virtiofsd-Prevent-multiply-running-with-same-vhost_u.patch new file mode 100644 index 0000000..d3f2ebc --- /dev/null +++ b/0090-virtiofsd-Prevent-multiply-running-with-same-vhost_u.patch @@ -0,0 +1,127 @@ +From: Masayoshi Mizuma +Date: Mon, 27 Jan 2020 19:01:59 +0000 +Subject: [PATCH] virtiofsd: Prevent multiply running with same + vhost_user_socket +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +virtiofsd can run multiply even if the vhost_user_socket is same path. + + ]# ./virtiofsd -o vhost_user_socket=/tmp/vhostqemu -o source=/tmp/share & + [1] 244965 + virtio_session_mount: Waiting for vhost-user socket connection... + ]# ./virtiofsd -o vhost_user_socket=/tmp/vhostqemu -o source=/tmp/share & + [2] 244966 + virtio_session_mount: Waiting for vhost-user socket connection... + ]# + +The user will get confused about the situation and maybe the cause of the +unexpected problem. So it's better to prevent the multiple running. + +Create a regular file under localstatedir directory to exclude the +vhost_user_socket. To create and lock the file, use qemu_write_pidfile() +because the API has some sanity checks and file lock. + +Signed-off-by: Masayoshi Mizuma +Signed-off-by: Dr. David Alan Gilbert + Applied fixes from Stefan's review and moved osdep include +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 96814800d2b49d18737c36e021c387697ec40c62) +--- + tools/virtiofsd/fuse_lowlevel.c | 1 + + tools/virtiofsd/fuse_virtio.c | 49 ++++++++++++++++++++++++++++++++- + 2 files changed, 49 insertions(+), 1 deletion(-) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 440508a6ec..aac282f278 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -18,6 +18,7 @@ + + #include + #include ++#include + #include + #include + #include +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index e7bd772805..b7948def27 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -13,11 +13,12 @@ + + #include "qemu/osdep.h" + #include "qemu/iov.h" +-#include "fuse_virtio.h" ++#include "qapi/error.h" + #include "fuse_i.h" + #include "standard-headers/linux/fuse.h" + #include "fuse_misc.h" + #include "fuse_opt.h" ++#include "fuse_virtio.h" + + #include + #include +@@ -743,6 +744,42 @@ int virtio_loop(struct fuse_session *se) + return 0; + } + ++static void strreplace(char *s, char old, char new) ++{ ++ for (; *s; ++s) { ++ if (*s == old) { ++ *s = new; ++ } ++ } ++} ++ ++static bool fv_socket_lock(struct fuse_session *se) ++{ ++ g_autofree gchar *sk_name = NULL; ++ g_autofree gchar *pidfile = NULL; ++ g_autofree gchar *dir = NULL; ++ Error *local_err = NULL; ++ ++ dir = qemu_get_local_state_pathname("run/virtiofsd"); ++ ++ if (g_mkdir_with_parents(dir, S_IRWXU) < 0) { ++ fuse_log(FUSE_LOG_ERR, "%s: Failed to create directory %s: %s", ++ __func__, dir, strerror(errno)); ++ return false; ++ } ++ ++ sk_name = g_strdup(se->vu_socket_path); ++ strreplace(sk_name, '/', '.'); ++ pidfile = g_strdup_printf("%s/%s.pid", dir, sk_name); ++ ++ if (!qemu_write_pidfile(pidfile, &local_err)) { ++ error_report_err(local_err); ++ return false; ++ } ++ ++ return true; ++} ++ + static int fv_create_listen_socket(struct fuse_session *se) + { + struct sockaddr_un un; +@@ -758,6 +795,16 @@ static int fv_create_listen_socket(struct fuse_session *se) + return -1; + } + ++ if (!strlen(se->vu_socket_path)) { ++ fuse_log(FUSE_LOG_ERR, "Socket path is empty\n"); ++ return -1; ++ } ++ ++ /* Check the vu_socket_path is already used */ ++ if (!fv_socket_lock(se)) { ++ return -1; ++ } ++ + /* + * Create the Unix socket to communicate with qemu + * based on QEMU's vhost-user-bridge diff --git a/0091-virtiofsd-enable-PARALLEL_DIROPS-during-INIT.patch b/0091-virtiofsd-enable-PARALLEL_DIROPS-during-INIT.patch new file mode 100644 index 0000000..dfdd3b3 --- /dev/null +++ b/0091-virtiofsd-enable-PARALLEL_DIROPS-during-INIT.patch @@ -0,0 +1,31 @@ +From: Liu Bo +Date: Mon, 27 Jan 2020 19:02:00 +0000 +Subject: [PATCH] virtiofsd: enable PARALLEL_DIROPS during INIT +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +lookup is a RO operations, PARALLEL_DIROPS can be enabled. + +Signed-off-by: Liu Bo +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit b7ed733a3841c4d489d3bd6ca7ed23c84db119c2) +--- + tools/virtiofsd/fuse_lowlevel.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index aac282f278..70568d22a4 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -2062,6 +2062,9 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, + if (se->conn.want & FUSE_CAP_ASYNC_READ) { + outarg.flags |= FUSE_ASYNC_READ; + } ++ if (se->conn.want & FUSE_CAP_PARALLEL_DIROPS) { ++ outarg.flags |= FUSE_PARALLEL_DIROPS; ++ } + if (se->conn.want & FUSE_CAP_POSIX_LOCKS) { + outarg.flags |= FUSE_POSIX_LOCKS; + } diff --git a/0092-virtiofsd-fix-incorrect-error-handling-in-lo_do_look.patch b/0092-virtiofsd-fix-incorrect-error-handling-in-lo_do_look.patch new file mode 100644 index 0000000..fa99bf8 --- /dev/null +++ b/0092-virtiofsd-fix-incorrect-error-handling-in-lo_do_look.patch @@ -0,0 +1,27 @@ +From: Eric Ren +Date: Mon, 27 Jan 2020 19:02:01 +0000 +Subject: [PATCH] virtiofsd: fix incorrect error handling in lo_do_lookup +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Eric Ren +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit fc3f0041b43b6c64aa97b3558a6abe1a10028354) +--- + tools/virtiofsd/passthrough_ll.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index e8dc5c7320..05b5f898db 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -814,7 +814,6 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + close(newfd); + newfd = -1; + } else { +- saverr = ENOMEM; + inode = calloc(1, sizeof(struct lo_inode)); + if (!inode) { + goto out_err; diff --git a/0093-Virtiofsd-fix-memory-leak-on-fuse-queueinfo.patch b/0093-Virtiofsd-fix-memory-leak-on-fuse-queueinfo.patch new file mode 100644 index 0000000..bef1ac0 --- /dev/null +++ b/0093-Virtiofsd-fix-memory-leak-on-fuse-queueinfo.patch @@ -0,0 +1,44 @@ +From: Liu Bo +Date: Mon, 27 Jan 2020 19:02:02 +0000 +Subject: [PATCH] Virtiofsd: fix memory leak on fuse queueinfo + +For fuse's queueinfo, both queueinfo array and queueinfos are allocated in +fv_queue_set_started() but not cleaned up when the daemon process quits. + +This fixes the leak in proper places. + +Signed-off-by: Liu Bo +Signed-off-by: Eric Ren +Reviewed-by: Misono Tomohiro +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 740b0b700a6338a1cf60c26229651ac5f6724944) +--- + tools/virtiofsd/fuse_virtio.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index b7948def27..fb8d6d1379 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -625,6 +625,8 @@ static void fv_queue_cleanup_thread(struct fv_VuDev *vud, int qidx) + } + close(ourqi->kill_fd); + ourqi->kick_fd = -1; ++ free(vud->qi[qidx]); ++ vud->qi[qidx] = NULL; + } + + /* Callback from libvhost-user on start or stop of a queue */ +@@ -884,6 +886,12 @@ int virtio_session_mount(struct fuse_session *se) + void virtio_session_close(struct fuse_session *se) + { + close(se->vu_socketfd); ++ ++ if (!se->virtio_dev) { ++ return; ++ } ++ ++ free(se->virtio_dev->qi); + free(se->virtio_dev); + se->virtio_dev = NULL; + } diff --git a/0094-virtiofsd-Support-remote-posix-locks.patch b/0094-virtiofsd-Support-remote-posix-locks.patch new file mode 100644 index 0000000..83b237c --- /dev/null +++ b/0094-virtiofsd-Support-remote-posix-locks.patch @@ -0,0 +1,336 @@ +From: Vivek Goyal +Date: Mon, 27 Jan 2020 19:02:03 +0000 +Subject: [PATCH] virtiofsd: Support remote posix locks + +Doing posix locks with-in guest kernel are not sufficient if a file/dir +is being shared by multiple guests. So we need the notion of daemon doing +the locks which are visible to rest of the guests. + +Given posix locks are per process, one can not call posix lock API on host, +otherwise bunch of basic posix locks properties are broken. For example, +If two processes (A and B) in guest open the file and take locks on different +sections of file, if one of the processes closes the fd, it will close +fd on virtiofsd and all posix locks on file will go away. This means if +process A closes the fd, then locks of process B will go away too. + +Similar other problems exist too. + +This patch set tries to emulate posix locks while using open file +description locks provided on Linux. + +Daemon provides two options (-o posix_lock, -o no_posix_lock) to enable +or disable posix locking in daemon. By default it is enabled. + +There are few issues though. + +- GETLK() returns pid of process holding lock. As we are emulating locks + using OFD, and these locks are not per process and don't return pid + of process, so GETLK() in guest does not reuturn process pid. + +- As of now only F_SETLK is supported and not F_SETLKW. We can't block + the thread in virtiofsd for arbitrary long duration as there is only + one thread serving the queue. That means unlock request will not make + it to daemon and F_SETLKW will block infinitely and bring virtio-fs + to a halt. This is a solvable problem though and will require significant + changes in virtiofsd and kernel. Left as a TODO item for now. + +Signed-off-by: Vivek Goyal +Reviewed-by: Masayoshi Mizuma +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 0e81414c54161296212f6bc8a1c70526c4a9755a) +--- + tools/virtiofsd/helper.c | 3 + + tools/virtiofsd/passthrough_ll.c | 189 +++++++++++++++++++++++++++++++ + 2 files changed, 192 insertions(+) + +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index 567202444a..33749bfcb7 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -156,6 +156,9 @@ void fuse_cmdline_help(void) + " allowed (default: 10)\n" + " -o norace disable racy fallback\n" + " default: false\n" ++ " -o posix_lock|no_posix_lock\n" ++ " enable/disable remote posix lock\n" ++ " default: posix_lock\n" + " -o readdirplus|no_readdirplus\n" + " enable/disable readirplus\n" + " default: readdirplus except with " +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 05b5f898db..9414935b52 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -67,6 +67,12 @@ + #include "passthrough_helpers.h" + #include "seccomp.h" + ++/* Keep track of inode posix locks for each owner. */ ++struct lo_inode_plock { ++ uint64_t lock_owner; ++ int fd; /* fd for OFD locks */ ++}; ++ + struct lo_map_elem { + union { + struct lo_inode *inode; +@@ -95,6 +101,8 @@ struct lo_inode { + struct lo_key key; + uint64_t refcount; /* protected by lo->mutex */ + fuse_ino_t fuse_ino; ++ pthread_mutex_t plock_mutex; ++ GHashTable *posix_locks; /* protected by lo_inode->plock_mutex */ + }; + + struct lo_cred { +@@ -114,6 +122,7 @@ struct lo_data { + int norace; + int writeback; + int flock; ++ int posix_lock; + int xattr; + char *source; + double timeout; +@@ -137,6 +146,8 @@ static const struct fuse_opt lo_opts[] = { + { "source=%s", offsetof(struct lo_data, source), 0 }, + { "flock", offsetof(struct lo_data, flock), 1 }, + { "no_flock", offsetof(struct lo_data, flock), 0 }, ++ { "posix_lock", offsetof(struct lo_data, posix_lock), 1 }, ++ { "no_posix_lock", offsetof(struct lo_data, posix_lock), 0 }, + { "xattr", offsetof(struct lo_data, xattr), 1 }, + { "no_xattr", offsetof(struct lo_data, xattr), 0 }, + { "timeout=%lf", offsetof(struct lo_data, timeout), 0 }, +@@ -485,6 +496,17 @@ static void lo_init(void *userdata, struct fuse_conn_info *conn) + fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); + conn->want |= FUSE_CAP_FLOCK_LOCKS; + } ++ ++ if (conn->capable & FUSE_CAP_POSIX_LOCKS) { ++ if (lo->posix_lock) { ++ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating posix locks\n"); ++ conn->want |= FUSE_CAP_POSIX_LOCKS; ++ } else { ++ fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling posix locks\n"); ++ conn->want &= ~FUSE_CAP_POSIX_LOCKS; ++ } ++ } ++ + if ((lo->cache == CACHE_NONE && !lo->readdirplus_set) || + lo->readdirplus_clear) { + fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling readdirplus\n"); +@@ -772,6 +794,19 @@ static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st) + return p; + } + ++/* value_destroy_func for posix_locks GHashTable */ ++static void posix_locks_value_destroy(gpointer data) ++{ ++ struct lo_inode_plock *plock = data; ++ ++ /* ++ * We had used open() for locks and had only one fd. So ++ * closing this fd should release all OFD locks. ++ */ ++ close(plock->fd); ++ free(plock); ++} ++ + static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + struct fuse_entry_param *e) + { +@@ -825,6 +860,9 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + newfd = -1; + inode->key.ino = e->attr.st_ino; + inode->key.dev = e->attr.st_dev; ++ pthread_mutex_init(&inode->plock_mutex, NULL); ++ inode->posix_locks = g_hash_table_new_full( ++ g_direct_hash, g_direct_equal, NULL, posix_locks_value_destroy); + + pthread_mutex_lock(&lo->mutex); + inode->fuse_ino = lo_add_inode_mapping(req, inode); +@@ -1160,6 +1198,11 @@ static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, + if (!inode->refcount) { + lo_map_remove(&lo->ino_map, inode->fuse_ino); + g_hash_table_remove(lo->inodes, &inode->key); ++ if (g_hash_table_size(inode->posix_locks)) { ++ fuse_log(FUSE_LOG_WARNING, "Hash table is not empty\n"); ++ } ++ g_hash_table_destroy(inode->posix_locks); ++ pthread_mutex_destroy(&inode->plock_mutex); + pthread_mutex_unlock(&lo->mutex); + close(inode->fd); + free(inode); +@@ -1516,6 +1559,136 @@ out: + } + } + ++/* Should be called with inode->plock_mutex held */ ++static struct lo_inode_plock *lookup_create_plock_ctx(struct lo_data *lo, ++ struct lo_inode *inode, ++ uint64_t lock_owner, ++ pid_t pid, int *err) ++{ ++ struct lo_inode_plock *plock; ++ char procname[64]; ++ int fd; ++ ++ plock = ++ g_hash_table_lookup(inode->posix_locks, GUINT_TO_POINTER(lock_owner)); ++ ++ if (plock) { ++ return plock; ++ } ++ ++ plock = malloc(sizeof(struct lo_inode_plock)); ++ if (!plock) { ++ *err = ENOMEM; ++ return NULL; ++ } ++ ++ /* Open another instance of file which can be used for ofd locks. */ ++ sprintf(procname, "%i", inode->fd); ++ ++ /* TODO: What if file is not writable? */ ++ fd = openat(lo->proc_self_fd, procname, O_RDWR); ++ if (fd == -1) { ++ *err = errno; ++ free(plock); ++ return NULL; ++ } ++ ++ plock->lock_owner = lock_owner; ++ plock->fd = fd; ++ g_hash_table_insert(inode->posix_locks, GUINT_TO_POINTER(plock->lock_owner), ++ plock); ++ return plock; ++} ++ ++static void lo_getlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, ++ struct flock *lock) ++{ ++ struct lo_data *lo = lo_data(req); ++ struct lo_inode *inode; ++ struct lo_inode_plock *plock; ++ int ret, saverr = 0; ++ ++ fuse_log(FUSE_LOG_DEBUG, ++ "lo_getlk(ino=%" PRIu64 ", flags=%d)" ++ " owner=0x%lx, l_type=%d l_start=0x%lx" ++ " l_len=0x%lx\n", ++ ino, fi->flags, fi->lock_owner, lock->l_type, lock->l_start, ++ lock->l_len); ++ ++ inode = lo_inode(req, ino); ++ if (!inode) { ++ fuse_reply_err(req, EBADF); ++ return; ++ } ++ ++ pthread_mutex_lock(&inode->plock_mutex); ++ plock = ++ lookup_create_plock_ctx(lo, inode, fi->lock_owner, lock->l_pid, &ret); ++ if (!plock) { ++ pthread_mutex_unlock(&inode->plock_mutex); ++ fuse_reply_err(req, ret); ++ return; ++ } ++ ++ ret = fcntl(plock->fd, F_OFD_GETLK, lock); ++ if (ret == -1) { ++ saverr = errno; ++ } ++ pthread_mutex_unlock(&inode->plock_mutex); ++ ++ if (saverr) { ++ fuse_reply_err(req, saverr); ++ } else { ++ fuse_reply_lock(req, lock); ++ } ++} ++ ++static void lo_setlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, ++ struct flock *lock, int sleep) ++{ ++ struct lo_data *lo = lo_data(req); ++ struct lo_inode *inode; ++ struct lo_inode_plock *plock; ++ int ret, saverr = 0; ++ ++ fuse_log(FUSE_LOG_DEBUG, ++ "lo_setlk(ino=%" PRIu64 ", flags=%d)" ++ " cmd=%d pid=%d owner=0x%lx sleep=%d l_whence=%d" ++ " l_start=0x%lx l_len=0x%lx\n", ++ ino, fi->flags, lock->l_type, lock->l_pid, fi->lock_owner, sleep, ++ lock->l_whence, lock->l_start, lock->l_len); ++ ++ if (sleep) { ++ fuse_reply_err(req, EOPNOTSUPP); ++ return; ++ } ++ ++ inode = lo_inode(req, ino); ++ if (!inode) { ++ fuse_reply_err(req, EBADF); ++ return; ++ } ++ ++ pthread_mutex_lock(&inode->plock_mutex); ++ plock = ++ lookup_create_plock_ctx(lo, inode, fi->lock_owner, lock->l_pid, &ret); ++ ++ if (!plock) { ++ pthread_mutex_unlock(&inode->plock_mutex); ++ fuse_reply_err(req, ret); ++ return; ++ } ++ ++ /* TODO: Is it alright to modify flock? */ ++ lock->l_pid = 0; ++ ret = fcntl(plock->fd, F_OFD_SETLK, lock); ++ if (ret == -1) { ++ saverr = errno; ++ } ++ pthread_mutex_unlock(&inode->plock_mutex); ++ fuse_reply_err(req, saverr); ++} ++ + static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, + struct fuse_file_info *fi) + { +@@ -1617,6 +1790,19 @@ static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) + { + int res; + (void)ino; ++ struct lo_inode *inode; ++ ++ inode = lo_inode(req, ino); ++ if (!inode) { ++ fuse_reply_err(req, EBADF); ++ return; ++ } ++ ++ /* An fd is going away. Cleanup associated posix locks */ ++ pthread_mutex_lock(&inode->plock_mutex); ++ g_hash_table_remove(inode->posix_locks, GUINT_TO_POINTER(fi->lock_owner)); ++ pthread_mutex_unlock(&inode->plock_mutex); ++ + res = close(dup(lo_fi_fd(req, fi))); + fuse_reply_err(req, res == -1 ? errno : 0); + } +@@ -2080,6 +2266,8 @@ static struct fuse_lowlevel_ops lo_oper = { + .releasedir = lo_releasedir, + .fsyncdir = lo_fsyncdir, + .create = lo_create, ++ .getlk = lo_getlk, ++ .setlk = lo_setlk, + .open = lo_open, + .release = lo_release, + .flush = lo_flush, +@@ -2434,6 +2622,7 @@ int main(int argc, char *argv[]) + struct lo_data lo = { + .debug = 0, + .writeback = 0, ++ .posix_lock = 1, + .proc_self_fd = -1, + }; + struct lo_map_elem *root_elem; diff --git a/0095-virtiofsd-use-fuse_lowlevel_is_virtio-in-fuse_sessio.patch b/0095-virtiofsd-use-fuse_lowlevel_is_virtio-in-fuse_sessio.patch new file mode 100644 index 0000000..357130e --- /dev/null +++ b/0095-virtiofsd-use-fuse_lowlevel_is_virtio-in-fuse_sessio.patch @@ -0,0 +1,40 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:02:04 +0000 +Subject: [PATCH] virtiofsd: use fuse_lowlevel_is_virtio() in + fuse_session_destroy() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +vu_socket_path is NULL when --fd=FDNUM was used. Use +fuse_lowlevel_is_virtio() instead. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 620e9d8d9cee6df7fe71168dea950dba0cc21a4a) +--- + tools/virtiofsd/fuse_lowlevel.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 70568d22a4..dab6a31e08 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -2537,12 +2537,13 @@ void fuse_session_destroy(struct fuse_session *se) + close(se->fd); + } + +- if (se->vu_socket_path) { ++ if (fuse_lowlevel_is_virtio(se)) { + virtio_session_close(se); +- free(se->vu_socket_path); +- se->vu_socket_path = NULL; + } + ++ free(se->vu_socket_path); ++ se->vu_socket_path = NULL; ++ + free(se); + } + diff --git a/0096-virtiofsd-prevent-fv_queue_thread-vs-virtio_loop-rac.patch b/0096-virtiofsd-prevent-fv_queue_thread-vs-virtio_loop-rac.patch new file mode 100644 index 0000000..0129a03 --- /dev/null +++ b/0096-virtiofsd-prevent-fv_queue_thread-vs-virtio_loop-rac.patch @@ -0,0 +1,132 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:02:05 +0000 +Subject: [PATCH] virtiofsd: prevent fv_queue_thread() vs virtio_loop() races +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +We call into libvhost-user from the virtqueue handler thread and the +vhost-user message processing thread without a lock. There is nothing +protecting the virtqueue handler thread if the vhost-user message +processing thread changes the virtqueue or memory table while it is +running. + +This patch introduces a read-write lock. Virtqueue handler threads are +readers. The vhost-user message processing thread is a writer. This +will allow concurrency for multiqueue in the future while protecting +against fv_queue_thread() vs virtio_loop() races. + +Note that the critical sections could be made smaller but it would be +more invasive and require libvhost-user changes. Let's start simple and +improve performance later, if necessary. Another option would be an +RCU-style approach with lighter-weight primitives. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit e7b337326d594b71b07cd6dbb332c49c122c80a4) +--- + tools/virtiofsd/fuse_virtio.c | 34 +++++++++++++++++++++++++++++++++- + 1 file changed, 33 insertions(+), 1 deletion(-) + +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index fb8d6d1379..f6242f9338 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -58,6 +58,18 @@ struct fv_VuDev { + VuDev dev; + struct fuse_session *se; + ++ /* ++ * Either handle virtqueues or vhost-user protocol messages. Don't do ++ * both at the same time since that could lead to race conditions if ++ * virtqueues or memory tables change while another thread is accessing ++ * them. ++ * ++ * The assumptions are: ++ * 1. fv_queue_thread() reads/writes to virtqueues and only reads VuDev. ++ * 2. virtio_loop() reads/writes virtqueues and VuDev. ++ */ ++ pthread_rwlock_t vu_dispatch_rwlock; ++ + /* + * The following pair of fields are only accessed in the main + * virtio_loop +@@ -415,6 +427,8 @@ static void *fv_queue_thread(void *opaque) + qi->qidx, qi->kick_fd); + while (1) { + struct pollfd pf[2]; ++ int ret; ++ + pf[0].fd = qi->kick_fd; + pf[0].events = POLLIN; + pf[0].revents = 0; +@@ -461,6 +475,9 @@ static void *fv_queue_thread(void *opaque) + fuse_log(FUSE_LOG_ERR, "Eventfd_read for queue: %m\n"); + break; + } ++ /* Mutual exclusion with virtio_loop() */ ++ ret = pthread_rwlock_rdlock(&qi->virtio_dev->vu_dispatch_rwlock); ++ assert(ret == 0); /* there is no possible error case */ + /* out is from guest, in is too guest */ + unsigned int in_bytes, out_bytes; + vu_queue_get_avail_bytes(dev, q, &in_bytes, &out_bytes, ~0, ~0); +@@ -469,6 +486,7 @@ static void *fv_queue_thread(void *opaque) + "%s: Queue %d gave evalue: %zx available: in: %u out: %u\n", + __func__, qi->qidx, (size_t)evalue, in_bytes, out_bytes); + ++ + while (1) { + bool allocated_bufv = false; + struct fuse_bufvec bufv; +@@ -597,6 +615,8 @@ static void *fv_queue_thread(void *opaque) + free(elem); + elem = NULL; + } ++ ++ pthread_rwlock_unlock(&qi->virtio_dev->vu_dispatch_rwlock); + } + out: + pthread_mutex_destroy(&ch.lock); +@@ -711,6 +731,8 @@ int virtio_loop(struct fuse_session *se) + + while (!fuse_session_exited(se)) { + struct pollfd pf[1]; ++ bool ok; ++ int ret; + pf[0].fd = se->vu_socketfd; + pf[0].events = POLLIN; + pf[0].revents = 0; +@@ -735,7 +757,15 @@ int virtio_loop(struct fuse_session *se) + } + assert(pf[0].revents & POLLIN); + fuse_log(FUSE_LOG_DEBUG, "%s: Got VU event\n", __func__); +- if (!vu_dispatch(&se->virtio_dev->dev)) { ++ /* Mutual exclusion with fv_queue_thread() */ ++ ret = pthread_rwlock_wrlock(&se->virtio_dev->vu_dispatch_rwlock); ++ assert(ret == 0); /* there is no possible error case */ ++ ++ ok = vu_dispatch(&se->virtio_dev->dev); ++ ++ pthread_rwlock_unlock(&se->virtio_dev->vu_dispatch_rwlock); ++ ++ if (!ok) { + fuse_log(FUSE_LOG_ERR, "%s: vu_dispatch failed\n", __func__); + break; + } +@@ -877,6 +907,7 @@ int virtio_session_mount(struct fuse_session *se) + + se->vu_socketfd = data_sock; + se->virtio_dev->se = se; ++ pthread_rwlock_init(&se->virtio_dev->vu_dispatch_rwlock, NULL); + vu_init(&se->virtio_dev->dev, 2, se->vu_socketfd, fv_panic, fv_set_watch, + fv_remove_watch, &fv_iface); + +@@ -892,6 +923,7 @@ void virtio_session_close(struct fuse_session *se) + } + + free(se->virtio_dev->qi); ++ pthread_rwlock_destroy(&se->virtio_dev->vu_dispatch_rwlock); + free(se->virtio_dev); + se->virtio_dev = NULL; + } diff --git a/0097-virtiofsd-make-lo_release-atomic.patch b/0097-virtiofsd-make-lo_release-atomic.patch new file mode 100644 index 0000000..8f75b74 --- /dev/null +++ b/0097-virtiofsd-make-lo_release-atomic.patch @@ -0,0 +1,46 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:02:06 +0000 +Subject: [PATCH] virtiofsd: make lo_release() atomic +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Hold the lock across both lo_map_get() and lo_map_remove() to prevent +races between two FUSE_RELEASE requests. In this case I don't see a +serious bug but it's safer to do things atomically. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit baed65c060c0e524530bc243eec427fb408bd477) +--- + tools/virtiofsd/passthrough_ll.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 9414935b52..690edbc4c5 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1772,14 +1772,18 @@ static void lo_release(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) + { + struct lo_data *lo = lo_data(req); +- int fd; ++ struct lo_map_elem *elem; ++ int fd = -1; + + (void)ino; + +- fd = lo_fi_fd(req, fi); +- + pthread_mutex_lock(&lo->mutex); +- lo_map_remove(&lo->fd_map, fi->fh); ++ elem = lo_map_get(&lo->fd_map, fi->fh); ++ if (elem) { ++ fd = elem->fd; ++ elem = NULL; ++ lo_map_remove(&lo->fd_map, fi->fh); ++ } + pthread_mutex_unlock(&lo->mutex); + + close(fd); diff --git a/0098-virtiofsd-prevent-races-with-lo_dirp_put.patch b/0098-virtiofsd-prevent-races-with-lo_dirp_put.patch new file mode 100644 index 0000000..a900bc8 --- /dev/null +++ b/0098-virtiofsd-prevent-races-with-lo_dirp_put.patch @@ -0,0 +1,131 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:02:07 +0000 +Subject: [PATCH] virtiofsd: prevent races with lo_dirp_put() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Introduce lo_dirp_put() so that FUSE_RELEASEDIR does not cause +use-after-free races with other threads that are accessing lo_dirp. + +Also make lo_releasedir() atomic to prevent FUSE_RELEASEDIR racing with +itself. This prevents double-frees. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit acefdde73b403576a241ebd8dbe8431ddc0d9442) +--- + tools/virtiofsd/passthrough_ll.c | 41 +++++++++++++++++++++++++++----- + 1 file changed, 35 insertions(+), 6 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 690edbc4c5..2d703b57e5 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1284,11 +1284,28 @@ static void lo_readlink(fuse_req_t req, fuse_ino_t ino) + } + + struct lo_dirp { ++ gint refcount; + DIR *dp; + struct dirent *entry; + off_t offset; + }; + ++static void lo_dirp_put(struct lo_dirp **dp) ++{ ++ struct lo_dirp *d = *dp; ++ ++ if (!d) { ++ return; ++ } ++ *dp = NULL; ++ ++ if (g_atomic_int_dec_and_test(&d->refcount)) { ++ closedir(d->dp); ++ free(d); ++ } ++} ++ ++/* Call lo_dirp_put() on the return value when no longer needed */ + static struct lo_dirp *lo_dirp(fuse_req_t req, struct fuse_file_info *fi) + { + struct lo_data *lo = lo_data(req); +@@ -1296,6 +1313,9 @@ static struct lo_dirp *lo_dirp(fuse_req_t req, struct fuse_file_info *fi) + + pthread_mutex_lock(&lo->mutex); + elem = lo_map_get(&lo->dirp_map, fi->fh); ++ if (elem) { ++ g_atomic_int_inc(&elem->dirp->refcount); ++ } + pthread_mutex_unlock(&lo->mutex); + if (!elem) { + return NULL; +@@ -1331,6 +1351,7 @@ static void lo_opendir(fuse_req_t req, fuse_ino_t ino, + d->offset = 0; + d->entry = NULL; + ++ g_atomic_int_set(&d->refcount, 1); /* paired with lo_releasedir() */ + pthread_mutex_lock(&lo->mutex); + fh = lo_add_dirp_mapping(req, d); + pthread_mutex_unlock(&lo->mutex); +@@ -1364,7 +1385,7 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + off_t offset, struct fuse_file_info *fi, int plus) + { + struct lo_data *lo = lo_data(req); +- struct lo_dirp *d; ++ struct lo_dirp *d = NULL; + struct lo_inode *dinode; + char *buf = NULL; + char *p; +@@ -1454,6 +1475,8 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + + err = 0; + error: ++ lo_dirp_put(&d); ++ + /* + * If there's an error, we can only signal it if we haven't stored + * any entries yet - otherwise we'd end up with wrong lookup +@@ -1484,22 +1507,25 @@ static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, + struct fuse_file_info *fi) + { + struct lo_data *lo = lo_data(req); ++ struct lo_map_elem *elem; + struct lo_dirp *d; + + (void)ino; + +- d = lo_dirp(req, fi); +- if (!d) { ++ pthread_mutex_lock(&lo->mutex); ++ elem = lo_map_get(&lo->dirp_map, fi->fh); ++ if (!elem) { ++ pthread_mutex_unlock(&lo->mutex); + fuse_reply_err(req, EBADF); + return; + } + +- pthread_mutex_lock(&lo->mutex); ++ d = elem->dirp; + lo_map_remove(&lo->dirp_map, fi->fh); + pthread_mutex_unlock(&lo->mutex); + +- closedir(d->dp); +- free(d); ++ lo_dirp_put(&d); /* paired with lo_opendir() */ ++ + fuse_reply_err(req, 0); + } + +@@ -1710,6 +1736,9 @@ static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, + } else { + res = fsync(fd); + } ++ ++ lo_dirp_put(&d); ++ + fuse_reply_err(req, res == -1 ? errno : 0); + } + diff --git a/0099-virtiofsd-rename-inode-refcount-to-inode-nlookup.patch b/0099-virtiofsd-rename-inode-refcount-to-inode-nlookup.patch new file mode 100644 index 0000000..2025112 --- /dev/null +++ b/0099-virtiofsd-rename-inode-refcount-to-inode-nlookup.patch @@ -0,0 +1,123 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:02:08 +0000 +Subject: [PATCH] virtiofsd: rename inode->refcount to inode->nlookup +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This reference counter plays a specific role in the FUSE protocol. It's +not a generic object reference counter and the FUSE kernel code calls it +"nlookup". + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 1222f015558fc34cea02aa3a5a92de608c82cec8) +--- + tools/virtiofsd/passthrough_ll.c | 37 +++++++++++++++++++++----------- + 1 file changed, 25 insertions(+), 12 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 2d703b57e5..c819b5f782 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -99,7 +99,20 @@ struct lo_inode { + int fd; + bool is_symlink; + struct lo_key key; +- uint64_t refcount; /* protected by lo->mutex */ ++ ++ /* ++ * This counter keeps the inode alive during the FUSE session. ++ * Incremented when the FUSE inode number is sent in a reply ++ * (FUSE_LOOKUP, FUSE_READDIRPLUS, etc). Decremented when an inode is ++ * released by requests like FUSE_FORGET, FUSE_RMDIR, FUSE_RENAME, etc. ++ * ++ * Note that this value is untrusted because the client can manipulate ++ * it arbitrarily using FUSE_FORGET requests. ++ * ++ * Protected by lo->mutex. ++ */ ++ uint64_t nlookup; ++ + fuse_ino_t fuse_ino; + pthread_mutex_t plock_mutex; + GHashTable *posix_locks; /* protected by lo_inode->plock_mutex */ +@@ -568,7 +581,7 @@ retry: + if (last == path) { + p = &lo->root; + pthread_mutex_lock(&lo->mutex); +- p->refcount++; ++ p->nlookup++; + pthread_mutex_unlock(&lo->mutex); + } else { + *last = '\0'; +@@ -786,8 +799,8 @@ static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st) + pthread_mutex_lock(&lo->mutex); + p = g_hash_table_lookup(lo->inodes, &key); + if (p) { +- assert(p->refcount > 0); +- p->refcount++; ++ assert(p->nlookup > 0); ++ p->nlookup++; + } + pthread_mutex_unlock(&lo->mutex); + +@@ -855,7 +868,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + } + + inode->is_symlink = S_ISLNK(e->attr.st_mode); +- inode->refcount = 1; ++ inode->nlookup = 1; + inode->fd = newfd; + newfd = -1; + inode->key.ino = e->attr.st_ino; +@@ -1112,7 +1125,7 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, + } + + pthread_mutex_lock(&lo->mutex); +- inode->refcount++; ++ inode->nlookup++; + pthread_mutex_unlock(&lo->mutex); + e.ino = inode->fuse_ino; + +@@ -1193,9 +1206,9 @@ static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, + } + + pthread_mutex_lock(&lo->mutex); +- assert(inode->refcount >= n); +- inode->refcount -= n; +- if (!inode->refcount) { ++ assert(inode->nlookup >= n); ++ inode->nlookup -= n; ++ if (!inode->nlookup) { + lo_map_remove(&lo->ino_map, inode->fuse_ino); + g_hash_table_remove(lo->inodes, &inode->key); + if (g_hash_table_size(inode->posix_locks)) { +@@ -1216,7 +1229,7 @@ static int unref_all_inodes_cb(gpointer key, gpointer value, gpointer user_data) + struct lo_inode *inode = value; + struct lo_data *lo = user_data; + +- inode->refcount = 0; ++ inode->nlookup = 0; + lo_map_remove(&lo->ino_map, inode->fuse_ino); + close(inode->fd); + +@@ -1241,7 +1254,7 @@ static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) + } + + fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", +- (unsigned long long)ino, (unsigned long long)inode->refcount, ++ (unsigned long long)ino, (unsigned long long)inode->nlookup, + (unsigned long long)nlookup); + + unref_inode_lolocked(lo, inode, nlookup); +@@ -2609,7 +2622,7 @@ static void setup_root(struct lo_data *lo, struct lo_inode *root) + root->fd = fd; + root->key.ino = stat.st_ino; + root->key.dev = stat.st_dev; +- root->refcount = 2; ++ root->nlookup = 2; + } + + static guint lo_key_hash(gconstpointer key) diff --git a/0100-libvhost-user-Fix-some-memtable-remap-cases.patch b/0100-libvhost-user-Fix-some-memtable-remap-cases.patch new file mode 100644 index 0000000..da82476 --- /dev/null +++ b/0100-libvhost-user-Fix-some-memtable-remap-cases.patch @@ -0,0 +1,98 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:02:09 +0000 +Subject: [PATCH] libvhost-user: Fix some memtable remap cases + +If a new setmemtable command comes in once the vhost threads are +running, it will remap the guests address space and the threads +will now be looking in the wrong place. + +Fortunately we're running this command under lock, so we can +update the queue mappings so that threads will look in the new-right +place. + +Note: This doesn't fix things that the threads might be doing +without a lock (e.g. a readv/writev!) That's for another time. + +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 49e9ec749d4db62ae51f76354143cee183912a1d) +--- + contrib/libvhost-user/libvhost-user.c | 33 ++++++++++++++++++++------- + contrib/libvhost-user/libvhost-user.h | 3 +++ + 2 files changed, 28 insertions(+), 8 deletions(-) + +diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c +index 63e41062a4..b89bf18501 100644 +--- a/contrib/libvhost-user/libvhost-user.c ++++ b/contrib/libvhost-user/libvhost-user.c +@@ -564,6 +564,21 @@ vu_reset_device_exec(VuDev *dev, VhostUserMsg *vmsg) + return false; + } + ++static bool ++map_ring(VuDev *dev, VuVirtq *vq) ++{ ++ vq->vring.desc = qva_to_va(dev, vq->vra.desc_user_addr); ++ vq->vring.used = qva_to_va(dev, vq->vra.used_user_addr); ++ vq->vring.avail = qva_to_va(dev, vq->vra.avail_user_addr); ++ ++ DPRINT("Setting virtq addresses:\n"); ++ DPRINT(" vring_desc at %p\n", vq->vring.desc); ++ DPRINT(" vring_used at %p\n", vq->vring.used); ++ DPRINT(" vring_avail at %p\n", vq->vring.avail); ++ ++ return !(vq->vring.desc && vq->vring.used && vq->vring.avail); ++} ++ + static bool + vu_set_mem_table_exec_postcopy(VuDev *dev, VhostUserMsg *vmsg) + { +@@ -767,6 +782,14 @@ vu_set_mem_table_exec(VuDev *dev, VhostUserMsg *vmsg) + close(vmsg->fds[i]); + } + ++ for (i = 0; i < dev->max_queues; i++) { ++ if (dev->vq[i].vring.desc) { ++ if (map_ring(dev, &dev->vq[i])) { ++ vu_panic(dev, "remaping queue %d during setmemtable", i); ++ } ++ } ++ } ++ + return false; + } + +@@ -853,18 +876,12 @@ vu_set_vring_addr_exec(VuDev *dev, VhostUserMsg *vmsg) + DPRINT(" avail_user_addr: 0x%016" PRIx64 "\n", vra->avail_user_addr); + DPRINT(" log_guest_addr: 0x%016" PRIx64 "\n", vra->log_guest_addr); + ++ vq->vra = *vra; + vq->vring.flags = vra->flags; +- vq->vring.desc = qva_to_va(dev, vra->desc_user_addr); +- vq->vring.used = qva_to_va(dev, vra->used_user_addr); +- vq->vring.avail = qva_to_va(dev, vra->avail_user_addr); + vq->vring.log_guest_addr = vra->log_guest_addr; + +- DPRINT("Setting virtq addresses:\n"); +- DPRINT(" vring_desc at %p\n", vq->vring.desc); +- DPRINT(" vring_used at %p\n", vq->vring.used); +- DPRINT(" vring_avail at %p\n", vq->vring.avail); + +- if (!(vq->vring.desc && vq->vring.used && vq->vring.avail)) { ++ if (map_ring(dev, vq)) { + vu_panic(dev, "Invalid vring_addr message"); + return false; + } +diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h +index 1844b6f8d4..5cb7708559 100644 +--- a/contrib/libvhost-user/libvhost-user.h ++++ b/contrib/libvhost-user/libvhost-user.h +@@ -327,6 +327,9 @@ typedef struct VuVirtq { + int err_fd; + unsigned int enable; + bool started; ++ ++ /* Guest addresses of our ring */ ++ struct vhost_vring_addr vra; + } VuVirtq; + + enum VuWatchCondtion { diff --git a/0101-virtiofsd-passthrough_ll-fix-refcounting-on-remove-r.patch b/0101-virtiofsd-passthrough_ll-fix-refcounting-on-remove-r.patch new file mode 100644 index 0000000..e8ba18f --- /dev/null +++ b/0101-virtiofsd-passthrough_ll-fix-refcounting-on-remove-r.patch @@ -0,0 +1,123 @@ +From: Miklos Szeredi +Date: Mon, 27 Jan 2020 19:02:10 +0000 +Subject: [PATCH] virtiofsd: passthrough_ll: fix refcounting on remove/rename + +Signed-off-by: Miklos Szeredi +Reviewed-by: Misono Tomohiro +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 9257e514d861afa759c36704e1904d43ca3fec88) +--- + tools/virtiofsd/passthrough_ll.c | 50 +++++++++++++++++++++++++++++++- + 1 file changed, 49 insertions(+), 1 deletion(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index c819b5f782..e3a6d6b611 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1140,17 +1140,42 @@ out_err: + fuse_reply_err(req, saverr); + } + ++static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, ++ const char *name) ++{ ++ int res; ++ struct stat attr; ++ ++ res = fstatat(lo_fd(req, parent), name, &attr, ++ AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); ++ if (res == -1) { ++ return NULL; ++ } ++ ++ return lo_find(lo_data(req), &attr); ++} ++ + static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) + { + int res; ++ struct lo_inode *inode; ++ struct lo_data *lo = lo_data(req); ++ + if (!is_safe_path_component(name)) { + fuse_reply_err(req, EINVAL); + return; + } + ++ inode = lookup_name(req, parent, name); ++ if (!inode) { ++ fuse_reply_err(req, EIO); ++ return; ++ } ++ + res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); + + fuse_reply_err(req, res == -1 ? errno : 0); ++ unref_inode_lolocked(lo, inode, 1); + } + + static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, +@@ -1158,12 +1183,23 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, + unsigned int flags) + { + int res; ++ struct lo_inode *oldinode; ++ struct lo_inode *newinode; ++ struct lo_data *lo = lo_data(req); + + if (!is_safe_path_component(name) || !is_safe_path_component(newname)) { + fuse_reply_err(req, EINVAL); + return; + } + ++ oldinode = lookup_name(req, parent, name); ++ newinode = lookup_name(req, newparent, newname); ++ ++ if (!oldinode) { ++ fuse_reply_err(req, EIO); ++ goto out; ++ } ++ + if (flags) { + #ifndef SYS_renameat2 + fuse_reply_err(req, EINVAL); +@@ -1176,26 +1212,38 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, + fuse_reply_err(req, res == -1 ? errno : 0); + } + #endif +- return; ++ goto out; + } + + res = renameat(lo_fd(req, parent), name, lo_fd(req, newparent), newname); + + fuse_reply_err(req, res == -1 ? errno : 0); ++out: ++ unref_inode_lolocked(lo, oldinode, 1); ++ unref_inode_lolocked(lo, newinode, 1); + } + + static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) + { + int res; ++ struct lo_inode *inode; ++ struct lo_data *lo = lo_data(req); + + if (!is_safe_path_component(name)) { + fuse_reply_err(req, EINVAL); + return; + } + ++ inode = lookup_name(req, parent, name); ++ if (!inode) { ++ fuse_reply_err(req, EIO); ++ return; ++ } ++ + res = unlinkat(lo_fd(req, parent), name, 0); + + fuse_reply_err(req, res == -1 ? errno : 0); ++ unref_inode_lolocked(lo, inode, 1); + } + + static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, diff --git a/0102-virtiofsd-introduce-inode-refcount-to-prevent-use-af.patch b/0102-virtiofsd-introduce-inode-refcount-to-prevent-use-af.patch new file mode 100644 index 0000000..25e6f71 --- /dev/null +++ b/0102-virtiofsd-introduce-inode-refcount-to-prevent-use-af.patch @@ -0,0 +1,569 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:02:11 +0000 +Subject: [PATCH] virtiofsd: introduce inode refcount to prevent use-after-free + +If thread A is using an inode it must not be deleted by thread B when +processing a FUSE_FORGET request. + +The FUSE protocol itself already has a counter called nlookup that is +used in FUSE_FORGET messages. We cannot trust this counter since the +untrusted client can manipulate it via FUSE_FORGET messages. + +Introduce a new refcount to keep inodes alive for the required lifespan. +lo_inode_put() must be called to release a reference. FUSE's nlookup +counter holds exactly one reference so that the inode stays alive as +long as the client still wants to remember it. + +Note that the lo_inode->is_symlink field is moved to avoid creating a +hole in the struct due to struct field alignment. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Misono Tomohiro +Reviewed-by: Sergio Lopez +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit c241aa9457d88c6a0d027f48fadfed131646bce3) +--- + tools/virtiofsd/passthrough_ll.c | 169 ++++++++++++++++++++++++++----- + 1 file changed, 146 insertions(+), 23 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index e3a6d6b611..ab1613586e 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -97,7 +97,13 @@ struct lo_key { + + struct lo_inode { + int fd; +- bool is_symlink; ++ ++ /* ++ * Atomic reference count for this object. The nlookup field holds a ++ * reference and release it when nlookup reaches 0. ++ */ ++ gint refcount; ++ + struct lo_key key; + + /* +@@ -116,6 +122,8 @@ struct lo_inode { + fuse_ino_t fuse_ino; + pthread_mutex_t plock_mutex; + GHashTable *posix_locks; /* protected by lo_inode->plock_mutex */ ++ ++ bool is_symlink; + }; + + struct lo_cred { +@@ -471,6 +479,23 @@ static ssize_t lo_add_inode_mapping(fuse_req_t req, struct lo_inode *inode) + return elem - lo_data(req)->ino_map.elems; + } + ++static void lo_inode_put(struct lo_data *lo, struct lo_inode **inodep) ++{ ++ struct lo_inode *inode = *inodep; ++ ++ if (!inode) { ++ return; ++ } ++ ++ *inodep = NULL; ++ ++ if (g_atomic_int_dec_and_test(&inode->refcount)) { ++ close(inode->fd); ++ free(inode); ++ } ++} ++ ++/* Caller must release refcount using lo_inode_put() */ + static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) + { + struct lo_data *lo = lo_data(req); +@@ -478,6 +503,9 @@ static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) + + pthread_mutex_lock(&lo->mutex); + elem = lo_map_get(&lo->ino_map, ino); ++ if (elem) { ++ g_atomic_int_inc(&elem->inode->refcount); ++ } + pthread_mutex_unlock(&lo->mutex); + + if (!elem) { +@@ -487,10 +515,23 @@ static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) + return elem->inode; + } + ++/* ++ * TODO Remove this helper and force callers to hold an inode refcount until ++ * they are done with the fd. This will be done in a later patch to make ++ * review easier. ++ */ + static int lo_fd(fuse_req_t req, fuse_ino_t ino) + { + struct lo_inode *inode = lo_inode(req, ino); +- return inode ? inode->fd : -1; ++ int fd; ++ ++ if (!inode) { ++ return -1; ++ } ++ ++ fd = inode->fd; ++ lo_inode_put(lo_data(req), &inode); ++ return fd; + } + + static void lo_init(void *userdata, struct fuse_conn_info *conn) +@@ -545,6 +586,10 @@ static void lo_getattr(fuse_req_t req, fuse_ino_t ino, + fuse_reply_attr(req, &buf, lo->timeout); + } + ++/* ++ * Increments parent->nlookup and caller must release refcount using ++ * lo_inode_put(&parent). ++ */ + static int lo_parent_and_name(struct lo_data *lo, struct lo_inode *inode, + char path[PATH_MAX], struct lo_inode **parent) + { +@@ -582,6 +627,7 @@ retry: + p = &lo->root; + pthread_mutex_lock(&lo->mutex); + p->nlookup++; ++ g_atomic_int_inc(&p->refcount); + pthread_mutex_unlock(&lo->mutex); + } else { + *last = '\0'; +@@ -625,6 +671,7 @@ retry: + + fail_unref: + unref_inode_lolocked(lo, p, 1); ++ lo_inode_put(lo, &p); + fail: + if (retries) { + retries--; +@@ -663,6 +710,7 @@ fallback: + if (res != -1) { + res = utimensat(parent->fd, path, tv, AT_SYMLINK_NOFOLLOW); + unref_inode_lolocked(lo, parent, 1); ++ lo_inode_put(lo, &parent); + } + + return res; +@@ -780,11 +828,13 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, + goto out_err; + } + } ++ lo_inode_put(lo, &inode); + + return lo_getattr(req, ino, fi); + + out_err: + saverr = errno; ++ lo_inode_put(lo, &inode); + fuse_reply_err(req, saverr); + } + +@@ -801,6 +851,7 @@ static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st) + if (p) { + assert(p->nlookup > 0); + p->nlookup++; ++ g_atomic_int_inc(&p->refcount); + } + pthread_mutex_unlock(&lo->mutex); + +@@ -820,6 +871,10 @@ static void posix_locks_value_destroy(gpointer data) + free(plock); + } + ++/* ++ * Increments nlookup and caller must release refcount using ++ * lo_inode_put(&parent). ++ */ + static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + struct fuse_entry_param *e) + { +@@ -827,7 +882,8 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + int res; + int saverr; + struct lo_data *lo = lo_data(req); +- struct lo_inode *inode, *dir = lo_inode(req, parent); ++ struct lo_inode *inode = NULL; ++ struct lo_inode *dir = lo_inode(req, parent); + + /* + * name_to_handle_at() and open_by_handle_at() can reach here with fuse +@@ -868,6 +924,13 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + } + + inode->is_symlink = S_ISLNK(e->attr.st_mode); ++ ++ /* ++ * One for the caller and one for nlookup (released in ++ * unref_inode_lolocked()) ++ */ ++ g_atomic_int_set(&inode->refcount, 2); ++ + inode->nlookup = 1; + inode->fd = newfd; + newfd = -1; +@@ -883,6 +946,8 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, + pthread_mutex_unlock(&lo->mutex); + } + e->ino = inode->fuse_ino; ++ lo_inode_put(lo, &inode); ++ lo_inode_put(lo, &dir); + + fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long)parent, + name, (unsigned long long)e->ino); +@@ -894,6 +959,8 @@ out_err: + if (newfd != -1) { + close(newfd); + } ++ lo_inode_put(lo, &inode); ++ lo_inode_put(lo, &dir); + return saverr; + } + +@@ -991,6 +1058,7 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, + { + int res; + int saverr; ++ struct lo_data *lo = lo_data(req); + struct lo_inode *dir; + struct fuse_entry_param e; + struct lo_cred old = {}; +@@ -1032,9 +1100,11 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, + name, (unsigned long long)e.ino); + + fuse_reply_entry(req, &e); ++ lo_inode_put(lo, &dir); + return; + + out: ++ lo_inode_put(lo, &dir); + fuse_reply_err(req, saverr); + } + +@@ -1085,6 +1155,7 @@ fallback: + if (res != -1) { + res = linkat(parent->fd, path, dfd, name, 0); + unref_inode_lolocked(lo, parent, 1); ++ lo_inode_put(lo, &parent); + } + + return res; +@@ -1095,6 +1166,7 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, + { + int res; + struct lo_data *lo = lo_data(req); ++ struct lo_inode *parent_inode; + struct lo_inode *inode; + struct fuse_entry_param e; + int saverr; +@@ -1104,17 +1176,18 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, + return; + } + ++ parent_inode = lo_inode(req, parent); + inode = lo_inode(req, ino); +- if (!inode) { +- fuse_reply_err(req, EBADF); +- return; ++ if (!parent_inode || !inode) { ++ errno = EBADF; ++ goto out_err; + } + + memset(&e, 0, sizeof(struct fuse_entry_param)); + e.attr_timeout = lo->timeout; + e.entry_timeout = lo->timeout; + +- res = linkat_empty_nofollow(lo, inode, lo_fd(req, parent), name); ++ res = linkat_empty_nofollow(lo, inode, parent_inode->fd, name); + if (res == -1) { + goto out_err; + } +@@ -1133,13 +1206,18 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, + name, (unsigned long long)e.ino); + + fuse_reply_entry(req, &e); ++ lo_inode_put(lo, &parent_inode); ++ lo_inode_put(lo, &inode); + return; + + out_err: + saverr = errno; ++ lo_inode_put(lo, &parent_inode); ++ lo_inode_put(lo, &inode); + fuse_reply_err(req, saverr); + } + ++/* Increments nlookup and caller must release refcount using lo_inode_put() */ + static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, + const char *name) + { +@@ -1176,6 +1254,7 @@ static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) + + fuse_reply_err(req, res == -1 ? errno : 0); + unref_inode_lolocked(lo, inode, 1); ++ lo_inode_put(lo, &inode); + } + + static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, +@@ -1183,8 +1262,10 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, + unsigned int flags) + { + int res; +- struct lo_inode *oldinode; +- struct lo_inode *newinode; ++ struct lo_inode *parent_inode; ++ struct lo_inode *newparent_inode; ++ struct lo_inode *oldinode = NULL; ++ struct lo_inode *newinode = NULL; + struct lo_data *lo = lo_data(req); + + if (!is_safe_path_component(name) || !is_safe_path_component(newname)) { +@@ -1192,6 +1273,13 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, + return; + } + ++ parent_inode = lo_inode(req, parent); ++ newparent_inode = lo_inode(req, newparent); ++ if (!parent_inode || !newparent_inode) { ++ fuse_reply_err(req, EBADF); ++ goto out; ++ } ++ + oldinode = lookup_name(req, parent, name); + newinode = lookup_name(req, newparent, newname); + +@@ -1204,8 +1292,8 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, + #ifndef SYS_renameat2 + fuse_reply_err(req, EINVAL); + #else +- res = syscall(SYS_renameat2, lo_fd(req, parent), name, +- lo_fd(req, newparent), newname, flags); ++ res = syscall(SYS_renameat2, parent_inode->fd, name, ++ newparent_inode->fd, newname, flags); + if (res == -1 && errno == ENOSYS) { + fuse_reply_err(req, EINVAL); + } else { +@@ -1215,12 +1303,16 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, + goto out; + } + +- res = renameat(lo_fd(req, parent), name, lo_fd(req, newparent), newname); ++ res = renameat(parent_inode->fd, name, newparent_inode->fd, newname); + + fuse_reply_err(req, res == -1 ? errno : 0); + out: + unref_inode_lolocked(lo, oldinode, 1); + unref_inode_lolocked(lo, newinode, 1); ++ lo_inode_put(lo, &oldinode); ++ lo_inode_put(lo, &newinode); ++ lo_inode_put(lo, &parent_inode); ++ lo_inode_put(lo, &newparent_inode); + } + + static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) +@@ -1244,6 +1336,7 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) + + fuse_reply_err(req, res == -1 ? errno : 0); + unref_inode_lolocked(lo, inode, 1); ++ lo_inode_put(lo, &inode); + } + + static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, +@@ -1265,8 +1358,9 @@ static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, + g_hash_table_destroy(inode->posix_locks); + pthread_mutex_destroy(&inode->plock_mutex); + pthread_mutex_unlock(&lo->mutex); +- close(inode->fd); +- free(inode); ++ ++ /* Drop our refcount from lo_do_lookup() */ ++ lo_inode_put(lo, &inode); + } else { + pthread_mutex_unlock(&lo->mutex); + } +@@ -1280,6 +1374,7 @@ static int unref_all_inodes_cb(gpointer key, gpointer value, gpointer user_data) + inode->nlookup = 0; + lo_map_remove(&lo->ino_map, inode->fuse_ino); + close(inode->fd); ++ lo_inode_put(lo, &inode); /* Drop our refcount from lo_do_lookup() */ + + return TRUE; + } +@@ -1306,6 +1401,7 @@ static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) + (unsigned long long)nlookup); + + unref_inode_lolocked(lo, inode, nlookup); ++ lo_inode_put(lo, &inode); + } + + static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) +@@ -1537,6 +1633,7 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, + err = 0; + error: + lo_dirp_put(&d); ++ lo_inode_put(lo, &dinode); + + /* + * If there's an error, we can only signal it if we haven't stored +@@ -1595,6 +1692,7 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, + { + int fd; + struct lo_data *lo = lo_data(req); ++ struct lo_inode *parent_inode; + struct fuse_entry_param e; + int err; + struct lo_cred old = {}; +@@ -1607,12 +1705,18 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, + return; + } + ++ parent_inode = lo_inode(req, parent); ++ if (!parent_inode) { ++ fuse_reply_err(req, EBADF); ++ return; ++ } ++ + err = lo_change_cred(req, &old); + if (err) { + goto out; + } + +- fd = openat(lo_fd(req, parent), name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, ++ fd = openat(parent_inode->fd, name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, + mode); + err = fd == -1 ? errno : 0; + lo_restore_cred(&old); +@@ -1625,8 +1729,8 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, + pthread_mutex_unlock(&lo->mutex); + if (fh == -1) { + close(fd); +- fuse_reply_err(req, ENOMEM); +- return; ++ err = ENOMEM; ++ goto out; + } + + fi->fh = fh; +@@ -1639,6 +1743,8 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, + } + + out: ++ lo_inode_put(lo, &parent_inode); ++ + if (err) { + fuse_reply_err(req, err); + } else { +@@ -1712,16 +1818,18 @@ static void lo_getlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + plock = + lookup_create_plock_ctx(lo, inode, fi->lock_owner, lock->l_pid, &ret); + if (!plock) { +- pthread_mutex_unlock(&inode->plock_mutex); +- fuse_reply_err(req, ret); +- return; ++ saverr = ret; ++ goto out; + } + + ret = fcntl(plock->fd, F_OFD_GETLK, lock); + if (ret == -1) { + saverr = errno; + } ++ ++out: + pthread_mutex_unlock(&inode->plock_mutex); ++ lo_inode_put(lo, &inode); + + if (saverr) { + fuse_reply_err(req, saverr); +@@ -1761,9 +1869,8 @@ static void lo_setlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + lookup_create_plock_ctx(lo, inode, fi->lock_owner, lock->l_pid, &ret); + + if (!plock) { +- pthread_mutex_unlock(&inode->plock_mutex); +- fuse_reply_err(req, ret); +- return; ++ saverr = ret; ++ goto out; + } + + /* TODO: Is it alright to modify flock? */ +@@ -1772,7 +1879,11 @@ static void lo_setlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, + if (ret == -1) { + saverr = errno; + } ++ ++out: + pthread_mutex_unlock(&inode->plock_mutex); ++ lo_inode_put(lo, &inode); ++ + fuse_reply_err(req, saverr); + } + +@@ -1898,6 +2009,7 @@ static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) + pthread_mutex_unlock(&inode->plock_mutex); + + res = close(dup(lo_fi_fd(req, fi))); ++ lo_inode_put(lo_data(req), &inode); + fuse_reply_err(req, res == -1 ? errno : 0); + } + +@@ -2115,11 +2227,14 @@ out_free: + if (fd >= 0) { + close(fd); + } ++ ++ lo_inode_put(lo, &inode); + return; + + out_err: + saverr = errno; + out: ++ lo_inode_put(lo, &inode); + fuse_reply_err(req, saverr); + goto out_free; + } +@@ -2190,11 +2305,14 @@ out_free: + if (fd >= 0) { + close(fd); + } ++ ++ lo_inode_put(lo, &inode); + return; + + out_err: + saverr = errno; + out: ++ lo_inode_put(lo, &inode); + fuse_reply_err(req, saverr); + goto out_free; + } +@@ -2243,6 +2361,8 @@ out: + if (fd >= 0) { + close(fd); + } ++ ++ lo_inode_put(lo, &inode); + fuse_reply_err(req, saverr); + } + +@@ -2289,6 +2409,8 @@ out: + if (fd >= 0) { + close(fd); + } ++ ++ lo_inode_put(lo, &inode); + fuse_reply_err(req, saverr); + } + +@@ -2671,6 +2793,7 @@ static void setup_root(struct lo_data *lo, struct lo_inode *root) + root->key.ino = stat.st_ino; + root->key.dev = stat.st_dev; + root->nlookup = 2; ++ g_atomic_int_set(&root->refcount, 2); + } + + static guint lo_key_hash(gconstpointer key) diff --git a/0103-virtiofsd-do-not-always-set-FUSE_FLOCK_LOCKS.patch b/0103-virtiofsd-do-not-always-set-FUSE_FLOCK_LOCKS.patch new file mode 100644 index 0000000..ae53c17 --- /dev/null +++ b/0103-virtiofsd-do-not-always-set-FUSE_FLOCK_LOCKS.patch @@ -0,0 +1,38 @@ +From: Peng Tao +Date: Mon, 27 Jan 2020 19:02:12 +0000 +Subject: [PATCH] virtiofsd: do not always set FUSE_FLOCK_LOCKS + +Right now we always enable it regardless of given commandlines. +Fix it by setting the flag relying on the lo->flock bit. + +Signed-off-by: Peng Tao +Reviewed-by: Misono Tomohiro +Reviewed-by: Sergio Lopez +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit e468d4af5f5192ab33283464a9f6933044ce47f7) +--- + tools/virtiofsd/passthrough_ll.c | 11 ++++++++--- + 1 file changed, 8 insertions(+), 3 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index ab1613586e..ccbbec18b0 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -546,9 +546,14 @@ static void lo_init(void *userdata, struct fuse_conn_info *conn) + fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); + conn->want |= FUSE_CAP_WRITEBACK_CACHE; + } +- if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { +- fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); +- conn->want |= FUSE_CAP_FLOCK_LOCKS; ++ if (conn->capable & FUSE_CAP_FLOCK_LOCKS) { ++ if (lo->flock) { ++ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); ++ conn->want |= FUSE_CAP_FLOCK_LOCKS; ++ } else { ++ fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling flock locks\n"); ++ conn->want &= ~FUSE_CAP_FLOCK_LOCKS; ++ } + } + + if (conn->capable & FUSE_CAP_POSIX_LOCKS) { diff --git a/0104-virtiofsd-convert-more-fprintf-and-perror-to-use-fus.patch b/0104-virtiofsd-convert-more-fprintf-and-perror-to-use-fus.patch new file mode 100644 index 0000000..aabec08 --- /dev/null +++ b/0104-virtiofsd-convert-more-fprintf-and-perror-to-use-fus.patch @@ -0,0 +1,83 @@ +From: Eryu Guan +Date: Mon, 27 Jan 2020 19:02:13 +0000 +Subject: [PATCH] virtiofsd: convert more fprintf and perror to use fuse log + infra +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Signed-off-by: Eryu Guan +Reviewed-by: Daniel P. Berrangé +Reviewed-by: Misono Tomohiro +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit fc1aed0bf96259d0b46b1cfea7497b7762c4ee3d) +--- + tools/virtiofsd/fuse_signals.c | 7 +++++-- + tools/virtiofsd/helper.c | 9 ++++++--- + 2 files changed, 11 insertions(+), 5 deletions(-) + +diff --git a/tools/virtiofsd/fuse_signals.c b/tools/virtiofsd/fuse_signals.c +index dc7c8ac025..f18625b6e2 100644 +--- a/tools/virtiofsd/fuse_signals.c ++++ b/tools/virtiofsd/fuse_signals.c +@@ -12,6 +12,7 @@ + #include "fuse_i.h" + #include "fuse_lowlevel.h" + ++#include + #include + #include + #include +@@ -47,13 +48,15 @@ static int set_one_signal_handler(int sig, void (*handler)(int), int remove) + sa.sa_flags = 0; + + if (sigaction(sig, NULL, &old_sa) == -1) { +- perror("fuse: cannot get old signal handler"); ++ fuse_log(FUSE_LOG_ERR, "fuse: cannot get old signal handler: %s\n", ++ strerror(errno)); + return -1; + } + + if (old_sa.sa_handler == (remove ? handler : SIG_DFL) && + sigaction(sig, &sa, NULL) == -1) { +- perror("fuse: cannot set signal handler"); ++ fuse_log(FUSE_LOG_ERR, "fuse: cannot set signal handler: %s\n", ++ strerror(errno)); + return -1; + } + return 0; +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index 33749bfcb7..f98d8f2eb2 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -208,7 +208,8 @@ int fuse_daemonize(int foreground) + char completed; + + if (pipe(waiter)) { +- perror("fuse_daemonize: pipe"); ++ fuse_log(FUSE_LOG_ERR, "fuse_daemonize: pipe: %s\n", ++ strerror(errno)); + return -1; + } + +@@ -218,7 +219,8 @@ int fuse_daemonize(int foreground) + */ + switch (fork()) { + case -1: +- perror("fuse_daemonize: fork"); ++ fuse_log(FUSE_LOG_ERR, "fuse_daemonize: fork: %s\n", ++ strerror(errno)); + return -1; + case 0: + break; +@@ -228,7 +230,8 @@ int fuse_daemonize(int foreground) + } + + if (setsid() == -1) { +- perror("fuse_daemonize: setsid"); ++ fuse_log(FUSE_LOG_ERR, "fuse_daemonize: setsid: %s\n", ++ strerror(errno)); + return -1; + } + diff --git a/0105-virtiofsd-Reset-O_DIRECT-flag-during-file-open.patch b/0105-virtiofsd-Reset-O_DIRECT-flag-during-file-open.patch new file mode 100644 index 0000000..bbd90a3 --- /dev/null +++ b/0105-virtiofsd-Reset-O_DIRECT-flag-during-file-open.patch @@ -0,0 +1,56 @@ +From: Vivek Goyal +Date: Mon, 27 Jan 2020 19:02:14 +0000 +Subject: [PATCH] virtiofsd: Reset O_DIRECT flag during file open +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +If an application wants to do direct IO and opens a file with O_DIRECT +in guest, that does not necessarily mean that we need to bypass page +cache on host as well. So reset this flag on host. + +If somebody needs to bypass page cache on host as well (and it is safe to +do so), we can add a knob in daemon later to control this behavior. + +I check virtio-9p and they do reset O_DIRECT flag. + +Signed-off-by: Vivek Goyal +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 65da4539803373ec4eec97ffc49ee90083e56efd) +--- + tools/virtiofsd/passthrough_ll.c | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index ccbbec18b0..948cb19c77 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1721,6 +1721,13 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, + goto out; + } + ++ /* ++ * O_DIRECT in guest should not necessarily mean bypassing page ++ * cache on host as well. If somebody needs that behavior, it ++ * probably should be a configuration knob in daemon. ++ */ ++ fi->flags &= ~O_DIRECT; ++ + fd = openat(parent_inode->fd, name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, + mode); + err = fd == -1 ? errno : 0; +@@ -1950,6 +1957,13 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) + fi->flags &= ~O_APPEND; + } + ++ /* ++ * O_DIRECT in guest should not necessarily mean bypassing page ++ * cache on host as well. If somebody needs that behavior, it ++ * probably should be a configuration knob in daemon. ++ */ ++ fi->flags &= ~O_DIRECT; ++ + sprintf(buf, "%i", lo_fd(req, ino)); + fd = openat(lo->proc_self_fd, buf, fi->flags & ~O_NOFOLLOW); + if (fd == -1) { diff --git a/0106-virtiofsd-Fix-data-corruption-with-O_APPEND-write-in.patch b/0106-virtiofsd-Fix-data-corruption-with-O_APPEND-write-in.patch new file mode 100644 index 0000000..ec53bd4 --- /dev/null +++ b/0106-virtiofsd-Fix-data-corruption-with-O_APPEND-write-in.patch @@ -0,0 +1,120 @@ +From: Misono Tomohiro +Date: Mon, 27 Jan 2020 19:02:15 +0000 +Subject: [PATCH] virtiofsd: Fix data corruption with O_APPEND write in + writeback mode +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When writeback mode is enabled (-o writeback), O_APPEND handling is +done in kernel. Therefore virtiofsd clears O_APPEND flag when open. +Otherwise O_APPEND flag takes precedence over pwrite() and write +data may corrupt. + +Currently clearing O_APPEND flag is done in lo_open(), but we also +need the same operation in lo_create(). So, factor out the flag +update operation in lo_open() to update_open_flags() and call it +in both lo_open() and lo_create(). + +This fixes the failure of xfstest generic/069 in writeback mode +(which tests O_APPEND write data integrity). + +Signed-off-by: Misono Tomohiro +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 8e4e41e39eac5ee5f378d66f069a2f70a1734317) +--- + tools/virtiofsd/passthrough_ll.c | 66 ++++++++++++++++---------------- + 1 file changed, 33 insertions(+), 33 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 948cb19c77..4c61ac5065 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1692,6 +1692,37 @@ static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, + fuse_reply_err(req, 0); + } + ++static void update_open_flags(int writeback, struct fuse_file_info *fi) ++{ ++ /* ++ * With writeback cache, kernel may send read requests even ++ * when userspace opened write-only ++ */ ++ if (writeback && (fi->flags & O_ACCMODE) == O_WRONLY) { ++ fi->flags &= ~O_ACCMODE; ++ fi->flags |= O_RDWR; ++ } ++ ++ /* ++ * With writeback cache, O_APPEND is handled by the kernel. ++ * This breaks atomicity (since the file may change in the ++ * underlying filesystem, so that the kernel's idea of the ++ * end of the file isn't accurate anymore). In this example, ++ * we just accept that. A more rigorous filesystem may want ++ * to return an error here ++ */ ++ if (writeback && (fi->flags & O_APPEND)) { ++ fi->flags &= ~O_APPEND; ++ } ++ ++ /* ++ * O_DIRECT in guest should not necessarily mean bypassing page ++ * cache on host as well. If somebody needs that behavior, it ++ * probably should be a configuration knob in daemon. ++ */ ++ fi->flags &= ~O_DIRECT; ++} ++ + static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, struct fuse_file_info *fi) + { +@@ -1721,12 +1752,7 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, + goto out; + } + +- /* +- * O_DIRECT in guest should not necessarily mean bypassing page +- * cache on host as well. If somebody needs that behavior, it +- * probably should be a configuration knob in daemon. +- */ +- fi->flags &= ~O_DIRECT; ++ update_open_flags(lo->writeback, fi); + + fd = openat(parent_inode->fd, name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, + mode); +@@ -1936,33 +1962,7 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) + fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino, + fi->flags); + +- /* +- * With writeback cache, kernel may send read requests even +- * when userspace opened write-only +- */ +- if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) { +- fi->flags &= ~O_ACCMODE; +- fi->flags |= O_RDWR; +- } +- +- /* +- * With writeback cache, O_APPEND is handled by the kernel. +- * This breaks atomicity (since the file may change in the +- * underlying filesystem, so that the kernel's idea of the +- * end of the file isn't accurate anymore). In this example, +- * we just accept that. A more rigorous filesystem may want +- * to return an error here +- */ +- if (lo->writeback && (fi->flags & O_APPEND)) { +- fi->flags &= ~O_APPEND; +- } +- +- /* +- * O_DIRECT in guest should not necessarily mean bypassing page +- * cache on host as well. If somebody needs that behavior, it +- * probably should be a configuration knob in daemon. +- */ +- fi->flags &= ~O_DIRECT; ++ update_open_flags(lo->writeback, fi); + + sprintf(buf, "%i", lo_fd(req, ino)); + fd = openat(lo->proc_self_fd, buf, fi->flags & ~O_NOFOLLOW); diff --git a/0107-virtiofsd-passthrough_ll-Use-cache_readdir-for-direc.patch b/0107-virtiofsd-passthrough_ll-Use-cache_readdir-for-direc.patch new file mode 100644 index 0000000..d78ba2f --- /dev/null +++ b/0107-virtiofsd-passthrough_ll-Use-cache_readdir-for-direc.patch @@ -0,0 +1,29 @@ +From: Misono Tomohiro +Date: Mon, 27 Jan 2020 19:02:16 +0000 +Subject: [PATCH] virtiofsd: passthrough_ll: Use cache_readdir for directory + open + +Since keep_cache(FOPEN_KEEP_CACHE) has no effect for directory as +described in fuse_common.h, use cache_readdir(FOPNE_CACHE_DIR) for +diretory open when cache=always mode. + +Signed-off-by: Misono Tomohiro +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 9b610b09b49b1aada256097b338d49da805da6ae) +--- + tools/virtiofsd/passthrough_ll.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 4c61ac5065..79b8b71a4f 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1523,7 +1523,7 @@ static void lo_opendir(fuse_req_t req, fuse_ino_t ino, + + fi->fh = fh; + if (lo->cache == CACHE_ALWAYS) { +- fi->keep_cache = 1; ++ fi->cache_readdir = 1; + } + fuse_reply_open(req, fi); + return; diff --git a/0108-virtiofsd-add-definition-of-fuse_buf_writev.patch b/0108-virtiofsd-add-definition-of-fuse_buf_writev.patch new file mode 100644 index 0000000..34925c1 --- /dev/null +++ b/0108-virtiofsd-add-definition-of-fuse_buf_writev.patch @@ -0,0 +1,77 @@ +From: piaojun +Date: Mon, 27 Jan 2020 19:02:17 +0000 +Subject: [PATCH] virtiofsd: add definition of fuse_buf_writev() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Define fuse_buf_writev() which use pwritev and writev to improve io +bandwidth. Especially, the src bufs with 0 size should be skipped as +their mems are not *block_size* aligned which will cause writev failed +in direct io mode. + +Signed-off-by: Jun Piao +Suggested-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 9ceaaa15cf21073c2b23058c374f61c30cd39c31) +--- + tools/virtiofsd/buffer.c | 38 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 38 insertions(+) + +diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c +index 42a608f6bd..37befebac2 100644 +--- a/tools/virtiofsd/buffer.c ++++ b/tools/virtiofsd/buffer.c +@@ -14,6 +14,7 @@ + #include "fuse_lowlevel.h" + #include + #include ++#include + #include + #include + +@@ -33,6 +34,43 @@ size_t fuse_buf_size(const struct fuse_bufvec *bufv) + return size; + } + ++__attribute__((unused)) ++static ssize_t fuse_buf_writev(struct fuse_buf *out_buf, ++ struct fuse_bufvec *in_buf) ++{ ++ ssize_t res, i, j; ++ size_t iovcnt = in_buf->count; ++ struct iovec *iov; ++ int fd = out_buf->fd; ++ ++ iov = calloc(iovcnt, sizeof(struct iovec)); ++ if (!iov) { ++ return -ENOMEM; ++ } ++ ++ for (i = 0, j = 0; i < iovcnt; i++) { ++ /* Skip the buf with 0 size */ ++ if (in_buf->buf[i].size) { ++ iov[j].iov_base = in_buf->buf[i].mem; ++ iov[j].iov_len = in_buf->buf[i].size; ++ j++; ++ } ++ } ++ ++ if (out_buf->flags & FUSE_BUF_FD_SEEK) { ++ res = pwritev(fd, iov, iovcnt, out_buf->pos); ++ } else { ++ res = writev(fd, iov, iovcnt); ++ } ++ ++ if (res == -1) { ++ res = -errno; ++ } ++ ++ free(iov); ++ return res; ++} ++ + static size_t min_size(size_t s1, size_t s2) + { + return s1 < s2 ? s1 : s2; diff --git a/0109-virtiofsd-use-fuse_buf_writev-to-replace-fuse_buf_wr.patch b/0109-virtiofsd-use-fuse_buf_writev-to-replace-fuse_buf_wr.patch new file mode 100644 index 0000000..4ed2da4 --- /dev/null +++ b/0109-virtiofsd-use-fuse_buf_writev-to-replace-fuse_buf_wr.patch @@ -0,0 +1,66 @@ +From: piaojun +Date: Mon, 27 Jan 2020 19:02:18 +0000 +Subject: [PATCH] virtiofsd: use fuse_buf_writev to replace fuse_buf_write for + better performance +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +fuse_buf_writev() only handles the normal write in which src is buffer +and dest is fd. Specially if src buffer represents guest physical +address that can't be mapped by the daemon process, IO must be bounced +back to the VMM to do it by fuse_buf_copy(). + +Signed-off-by: Jun Piao +Suggested-by: Dr. David Alan Gilbert +Suggested-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit c465bba2c90a810f6e71e4f2646b1b4ee4b478de) +--- + tools/virtiofsd/buffer.c | 20 ++++++++++++++++++-- + 1 file changed, 18 insertions(+), 2 deletions(-) + +diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c +index 37befebac2..27c1377f22 100644 +--- a/tools/virtiofsd/buffer.c ++++ b/tools/virtiofsd/buffer.c +@@ -34,7 +34,6 @@ size_t fuse_buf_size(const struct fuse_bufvec *bufv) + return size; + } + +-__attribute__((unused)) + static ssize_t fuse_buf_writev(struct fuse_buf *out_buf, + struct fuse_bufvec *in_buf) + { +@@ -262,12 +261,29 @@ static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len) + + ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv) + { +- size_t copied = 0; ++ size_t copied = 0, i; + + if (dstv == srcv) { + return fuse_buf_size(dstv); + } + ++ /* ++ * use writev to improve bandwidth when all the ++ * src buffers already mapped by the daemon ++ * process ++ */ ++ for (i = 0; i < srcv->count; i++) { ++ if (srcv->buf[i].flags & FUSE_BUF_IS_FD) { ++ break; ++ } ++ } ++ if ((i == srcv->count) && (dstv->count == 1) && ++ (dstv->idx == 0) && ++ (dstv->buf[0].flags & FUSE_BUF_IS_FD)) { ++ dstv->buf[0].pos += dstv->off; ++ return fuse_buf_writev(&dstv->buf[0], srcv); ++ } ++ + for (;;) { + const struct fuse_buf *src = fuse_bufvec_current(srcv); + const struct fuse_buf *dst = fuse_bufvec_current(dstv); diff --git a/0110-virtiofsd-process-requests-in-a-thread-pool.patch b/0110-virtiofsd-process-requests-in-a-thread-pool.patch new file mode 100644 index 0000000..b83ae83 --- /dev/null +++ b/0110-virtiofsd-process-requests-in-a-thread-pool.patch @@ -0,0 +1,514 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:02:19 +0000 +Subject: [PATCH] virtiofsd: process requests in a thread pool + +Introduce a thread pool so that fv_queue_thread() just pops +VuVirtqElements and hands them to the thread pool. For the time being +only one worker thread is allowed since passthrough_ll.c is not +thread-safe yet. Future patches will lift this restriction so that +multiple FUSE requests can be processed in parallel. + +The main new concept is struct FVRequest, which contains both +VuVirtqElement and struct fuse_chan. We now have fv_VuDev for a device, +fv_QueueInfo for a virtqueue, and FVRequest for a request. Some of +fv_QueueInfo's fields are moved into FVRequest because they are +per-request. The name FVRequest conforms to QEMU coding style and I +expect the struct fv_* types will be renamed in a future refactoring. + +This patch series is not optimal. fbuf reuse is dropped so each request +does malloc(se->bufsize), but there is no clean and cheap way to keep +this with a thread pool. The vq_lock mutex is held for longer than +necessary, especially during the eventfd_write() syscall. Performance +can be improved in the future. + +prctl(2) had to be added to the seccomp whitelist because glib invokes +it. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Misono Tomohiro +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit a3d756c5aecccc4c0e51060a7e2f1c87bf8f1180) +--- + tools/virtiofsd/fuse_virtio.c | 359 +++++++++++++++++++--------------- + 1 file changed, 201 insertions(+), 158 deletions(-) + +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index f6242f9338..0dcf2ef57a 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -22,6 +22,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -37,17 +38,28 @@ + struct fv_VuDev; + struct fv_QueueInfo { + pthread_t thread; ++ /* ++ * This lock protects the VuVirtq preventing races between ++ * fv_queue_thread() and fv_queue_worker(). ++ */ ++ pthread_mutex_t vq_lock; ++ + struct fv_VuDev *virtio_dev; + + /* Our queue index, corresponds to array position */ + int qidx; + int kick_fd; + int kill_fd; /* For killing the thread */ ++}; + +- /* The element for the command currently being processed */ +- VuVirtqElement *qe; ++/* A FUSE request */ ++typedef struct { ++ VuVirtqElement elem; ++ struct fuse_chan ch; ++ ++ /* Used to complete requests that involve no reply */ + bool reply_sent; +-}; ++} FVRequest; + + /* + * We pass the dev element into libvhost-user +@@ -191,8 +203,11 @@ static void copy_iov(struct iovec *src_iov, int src_count, + int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, + struct iovec *iov, int count) + { +- VuVirtqElement *elem; +- VuVirtq *q; ++ FVRequest *req = container_of(ch, FVRequest, ch); ++ struct fv_QueueInfo *qi = ch->qi; ++ VuDev *dev = &se->virtio_dev->dev; ++ VuVirtq *q = vu_get_queue(dev, qi->qidx); ++ VuVirtqElement *elem = &req->elem; + int ret = 0; + + assert(count >= 1); +@@ -205,11 +220,7 @@ int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, + + /* unique == 0 is notification, which we don't support */ + assert(out->unique); +- /* For virtio we always have ch */ +- assert(ch); +- assert(!ch->qi->reply_sent); +- elem = ch->qi->qe; +- q = &ch->qi->virtio_dev->dev.vq[ch->qi->qidx]; ++ assert(!req->reply_sent); + + /* The 'in' part of the elem is to qemu */ + unsigned int in_num = elem->in_num; +@@ -236,9 +247,15 @@ int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, + } + + copy_iov(iov, count, in_sg, in_num, tosend_len); +- vu_queue_push(&se->virtio_dev->dev, q, elem, tosend_len); +- vu_queue_notify(&se->virtio_dev->dev, q); +- ch->qi->reply_sent = true; ++ ++ pthread_rwlock_rdlock(&qi->virtio_dev->vu_dispatch_rwlock); ++ pthread_mutex_lock(&qi->vq_lock); ++ vu_queue_push(dev, q, elem, tosend_len); ++ vu_queue_notify(dev, q); ++ pthread_mutex_unlock(&qi->vq_lock); ++ pthread_rwlock_unlock(&qi->virtio_dev->vu_dispatch_rwlock); ++ ++ req->reply_sent = true; + + err: + return ret; +@@ -254,9 +271,12 @@ int virtio_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, + struct iovec *iov, int count, struct fuse_bufvec *buf, + size_t len) + { ++ FVRequest *req = container_of(ch, FVRequest, ch); ++ struct fv_QueueInfo *qi = ch->qi; ++ VuDev *dev = &se->virtio_dev->dev; ++ VuVirtq *q = vu_get_queue(dev, qi->qidx); ++ VuVirtqElement *elem = &req->elem; + int ret = 0; +- VuVirtqElement *elem; +- VuVirtq *q; + + assert(count >= 1); + assert(iov[0].iov_len >= sizeof(struct fuse_out_header)); +@@ -275,11 +295,7 @@ int virtio_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, + /* unique == 0 is notification which we don't support */ + assert(out->unique); + +- /* For virtio we always have ch */ +- assert(ch); +- assert(!ch->qi->reply_sent); +- elem = ch->qi->qe; +- q = &ch->qi->virtio_dev->dev.vq[ch->qi->qidx]; ++ assert(!req->reply_sent); + + /* The 'in' part of the elem is to qemu */ + unsigned int in_num = elem->in_num; +@@ -395,33 +411,175 @@ int virtio_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, + + ret = 0; + +- vu_queue_push(&se->virtio_dev->dev, q, elem, tosend_len); +- vu_queue_notify(&se->virtio_dev->dev, q); ++ pthread_rwlock_rdlock(&qi->virtio_dev->vu_dispatch_rwlock); ++ pthread_mutex_lock(&qi->vq_lock); ++ vu_queue_push(dev, q, elem, tosend_len); ++ vu_queue_notify(dev, q); ++ pthread_mutex_unlock(&qi->vq_lock); ++ pthread_rwlock_unlock(&qi->virtio_dev->vu_dispatch_rwlock); + + err: + if (ret == 0) { +- ch->qi->reply_sent = true; ++ req->reply_sent = true; + } + + return ret; + } + ++/* Process one FVRequest in a thread pool */ ++static void fv_queue_worker(gpointer data, gpointer user_data) ++{ ++ struct fv_QueueInfo *qi = user_data; ++ struct fuse_session *se = qi->virtio_dev->se; ++ struct VuDev *dev = &qi->virtio_dev->dev; ++ FVRequest *req = data; ++ VuVirtqElement *elem = &req->elem; ++ struct fuse_buf fbuf = {}; ++ bool allocated_bufv = false; ++ struct fuse_bufvec bufv; ++ struct fuse_bufvec *pbufv; ++ ++ assert(se->bufsize > sizeof(struct fuse_in_header)); ++ ++ /* ++ * An element contains one request and the space to send our response ++ * They're spread over multiple descriptors in a scatter/gather set ++ * and we can't trust the guest to keep them still; so copy in/out. ++ */ ++ fbuf.mem = malloc(se->bufsize); ++ assert(fbuf.mem); ++ ++ fuse_mutex_init(&req->ch.lock); ++ req->ch.fd = -1; ++ req->ch.qi = qi; ++ ++ /* The 'out' part of the elem is from qemu */ ++ unsigned int out_num = elem->out_num; ++ struct iovec *out_sg = elem->out_sg; ++ size_t out_len = iov_size(out_sg, out_num); ++ fuse_log(FUSE_LOG_DEBUG, ++ "%s: elem %d: with %d out desc of length %zd\n", ++ __func__, elem->index, out_num, out_len); ++ ++ /* ++ * The elem should contain a 'fuse_in_header' (in to fuse) ++ * plus the data based on the len in the header. ++ */ ++ if (out_len < sizeof(struct fuse_in_header)) { ++ fuse_log(FUSE_LOG_ERR, "%s: elem %d too short for in_header\n", ++ __func__, elem->index); ++ assert(0); /* TODO */ ++ } ++ if (out_len > se->bufsize) { ++ fuse_log(FUSE_LOG_ERR, "%s: elem %d too large for buffer\n", __func__, ++ elem->index); ++ assert(0); /* TODO */ ++ } ++ /* Copy just the first element and look at it */ ++ copy_from_iov(&fbuf, 1, out_sg); ++ ++ pbufv = NULL; /* Compiler thinks an unitialised path */ ++ if (out_num > 2 && ++ out_sg[0].iov_len == sizeof(struct fuse_in_header) && ++ ((struct fuse_in_header *)fbuf.mem)->opcode == FUSE_WRITE && ++ out_sg[1].iov_len == sizeof(struct fuse_write_in)) { ++ /* ++ * For a write we don't actually need to copy the ++ * data, we can just do it straight out of guest memory ++ * but we must still copy the headers in case the guest ++ * was nasty and changed them while we were using them. ++ */ ++ fuse_log(FUSE_LOG_DEBUG, "%s: Write special case\n", __func__); ++ ++ /* copy the fuse_write_in header afte rthe fuse_in_header */ ++ fbuf.mem += out_sg->iov_len; ++ copy_from_iov(&fbuf, 1, out_sg + 1); ++ fbuf.mem -= out_sg->iov_len; ++ fbuf.size = out_sg[0].iov_len + out_sg[1].iov_len; ++ ++ /* Allocate the bufv, with space for the rest of the iov */ ++ pbufv = malloc(sizeof(struct fuse_bufvec) + ++ sizeof(struct fuse_buf) * (out_num - 2)); ++ if (!pbufv) { ++ fuse_log(FUSE_LOG_ERR, "%s: pbufv malloc failed\n", ++ __func__); ++ goto out; ++ } ++ ++ allocated_bufv = true; ++ pbufv->count = 1; ++ pbufv->buf[0] = fbuf; ++ ++ size_t iovindex, pbufvindex; ++ iovindex = 2; /* 2 headers, separate iovs */ ++ pbufvindex = 1; /* 2 headers, 1 fusebuf */ ++ ++ for (; iovindex < out_num; iovindex++, pbufvindex++) { ++ pbufv->count++; ++ pbufv->buf[pbufvindex].pos = ~0; /* Dummy */ ++ pbufv->buf[pbufvindex].flags = 0; ++ pbufv->buf[pbufvindex].mem = out_sg[iovindex].iov_base; ++ pbufv->buf[pbufvindex].size = out_sg[iovindex].iov_len; ++ } ++ } else { ++ /* Normal (non fast write) path */ ++ ++ /* Copy the rest of the buffer */ ++ fbuf.mem += out_sg->iov_len; ++ copy_from_iov(&fbuf, out_num - 1, out_sg + 1); ++ fbuf.mem -= out_sg->iov_len; ++ fbuf.size = out_len; ++ ++ /* TODO! Endianness of header */ ++ ++ /* TODO: Add checks for fuse_session_exited */ ++ bufv.buf[0] = fbuf; ++ bufv.count = 1; ++ pbufv = &bufv; ++ } ++ pbufv->idx = 0; ++ pbufv->off = 0; ++ fuse_session_process_buf_int(se, pbufv, &req->ch); ++ ++out: ++ if (allocated_bufv) { ++ free(pbufv); ++ } ++ ++ /* If the request has no reply, still recycle the virtqueue element */ ++ if (!req->reply_sent) { ++ struct VuVirtq *q = vu_get_queue(dev, qi->qidx); ++ ++ fuse_log(FUSE_LOG_DEBUG, "%s: elem %d no reply sent\n", __func__, ++ elem->index); ++ ++ pthread_rwlock_rdlock(&qi->virtio_dev->vu_dispatch_rwlock); ++ pthread_mutex_lock(&qi->vq_lock); ++ vu_queue_push(dev, q, elem, 0); ++ vu_queue_notify(dev, q); ++ pthread_mutex_unlock(&qi->vq_lock); ++ pthread_rwlock_unlock(&qi->virtio_dev->vu_dispatch_rwlock); ++ } ++ ++ pthread_mutex_destroy(&req->ch.lock); ++ free(fbuf.mem); ++ free(req); ++} ++ + /* Thread function for individual queues, created when a queue is 'started' */ + static void *fv_queue_thread(void *opaque) + { + struct fv_QueueInfo *qi = opaque; + struct VuDev *dev = &qi->virtio_dev->dev; + struct VuVirtq *q = vu_get_queue(dev, qi->qidx); +- struct fuse_session *se = qi->virtio_dev->se; +- struct fuse_chan ch; +- struct fuse_buf fbuf; ++ GThreadPool *pool; + +- fbuf.mem = NULL; +- fbuf.flags = 0; +- +- fuse_mutex_init(&ch.lock); +- ch.fd = (int)0xdaff0d111; +- ch.qi = qi; ++ pool = g_thread_pool_new(fv_queue_worker, qi, 1 /* TODO max_threads */, ++ TRUE, NULL); ++ if (!pool) { ++ fuse_log(FUSE_LOG_ERR, "%s: g_thread_pool_new failed\n", __func__); ++ return NULL; ++ } + + fuse_log(FUSE_LOG_INFO, "%s: Start for queue %d kick_fd %d\n", __func__, + qi->qidx, qi->kick_fd); +@@ -478,6 +636,7 @@ static void *fv_queue_thread(void *opaque) + /* Mutual exclusion with virtio_loop() */ + ret = pthread_rwlock_rdlock(&qi->virtio_dev->vu_dispatch_rwlock); + assert(ret == 0); /* there is no possible error case */ ++ pthread_mutex_lock(&qi->vq_lock); + /* out is from guest, in is too guest */ + unsigned int in_bytes, out_bytes; + vu_queue_get_avail_bytes(dev, q, &in_bytes, &out_bytes, ~0, ~0); +@@ -486,141 +645,22 @@ static void *fv_queue_thread(void *opaque) + "%s: Queue %d gave evalue: %zx available: in: %u out: %u\n", + __func__, qi->qidx, (size_t)evalue, in_bytes, out_bytes); + +- + while (1) { +- bool allocated_bufv = false; +- struct fuse_bufvec bufv; +- struct fuse_bufvec *pbufv; +- +- /* +- * An element contains one request and the space to send our +- * response They're spread over multiple descriptors in a +- * scatter/gather set and we can't trust the guest to keep them +- * still; so copy in/out. +- */ +- VuVirtqElement *elem = vu_queue_pop(dev, q, sizeof(VuVirtqElement)); +- if (!elem) { ++ FVRequest *req = vu_queue_pop(dev, q, sizeof(FVRequest)); ++ if (!req) { + break; + } + +- qi->qe = elem; +- qi->reply_sent = false; ++ req->reply_sent = false; + +- if (!fbuf.mem) { +- fbuf.mem = malloc(se->bufsize); +- assert(fbuf.mem); +- assert(se->bufsize > sizeof(struct fuse_in_header)); +- } +- /* The 'out' part of the elem is from qemu */ +- unsigned int out_num = elem->out_num; +- struct iovec *out_sg = elem->out_sg; +- size_t out_len = iov_size(out_sg, out_num); +- fuse_log(FUSE_LOG_DEBUG, +- "%s: elem %d: with %d out desc of length %zd\n", __func__, +- elem->index, out_num, out_len); +- +- /* +- * The elem should contain a 'fuse_in_header' (in to fuse) +- * plus the data based on the len in the header. +- */ +- if (out_len < sizeof(struct fuse_in_header)) { +- fuse_log(FUSE_LOG_ERR, "%s: elem %d too short for in_header\n", +- __func__, elem->index); +- assert(0); /* TODO */ +- } +- if (out_len > se->bufsize) { +- fuse_log(FUSE_LOG_ERR, "%s: elem %d too large for buffer\n", +- __func__, elem->index); +- assert(0); /* TODO */ +- } +- /* Copy just the first element and look at it */ +- copy_from_iov(&fbuf, 1, out_sg); +- +- if (out_num > 2 && +- out_sg[0].iov_len == sizeof(struct fuse_in_header) && +- ((struct fuse_in_header *)fbuf.mem)->opcode == FUSE_WRITE && +- out_sg[1].iov_len == sizeof(struct fuse_write_in)) { +- /* +- * For a write we don't actually need to copy the +- * data, we can just do it straight out of guest memory +- * but we must still copy the headers in case the guest +- * was nasty and changed them while we were using them. +- */ +- fuse_log(FUSE_LOG_DEBUG, "%s: Write special case\n", __func__); +- +- /* copy the fuse_write_in header after the fuse_in_header */ +- fbuf.mem += out_sg->iov_len; +- copy_from_iov(&fbuf, 1, out_sg + 1); +- fbuf.mem -= out_sg->iov_len; +- fbuf.size = out_sg[0].iov_len + out_sg[1].iov_len; +- +- /* Allocate the bufv, with space for the rest of the iov */ +- allocated_bufv = true; +- pbufv = malloc(sizeof(struct fuse_bufvec) + +- sizeof(struct fuse_buf) * (out_num - 2)); +- if (!pbufv) { +- vu_queue_unpop(dev, q, elem, 0); +- free(elem); +- fuse_log(FUSE_LOG_ERR, "%s: pbufv malloc failed\n", +- __func__); +- goto out; +- } +- +- pbufv->count = 1; +- pbufv->buf[0] = fbuf; +- +- size_t iovindex, pbufvindex; +- iovindex = 2; /* 2 headers, separate iovs */ +- pbufvindex = 1; /* 2 headers, 1 fusebuf */ +- +- for (; iovindex < out_num; iovindex++, pbufvindex++) { +- pbufv->count++; +- pbufv->buf[pbufvindex].pos = ~0; /* Dummy */ +- pbufv->buf[pbufvindex].flags = 0; +- pbufv->buf[pbufvindex].mem = out_sg[iovindex].iov_base; +- pbufv->buf[pbufvindex].size = out_sg[iovindex].iov_len; +- } +- } else { +- /* Normal (non fast write) path */ +- +- /* Copy the rest of the buffer */ +- fbuf.mem += out_sg->iov_len; +- copy_from_iov(&fbuf, out_num - 1, out_sg + 1); +- fbuf.mem -= out_sg->iov_len; +- fbuf.size = out_len; +- +- /* TODO! Endianness of header */ +- +- /* TODO: Add checks for fuse_session_exited */ +- bufv.buf[0] = fbuf; +- bufv.count = 1; +- pbufv = &bufv; +- } +- pbufv->idx = 0; +- pbufv->off = 0; +- fuse_session_process_buf_int(se, pbufv, &ch); +- +- if (allocated_bufv) { +- free(pbufv); +- } +- +- if (!qi->reply_sent) { +- fuse_log(FUSE_LOG_DEBUG, "%s: elem %d no reply sent\n", +- __func__, elem->index); +- /* I think we've still got to recycle the element */ +- vu_queue_push(dev, q, elem, 0); +- vu_queue_notify(dev, q); +- } +- qi->qe = NULL; +- free(elem); +- elem = NULL; ++ g_thread_pool_push(pool, req, NULL); + } + ++ pthread_mutex_unlock(&qi->vq_lock); + pthread_rwlock_unlock(&qi->virtio_dev->vu_dispatch_rwlock); + } +-out: +- pthread_mutex_destroy(&ch.lock); +- free(fbuf.mem); ++ ++ g_thread_pool_free(pool, FALSE, TRUE); + + return NULL; + } +@@ -643,6 +683,7 @@ static void fv_queue_cleanup_thread(struct fv_VuDev *vud, int qidx) + fuse_log(FUSE_LOG_ERR, "%s: Failed to join thread idx %d err %d\n", + __func__, qidx, ret); + } ++ pthread_mutex_destroy(&ourqi->vq_lock); + close(ourqi->kill_fd); + ourqi->kick_fd = -1; + free(vud->qi[qidx]); +@@ -696,6 +737,8 @@ static void fv_queue_set_started(VuDev *dev, int qidx, bool started) + + ourqi->kill_fd = eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE); + assert(ourqi->kill_fd != -1); ++ pthread_mutex_init(&ourqi->vq_lock, NULL); ++ + if (pthread_create(&ourqi->thread, NULL, fv_queue_thread, ourqi)) { + fuse_log(FUSE_LOG_ERR, "%s: Failed to create thread for queue %d\n", + __func__, qidx); diff --git a/0111-virtiofsd-prevent-FUSE_INIT-FUSE_DESTROY-races.patch b/0111-virtiofsd-prevent-FUSE_INIT-FUSE_DESTROY-races.patch new file mode 100644 index 0000000..c2d8e55 --- /dev/null +++ b/0111-virtiofsd-prevent-FUSE_INIT-FUSE_DESTROY-races.patch @@ -0,0 +1,84 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:02:20 +0000 +Subject: [PATCH] virtiofsd: prevent FUSE_INIT/FUSE_DESTROY races + +When running with multiple threads it can be tricky to handle +FUSE_INIT/FUSE_DESTROY in parallel with other request types or in +parallel with themselves. Serialize FUSE_INIT and FUSE_DESTROY so that +malicious clients cannot trigger race conditions. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Masayoshi Mizuma +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit cdc497c6925be745bc895355bd4674a17a4b2a8b) +--- + tools/virtiofsd/fuse_i.h | 1 + + tools/virtiofsd/fuse_lowlevel.c | 18 ++++++++++++++++++ + 2 files changed, 19 insertions(+) + +diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h +index a20854f1c4..1447d86866 100644 +--- a/tools/virtiofsd/fuse_i.h ++++ b/tools/virtiofsd/fuse_i.h +@@ -61,6 +61,7 @@ struct fuse_session { + struct fuse_req list; + struct fuse_req interrupts; + pthread_mutex_t lock; ++ pthread_rwlock_t init_rwlock; + int got_destroy; + int broken_splice_nonblock; + uint64_t notify_ctr; +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index dab6a31e08..79a4031266 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -2428,6 +2428,19 @@ void fuse_session_process_buf_int(struct fuse_session *se, + req->ctx.pid = in->pid; + req->ch = ch; + ++ /* ++ * INIT and DESTROY requests are serialized, all other request types ++ * run in parallel. This prevents races between FUSE_INIT and ordinary ++ * requests, FUSE_INIT and FUSE_INIT, FUSE_INIT and FUSE_DESTROY, and ++ * FUSE_DESTROY and FUSE_DESTROY. ++ */ ++ if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT || ++ in->opcode == FUSE_DESTROY) { ++ pthread_rwlock_wrlock(&se->init_rwlock); ++ } else { ++ pthread_rwlock_rdlock(&se->init_rwlock); ++ } ++ + err = EIO; + if (!se->got_init) { + enum fuse_opcode expected; +@@ -2485,10 +2498,13 @@ void fuse_session_process_buf_int(struct fuse_session *se, + } else { + fuse_ll_ops[in->opcode].func(req, in->nodeid, &iter); + } ++ ++ pthread_rwlock_unlock(&se->init_rwlock); + return; + + reply_err: + fuse_reply_err(req, err); ++ pthread_rwlock_unlock(&se->init_rwlock); + } + + #define LL_OPTION(n, o, v) \ +@@ -2531,6 +2547,7 @@ void fuse_session_destroy(struct fuse_session *se) + se->op.destroy(se->userdata); + } + } ++ pthread_rwlock_destroy(&se->init_rwlock); + pthread_mutex_destroy(&se->lock); + free(se->cuse_data); + if (se->fd != -1) { +@@ -2610,6 +2627,7 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, + list_init_req(&se->list); + list_init_req(&se->interrupts); + fuse_mutex_init(&se->lock); ++ pthread_rwlock_init(&se->init_rwlock, NULL); + + memcpy(&se->op, op, op_size); + se->owner = getuid(); diff --git a/0112-virtiofsd-fix-lo_destroy-resource-leaks.patch b/0112-virtiofsd-fix-lo_destroy-resource-leaks.patch new file mode 100644 index 0000000..20a6143 --- /dev/null +++ b/0112-virtiofsd-fix-lo_destroy-resource-leaks.patch @@ -0,0 +1,78 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:02:21 +0000 +Subject: [PATCH] virtiofsd: fix lo_destroy() resource leaks +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Now that lo_destroy() is serialized we can call unref_inode() so that +all inode resources are freed. + +Signed-off-by: Stefan Hajnoczi +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 28f7a3b026f231bfe8de5fed6a18a8d27b1dfcee) +--- + tools/virtiofsd/passthrough_ll.c | 41 ++++++++++++++++---------------- + 1 file changed, 20 insertions(+), 21 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index 79b8b71a4f..eb001b9d1e 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1371,26 +1371,6 @@ static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, + } + } + +-static int unref_all_inodes_cb(gpointer key, gpointer value, gpointer user_data) +-{ +- struct lo_inode *inode = value; +- struct lo_data *lo = user_data; +- +- inode->nlookup = 0; +- lo_map_remove(&lo->ino_map, inode->fuse_ino); +- close(inode->fd); +- lo_inode_put(lo, &inode); /* Drop our refcount from lo_do_lookup() */ +- +- return TRUE; +-} +- +-static void unref_all_inodes(struct lo_data *lo) +-{ +- pthread_mutex_lock(&lo->mutex); +- g_hash_table_foreach_remove(lo->inodes, unref_all_inodes_cb, lo); +- pthread_mutex_unlock(&lo->mutex); +-} +- + static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) + { + struct lo_data *lo = lo_data(req); +@@ -2477,7 +2457,26 @@ static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, + static void lo_destroy(void *userdata) + { + struct lo_data *lo = (struct lo_data *)userdata; +- unref_all_inodes(lo); ++ ++ /* ++ * Normally lo->mutex must be taken when traversing lo->inodes but ++ * lo_destroy() is a serialized request so no races are possible here. ++ * ++ * In addition, we cannot acquire lo->mutex since unref_inode() takes it ++ * too and this would result in a recursive lock. ++ */ ++ while (true) { ++ GHashTableIter iter; ++ gpointer key, value; ++ ++ g_hash_table_iter_init(&iter, lo->inodes); ++ if (!g_hash_table_iter_next(&iter, &key, &value)) { ++ break; ++ } ++ ++ struct lo_inode *inode = value; ++ unref_inode_lolocked(lo, inode, inode->nlookup); ++ } + } + + static struct fuse_lowlevel_ops lo_oper = { diff --git a/0113-virtiofsd-add-thread-pool-size-NUM-option.patch b/0113-virtiofsd-add-thread-pool-size-NUM-option.patch new file mode 100644 index 0000000..2cb742c --- /dev/null +++ b/0113-virtiofsd-add-thread-pool-size-NUM-option.patch @@ -0,0 +1,90 @@ +From: Stefan Hajnoczi +Date: Mon, 27 Jan 2020 19:02:22 +0000 +Subject: [PATCH] virtiofsd: add --thread-pool-size=NUM option +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add an option to control the size of the thread pool. Requests are now +processed in parallel by default. + +Signed-off-by: Stefan Hajnoczi +Reviewed-by: Daniel P. Berrangé +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 951b3120dbc971f08681e1d860360e4a1e638902) +--- + tools/virtiofsd/fuse_i.h | 1 + + tools/virtiofsd/fuse_lowlevel.c | 7 ++++++- + tools/virtiofsd/fuse_virtio.c | 5 +++-- + 3 files changed, 10 insertions(+), 3 deletions(-) + +diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h +index 1447d86866..4e47e5880d 100644 +--- a/tools/virtiofsd/fuse_i.h ++++ b/tools/virtiofsd/fuse_i.h +@@ -72,6 +72,7 @@ struct fuse_session { + int vu_listen_fd; + int vu_socketfd; + struct fv_VuDev *virtio_dev; ++ int thread_pool_size; + }; + + struct fuse_chan { +diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c +index 79a4031266..de2e2e0c65 100644 +--- a/tools/virtiofsd/fuse_lowlevel.c ++++ b/tools/virtiofsd/fuse_lowlevel.c +@@ -28,6 +28,7 @@ + #include + #include + ++#define THREAD_POOL_SIZE 64 + + #define OFFSET_MAX 0x7fffffffffffffffLL + +@@ -2519,6 +2520,7 @@ static const struct fuse_opt fuse_ll_opts[] = { + LL_OPTION("allow_root", deny_others, 1), + LL_OPTION("--socket-path=%s", vu_socket_path, 0), + LL_OPTION("--fd=%d", vu_listen_fd, 0), ++ LL_OPTION("--thread-pool-size=%d", thread_pool_size, 0), + FUSE_OPT_END + }; + +@@ -2537,7 +2539,9 @@ void fuse_lowlevel_help(void) + printf( + " -o allow_root allow access by root\n" + " --socket-path=PATH path for the vhost-user socket\n" +- " --fd=FDNUM fd number of vhost-user socket\n"); ++ " --fd=FDNUM fd number of vhost-user socket\n" ++ " --thread-pool-size=NUM thread pool size limit (default %d)\n", ++ THREAD_POOL_SIZE); + } + + void fuse_session_destroy(struct fuse_session *se) +@@ -2591,6 +2595,7 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, + } + se->fd = -1; + se->vu_listen_fd = -1; ++ se->thread_pool_size = THREAD_POOL_SIZE; + se->conn.max_write = UINT_MAX; + se->conn.max_readahead = UINT_MAX; + +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index 0dcf2ef57a..9f6582343c 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -572,10 +572,11 @@ static void *fv_queue_thread(void *opaque) + struct fv_QueueInfo *qi = opaque; + struct VuDev *dev = &qi->virtio_dev->dev; + struct VuVirtq *q = vu_get_queue(dev, qi->qidx); ++ struct fuse_session *se = qi->virtio_dev->se; + GThreadPool *pool; + +- pool = g_thread_pool_new(fv_queue_worker, qi, 1 /* TODO max_threads */, +- TRUE, NULL); ++ pool = g_thread_pool_new(fv_queue_worker, qi, se->thread_pool_size, TRUE, ++ NULL); + if (!pool) { + fuse_log(FUSE_LOG_ERR, "%s: g_thread_pool_new failed\n", __func__); + return NULL; diff --git a/0114-virtiofsd-Convert-lo_destroy-to-take-the-lo-mutex-lo.patch b/0114-virtiofsd-Convert-lo_destroy-to-take-the-lo-mutex-lo.patch new file mode 100644 index 0000000..142429b --- /dev/null +++ b/0114-virtiofsd-Convert-lo_destroy-to-take-the-lo-mutex-lo.patch @@ -0,0 +1,96 @@ +From: "Dr. David Alan Gilbert" +Date: Mon, 27 Jan 2020 19:02:23 +0000 +Subject: [PATCH] virtiofsd: Convert lo_destroy to take the lo->mutex lock + itself +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +lo_destroy was relying on some implicit knowledge of the locking; +we can avoid this if we create an unref_inode that doesn't take +the lock and then grab it for the whole of the lo_destroy. + +Suggested-by: Vivek Goyal +Signed-off-by: Dr. David Alan Gilbert +Reviewed-by: Philippe Mathieu-Daudé +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit fe4c15798a48143dd6b1f58d2d3cad12206ce211) +--- + tools/virtiofsd/passthrough_ll.c | 31 +++++++++++++++++-------------- + 1 file changed, 17 insertions(+), 14 deletions(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index eb001b9d1e..fc15d61510 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -1344,14 +1344,13 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) + lo_inode_put(lo, &inode); + } + +-static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, +- uint64_t n) ++/* To be called with lo->mutex held */ ++static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) + { + if (!inode) { + return; + } + +- pthread_mutex_lock(&lo->mutex); + assert(inode->nlookup >= n); + inode->nlookup -= n; + if (!inode->nlookup) { +@@ -1362,15 +1361,24 @@ static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, + } + g_hash_table_destroy(inode->posix_locks); + pthread_mutex_destroy(&inode->plock_mutex); +- pthread_mutex_unlock(&lo->mutex); + + /* Drop our refcount from lo_do_lookup() */ + lo_inode_put(lo, &inode); +- } else { +- pthread_mutex_unlock(&lo->mutex); + } + } + ++static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, ++ uint64_t n) ++{ ++ if (!inode) { ++ return; ++ } ++ ++ pthread_mutex_lock(&lo->mutex); ++ unref_inode(lo, inode, n); ++ pthread_mutex_unlock(&lo->mutex); ++} ++ + static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) + { + struct lo_data *lo = lo_data(req); +@@ -2458,13 +2466,7 @@ static void lo_destroy(void *userdata) + { + struct lo_data *lo = (struct lo_data *)userdata; + +- /* +- * Normally lo->mutex must be taken when traversing lo->inodes but +- * lo_destroy() is a serialized request so no races are possible here. +- * +- * In addition, we cannot acquire lo->mutex since unref_inode() takes it +- * too and this would result in a recursive lock. +- */ ++ pthread_mutex_lock(&lo->mutex); + while (true) { + GHashTableIter iter; + gpointer key, value; +@@ -2475,8 +2477,9 @@ static void lo_destroy(void *userdata) + } + + struct lo_inode *inode = value; +- unref_inode_lolocked(lo, inode, inode->nlookup); ++ unref_inode(lo, inode, inode->nlookup); + } ++ pthread_mutex_unlock(&lo->mutex); + } + + static struct fuse_lowlevel_ops lo_oper = { diff --git a/0115-virtiofsd-passthrough_ll-Pass-errno-to-fuse_reply_er.patch b/0115-virtiofsd-passthrough_ll-Pass-errno-to-fuse_reply_er.patch new file mode 100644 index 0000000..91a4062 --- /dev/null +++ b/0115-virtiofsd-passthrough_ll-Pass-errno-to-fuse_reply_er.patch @@ -0,0 +1,34 @@ +From: Xiao Yang +Date: Mon, 27 Jan 2020 19:02:24 +0000 +Subject: [PATCH] virtiofsd/passthrough_ll: Pass errno to fuse_reply_err() + +lo_copy_file_range() passes -errno to fuse_reply_err() and then fuse_reply_err() +changes it to errno again, so that subsequent fuse_send_reply_iov_nofree() catches +the wrong errno.(i.e. reports "fuse: bad error value: ..."). + +Make fuse_send_reply_iov_nofree() accept the correct -errno by passing errno +directly in lo_copy_file_range(). + +Signed-off-by: Xiao Yang +Reviewed-by: Eryu Guan + +dgilbert: Sent upstream and now Merged as aa1185e153f774f1df65 +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit a931b6861e59c78d861017e9c6a9c161ff49a163) +--- + tools/virtiofsd/passthrough_ll.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c +index fc15d61510..e6f2399efc 100644 +--- a/tools/virtiofsd/passthrough_ll.c ++++ b/tools/virtiofsd/passthrough_ll.c +@@ -2441,7 +2441,7 @@ static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, + + res = copy_file_range(in_fd, &off_in, out_fd, &off_out, len, flags); + if (res < 0) { +- fuse_reply_err(req, -errno); ++ fuse_reply_err(req, errno); + } else { + fuse_reply_write(req, res); + } diff --git a/0116-virtiofsd-stop-all-queue-threads-on-exit-in-virtio_l.patch b/0116-virtiofsd-stop-all-queue-threads-on-exit-in-virtio_l.patch new file mode 100644 index 0000000..6769996 --- /dev/null +++ b/0116-virtiofsd-stop-all-queue-threads-on-exit-in-virtio_l.patch @@ -0,0 +1,52 @@ +From: Eryu Guan +Date: Mon, 27 Jan 2020 19:02:25 +0000 +Subject: [PATCH] virtiofsd: stop all queue threads on exit in virtio_loop() + +On guest graceful shutdown, virtiofsd receives VHOST_USER_GET_VRING_BASE +request from VMM and shuts down virtqueues by calling fv_set_started(), +which joins fv_queue_thread() threads. So when virtio_loop() returns, +there should be no thread is still accessing data in fuse session and/or +virtio dev. + +But on abnormal exit, e.g. guest got killed for whatever reason, +vhost-user socket is closed and virtio_loop() breaks out the main loop +and returns to main(). But it's possible fv_queue_worker()s are still +working and accessing fuse session and virtio dev, which results in +crash or use-after-free. + +Fix it by stopping fv_queue_thread()s before virtio_loop() returns, +to make sure there's no-one could access fuse session and virtio dev. + +Reported-by: Qingming Su +Signed-off-by: Eryu Guan +Reviewed-by: Stefan Hajnoczi +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 9883df8ccae6d744a0c8d9cbf9d62b1797d70ebd) +--- + tools/virtiofsd/fuse_virtio.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c +index 9f6582343c..80a6e929df 100644 +--- a/tools/virtiofsd/fuse_virtio.c ++++ b/tools/virtiofsd/fuse_virtio.c +@@ -815,6 +815,19 @@ int virtio_loop(struct fuse_session *se) + } + } + ++ /* ++ * Make sure all fv_queue_thread()s quit on exit, as we're about to ++ * free virtio dev and fuse session, no one should access them anymore. ++ */ ++ for (int i = 0; i < se->virtio_dev->nqueues; i++) { ++ if (!se->virtio_dev->qi[i]) { ++ continue; ++ } ++ ++ fuse_log(FUSE_LOG_INFO, "%s: Stopping queue %d thread\n", __func__, i); ++ fv_queue_cleanup_thread(se->virtio_dev, i); ++ } ++ + fuse_log(FUSE_LOG_INFO, "%s: Exit\n", __func__); + + return 0; diff --git a/0117-virtiofsd-add-some-options-to-the-help-message.patch b/0117-virtiofsd-add-some-options-to-the-help-message.patch new file mode 100644 index 0000000..8fbb062 --- /dev/null +++ b/0117-virtiofsd-add-some-options-to-the-help-message.patch @@ -0,0 +1,55 @@ +From: Masayoshi Mizuma +Date: Mon, 27 Jan 2020 19:02:26 +0000 +Subject: [PATCH] virtiofsd: add some options to the help message + +Add following options to the help message: +- cache +- flock|no_flock +- norace +- posix_lock|no_posix_lock +- readdirplus|no_readdirplus +- timeout +- writeback|no_writeback +- xattr|no_xattr + +Signed-off-by: Masayoshi Mizuma + +dgilbert: Split cache, norace, posix_lock, readdirplus off + into our own earlier patches that added the options + +Reviewed-by: Dr. David Alan Gilbert +Reviewed-by: Misono Tomohiro +Signed-off-by: Dr. David Alan Gilbert +(cherry picked from commit 1d59b1b210d7c3b0bdf4b10ebe0bb1fccfcb8b95) +--- + tools/virtiofsd/helper.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c +index f98d8f2eb2..0801cf752c 100644 +--- a/tools/virtiofsd/helper.c ++++ b/tools/virtiofsd/helper.c +@@ -148,6 +148,8 @@ void fuse_cmdline_help(void) + " -o cache= cache mode. could be one of \"auto, " + "always, none\"\n" + " default: auto\n" ++ " -o flock|no_flock enable/disable flock\n" ++ " default: no_flock\n" + " -o log_level= log level, default to \"info\"\n" + " level could be one of \"debug, " + "info, warn, err\"\n" +@@ -163,7 +165,13 @@ void fuse_cmdline_help(void) + " enable/disable readirplus\n" + " default: readdirplus except with " + "cache=none\n" +- ); ++ " -o timeout= I/O timeout (second)\n" ++ " default: depends on cache= option.\n" ++ " -o writeback|no_writeback enable/disable writeback cache\n" ++ " default: no_writeback\n" ++ " -o xattr|no_xattr enable/disable xattr\n" ++ " default: no_xattr\n" ++ ); + } + + static int fuse_helper_opt_proc(void *data, const char *arg, int key, diff --git a/qemu.spec b/qemu.spec index eb965e5..0328d23 100644 --- a/qemu.spec +++ b/qemu.spec @@ -161,24 +161,13 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 4.2.0 -Release: 3%{?rcrel}%{?dist} +Release: 4%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ Source0: http://wiki.qemu-project.org/download/%{name}-%{version}%{?rcstr}.tar.xz -# Fix a test suite error -Patch1: 0001-tests-fix-modules-test-duplicate-test-case-error.patch - -# Miscellaneous fixes for RISC-V, merged upstream in commit -# ba2ed84fe6a78f64b2da441750fc6e925d94106a. -Patch2: 0001-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch -Patch3: 0002-riscv-Set-xPIE-to-1-after-xRET.patch -Patch4: 0003-target-riscv-Fix-tb-flags-FS-status.patch -Patch5: 0004-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch -Patch6: 0005-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch - # guest agent service Source10: qemu-guest-agent.service Source17: qemu-ga.sysconfig @@ -196,6 +185,127 @@ Source20: kvm-x86.modprobe.conf # /etc/security/limits.d/95-kvm-ppc64-memlock.conf Source21: 95-kvm-ppc64-memlock.conf +# Fix a test suite error +Patch0001: 0001-tests-fix-modules-test-duplicate-test-case-error.patch +# Miscellaneous fixes for RISC-V +Patch0002: 0002-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch +Patch0003: 0003-riscv-Set-xPIE-to-1-after-xRET.patch +Patch0004: 0004-target-riscv-Fix-tb-flags-FS-status.patch +Patch0005: 0005-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch +Patch0006: 0006-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch +# virtio-fs support +Patch0007: 0007-virtio-fs-fix-MSI-X-nvectors-calculation.patch +Patch0008: 0008-vhost-user-fs-remove-vhostfd-property.patch +Patch0009: 0009-build-rename-CONFIG_LIBCAP-to-CONFIG_LIBCAP_NG.patch +Patch0010: 0010-virtiofsd-Pull-in-upstream-headers.patch +Patch0011: 0011-virtiofsd-Pull-in-kernel-s-fuse.h.patch +Patch0012: 0012-virtiofsd-Add-auxiliary-.c-s.patch +Patch0013: 0013-virtiofsd-Add-fuse_lowlevel.c.patch +Patch0014: 0014-virtiofsd-Add-passthrough_ll.patch +Patch0015: 0015-virtiofsd-Trim-down-imported-files.patch +Patch0016: 0016-virtiofsd-Format-imported-files-to-qemu-style.patch +Patch0017: 0017-virtiofsd-remove-mountpoint-dummy-argument.patch +Patch0018: 0018-virtiofsd-remove-unused-notify-reply-support.patch +Patch0019: 0019-virtiofsd-Remove-unused-enum-fuse_buf_copy_flags.patch +Patch0020: 0020-virtiofsd-Fix-fuse_daemonize-ignored-return-values.patch +Patch0021: 0021-virtiofsd-Fix-common-header-and-define-for-QEMU-buil.patch +Patch0022: 0022-virtiofsd-Trim-out-compatibility-code.patch +Patch0023: 0023-vitriofsd-passthrough_ll-fix-fallocate-ifdefs.patch +Patch0024: 0024-virtiofsd-Make-fsync-work-even-if-only-inode-is-pass.patch +Patch0025: 0025-virtiofsd-Add-options-for-virtio.patch +Patch0026: 0026-virtiofsd-add-o-source-PATH-to-help-output.patch +Patch0027: 0027-virtiofsd-Open-vhost-connection-instead-of-mounting.patch +Patch0028: 0028-virtiofsd-Start-wiring-up-vhost-user.patch +Patch0029: 0029-virtiofsd-Add-main-virtio-loop.patch +Patch0030: 0030-virtiofsd-get-set-features-callbacks.patch +Patch0031: 0031-virtiofsd-Start-queue-threads.patch +Patch0032: 0032-virtiofsd-Poll-kick_fd-for-queue.patch +Patch0033: 0033-virtiofsd-Start-reading-commands-from-queue.patch +Patch0034: 0034-virtiofsd-Send-replies-to-messages.patch +Patch0035: 0035-virtiofsd-Keep-track-of-replies.patch +Patch0036: 0036-virtiofsd-Add-Makefile-wiring-for-virtiofsd-contrib.patch +Patch0037: 0037-virtiofsd-Fast-path-for-virtio-read.patch +Patch0038: 0038-virtiofsd-add-fd-FDNUM-fd-passing-option.patch +Patch0039: 0039-virtiofsd-make-f-foreground-the-default.patch +Patch0040: 0040-virtiofsd-add-vhost-user.json-file.patch +Patch0041: 0041-virtiofsd-add-print-capabilities-option.patch +Patch0042: 0042-virtiofs-Add-maintainers-entry.patch +Patch0043: 0043-virtiofsd-passthrough_ll-create-new-files-in-caller-.patch +Patch0044: 0044-virtiofsd-passthrough_ll-add-lo_map-for-ino-fh-indir.patch +Patch0045: 0045-virtiofsd-passthrough_ll-add-ino_map-to-hide-lo_inod.patch +Patch0046: 0046-virtiofsd-passthrough_ll-add-dirp_map-to-hide-lo_dir.patch +Patch0047: 0047-virtiofsd-passthrough_ll-add-fd_map-to-hide-file-des.patch +Patch0048: 0048-virtiofsd-passthrough_ll-add-fallback-for-racy-ops.patch +Patch0049: 0049-virtiofsd-validate-path-components.patch +Patch0050: 0050-virtiofsd-Plumb-fuse_bufvec-through-to-do_write_buf.patch +Patch0051: 0051-virtiofsd-Pass-write-iov-s-all-the-way-through.patch +Patch0052: 0052-virtiofsd-add-fuse_mbuf_iter-API.patch +Patch0053: 0053-virtiofsd-validate-input-buffer-sizes-in-do_write_bu.patch +Patch0054: 0054-virtiofsd-check-input-buffer-size-in-fuse_lowlevel.c.patch +Patch0055: 0055-virtiofsd-prevent-.-escape-in-lo_do_lookup.patch +Patch0056: 0056-virtiofsd-prevent-.-escape-in-lo_do_readdir.patch +Patch0057: 0057-virtiofsd-use-proc-self-fd-O_PATH-file-descriptor.patch +Patch0058: 0058-virtiofsd-sandbox-mount-namespace.patch +Patch0059: 0059-virtiofsd-move-to-an-empty-network-namespace.patch +Patch0060: 0060-virtiofsd-move-to-a-new-pid-namespace.patch +Patch0061: 0061-virtiofsd-add-seccomp-whitelist.patch +Patch0062: 0062-virtiofsd-Parse-flag-FUSE_WRITE_KILL_PRIV.patch +Patch0063: 0063-virtiofsd-cap-ng-helpers.patch +Patch0064: 0064-virtiofsd-Drop-CAP_FSETID-if-client-asked-for-it.patch +Patch0065: 0065-virtiofsd-set-maximum-RLIMIT_NOFILE-limit.patch +Patch0066: 0066-virtiofsd-fix-libfuse-information-leaks.patch +Patch0067: 0067-virtiofsd-add-syslog-command-line-option.patch +Patch0068: 0068-virtiofsd-print-log-only-when-priority-is-high-enoug.patch +Patch0069: 0069-virtiofsd-Add-ID-to-the-log-with-FUSE_LOG_DEBUG-leve.patch +Patch0070: 0070-virtiofsd-Add-timestamp-to-the-log-with-FUSE_LOG_DEB.patch +Patch0071: 0071-virtiofsd-Handle-reinit.patch +Patch0072: 0072-virtiofsd-Handle-hard-reboot.patch +Patch0073: 0073-virtiofsd-Kill-threads-when-queues-are-stopped.patch +Patch0074: 0074-vhost-user-Print-unexpected-slave-message-types.patch +Patch0075: 0075-contrib-libvhost-user-Protect-slave-fd-with-mutex.patch +Patch0076: 0076-virtiofsd-passthrough_ll-add-renameat2-support.patch +Patch0077: 0077-virtiofsd-passthrough_ll-disable-readdirplus-on-cach.patch +Patch0078: 0078-virtiofsd-passthrough_ll-control-readdirplus.patch +Patch0079: 0079-virtiofsd-rename-unref_inode-to-unref_inode_lolocked.patch +Patch0080: 0080-virtiofsd-fail-when-parent-inode-isn-t-known-in-lo_d.patch +Patch0081: 0081-virtiofsd-extract-root-inode-init-into-setup_root.patch +Patch0082: 0082-virtiofsd-passthrough_ll-clean-up-cache-related-opti.patch +Patch0083: 0083-virtiofsd-passthrough_ll-use-hashtable.patch +Patch0084: 0084-virtiofsd-Clean-up-inodes-on-destroy.patch +Patch0085: 0085-virtiofsd-support-nanosecond-resolution-for-file-tim.patch +Patch0086: 0086-virtiofsd-fix-error-handling-in-main.patch +Patch0087: 0087-virtiofsd-cleanup-allocated-resource-in-se.patch +Patch0088: 0088-virtiofsd-fix-memory-leak-on-lo.source.patch +Patch0089: 0089-virtiofsd-add-helper-for-lo_data-cleanup.patch +Patch0090: 0090-virtiofsd-Prevent-multiply-running-with-same-vhost_u.patch +Patch0091: 0091-virtiofsd-enable-PARALLEL_DIROPS-during-INIT.patch +Patch0092: 0092-virtiofsd-fix-incorrect-error-handling-in-lo_do_look.patch +Patch0093: 0093-Virtiofsd-fix-memory-leak-on-fuse-queueinfo.patch +Patch0094: 0094-virtiofsd-Support-remote-posix-locks.patch +Patch0095: 0095-virtiofsd-use-fuse_lowlevel_is_virtio-in-fuse_sessio.patch +Patch0096: 0096-virtiofsd-prevent-fv_queue_thread-vs-virtio_loop-rac.patch +Patch0097: 0097-virtiofsd-make-lo_release-atomic.patch +Patch0098: 0098-virtiofsd-prevent-races-with-lo_dirp_put.patch +Patch0099: 0099-virtiofsd-rename-inode-refcount-to-inode-nlookup.patch +Patch0100: 0100-libvhost-user-Fix-some-memtable-remap-cases.patch +Patch0101: 0101-virtiofsd-passthrough_ll-fix-refcounting-on-remove-r.patch +Patch0102: 0102-virtiofsd-introduce-inode-refcount-to-prevent-use-af.patch +Patch0103: 0103-virtiofsd-do-not-always-set-FUSE_FLOCK_LOCKS.patch +Patch0104: 0104-virtiofsd-convert-more-fprintf-and-perror-to-use-fus.patch +Patch0105: 0105-virtiofsd-Reset-O_DIRECT-flag-during-file-open.patch +Patch0106: 0106-virtiofsd-Fix-data-corruption-with-O_APPEND-write-in.patch +Patch0107: 0107-virtiofsd-passthrough_ll-Use-cache_readdir-for-direc.patch +Patch0108: 0108-virtiofsd-add-definition-of-fuse_buf_writev.patch +Patch0109: 0109-virtiofsd-use-fuse_buf_writev-to-replace-fuse_buf_wr.patch +Patch0110: 0110-virtiofsd-process-requests-in-a-thread-pool.patch +Patch0111: 0111-virtiofsd-prevent-FUSE_INIT-FUSE_DESTROY-races.patch +Patch0112: 0112-virtiofsd-fix-lo_destroy-resource-leaks.patch +Patch0113: 0113-virtiofsd-add-thread-pool-size-NUM-option.patch +Patch0114: 0114-virtiofsd-Convert-lo_destroy-to-take-the-lo-mutex-lo.patch +Patch0115: 0115-virtiofsd-passthrough_ll-Pass-errno-to-fuse_reply_er.patch +Patch0116: 0116-virtiofsd-stop-all-queue-threads-on-exit-in-virtio_l.patch +Patch0117: 0117-virtiofsd-add-some-options-to-the-help-message.patch + # documentation deps BuildRequires: texinfo @@ -1442,6 +1552,7 @@ getent passwd qemu >/dev/null || \ %{_datadir}/%{name}/pxe-vmxnet3.rom %{_datadir}/%{name}/efi-vmxnet3.rom %{_datadir}/%{name}/vhost-user/50-qemu-gpu.json +%{_datadir}/%{name}/vhost-user/50-qemu-virtiofsd.json %{_mandir}/man1/qemu.1* %{_mandir}/man1/qemu-trace-stap.1* %{_mandir}/man1/virtfs-proxy-helper.1* @@ -1459,6 +1570,7 @@ getent passwd qemu >/dev/null || \ %{_unitdir}/qemu-pr-helper.socket %attr(4755, root, root) %{_libexecdir}/qemu-bridge-helper %{_libexecdir}/vhost-user-gpu +%{_libexecdir}/virtiofsd %config(noreplace) %{_sysconfdir}/sasl2/qemu.conf %dir %{_sysconfdir}/qemu %config(noreplace) %{_sysconfdir}/qemu/bridge.conf @@ -1895,7 +2007,10 @@ getent passwd qemu >/dev/null || \ %changelog -* Sat Jan 25 2019 Mohan Boddu - 4.2.0-3 +* Tue Jan 28 2020 Cole Robinson - 2:4.2.0-4 +- virtio-fs support + +* Sat Jan 25 2020 Richard W.M. Jones - 4.2.0-3 - Add miscellaneous fixes for RISC-V (RHBZ#1794902). * Thu Dec 19 2019 Mohan Boddu - 4.2.0-2 From 023288b71ad15f5abc09597f209748628e6a7ab2 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Sat, 15 Feb 2020 21:11:36 -0500 Subject: [PATCH 25/32] qemu-4.2.0-5.fc32 Fix ppc shutdown issue (bz #1784961) --- ...ger-a-CAS-reboot-for-XICS-XIVE-mode-.patch | 90 +++++++++++++++++++ qemu.spec | 8 +- 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 0201-spapr-Don-t-trigger-a-CAS-reboot-for-XICS-XIVE-mode-.patch diff --git a/0201-spapr-Don-t-trigger-a-CAS-reboot-for-XICS-XIVE-mode-.patch b/0201-spapr-Don-t-trigger-a-CAS-reboot-for-XICS-XIVE-mode-.patch new file mode 100644 index 0000000..812a801 --- /dev/null +++ b/0201-spapr-Don-t-trigger-a-CAS-reboot-for-XICS-XIVE-mode-.patch @@ -0,0 +1,90 @@ +From: David Gibson +Date: Fri, 18 Oct 2019 15:19:31 +1100 +Subject: [PATCH] spapr: Don't trigger a CAS reboot for XICS/XIVE mode + changeover + +PAPR allows the interrupt controller used on a POWER9 machine (XICS or +XIVE) to be selected by the guest operating system, by using the +ibm,client-architecture-support (CAS) feature negotiation call. + +Currently, if the guest selects an interrupt controller different from the +one selected at initial boot, this causes the system to be reset with the +new model and the boot starts again. This means we run through the SLOF +boot process twice, as well as any other bootloader (e.g. grub) in use +before the OS calls CAS. This can be confusing and/or inconvenient for +users. + +Thanks to two fairly recent changes, we no longer need this reboot. 1) we +now completely regenerate the device tree when CAS is called (meaning we +don't need special case updates for all the device tree changes caused by +the interrupt controller mode change), 2) we now have explicit code paths +to activate and deactivate the different interrupt controllers, rather than +just implicitly calling those at machine reset time. + +We can therefore eliminate the reboot for changing irq mode, simply by +putting a call to spapr_irq_update_active_intc() before we call +spapr_h_cas_compose_response() (which gives the updated device tree to +the guest firmware and OS). + +Signed-off-by: David Gibson +Reviewed-by: Cedric Le Goater +Reviewed-by: Greg Kurz +(cherry picked from commit 8deb8019d696c75e6ecaee7545026b62aba2f1bb) +--- + hw/ppc/spapr_hcall.c | 33 +++++++++++++-------------------- + 1 file changed, 13 insertions(+), 20 deletions(-) + +diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c +index 140f05c1c6..05a7ca275b 100644 +--- a/hw/ppc/spapr_hcall.c ++++ b/hw/ppc/spapr_hcall.c +@@ -1767,21 +1767,10 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, + } + spapr->cas_pre_isa3_guest = !spapr_ovec_test(ov1_guest, OV1_PPC_3_00); + spapr_ovec_cleanup(ov1_guest); +- if (!spapr->cas_reboot) { +- /* If spapr_machine_reset() did not set up a HPT but one is necessary +- * (because the guest isn't going to use radix) then set it up here. */ +- if ((spapr->patb_entry & PATE1_GR) && !guest_radix) { +- /* legacy hash or new hash: */ +- spapr_setup_hpt_and_vrma(spapr); +- } +- spapr->cas_reboot = +- (spapr_h_cas_compose_response(spapr, args[1], args[2], +- ov5_updates) != 0); +- } + + /* +- * Ensure the guest asks for an interrupt mode we support; otherwise +- * terminate the boot. ++ * Ensure the guest asks for an interrupt mode we support; ++ * otherwise terminate the boot. + */ + if (guest_xive) { + if (!spapr->irq->xive) { +@@ -1797,14 +1786,18 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, + } + } + +- /* +- * Generate a machine reset when we have an update of the +- * interrupt mode. Only required when the machine supports both +- * modes. +- */ ++ spapr_irq_update_active_intc(spapr); ++ + if (!spapr->cas_reboot) { +- spapr->cas_reboot = spapr_ovec_test(ov5_updates, OV5_XIVE_EXPLOIT) +- && spapr->irq->xics && spapr->irq->xive; ++ /* If spapr_machine_reset() did not set up a HPT but one is necessary ++ * (because the guest isn't going to use radix) then set it up here. */ ++ if ((spapr->patb_entry & PATE1_GR) && !guest_radix) { ++ /* legacy hash or new hash: */ ++ spapr_setup_hpt_and_vrma(spapr); ++ } ++ spapr->cas_reboot = ++ (spapr_h_cas_compose_response(spapr, args[1], args[2], ++ ov5_updates) != 0); + } + + spapr_ovec_cleanup(ov5_updates); diff --git a/qemu.spec b/qemu.spec index 0328d23..1720b5c 100644 --- a/qemu.spec +++ b/qemu.spec @@ -161,7 +161,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 4.2.0 -Release: 4%{?rcrel}%{?dist} +Release: 5%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -306,6 +306,9 @@ Patch0115: 0115-virtiofsd-passthrough_ll-Pass-errno-to-fuse_reply_er.patch Patch0116: 0116-virtiofsd-stop-all-queue-threads-on-exit-in-virtio_l.patch Patch0117: 0117-virtiofsd-add-some-options-to-the-help-message.patch +# Fix ppc shutdown issue (bz #1784961) +Patch0201: 0201-spapr-Don-t-trigger-a-CAS-reboot-for-XICS-XIVE-mode-.patch + # documentation deps BuildRequires: texinfo @@ -2007,6 +2010,9 @@ getent passwd qemu >/dev/null || \ %changelog +* Sat Feb 15 2020 Cole Robinson - 2:4.2.0-5 +- Fix ppc shutdown issue (bz #1784961) + * Tue Jan 28 2020 Cole Robinson - 2:4.2.0-4 - virtio-fs support From 377bb253e3c0d57d0d29cb2720d14e03b0e8f3f3 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 25 Feb 2020 14:47:44 -0500 Subject: [PATCH 26/32] qemu-4.2.0-6.fc33 Rebuild for libiscsi soname bump --- qemu.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qemu.spec b/qemu.spec index 1720b5c..db565ae 100644 --- a/qemu.spec +++ b/qemu.spec @@ -161,7 +161,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 4.2.0 -Release: 5%{?rcrel}%{?dist} +Release: 6%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -2010,6 +2010,9 @@ getent passwd qemu >/dev/null || \ %changelog +* Tue Feb 25 2020 Cole Robinson - 2:4.2.0-6 +- Rebuild for libiscsi soname bump + * Sat Feb 15 2020 Cole Robinson - 2:4.2.0-5 - Fix ppc shutdown issue (bz #1784961) From 492d6c1fff32243a7dbc5b9b9ab67fd2e6af2523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= Date: Tue, 17 Mar 2020 11:12:56 +0100 Subject: [PATCH 27/32] qemu-4.2.0-7.fc33 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix segfault with SR-IOV hot-{plug,unplug} (bz #1814017) Signed-off-by: Fabiano Fidêncio --- ...emove-irqchip-notifier-if-not-regist.patch | 45 +++++++++++++++++++ qemu.spec | 7 ++- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 0118-vfio-pci-Don-t-remove-irqchip-notifier-if-not-regist.patch diff --git a/0118-vfio-pci-Don-t-remove-irqchip-notifier-if-not-regist.patch b/0118-vfio-pci-Don-t-remove-irqchip-notifier-if-not-regist.patch new file mode 100644 index 0000000..a510fc1 --- /dev/null +++ b/0118-vfio-pci-Don-t-remove-irqchip-notifier-if-not-regist.patch @@ -0,0 +1,45 @@ +From 0446f8121723b134ca1d1ed0b73e96d4a0a8689d Mon Sep 17 00:00:00 2001 +From: Peter Xu +Date: Mon, 6 Jan 2020 13:34:45 -0700 +Subject: [PATCH] vfio/pci: Don't remove irqchip notifier if not registered + +The kvm irqchip notifier is only registered if the device supports +INTx, however it's unconditionally removed. If the assigned device +does not support INTx, this will cause QEMU to crash when unplugging +the device from the system. Change it to conditionally remove the +notifier only if the notify hook is setup. + +CC: Eduardo Habkost +CC: David Gibson +CC: Alex Williamson +Cc: qemu-stable@nongnu.org # v4.2 +Reported-by: yanghliu@redhat.com +Debugged-by: Eduardo Habkost +Fixes: c5478fea27ac ("vfio/pci: Respond to KVM irqchip change notifier") +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1782678 +Signed-off-by: Peter Xu +Reviewed-by: David Gibson +Reviewed-by: Greg Kurz +Signed-off-by: Alex Williamson +--- + hw/vfio/pci.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c +index 2d40b396f2..337a173ce7 100644 +--- a/hw/vfio/pci.c ++++ b/hw/vfio/pci.c +@@ -3076,7 +3076,9 @@ static void vfio_exitfn(PCIDevice *pdev) + vfio_unregister_req_notifier(vdev); + vfio_unregister_err_notifier(vdev); + pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); +- kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); ++ if (vdev->irqchip_change_notifier.notify) { ++ kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); ++ } + vfio_disable_interrupts(vdev); + if (vdev->intx.mmap_timer) { + timer_free(vdev->intx.mmap_timer); +-- +2.25.1 + diff --git a/qemu.spec b/qemu.spec index db565ae..66c2948 100644 --- a/qemu.spec +++ b/qemu.spec @@ -161,7 +161,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 4.2.0 -Release: 6%{?rcrel}%{?dist} +Release: 7%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -305,6 +305,8 @@ Patch0114: 0114-virtiofsd-Convert-lo_destroy-to-take-the-lo-mutex-lo.patch Patch0115: 0115-virtiofsd-passthrough_ll-Pass-errno-to-fuse_reply_er.patch Patch0116: 0116-virtiofsd-stop-all-queue-threads-on-exit-in-virtio_l.patch Patch0117: 0117-virtiofsd-add-some-options-to-the-help-message.patch +# Fix segfault with SR-IOV hot-{plug,unplug} +Patch0118: 0118-vfio-pci-Don-t-remove-irqchip-notifier-if-not-regist.patch # Fix ppc shutdown issue (bz #1784961) Patch0201: 0201-spapr-Don-t-trigger-a-CAS-reboot-for-XICS-XIVE-mode-.patch @@ -2010,6 +2012,9 @@ getent passwd qemu >/dev/null || \ %changelog +* Tue Mar 17 2020 Fabiano Fidêncio - 2:4.2.0-7 +- Fix segfault with SR-IOV hot-{plug,unplug} (bz #1814017) + * Tue Feb 25 2020 Cole Robinson - 2:4.2.0-6 - Rebuild for libiscsi soname bump From 8833af8dcd3cee5c14c7c24b0110dec3ecba7408 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Tue, 24 Mar 2020 18:37:35 -0400 Subject: [PATCH 28/32] Update to qemu-5.0.0-rc0 Signed-off-by: Cole Robinson --- .gitignore | 1 + ...dules-test-duplicate-test-case-error.patch | 35 - ...e_u-fix-a-memory-leak-in-soc_realize.patch | 30 - 0003-riscv-Set-xPIE-to-1-after-xRET.patch | 39 - ...-target-riscv-Fix-tb-flags-FS-status.patch | 44 - ...riscv-fsd-fsw-doesn-t-dirty-FP-state.patch | 37 - ...date-mstatus.SD-when-FS-is-set-dirty.patch | 43 - ...io-fs-fix-MSI-X-nvectors-calculation.patch | 41 - ...host-user-fs-remove-vhostfd-property.patch | 43 - ...me-CONFIG_LIBCAP-to-CONFIG_LIBCAP_NG.patch | 118 - 0010-virtiofsd-Pull-in-upstream-headers.patch | 4895 ----- 0011-virtiofsd-Pull-in-kernel-s-fuse.h.patch | 929 - 0012-virtiofsd-Add-auxiliary-.c-s.patch | 1371 -- 0013-virtiofsd-Add-fuse_lowlevel.c.patch | 3156 ---- 0014-virtiofsd-Add-passthrough_ll.patch | 1370 -- 0015-virtiofsd-Trim-down-imported-files.patch | 1565 -- ...-Format-imported-files-to-qemu-style.patch | 14727 ---------------- ...fsd-remove-mountpoint-dummy-argument.patch | 143 - ...d-remove-unused-notify-reply-support.patch | 278 - ...move-unused-enum-fuse_buf_copy_flags.patch | 252 - ...fuse_daemonize-ignored-return-values.patch | 104 - ...mmon-header-and-define-for-QEMU-buil.patch | 147 - ...irtiofsd-Trim-out-compatibility-code.patch | 529 - ...-passthrough_ll-fix-fallocate-ifdefs.patch | 36 - ...sync-work-even-if-only-inode-is-pass.patch | 79 - 0025-virtiofsd-Add-options-for-virtio.patch | 84 - ...fsd-add-o-source-PATH-to-help-output.patch | 30 - ...vhost-connection-instead-of-mounting.patch | 241 - ...virtiofsd-Start-wiring-up-vhost-user.patch | 231 - 0029-virtiofsd-Add-main-virtio-loop.patch | 89 - ...virtiofsd-get-set-features-callbacks.patch | 50 - 0031-virtiofsd-Start-queue-threads.patch | 148 - 0032-virtiofsd-Poll-kick_fd-for-queue.patch | 81 - ...sd-Start-reading-commands-from-queue.patch | 184 - 0034-virtiofsd-Send-replies-to-messages.patch | 183 - 0035-virtiofsd-Keep-track-of-replies.patch | 100 - ...akefile-wiring-for-virtiofsd-contrib.patch | 90 - ...-virtiofsd-Fast-path-for-virtio-read.patch | 220 - ...iofsd-add-fd-FDNUM-fd-passing-option.patch | 154 - ...tiofsd-make-f-foreground-the-default.patch | 60 - 0040-virtiofsd-add-vhost-user.json-file.patch | 57 - ...tiofsd-add-print-capabilities-option.patch | 105 - 0042-virtiofs-Add-maintainers-entry.patch | 36 - ...rough_ll-create-new-files-in-caller-.patch | 179 - ...rough_ll-add-lo_map-for-ino-fh-indir.patch | 162 - ...rough_ll-add-ino_map-to-hide-lo_inod.patch | 376 - ...rough_ll-add-dirp_map-to-hide-lo_dir.patch | 222 - ...rough_ll-add-fd_map-to-hide-file-des.patch | 308 - ...through_ll-add-fallback-for-racy-ops.patch | 284 - 0049-virtiofsd-validate-path-components.patch | 148 - ...-fuse_bufvec-through-to-do_write_buf.patch | 149 - ...Pass-write-iov-s-all-the-way-through.patch | 121 - 0052-virtiofsd-add-fuse_mbuf_iter-API.patch | 115 - ...te-input-buffer-sizes-in-do_write_bu.patch | 117 - ...input-buffer-size-in-fuse_lowlevel.c.patch | 1091 -- ...fsd-prevent-.-escape-in-lo_do_lookup.patch | 35 - ...sd-prevent-.-escape-in-lo_do_readdir.patch | 89 - ...-proc-self-fd-O_PATH-file-descriptor.patch | 374 - 0058-virtiofsd-sandbox-mount-namespace.patch | 150 - ...d-move-to-an-empty-network-namespace.patch | 50 - ...irtiofsd-move-to-a-new-pid-namespace.patch | 207 - 0061-virtiofsd-add-seccomp-whitelist.patch | 265 - ...ofsd-Parse-flag-FUSE_WRITE_KILL_PRIV.patch | 57 - 0063-virtiofsd-cap-ng-helpers.patch | 159 - ...op-CAP_FSETID-if-client-asked-for-it.patch | 156 - ...ofsd-set-maximum-RLIMIT_NOFILE-limit.patch | 77 - ...tiofsd-fix-libfuse-information-leaks.patch | 306 - ...iofsd-add-syslog-command-line-option.patch | 223 - ...log-only-when-priority-is-high-enoug.patch | 451 - ...-to-the-log-with-FUSE_LOG_DEBUG-leve.patch | 68 - ...mestamp-to-the-log-with-FUSE_LOG_DEB.patch | 56 - 0071-virtiofsd-Handle-reinit.patch | 37 - 0072-virtiofsd-Handle-hard-reboot.patch | 49 - ...Kill-threads-when-queues-are-stopped.patch | 126 - ...Print-unexpected-slave-message-types.patch | 32 - ...ost-user-Protect-slave-fd-with-mutex.patch | 118 - ...passthrough_ll-add-renameat2-support.patch | 33 - ...rough_ll-disable-readdirplus-on-cach.patch | 33 - ...d-passthrough_ll-control-readdirplus.patch | 60 - ...-unref_inode-to-unref_inode_lolocked.patch | 77 - ...hen-parent-inode-isn-t-known-in-lo_d.patch | 66 - ...ract-root-inode-init-into-setup_root.patch | 91 - ...rough_ll-clean-up-cache-related-opti.patch | 121 - ...rtiofsd-passthrough_ll-use-hashtable.patch | 195 - ...virtiofsd-Clean-up-inodes-on-destroy.patch | 69 - ...t-nanosecond-resolution-for-file-tim.patch | 66 - ...virtiofsd-fix-error-handling-in-main.patch | 47 - ...fsd-cleanup-allocated-resource-in-se.patch | 66 - ...rtiofsd-fix-memory-leak-on-lo.source.patch | 47 - ...iofsd-add-helper-for-lo_data-cleanup.patch | 72 - ...t-multiply-running-with-same-vhost_u.patch | 127 - ...d-enable-PARALLEL_DIROPS-during-INIT.patch | 31 - ...correct-error-handling-in-lo_do_look.patch | 27 - ...sd-fix-memory-leak-on-fuse-queueinfo.patch | 44 - ...virtiofsd-Support-remote-posix-locks.patch | 336 - ...se_lowlevel_is_virtio-in-fuse_sessio.patch | 40 - ...t-fv_queue_thread-vs-virtio_loop-rac.patch | 132 - 0097-virtiofsd-make-lo_release-atomic.patch | 46 - ...iofsd-prevent-races-with-lo_dirp_put.patch | 131 - ...name-inode-refcount-to-inode-nlookup.patch | 123 - ...t-user-Fix-some-memtable-remap-cases.patch | 98 - ...rough_ll-fix-refcounting-on-remove-r.patch | 123 - ...uce-inode-refcount-to-prevent-use-af.patch | 569 - ...d-do-not-always-set-FUSE_FLOCK_LOCKS.patch | 38 - ...t-more-fprintf-and-perror-to-use-fus.patch | 83 - ...Reset-O_DIRECT-flag-during-file-open.patch | 56 - ...ta-corruption-with-O_APPEND-write-in.patch | 120 - ...rough_ll-Use-cache_readdir-for-direc.patch | 29 - ...sd-add-definition-of-fuse_buf_writev.patch | 77 - ...se_buf_writev-to-replace-fuse_buf_wr.patch | 66 - ...sd-process-requests-in-a-thread-pool.patch | 514 - ...prevent-FUSE_INIT-FUSE_DESTROY-races.patch | 84 - ...tiofsd-fix-lo_destroy-resource-leaks.patch | 78 - ...ofsd-add-thread-pool-size-NUM-option.patch | 90 - ...t-lo_destroy-to-take-the-lo-mutex-lo.patch | 96 - ...rough_ll-Pass-errno-to-fuse_reply_er.patch | 34 - ...ll-queue-threads-on-exit-in-virtio_l.patch | 52 - ...add-some-options-to-the-help-message.patch | 55 - ...emove-irqchip-notifier-if-not-regist.patch | 45 - ...ger-a-CAS-reboot-for-XICS-XIVE-mode-.patch | 90 - qemu.spec | 145 +- sources | 2 +- 122 files changed, 12 insertions(+), 43124 deletions(-) delete mode 100644 0001-tests-fix-modules-test-duplicate-test-case-error.patch delete mode 100644 0002-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch delete mode 100644 0003-riscv-Set-xPIE-to-1-after-xRET.patch delete mode 100644 0004-target-riscv-Fix-tb-flags-FS-status.patch delete mode 100644 0005-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch delete mode 100644 0006-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch delete mode 100644 0007-virtio-fs-fix-MSI-X-nvectors-calculation.patch delete mode 100644 0008-vhost-user-fs-remove-vhostfd-property.patch delete mode 100644 0009-build-rename-CONFIG_LIBCAP-to-CONFIG_LIBCAP_NG.patch delete mode 100644 0010-virtiofsd-Pull-in-upstream-headers.patch delete mode 100644 0011-virtiofsd-Pull-in-kernel-s-fuse.h.patch delete mode 100644 0012-virtiofsd-Add-auxiliary-.c-s.patch delete mode 100644 0013-virtiofsd-Add-fuse_lowlevel.c.patch delete mode 100644 0014-virtiofsd-Add-passthrough_ll.patch delete mode 100644 0015-virtiofsd-Trim-down-imported-files.patch delete mode 100644 0016-virtiofsd-Format-imported-files-to-qemu-style.patch delete mode 100644 0017-virtiofsd-remove-mountpoint-dummy-argument.patch delete mode 100644 0018-virtiofsd-remove-unused-notify-reply-support.patch delete mode 100644 0019-virtiofsd-Remove-unused-enum-fuse_buf_copy_flags.patch delete mode 100644 0020-virtiofsd-Fix-fuse_daemonize-ignored-return-values.patch delete mode 100644 0021-virtiofsd-Fix-common-header-and-define-for-QEMU-buil.patch delete mode 100644 0022-virtiofsd-Trim-out-compatibility-code.patch delete mode 100644 0023-vitriofsd-passthrough_ll-fix-fallocate-ifdefs.patch delete mode 100644 0024-virtiofsd-Make-fsync-work-even-if-only-inode-is-pass.patch delete mode 100644 0025-virtiofsd-Add-options-for-virtio.patch delete mode 100644 0026-virtiofsd-add-o-source-PATH-to-help-output.patch delete mode 100644 0027-virtiofsd-Open-vhost-connection-instead-of-mounting.patch delete mode 100644 0028-virtiofsd-Start-wiring-up-vhost-user.patch delete mode 100644 0029-virtiofsd-Add-main-virtio-loop.patch delete mode 100644 0030-virtiofsd-get-set-features-callbacks.patch delete mode 100644 0031-virtiofsd-Start-queue-threads.patch delete mode 100644 0032-virtiofsd-Poll-kick_fd-for-queue.patch delete mode 100644 0033-virtiofsd-Start-reading-commands-from-queue.patch delete mode 100644 0034-virtiofsd-Send-replies-to-messages.patch delete mode 100644 0035-virtiofsd-Keep-track-of-replies.patch delete mode 100644 0036-virtiofsd-Add-Makefile-wiring-for-virtiofsd-contrib.patch delete mode 100644 0037-virtiofsd-Fast-path-for-virtio-read.patch delete mode 100644 0038-virtiofsd-add-fd-FDNUM-fd-passing-option.patch delete mode 100644 0039-virtiofsd-make-f-foreground-the-default.patch delete mode 100644 0040-virtiofsd-add-vhost-user.json-file.patch delete mode 100644 0041-virtiofsd-add-print-capabilities-option.patch delete mode 100644 0042-virtiofs-Add-maintainers-entry.patch delete mode 100644 0043-virtiofsd-passthrough_ll-create-new-files-in-caller-.patch delete mode 100644 0044-virtiofsd-passthrough_ll-add-lo_map-for-ino-fh-indir.patch delete mode 100644 0045-virtiofsd-passthrough_ll-add-ino_map-to-hide-lo_inod.patch delete mode 100644 0046-virtiofsd-passthrough_ll-add-dirp_map-to-hide-lo_dir.patch delete mode 100644 0047-virtiofsd-passthrough_ll-add-fd_map-to-hide-file-des.patch delete mode 100644 0048-virtiofsd-passthrough_ll-add-fallback-for-racy-ops.patch delete mode 100644 0049-virtiofsd-validate-path-components.patch delete mode 100644 0050-virtiofsd-Plumb-fuse_bufvec-through-to-do_write_buf.patch delete mode 100644 0051-virtiofsd-Pass-write-iov-s-all-the-way-through.patch delete mode 100644 0052-virtiofsd-add-fuse_mbuf_iter-API.patch delete mode 100644 0053-virtiofsd-validate-input-buffer-sizes-in-do_write_bu.patch delete mode 100644 0054-virtiofsd-check-input-buffer-size-in-fuse_lowlevel.c.patch delete mode 100644 0055-virtiofsd-prevent-.-escape-in-lo_do_lookup.patch delete mode 100644 0056-virtiofsd-prevent-.-escape-in-lo_do_readdir.patch delete mode 100644 0057-virtiofsd-use-proc-self-fd-O_PATH-file-descriptor.patch delete mode 100644 0058-virtiofsd-sandbox-mount-namespace.patch delete mode 100644 0059-virtiofsd-move-to-an-empty-network-namespace.patch delete mode 100644 0060-virtiofsd-move-to-a-new-pid-namespace.patch delete mode 100644 0061-virtiofsd-add-seccomp-whitelist.patch delete mode 100644 0062-virtiofsd-Parse-flag-FUSE_WRITE_KILL_PRIV.patch delete mode 100644 0063-virtiofsd-cap-ng-helpers.patch delete mode 100644 0064-virtiofsd-Drop-CAP_FSETID-if-client-asked-for-it.patch delete mode 100644 0065-virtiofsd-set-maximum-RLIMIT_NOFILE-limit.patch delete mode 100644 0066-virtiofsd-fix-libfuse-information-leaks.patch delete mode 100644 0067-virtiofsd-add-syslog-command-line-option.patch delete mode 100644 0068-virtiofsd-print-log-only-when-priority-is-high-enoug.patch delete mode 100644 0069-virtiofsd-Add-ID-to-the-log-with-FUSE_LOG_DEBUG-leve.patch delete mode 100644 0070-virtiofsd-Add-timestamp-to-the-log-with-FUSE_LOG_DEB.patch delete mode 100644 0071-virtiofsd-Handle-reinit.patch delete mode 100644 0072-virtiofsd-Handle-hard-reboot.patch delete mode 100644 0073-virtiofsd-Kill-threads-when-queues-are-stopped.patch delete mode 100644 0074-vhost-user-Print-unexpected-slave-message-types.patch delete mode 100644 0075-contrib-libvhost-user-Protect-slave-fd-with-mutex.patch delete mode 100644 0076-virtiofsd-passthrough_ll-add-renameat2-support.patch delete mode 100644 0077-virtiofsd-passthrough_ll-disable-readdirplus-on-cach.patch delete mode 100644 0078-virtiofsd-passthrough_ll-control-readdirplus.patch delete mode 100644 0079-virtiofsd-rename-unref_inode-to-unref_inode_lolocked.patch delete mode 100644 0080-virtiofsd-fail-when-parent-inode-isn-t-known-in-lo_d.patch delete mode 100644 0081-virtiofsd-extract-root-inode-init-into-setup_root.patch delete mode 100644 0082-virtiofsd-passthrough_ll-clean-up-cache-related-opti.patch delete mode 100644 0083-virtiofsd-passthrough_ll-use-hashtable.patch delete mode 100644 0084-virtiofsd-Clean-up-inodes-on-destroy.patch delete mode 100644 0085-virtiofsd-support-nanosecond-resolution-for-file-tim.patch delete mode 100644 0086-virtiofsd-fix-error-handling-in-main.patch delete mode 100644 0087-virtiofsd-cleanup-allocated-resource-in-se.patch delete mode 100644 0088-virtiofsd-fix-memory-leak-on-lo.source.patch delete mode 100644 0089-virtiofsd-add-helper-for-lo_data-cleanup.patch delete mode 100644 0090-virtiofsd-Prevent-multiply-running-with-same-vhost_u.patch delete mode 100644 0091-virtiofsd-enable-PARALLEL_DIROPS-during-INIT.patch delete mode 100644 0092-virtiofsd-fix-incorrect-error-handling-in-lo_do_look.patch delete mode 100644 0093-Virtiofsd-fix-memory-leak-on-fuse-queueinfo.patch delete mode 100644 0094-virtiofsd-Support-remote-posix-locks.patch delete mode 100644 0095-virtiofsd-use-fuse_lowlevel_is_virtio-in-fuse_sessio.patch delete mode 100644 0096-virtiofsd-prevent-fv_queue_thread-vs-virtio_loop-rac.patch delete mode 100644 0097-virtiofsd-make-lo_release-atomic.patch delete mode 100644 0098-virtiofsd-prevent-races-with-lo_dirp_put.patch delete mode 100644 0099-virtiofsd-rename-inode-refcount-to-inode-nlookup.patch delete mode 100644 0100-libvhost-user-Fix-some-memtable-remap-cases.patch delete mode 100644 0101-virtiofsd-passthrough_ll-fix-refcounting-on-remove-r.patch delete mode 100644 0102-virtiofsd-introduce-inode-refcount-to-prevent-use-af.patch delete mode 100644 0103-virtiofsd-do-not-always-set-FUSE_FLOCK_LOCKS.patch delete mode 100644 0104-virtiofsd-convert-more-fprintf-and-perror-to-use-fus.patch delete mode 100644 0105-virtiofsd-Reset-O_DIRECT-flag-during-file-open.patch delete mode 100644 0106-virtiofsd-Fix-data-corruption-with-O_APPEND-write-in.patch delete mode 100644 0107-virtiofsd-passthrough_ll-Use-cache_readdir-for-direc.patch delete mode 100644 0108-virtiofsd-add-definition-of-fuse_buf_writev.patch delete mode 100644 0109-virtiofsd-use-fuse_buf_writev-to-replace-fuse_buf_wr.patch delete mode 100644 0110-virtiofsd-process-requests-in-a-thread-pool.patch delete mode 100644 0111-virtiofsd-prevent-FUSE_INIT-FUSE_DESTROY-races.patch delete mode 100644 0112-virtiofsd-fix-lo_destroy-resource-leaks.patch delete mode 100644 0113-virtiofsd-add-thread-pool-size-NUM-option.patch delete mode 100644 0114-virtiofsd-Convert-lo_destroy-to-take-the-lo-mutex-lo.patch delete mode 100644 0115-virtiofsd-passthrough_ll-Pass-errno-to-fuse_reply_er.patch delete mode 100644 0116-virtiofsd-stop-all-queue-threads-on-exit-in-virtio_l.patch delete mode 100644 0117-virtiofsd-add-some-options-to-the-help-message.patch delete mode 100644 0118-vfio-pci-Don-t-remove-irqchip-notifier-if-not-regist.patch delete mode 100644 0201-spapr-Don-t-trigger-a-CAS-reboot-for-XICS-XIVE-mode-.patch diff --git a/.gitignore b/.gitignore index d8a4cd1..e96a8f0 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ /qemu-4.2.0-rc2.tar.xz /qemu-4.2.0-rc5.tar.xz /qemu-4.2.0.tar.xz +/qemu-5.0.0-rc0.tar.xz diff --git a/0001-tests-fix-modules-test-duplicate-test-case-error.patch b/0001-tests-fix-modules-test-duplicate-test-case-error.patch deleted file mode 100644 index 2848e42..0000000 --- a/0001-tests-fix-modules-test-duplicate-test-case-error.patch +++ /dev/null @@ -1,35 +0,0 @@ -From: Cole Robinson -Date: Wed, 13 Nov 2019 16:05:11 -0500 -Subject: [PATCH] tests: fix modules-test 'duplicate test case' error - -./configure --enable-sdl --audio-drv-list=sdl --enable-modules - -Will generate two identical test names: /$arch/module/load/sdl -Which generates an error like: - -(tests/modules-test:23814): GLib-ERROR **: 18:23:06.359: duplicate test case path: /aarch64//module/load/sdl - -Add the subsystem prefix in the name as well, so instead we get: - -/$arch/module/load/audio-sdl -/$arch/module/load/ui-sdl - -Signed-off-by: Cole Robinson ---- - tests/modules-test.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/tests/modules-test.c b/tests/modules-test.c -index d1a6ace218..88217686e1 100644 ---- a/tests/modules-test.c -+++ b/tests/modules-test.c -@@ -64,7 +64,8 @@ int main(int argc, char *argv[]) - g_test_init(&argc, &argv, NULL); - - for (i = 0; i < G_N_ELEMENTS(modules); i += 2) { -- char *testname = g_strdup_printf("/module/load/%s", modules[i + 1]); -+ char *testname = g_strdup_printf("/module/load/%s%s", -+ modules[i], modules[i + 1]); - qtest_add_data_func(testname, modules + i, test_modules_load); - g_free(testname); - } diff --git a/0002-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch b/0002-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch deleted file mode 100644 index 70d4fb1..0000000 --- a/0002-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch +++ /dev/null @@ -1,30 +0,0 @@ -From: Pan Nengyuan -Date: Tue, 10 Dec 2019 15:14:37 +0800 -Subject: [PATCH] riscv/sifive_u: fix a memory leak in soc_realize() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Fix a minor memory leak in riscv_sifive_u_soc_realize() - -Reported-by: Euler Robot -Signed-off-by: Pan Nengyuan -Reviewed-by: Philippe Mathieu-Daudé -Reviewed-by: Alistair Francis -Signed-off-by: Palmer Dabbelt ---- - hw/riscv/sifive_u.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c -index 0140e95732..0e12b3ccef 100644 ---- a/hw/riscv/sifive_u.c -+++ b/hw/riscv/sifive_u.c -@@ -542,6 +542,7 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) - SIFIVE_U_PLIC_CONTEXT_BASE, - SIFIVE_U_PLIC_CONTEXT_STRIDE, - memmap[SIFIVE_U_PLIC].size); -+ g_free(plic_hart_config); - sifive_uart_create(system_memory, memmap[SIFIVE_U_UART0].base, - serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART0_IRQ)); - sifive_uart_create(system_memory, memmap[SIFIVE_U_UART1].base, diff --git a/0003-riscv-Set-xPIE-to-1-after-xRET.patch b/0003-riscv-Set-xPIE-to-1-after-xRET.patch deleted file mode 100644 index 25a5123..0000000 --- a/0003-riscv-Set-xPIE-to-1-after-xRET.patch +++ /dev/null @@ -1,39 +0,0 @@ -From: Yiting Wang -Date: Fri, 3 Jan 2020 11:53:42 +0800 -Subject: [PATCH] riscv: Set xPIE to 1 after xRET - -When executing an xRET instruction, supposing xPP holds the -value y, xIE is set to xPIE; the privilege mode is changed to y; -xPIE is set to 1. But QEMU sets xPIE to 0 incorrectly. - -Signed-off-by: Yiting Wang -Reviewed-by: Bin Meng -Tested-by: Bin Meng -Reviewed-by: Alistair Francis -Signed-off-by: Palmer Dabbelt ---- - target/riscv/op_helper.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c -index 331cc36232..e87c9115bc 100644 ---- a/target/riscv/op_helper.c -+++ b/target/riscv/op_helper.c -@@ -93,7 +93,7 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) - env->priv_ver >= PRIV_VERSION_1_10_0 ? - MSTATUS_SIE : MSTATUS_UIE << prev_priv, - get_field(mstatus, MSTATUS_SPIE)); -- mstatus = set_field(mstatus, MSTATUS_SPIE, 0); -+ mstatus = set_field(mstatus, MSTATUS_SPIE, 1); - mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); - riscv_cpu_set_mode(env, prev_priv); - env->mstatus = mstatus; -@@ -118,7 +118,7 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) - env->priv_ver >= PRIV_VERSION_1_10_0 ? - MSTATUS_MIE : MSTATUS_UIE << prev_priv, - get_field(mstatus, MSTATUS_MPIE)); -- mstatus = set_field(mstatus, MSTATUS_MPIE, 0); -+ mstatus = set_field(mstatus, MSTATUS_MPIE, 1); - mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); - riscv_cpu_set_mode(env, prev_priv); - env->mstatus = mstatus; diff --git a/0004-target-riscv-Fix-tb-flags-FS-status.patch b/0004-target-riscv-Fix-tb-flags-FS-status.patch deleted file mode 100644 index d0fc7b5..0000000 --- a/0004-target-riscv-Fix-tb-flags-FS-status.patch +++ /dev/null @@ -1,44 +0,0 @@ -From: ShihPo Hung -Date: Tue, 14 Jan 2020 22:17:31 -0800 -Subject: [PATCH] target/riscv: Fix tb->flags FS status - -It was found that running libquantum on riscv-linux qemu produced an -incorrect result. After investigation, FP registers are not saved -during context switch due to incorrect mstatus.FS. - -In current implementation tb->flags merges all non-disabled state to -dirty. This means the code in mark_fs_dirty in translate.c that -handles initial and clean states is unreachable. - -This patch fixes it and is successfully tested with: - libquantum - -Thanks to Richard for pointing out the actual bug. - -v3: remove the redundant condition -v2: root cause FS problem - -Suggested-by: Richard Henderson -Signed-off-by: ShihPo Hung -Reviewed-by: Richard Henderson -Signed-off-by: Palmer Dabbelt ---- - target/riscv/cpu.h | 5 +---- - 1 file changed, 1 insertion(+), 4 deletions(-) - -diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h -index e59343e13c..de0a8d893a 100644 ---- a/target/riscv/cpu.h -+++ b/target/riscv/cpu.h -@@ -293,10 +293,7 @@ static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, - #ifdef CONFIG_USER_ONLY - *flags = TB_FLAGS_MSTATUS_FS; - #else -- *flags = cpu_mmu_index(env, 0); -- if (riscv_cpu_fp_enabled(env)) { -- *flags |= TB_FLAGS_MSTATUS_FS; -- } -+ *flags = cpu_mmu_index(env, 0) | (env->mstatus & MSTATUS_FS); - #endif - } - diff --git a/0005-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch b/0005-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch deleted file mode 100644 index 1ca5360..0000000 --- a/0005-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch +++ /dev/null @@ -1,37 +0,0 @@ -From: ShihPo Hung -Date: Tue, 14 Jan 2020 22:17:32 -0800 -Subject: [PATCH] target/riscv: fsd/fsw doesn't dirty FP state - -Signed-off-by: ShihPo Hung -Reviewed-by: Richard Henderson -Reviewed-by: Alistair Francis -Signed-off-by: Palmer Dabbelt ---- - target/riscv/insn_trans/trans_rvd.inc.c | 1 - - target/riscv/insn_trans/trans_rvf.inc.c | 1 - - 2 files changed, 2 deletions(-) - -diff --git a/target/riscv/insn_trans/trans_rvd.inc.c b/target/riscv/insn_trans/trans_rvd.inc.c -index 393fa0248c..ea1044f13b 100644 ---- a/target/riscv/insn_trans/trans_rvd.inc.c -+++ b/target/riscv/insn_trans/trans_rvd.inc.c -@@ -43,7 +43,6 @@ static bool trans_fsd(DisasContext *ctx, arg_fsd *a) - - tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], t0, ctx->mem_idx, MO_TEQ); - -- mark_fs_dirty(ctx); - tcg_temp_free(t0); - return true; - } -diff --git a/target/riscv/insn_trans/trans_rvf.inc.c b/target/riscv/insn_trans/trans_rvf.inc.c -index 172dbfa919..e23cd639a6 100644 ---- a/target/riscv/insn_trans/trans_rvf.inc.c -+++ b/target/riscv/insn_trans/trans_rvf.inc.c -@@ -52,7 +52,6 @@ static bool trans_fsw(DisasContext *ctx, arg_fsw *a) - tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], t0, ctx->mem_idx, MO_TEUL); - - tcg_temp_free(t0); -- mark_fs_dirty(ctx); - return true; - } - diff --git a/0006-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch b/0006-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch deleted file mode 100644 index de413fb..0000000 --- a/0006-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch +++ /dev/null @@ -1,43 +0,0 @@ -From: ShihPo Hung -Date: Tue, 14 Jan 2020 22:17:33 -0800 -Subject: [PATCH] target/riscv: update mstatus.SD when FS is set dirty - -remove the check becuase SD bit should summarize FS and XS fields -unconditionally. - -Signed-off-by: ShihPo Hung -Reviewed-by: Richard Henderson -Reviewed-by: Alistair Francis -Signed-off-by: Palmer Dabbelt ---- - target/riscv/csr.c | 3 +-- - target/riscv/translate.c | 2 +- - 2 files changed, 2 insertions(+), 3 deletions(-) - -diff --git a/target/riscv/csr.c b/target/riscv/csr.c -index da02f9f0b1..0e34c292c5 100644 ---- a/target/riscv/csr.c -+++ b/target/riscv/csr.c -@@ -341,8 +341,7 @@ static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val) - - mstatus = (mstatus & ~mask) | (val & mask); - -- dirty = (riscv_cpu_fp_enabled(env) && -- ((mstatus & MSTATUS_FS) == MSTATUS_FS)) | -+ dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) | - ((mstatus & MSTATUS_XS) == MSTATUS_XS); - mstatus = set_field(mstatus, MSTATUS_SD, dirty); - env->mstatus = mstatus; -diff --git a/target/riscv/translate.c b/target/riscv/translate.c -index ab6a891dc3..8e40ed3ac4 100644 ---- a/target/riscv/translate.c -+++ b/target/riscv/translate.c -@@ -394,7 +394,7 @@ static void mark_fs_dirty(DisasContext *ctx) - - tmp = tcg_temp_new(); - tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); -- tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); -+ tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS | MSTATUS_SD); - tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); - tcg_temp_free(tmp); - } diff --git a/0007-virtio-fs-fix-MSI-X-nvectors-calculation.patch b/0007-virtio-fs-fix-MSI-X-nvectors-calculation.patch deleted file mode 100644 index 483a444..0000000 --- a/0007-virtio-fs-fix-MSI-X-nvectors-calculation.patch +++ /dev/null @@ -1,41 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:00:36 +0000 -Subject: [PATCH] virtio-fs: fix MSI-X nvectors calculation - -The following MSI-X vectors are required: - * VIRTIO Configuration Change - * hiprio virtqueue - * requests virtqueues - -Fix the calculation to reserve enough MSI-X vectors. Otherwise guest -drivers fall back to a sub-optional configuration where all virtqueues -share a single vector. - -This change does not break live migration compatibility since -vhost-user-fs-pci devices are not migratable yet. - -Reported-by: Vivek Goyal -Signed-off-by: Stefan Hajnoczi -Message-Id: <20191209110759.35227-1-stefanha@redhat.com> -Reviewed-by: Dr. David Alan Gilbert -Reviewed-by: Michael S. Tsirkin -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 366844f3d1329c6423dd752891a28ccb3ee8fddd) ---- - hw/virtio/vhost-user-fs-pci.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/hw/virtio/vhost-user-fs-pci.c b/hw/virtio/vhost-user-fs-pci.c -index 933a3f265b..e3a649d4a6 100644 ---- a/hw/virtio/vhost-user-fs-pci.c -+++ b/hw/virtio/vhost-user-fs-pci.c -@@ -40,7 +40,8 @@ static void vhost_user_fs_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) - DeviceState *vdev = DEVICE(&dev->vdev); - - if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { -- vpci_dev->nvectors = dev->vdev.conf.num_request_queues + 1; -+ /* Also reserve config change and hiprio queue vectors */ -+ vpci_dev->nvectors = dev->vdev.conf.num_request_queues + 2; - } - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); diff --git a/0008-vhost-user-fs-remove-vhostfd-property.patch b/0008-vhost-user-fs-remove-vhostfd-property.patch deleted file mode 100644 index c77730d..0000000 --- a/0008-vhost-user-fs-remove-vhostfd-property.patch +++ /dev/null @@ -1,43 +0,0 @@ -From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= -Date: Mon, 27 Jan 2020 19:00:37 +0000 -Subject: [PATCH] vhost-user-fs: remove "vhostfd" property -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The property doesn't make much sense for a vhost-user device. - -Signed-off-by: Marc-André Lureau -Message-Id: <20191116112016.14872-1-marcandre.lureau@redhat.com> -Reviewed-by: Stefan Hajnoczi -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 703857348724319735d9be7b5b996e6445c6e6b9) ---- - hw/virtio/vhost-user-fs.c | 1 - - include/hw/virtio/vhost-user-fs.h | 1 - - 2 files changed, 2 deletions(-) - -diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c -index f0df7f4746..ca0b7fc9de 100644 ---- a/hw/virtio/vhost-user-fs.c -+++ b/hw/virtio/vhost-user-fs.c -@@ -263,7 +263,6 @@ static Property vuf_properties[] = { - DEFINE_PROP_UINT16("num-request-queues", VHostUserFS, - conf.num_request_queues, 1), - DEFINE_PROP_UINT16("queue-size", VHostUserFS, conf.queue_size, 128), -- DEFINE_PROP_STRING("vhostfd", VHostUserFS, conf.vhostfd), - DEFINE_PROP_END_OF_LIST(), - }; - -diff --git a/include/hw/virtio/vhost-user-fs.h b/include/hw/virtio/vhost-user-fs.h -index 539885b458..9ff1bdb7cf 100644 ---- a/include/hw/virtio/vhost-user-fs.h -+++ b/include/hw/virtio/vhost-user-fs.h -@@ -28,7 +28,6 @@ typedef struct { - char *tag; - uint16_t num_request_queues; - uint16_t queue_size; -- char *vhostfd; - } VHostUserFSConf; - - typedef struct { diff --git a/0009-build-rename-CONFIG_LIBCAP-to-CONFIG_LIBCAP_NG.patch b/0009-build-rename-CONFIG_LIBCAP-to-CONFIG_LIBCAP_NG.patch deleted file mode 100644 index 02ec43e..0000000 --- a/0009-build-rename-CONFIG_LIBCAP-to-CONFIG_LIBCAP_NG.patch +++ /dev/null @@ -1,118 +0,0 @@ -From: Paolo Bonzini -Date: Mon, 27 Jan 2020 19:00:38 +0000 -Subject: [PATCH] build: rename CONFIG_LIBCAP to CONFIG_LIBCAP_NG - -Since we are actually testing for the newer capng library, rename the -symbol to match. - -Reviewed-by: Dr. David Alan Gilbert -Signed-off-by: Paolo Bonzini -(cherry picked from commit a358bca24026a377e0804e137a4499e4e041918d) ---- - configure | 2 +- - qemu-bridge-helper.c | 6 +++--- - scsi/qemu-pr-helper.c | 12 ++++++------ - 3 files changed, 10 insertions(+), 10 deletions(-) - -diff --git a/configure b/configure -index 6099be1d84..afe9393f04 100755 ---- a/configure -+++ b/configure -@@ -6759,7 +6759,7 @@ if test "$l2tpv3" = "yes" ; then - echo "CONFIG_L2TPV3=y" >> $config_host_mak - fi - if test "$cap_ng" = "yes" ; then -- echo "CONFIG_LIBCAP=y" >> $config_host_mak -+ echo "CONFIG_LIBCAP_NG=y" >> $config_host_mak - fi - echo "CONFIG_AUDIO_DRIVERS=$audio_drv_list" >> $config_host_mak - for drv in $audio_drv_list; do -diff --git a/qemu-bridge-helper.c b/qemu-bridge-helper.c -index 3d50ec094c..88b26747fc 100644 ---- a/qemu-bridge-helper.c -+++ b/qemu-bridge-helper.c -@@ -43,7 +43,7 @@ - - #include "net/tap-linux.h" - --#ifdef CONFIG_LIBCAP -+#ifdef CONFIG_LIBCAP_NG - #include - #endif - -@@ -207,7 +207,7 @@ static int send_fd(int c, int fd) - return sendmsg(c, &msg, 0); - } - --#ifdef CONFIG_LIBCAP -+#ifdef CONFIG_LIBCAP_NG - static int drop_privileges(void) - { - /* clear all capabilities */ -@@ -246,7 +246,7 @@ int main(int argc, char **argv) - int access_allowed, access_denied; - int ret = EXIT_SUCCESS; - --#ifdef CONFIG_LIBCAP -+#ifdef CONFIG_LIBCAP_NG - /* if we're run from an suid binary, immediately drop privileges preserving - * cap_net_admin */ - if (geteuid() == 0 && getuid() != geteuid()) { -diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c -index debb18f4aa..0659ceef09 100644 ---- a/scsi/qemu-pr-helper.c -+++ b/scsi/qemu-pr-helper.c -@@ -24,7 +24,7 @@ - #include - #include - --#ifdef CONFIG_LIBCAP -+#ifdef CONFIG_LIBCAP_NG - #include - #endif - #include -@@ -70,7 +70,7 @@ static int num_active_sockets = 1; - static int noisy; - static int verbose; - --#ifdef CONFIG_LIBCAP -+#ifdef CONFIG_LIBCAP_NG - static int uid = -1; - static int gid = -1; - #endif -@@ -97,7 +97,7 @@ static void usage(const char *name) - " (default '%s')\n" - " -T, --trace [[enable=]][,events=][,file=]\n" - " specify tracing options\n" --#ifdef CONFIG_LIBCAP -+#ifdef CONFIG_LIBCAP_NG - " -u, --user=USER user to drop privileges to\n" - " -g, --group=GROUP group to drop privileges to\n" - #endif -@@ -827,7 +827,7 @@ static void close_server_socket(void) - num_active_sockets--; - } - --#ifdef CONFIG_LIBCAP -+#ifdef CONFIG_LIBCAP_NG - static int drop_privileges(void) - { - /* clear all capabilities */ -@@ -920,7 +920,7 @@ int main(int argc, char **argv) - pidfile = g_strdup(optarg); - pidfile_specified = true; - break; --#ifdef CONFIG_LIBCAP -+#ifdef CONFIG_LIBCAP_NG - case 'u': { - unsigned long res; - struct passwd *userinfo = getpwnam(optarg); -@@ -1056,7 +1056,7 @@ int main(int argc, char **argv) - exit(EXIT_FAILURE); - } - --#ifdef CONFIG_LIBCAP -+#ifdef CONFIG_LIBCAP_NG - if (drop_privileges() < 0) { - error_report("Failed to drop privileges: %s", strerror(errno)); - exit(EXIT_FAILURE); diff --git a/0010-virtiofsd-Pull-in-upstream-headers.patch b/0010-virtiofsd-Pull-in-upstream-headers.patch deleted file mode 100644 index cc049ad..0000000 --- a/0010-virtiofsd-Pull-in-upstream-headers.patch +++ /dev/null @@ -1,4895 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:00:39 +0000 -Subject: [PATCH] virtiofsd: Pull in upstream headers -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Pull in headers fromlibfuse's upstream fuse-3.8.0 - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit ee46c78901eb7fa78e328e04c0494ad6d207238b) ---- - tools/virtiofsd/fuse.h | 1275 +++++++++++++++ - tools/virtiofsd/fuse_common.h | 823 ++++++++++ - tools/virtiofsd/fuse_i.h | 139 ++ - tools/virtiofsd/fuse_log.h | 82 + - tools/virtiofsd/fuse_lowlevel.h | 2089 +++++++++++++++++++++++++ - tools/virtiofsd/fuse_misc.h | 59 + - tools/virtiofsd/fuse_opt.h | 271 ++++ - tools/virtiofsd/passthrough_helpers.h | 76 + - 8 files changed, 4814 insertions(+) - create mode 100644 tools/virtiofsd/fuse.h - create mode 100644 tools/virtiofsd/fuse_common.h - create mode 100644 tools/virtiofsd/fuse_i.h - create mode 100644 tools/virtiofsd/fuse_log.h - create mode 100644 tools/virtiofsd/fuse_lowlevel.h - create mode 100644 tools/virtiofsd/fuse_misc.h - create mode 100644 tools/virtiofsd/fuse_opt.h - create mode 100644 tools/virtiofsd/passthrough_helpers.h - -diff --git a/tools/virtiofsd/fuse.h b/tools/virtiofsd/fuse.h -new file mode 100644 -index 0000000000..883f6e59fb ---- /dev/null -+++ b/tools/virtiofsd/fuse.h -@@ -0,0 +1,1275 @@ -+/* -+ FUSE: Filesystem in Userspace -+ Copyright (C) 2001-2007 Miklos Szeredi -+ -+ This program can be distributed under the terms of the GNU LGPLv2. -+ See the file COPYING.LIB. -+*/ -+ -+#ifndef FUSE_H_ -+#define FUSE_H_ -+ -+/** @file -+ * -+ * This file defines the library interface of FUSE -+ * -+ * IMPORTANT: you should define FUSE_USE_VERSION before including this header. -+ */ -+ -+#include "fuse_common.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+/* ----------------------------------------------------------- * -+ * Basic FUSE API * -+ * ----------------------------------------------------------- */ -+ -+/** Handle for a FUSE filesystem */ -+struct fuse; -+ -+/** -+ * Readdir flags, passed to ->readdir() -+ */ -+enum fuse_readdir_flags { -+ /** -+ * "Plus" mode. -+ * -+ * The kernel wants to prefill the inode cache during readdir. The -+ * filesystem may honour this by filling in the attributes and setting -+ * FUSE_FILL_DIR_FLAGS for the filler function. The filesystem may also -+ * just ignore this flag completely. -+ */ -+ FUSE_READDIR_PLUS = (1 << 0), -+}; -+ -+enum fuse_fill_dir_flags { -+ /** -+ * "Plus" mode: all file attributes are valid -+ * -+ * The attributes are used by the kernel to prefill the inode cache -+ * during a readdir. -+ * -+ * It is okay to set FUSE_FILL_DIR_PLUS if FUSE_READDIR_PLUS is not set -+ * and vice versa. -+ */ -+ FUSE_FILL_DIR_PLUS = (1 << 1), -+}; -+ -+/** Function to add an entry in a readdir() operation -+ * -+ * The *off* parameter can be any non-zero value that enables the -+ * filesystem to identify the current point in the directory -+ * stream. It does not need to be the actual physical position. A -+ * value of zero is reserved to indicate that seeking in directories -+ * is not supported. -+ * -+ * @param buf the buffer passed to the readdir() operation -+ * @param name the file name of the directory entry -+ * @param stat file attributes, can be NULL -+ * @param off offset of the next entry or zero -+ * @param flags fill flags -+ * @return 1 if buffer is full, zero otherwise -+ */ -+typedef int (*fuse_fill_dir_t) (void *buf, const char *name, -+ const struct stat *stbuf, off_t off, -+ enum fuse_fill_dir_flags flags); -+/** -+ * Configuration of the high-level API -+ * -+ * This structure is initialized from the arguments passed to -+ * fuse_new(), and then passed to the file system's init() handler -+ * which should ensure that the configuration is compatible with the -+ * file system implementation. -+ */ -+struct fuse_config { -+ /** -+ * If `set_gid` is non-zero, the st_gid attribute of each file -+ * is overwritten with the value of `gid`. -+ */ -+ int set_gid; -+ unsigned int gid; -+ -+ /** -+ * If `set_uid` is non-zero, the st_uid attribute of each file -+ * is overwritten with the value of `uid`. -+ */ -+ int set_uid; -+ unsigned int uid; -+ -+ /** -+ * If `set_mode` is non-zero, the any permissions bits set in -+ * `umask` are unset in the st_mode attribute of each file. -+ */ -+ int set_mode; -+ unsigned int umask; -+ -+ /** -+ * The timeout in seconds for which name lookups will be -+ * cached. -+ */ -+ double entry_timeout; -+ -+ /** -+ * The timeout in seconds for which a negative lookup will be -+ * cached. This means, that if file did not exist (lookup -+ * retuned ENOENT), the lookup will only be redone after the -+ * timeout, and the file/directory will be assumed to not -+ * exist until then. A value of zero means that negative -+ * lookups are not cached. -+ */ -+ double negative_timeout; -+ -+ /** -+ * The timeout in seconds for which file/directory attributes -+ * (as returned by e.g. the `getattr` handler) are cached. -+ */ -+ double attr_timeout; -+ -+ /** -+ * Allow requests to be interrupted -+ */ -+ int intr; -+ -+ /** -+ * Specify which signal number to send to the filesystem when -+ * a request is interrupted. The default is hardcoded to -+ * USR1. -+ */ -+ int intr_signal; -+ -+ /** -+ * Normally, FUSE assigns inodes to paths only for as long as -+ * the kernel is aware of them. With this option inodes are -+ * instead remembered for at least this many seconds. This -+ * will require more memory, but may be necessary when using -+ * applications that make use of inode numbers. -+ * -+ * A number of -1 means that inodes will be remembered for the -+ * entire life-time of the file-system process. -+ */ -+ int remember; -+ -+ /** -+ * The default behavior is that if an open file is deleted, -+ * the file is renamed to a hidden file (.fuse_hiddenXXX), and -+ * only removed when the file is finally released. This -+ * relieves the filesystem implementation of having to deal -+ * with this problem. This option disables the hiding -+ * behavior, and files are removed immediately in an unlink -+ * operation (or in a rename operation which overwrites an -+ * existing file). -+ * -+ * It is recommended that you not use the hard_remove -+ * option. When hard_remove is set, the following libc -+ * functions fail on unlinked files (returning errno of -+ * ENOENT): read(2), write(2), fsync(2), close(2), f*xattr(2), -+ * ftruncate(2), fstat(2), fchmod(2), fchown(2) -+ */ -+ int hard_remove; -+ -+ /** -+ * Honor the st_ino field in the functions getattr() and -+ * fill_dir(). This value is used to fill in the st_ino field -+ * in the stat(2), lstat(2), fstat(2) functions and the d_ino -+ * field in the readdir(2) function. The filesystem does not -+ * have to guarantee uniqueness, however some applications -+ * rely on this value being unique for the whole filesystem. -+ * -+ * Note that this does *not* affect the inode that libfuse -+ * and the kernel use internally (also called the "nodeid"). -+ */ -+ int use_ino; -+ -+ /** -+ * If use_ino option is not given, still try to fill in the -+ * d_ino field in readdir(2). If the name was previously -+ * looked up, and is still in the cache, the inode number -+ * found there will be used. Otherwise it will be set to -1. -+ * If use_ino option is given, this option is ignored. -+ */ -+ int readdir_ino; -+ -+ /** -+ * This option disables the use of page cache (file content cache) -+ * in the kernel for this filesystem. This has several affects: -+ * -+ * 1. Each read(2) or write(2) system call will initiate one -+ * or more read or write operations, data will not be -+ * cached in the kernel. -+ * -+ * 2. The return value of the read() and write() system calls -+ * will correspond to the return values of the read and -+ * write operations. This is useful for example if the -+ * file size is not known in advance (before reading it). -+ * -+ * Internally, enabling this option causes fuse to set the -+ * `direct_io` field of `struct fuse_file_info` - overwriting -+ * any value that was put there by the file system. -+ */ -+ int direct_io; -+ -+ /** -+ * This option disables flushing the cache of the file -+ * contents on every open(2). This should only be enabled on -+ * filesystems where the file data is never changed -+ * externally (not through the mounted FUSE filesystem). Thus -+ * it is not suitable for network filesystems and other -+ * intermediate filesystems. -+ * -+ * NOTE: if this option is not specified (and neither -+ * direct_io) data is still cached after the open(2), so a -+ * read(2) system call will not always initiate a read -+ * operation. -+ * -+ * Internally, enabling this option causes fuse to set the -+ * `keep_cache` field of `struct fuse_file_info` - overwriting -+ * any value that was put there by the file system. -+ */ -+ int kernel_cache; -+ -+ /** -+ * This option is an alternative to `kernel_cache`. Instead of -+ * unconditionally keeping cached data, the cached data is -+ * invalidated on open(2) if if the modification time or the -+ * size of the file has changed since it was last opened. -+ */ -+ int auto_cache; -+ -+ /** -+ * The timeout in seconds for which file attributes are cached -+ * for the purpose of checking if auto_cache should flush the -+ * file data on open. -+ */ -+ int ac_attr_timeout_set; -+ double ac_attr_timeout; -+ -+ /** -+ * If this option is given the file-system handlers for the -+ * following operations will not receive path information: -+ * read, write, flush, release, fsync, readdir, releasedir, -+ * fsyncdir, lock, ioctl and poll. -+ * -+ * For the truncate, getattr, chmod, chown and utimens -+ * operations the path will be provided only if the struct -+ * fuse_file_info argument is NULL. -+ */ -+ int nullpath_ok; -+ -+ /** -+ * The remaining options are used by libfuse internally and -+ * should not be touched. -+ */ -+ int show_help; -+ char *modules; -+ int debug; -+}; -+ -+ -+/** -+ * The file system operations: -+ * -+ * Most of these should work very similarly to the well known UNIX -+ * file system operations. A major exception is that instead of -+ * returning an error in 'errno', the operation should return the -+ * negated error value (-errno) directly. -+ * -+ * All methods are optional, but some are essential for a useful -+ * filesystem (e.g. getattr). Open, flush, release, fsync, opendir, -+ * releasedir, fsyncdir, access, create, truncate, lock, init and -+ * destroy are special purpose methods, without which a full featured -+ * filesystem can still be implemented. -+ * -+ * In general, all methods are expected to perform any necessary -+ * permission checking. However, a filesystem may delegate this task -+ * to the kernel by passing the `default_permissions` mount option to -+ * `fuse_new()`. In this case, methods will only be called if -+ * the kernel's permission check has succeeded. -+ * -+ * Almost all operations take a path which can be of any length. -+ */ -+struct fuse_operations { -+ /** Get file attributes. -+ * -+ * Similar to stat(). The 'st_dev' and 'st_blksize' fields are -+ * ignored. The 'st_ino' field is ignored except if the 'use_ino' -+ * mount option is given. In that case it is passed to userspace, -+ * but libfuse and the kernel will still assign a different -+ * inode for internal use (called the "nodeid"). -+ * -+ * `fi` will always be NULL if the file is not currently open, but -+ * may also be NULL if the file is open. -+ */ -+ int (*getattr) (const char *, struct stat *, struct fuse_file_info *fi); -+ -+ /** Read the target of a symbolic link -+ * -+ * The buffer should be filled with a null terminated string. The -+ * buffer size argument includes the space for the terminating -+ * null character. If the linkname is too long to fit in the -+ * buffer, it should be truncated. The return value should be 0 -+ * for success. -+ */ -+ int (*readlink) (const char *, char *, size_t); -+ -+ /** Create a file node -+ * -+ * This is called for creation of all non-directory, non-symlink -+ * nodes. If the filesystem defines a create() method, then for -+ * regular files that will be called instead. -+ */ -+ int (*mknod) (const char *, mode_t, dev_t); -+ -+ /** Create a directory -+ * -+ * Note that the mode argument may not have the type specification -+ * bits set, i.e. S_ISDIR(mode) can be false. To obtain the -+ * correct directory type bits use mode|S_IFDIR -+ * */ -+ int (*mkdir) (const char *, mode_t); -+ -+ /** Remove a file */ -+ int (*unlink) (const char *); -+ -+ /** Remove a directory */ -+ int (*rmdir) (const char *); -+ -+ /** Create a symbolic link */ -+ int (*symlink) (const char *, const char *); -+ -+ /** Rename a file -+ * -+ * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If -+ * RENAME_NOREPLACE is specified, the filesystem must not -+ * overwrite *newname* if it exists and return an error -+ * instead. If `RENAME_EXCHANGE` is specified, the filesystem -+ * must atomically exchange the two files, i.e. both must -+ * exist and neither may be deleted. -+ */ -+ int (*rename) (const char *, const char *, unsigned int flags); -+ -+ /** Create a hard link to a file */ -+ int (*link) (const char *, const char *); -+ -+ /** Change the permission bits of a file -+ * -+ * `fi` will always be NULL if the file is not currenlty open, but -+ * may also be NULL if the file is open. -+ */ -+ int (*chmod) (const char *, mode_t, struct fuse_file_info *fi); -+ -+ /** Change the owner and group of a file -+ * -+ * `fi` will always be NULL if the file is not currenlty open, but -+ * may also be NULL if the file is open. -+ * -+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -+ * expected to reset the setuid and setgid bits. -+ */ -+ int (*chown) (const char *, uid_t, gid_t, struct fuse_file_info *fi); -+ -+ /** Change the size of a file -+ * -+ * `fi` will always be NULL if the file is not currenlty open, but -+ * may also be NULL if the file is open. -+ * -+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -+ * expected to reset the setuid and setgid bits. -+ */ -+ int (*truncate) (const char *, off_t, struct fuse_file_info *fi); -+ -+ /** Open a file -+ * -+ * Open flags are available in fi->flags. The following rules -+ * apply. -+ * -+ * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be -+ * filtered out / handled by the kernel. -+ * -+ * - Access modes (O_RDONLY, O_WRONLY, O_RDWR, O_EXEC, O_SEARCH) -+ * should be used by the filesystem to check if the operation is -+ * permitted. If the ``-o default_permissions`` mount option is -+ * given, this check is already done by the kernel before calling -+ * open() and may thus be omitted by the filesystem. -+ * -+ * - When writeback caching is enabled, the kernel may send -+ * read requests even for files opened with O_WRONLY. The -+ * filesystem should be prepared to handle this. -+ * -+ * - When writeback caching is disabled, the filesystem is -+ * expected to properly handle the O_APPEND flag and ensure -+ * that each write is appending to the end of the file. -+ * -+ * - When writeback caching is enabled, the kernel will -+ * handle O_APPEND. However, unless all changes to the file -+ * come through the kernel this will not work reliably. The -+ * filesystem should thus either ignore the O_APPEND flag -+ * (and let the kernel handle it), or return an error -+ * (indicating that reliably O_APPEND is not available). -+ * -+ * Filesystem may store an arbitrary file handle (pointer, -+ * index, etc) in fi->fh, and use this in other all other file -+ * operations (read, write, flush, release, fsync). -+ * -+ * Filesystem may also implement stateless file I/O and not store -+ * anything in fi->fh. -+ * -+ * There are also some flags (direct_io, keep_cache) which the -+ * filesystem may set in fi, to change the way the file is opened. -+ * See fuse_file_info structure in for more details. -+ * -+ * If this request is answered with an error code of ENOSYS -+ * and FUSE_CAP_NO_OPEN_SUPPORT is set in -+ * `fuse_conn_info.capable`, this is treated as success and -+ * future calls to open will also succeed without being send -+ * to the filesystem process. -+ * -+ */ -+ int (*open) (const char *, struct fuse_file_info *); -+ -+ /** Read data from an open file -+ * -+ * Read should return exactly the number of bytes requested except -+ * on EOF or error, otherwise the rest of the data will be -+ * substituted with zeroes. An exception to this is when the -+ * 'direct_io' mount option is specified, in which case the return -+ * value of the read system call will reflect the return value of -+ * this operation. -+ */ -+ int (*read) (const char *, char *, size_t, off_t, -+ struct fuse_file_info *); -+ -+ /** Write data to an open file -+ * -+ * Write should return exactly the number of bytes requested -+ * except on error. An exception to this is when the 'direct_io' -+ * mount option is specified (see read operation). -+ * -+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -+ * expected to reset the setuid and setgid bits. -+ */ -+ int (*write) (const char *, const char *, size_t, off_t, -+ struct fuse_file_info *); -+ -+ /** Get file system statistics -+ * -+ * The 'f_favail', 'f_fsid' and 'f_flag' fields are ignored -+ */ -+ int (*statfs) (const char *, struct statvfs *); -+ -+ /** Possibly flush cached data -+ * -+ * BIG NOTE: This is not equivalent to fsync(). It's not a -+ * request to sync dirty data. -+ * -+ * Flush is called on each close() of a file descriptor, as opposed to -+ * release which is called on the close of the last file descriptor for -+ * a file. Under Linux, errors returned by flush() will be passed to -+ * userspace as errors from close(), so flush() is a good place to write -+ * back any cached dirty data. However, many applications ignore errors -+ * on close(), and on non-Linux systems, close() may succeed even if flush() -+ * returns an error. For these reasons, filesystems should not assume -+ * that errors returned by flush will ever be noticed or even -+ * delivered. -+ * -+ * NOTE: The flush() method may be called more than once for each -+ * open(). This happens if more than one file descriptor refers to an -+ * open file handle, e.g. due to dup(), dup2() or fork() calls. It is -+ * not possible to determine if a flush is final, so each flush should -+ * be treated equally. Multiple write-flush sequences are relatively -+ * rare, so this shouldn't be a problem. -+ * -+ * Filesystems shouldn't assume that flush will be called at any -+ * particular point. It may be called more times than expected, or not -+ * at all. -+ * -+ * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html -+ */ -+ int (*flush) (const char *, struct fuse_file_info *); -+ -+ /** Release an open file -+ * -+ * Release is called when there are no more references to an open -+ * file: all file descriptors are closed and all memory mappings -+ * are unmapped. -+ * -+ * For every open() call there will be exactly one release() call -+ * with the same flags and file handle. It is possible to -+ * have a file opened more than once, in which case only the last -+ * release will mean, that no more reads/writes will happen on the -+ * file. The return value of release is ignored. -+ */ -+ int (*release) (const char *, struct fuse_file_info *); -+ -+ /** Synchronize file contents -+ * -+ * If the datasync parameter is non-zero, then only the user data -+ * should be flushed, not the meta data. -+ */ -+ int (*fsync) (const char *, int, struct fuse_file_info *); -+ -+ /** Set extended attributes */ -+ int (*setxattr) (const char *, const char *, const char *, size_t, int); -+ -+ /** Get extended attributes */ -+ int (*getxattr) (const char *, const char *, char *, size_t); -+ -+ /** List extended attributes */ -+ int (*listxattr) (const char *, char *, size_t); -+ -+ /** Remove extended attributes */ -+ int (*removexattr) (const char *, const char *); -+ -+ /** Open directory -+ * -+ * Unless the 'default_permissions' mount option is given, -+ * this method should check if opendir is permitted for this -+ * directory. Optionally opendir may also return an arbitrary -+ * filehandle in the fuse_file_info structure, which will be -+ * passed to readdir, releasedir and fsyncdir. -+ */ -+ int (*opendir) (const char *, struct fuse_file_info *); -+ -+ /** Read directory -+ * -+ * The filesystem may choose between two modes of operation: -+ * -+ * 1) The readdir implementation ignores the offset parameter, and -+ * passes zero to the filler function's offset. The filler -+ * function will not return '1' (unless an error happens), so the -+ * whole directory is read in a single readdir operation. -+ * -+ * 2) The readdir implementation keeps track of the offsets of the -+ * directory entries. It uses the offset parameter and always -+ * passes non-zero offset to the filler function. When the buffer -+ * is full (or an error happens) the filler function will return -+ * '1'. -+ */ -+ int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t, -+ struct fuse_file_info *, enum fuse_readdir_flags); -+ -+ /** Release directory -+ */ -+ int (*releasedir) (const char *, struct fuse_file_info *); -+ -+ /** Synchronize directory contents -+ * -+ * If the datasync parameter is non-zero, then only the user data -+ * should be flushed, not the meta data -+ */ -+ int (*fsyncdir) (const char *, int, struct fuse_file_info *); -+ -+ /** -+ * Initialize filesystem -+ * -+ * The return value will passed in the `private_data` field of -+ * `struct fuse_context` to all file operations, and as a -+ * parameter to the destroy() method. It overrides the initial -+ * value provided to fuse_main() / fuse_new(). -+ */ -+ void *(*init) (struct fuse_conn_info *conn, -+ struct fuse_config *cfg); -+ -+ /** -+ * Clean up filesystem -+ * -+ * Called on filesystem exit. -+ */ -+ void (*destroy) (void *private_data); -+ -+ /** -+ * Check file access permissions -+ * -+ * This will be called for the access() system call. If the -+ * 'default_permissions' mount option is given, this method is not -+ * called. -+ * -+ * This method is not called under Linux kernel versions 2.4.x -+ */ -+ int (*access) (const char *, int); -+ -+ /** -+ * Create and open a file -+ * -+ * If the file does not exist, first create it with the specified -+ * mode, and then open it. -+ * -+ * If this method is not implemented or under Linux kernel -+ * versions earlier than 2.6.15, the mknod() and open() methods -+ * will be called instead. -+ */ -+ int (*create) (const char *, mode_t, struct fuse_file_info *); -+ -+ /** -+ * Perform POSIX file locking operation -+ * -+ * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW. -+ * -+ * For the meaning of fields in 'struct flock' see the man page -+ * for fcntl(2). The l_whence field will always be set to -+ * SEEK_SET. -+ * -+ * For checking lock ownership, the 'fuse_file_info->owner' -+ * argument must be used. -+ * -+ * For F_GETLK operation, the library will first check currently -+ * held locks, and if a conflicting lock is found it will return -+ * information without calling this method. This ensures, that -+ * for local locks the l_pid field is correctly filled in. The -+ * results may not be accurate in case of race conditions and in -+ * the presence of hard links, but it's unlikely that an -+ * application would rely on accurate GETLK results in these -+ * cases. If a conflicting lock is not found, this method will be -+ * called, and the filesystem may fill out l_pid by a meaningful -+ * value, or it may leave this field zero. -+ * -+ * For F_SETLK and F_SETLKW the l_pid field will be set to the pid -+ * of the process performing the locking operation. -+ * -+ * Note: if this method is not implemented, the kernel will still -+ * allow file locking to work locally. Hence it is only -+ * interesting for network filesystems and similar. -+ */ -+ int (*lock) (const char *, struct fuse_file_info *, int cmd, -+ struct flock *); -+ -+ /** -+ * Change the access and modification times of a file with -+ * nanosecond resolution -+ * -+ * This supersedes the old utime() interface. New applications -+ * should use this. -+ * -+ * `fi` will always be NULL if the file is not currenlty open, but -+ * may also be NULL if the file is open. -+ * -+ * See the utimensat(2) man page for details. -+ */ -+ int (*utimens) (const char *, const struct timespec tv[2], -+ struct fuse_file_info *fi); -+ -+ /** -+ * Map block index within file to block index within device -+ * -+ * Note: This makes sense only for block device backed filesystems -+ * mounted with the 'blkdev' option -+ */ -+ int (*bmap) (const char *, size_t blocksize, uint64_t *idx); -+ -+ /** -+ * Ioctl -+ * -+ * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in -+ * 64bit environment. The size and direction of data is -+ * determined by _IOC_*() decoding of cmd. For _IOC_NONE, -+ * data will be NULL, for _IOC_WRITE data is out area, for -+ * _IOC_READ in area and if both are set in/out area. In all -+ * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes. -+ * -+ * If flags has FUSE_IOCTL_DIR then the fuse_file_info refers to a -+ * directory file handle. -+ * -+ * Note : the unsigned long request submitted by the application -+ * is truncated to 32 bits. -+ */ -+ int (*ioctl) (const char *, unsigned int cmd, void *arg, -+ struct fuse_file_info *, unsigned int flags, void *data); -+ -+ /** -+ * Poll for IO readiness events -+ * -+ * Note: If ph is non-NULL, the client should notify -+ * when IO readiness events occur by calling -+ * fuse_notify_poll() with the specified ph. -+ * -+ * Regardless of the number of times poll with a non-NULL ph -+ * is received, single notification is enough to clear all. -+ * Notifying more times incurs overhead but doesn't harm -+ * correctness. -+ * -+ * The callee is responsible for destroying ph with -+ * fuse_pollhandle_destroy() when no longer in use. -+ */ -+ int (*poll) (const char *, struct fuse_file_info *, -+ struct fuse_pollhandle *ph, unsigned *reventsp); -+ -+ /** Write contents of buffer to an open file -+ * -+ * Similar to the write() method, but data is supplied in a -+ * generic buffer. Use fuse_buf_copy() to transfer data to -+ * the destination. -+ * -+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -+ * expected to reset the setuid and setgid bits. -+ */ -+ int (*write_buf) (const char *, struct fuse_bufvec *buf, off_t off, -+ struct fuse_file_info *); -+ -+ /** Store data from an open file in a buffer -+ * -+ * Similar to the read() method, but data is stored and -+ * returned in a generic buffer. -+ * -+ * No actual copying of data has to take place, the source -+ * file descriptor may simply be stored in the buffer for -+ * later data transfer. -+ * -+ * The buffer must be allocated dynamically and stored at the -+ * location pointed to by bufp. If the buffer contains memory -+ * regions, they too must be allocated using malloc(). The -+ * allocated memory will be freed by the caller. -+ */ -+ int (*read_buf) (const char *, struct fuse_bufvec **bufp, -+ size_t size, off_t off, struct fuse_file_info *); -+ /** -+ * Perform BSD file locking operation -+ * -+ * The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN -+ * -+ * Nonblocking requests will be indicated by ORing LOCK_NB to -+ * the above operations -+ * -+ * For more information see the flock(2) manual page. -+ * -+ * Additionally fi->owner will be set to a value unique to -+ * this open file. This same value will be supplied to -+ * ->release() when the file is released. -+ * -+ * Note: if this method is not implemented, the kernel will still -+ * allow file locking to work locally. Hence it is only -+ * interesting for network filesystems and similar. -+ */ -+ int (*flock) (const char *, struct fuse_file_info *, int op); -+ -+ /** -+ * Allocates space for an open file -+ * -+ * This function ensures that required space is allocated for specified -+ * file. If this function returns success then any subsequent write -+ * request to specified range is guaranteed not to fail because of lack -+ * of space on the file system media. -+ */ -+ int (*fallocate) (const char *, int, off_t, off_t, -+ struct fuse_file_info *); -+ -+ /** -+ * Copy a range of data from one file to another -+ * -+ * Performs an optimized copy between two file descriptors without the -+ * additional cost of transferring data through the FUSE kernel module -+ * to user space (glibc) and then back into the FUSE filesystem again. -+ * -+ * In case this method is not implemented, glibc falls back to reading -+ * data from the source and writing to the destination. Effectively -+ * doing an inefficient copy of the data. -+ */ -+ ssize_t (*copy_file_range) (const char *path_in, -+ struct fuse_file_info *fi_in, -+ off_t offset_in, const char *path_out, -+ struct fuse_file_info *fi_out, -+ off_t offset_out, size_t size, int flags); -+ -+ /** -+ * Find next data or hole after the specified offset -+ */ -+ off_t (*lseek) (const char *, off_t off, int whence, struct fuse_file_info *); -+}; -+ -+/** Extra context that may be needed by some filesystems -+ * -+ * The uid, gid and pid fields are not filled in case of a writepage -+ * operation. -+ */ -+struct fuse_context { -+ /** Pointer to the fuse object */ -+ struct fuse *fuse; -+ -+ /** User ID of the calling process */ -+ uid_t uid; -+ -+ /** Group ID of the calling process */ -+ gid_t gid; -+ -+ /** Process ID of the calling thread */ -+ pid_t pid; -+ -+ /** Private filesystem data */ -+ void *private_data; -+ -+ /** Umask of the calling process */ -+ mode_t umask; -+}; -+ -+/** -+ * Main function of FUSE. -+ * -+ * This is for the lazy. This is all that has to be called from the -+ * main() function. -+ * -+ * This function does the following: -+ * - parses command line options, and handles --help and -+ * --version -+ * - installs signal handlers for INT, HUP, TERM and PIPE -+ * - registers an exit handler to unmount the filesystem on program exit -+ * - creates a fuse handle -+ * - registers the operations -+ * - calls either the single-threaded or the multi-threaded event loop -+ * -+ * Most file systems will have to parse some file-system specific -+ * arguments before calling this function. It is recommended to do -+ * this with fuse_opt_parse() and a processing function that passes -+ * through any unknown options (this can also be achieved by just -+ * passing NULL as the processing function). That way, the remaining -+ * options can be passed directly to fuse_main(). -+ * -+ * fuse_main() accepts all options that can be passed to -+ * fuse_parse_cmdline(), fuse_new(), or fuse_session_new(). -+ * -+ * Option parsing skips argv[0], which is assumed to contain the -+ * program name. This element must always be present and is used to -+ * construct a basic ``usage: `` message for the --help -+ * output. argv[0] may also be set to the empty string. In this case -+ * the usage message is suppressed. This can be used by file systems -+ * to print their own usage line first. See hello.c for an example of -+ * how to do this. -+ * -+ * Note: this is currently implemented as a macro. -+ * -+ * The following error codes may be returned from fuse_main(): -+ * 1: Invalid option arguments -+ * 2: No mount point specified -+ * 3: FUSE setup failed -+ * 4: Mounting failed -+ * 5: Failed to daemonize (detach from session) -+ * 6: Failed to set up signal handlers -+ * 7: An error occured during the life of the file system -+ * -+ * @param argc the argument counter passed to the main() function -+ * @param argv the argument vector passed to the main() function -+ * @param op the file system operation -+ * @param private_data Initial value for the `private_data` -+ * field of `struct fuse_context`. May be overridden by the -+ * `struct fuse_operations.init` handler. -+ * @return 0 on success, nonzero on failure -+ * -+ * Example usage, see hello.c -+ */ -+/* -+ int fuse_main(int argc, char *argv[], const struct fuse_operations *op, -+ void *private_data); -+*/ -+#define fuse_main(argc, argv, op, private_data) \ -+ fuse_main_real(argc, argv, op, sizeof(*(op)), private_data) -+ -+/* ----------------------------------------------------------- * -+ * More detailed API * -+ * ----------------------------------------------------------- */ -+ -+/** -+ * Print available options (high- and low-level) to stdout. This is -+ * not an exhaustive list, but includes only those options that may be -+ * of interest to an end-user of a file system. -+ * -+ * The function looks at the argument vector only to determine if -+ * there are additional modules to be loaded (module=foo option), -+ * and attempts to call their help functions as well. -+ * -+ * @param args the argument vector. -+ */ -+void fuse_lib_help(struct fuse_args *args); -+ -+/** -+ * Create a new FUSE filesystem. -+ * -+ * This function accepts most file-system independent mount options -+ * (like context, nodev, ro - see mount(8)), as well as the -+ * FUSE-specific mount options from mount.fuse(8). -+ * -+ * If the --help option is specified, the function writes a help text -+ * to stdout and returns NULL. -+ * -+ * Option parsing skips argv[0], which is assumed to contain the -+ * program name. This element must always be present and is used to -+ * construct a basic ``usage: `` message for the --help output. If -+ * argv[0] is set to the empty string, no usage message is included in -+ * the --help output. -+ * -+ * If an unknown option is passed in, an error message is written to -+ * stderr and the function returns NULL. -+ * -+ * @param args argument vector -+ * @param op the filesystem operations -+ * @param op_size the size of the fuse_operations structure -+ * @param private_data Initial value for the `private_data` -+ * field of `struct fuse_context`. May be overridden by the -+ * `struct fuse_operations.init` handler. -+ * @return the created FUSE handle -+ */ -+#if FUSE_USE_VERSION == 30 -+struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op, -+ size_t op_size, void *private_data); -+#define fuse_new(args, op, size, data) fuse_new_30(args, op, size, data) -+#else -+struct fuse *fuse_new(struct fuse_args *args, const struct fuse_operations *op, -+ size_t op_size, void *private_data); -+#endif -+ -+/** -+ * Mount a FUSE file system. -+ * -+ * @param mountpoint the mount point path -+ * @param f the FUSE handle -+ * -+ * @return 0 on success, -1 on failure. -+ **/ -+int fuse_mount(struct fuse *f, const char *mountpoint); -+ -+/** -+ * Unmount a FUSE file system. -+ * -+ * See fuse_session_unmount() for additional information. -+ * -+ * @param f the FUSE handle -+ **/ -+void fuse_unmount(struct fuse *f); -+ -+/** -+ * Destroy the FUSE handle. -+ * -+ * NOTE: This function does not unmount the filesystem. If this is -+ * needed, call fuse_unmount() before calling this function. -+ * -+ * @param f the FUSE handle -+ */ -+void fuse_destroy(struct fuse *f); -+ -+/** -+ * FUSE event loop. -+ * -+ * Requests from the kernel are processed, and the appropriate -+ * operations are called. -+ * -+ * For a description of the return value and the conditions when the -+ * event loop exits, refer to the documentation of -+ * fuse_session_loop(). -+ * -+ * @param f the FUSE handle -+ * @return see fuse_session_loop() -+ * -+ * See also: fuse_loop_mt() -+ */ -+int fuse_loop(struct fuse *f); -+ -+/** -+ * Flag session as terminated -+ * -+ * This function will cause any running event loops to exit on -+ * the next opportunity. -+ * -+ * @param f the FUSE handle -+ */ -+void fuse_exit(struct fuse *f); -+ -+/** -+ * FUSE event loop with multiple threads -+ * -+ * Requests from the kernel are processed, and the appropriate -+ * operations are called. Request are processed in parallel by -+ * distributing them between multiple threads. -+ * -+ * For a description of the return value and the conditions when the -+ * event loop exits, refer to the documentation of -+ * fuse_session_loop(). -+ * -+ * Note: using fuse_loop() instead of fuse_loop_mt() means you are running in -+ * single-threaded mode, and that you will not have to worry about reentrancy, -+ * though you will have to worry about recursive lookups. In single-threaded -+ * mode, FUSE will wait for one callback to return before calling another. -+ * -+ * Enabling multiple threads, by using fuse_loop_mt(), will cause FUSE to make -+ * multiple simultaneous calls into the various callback functions given by your -+ * fuse_operations record. -+ * -+ * If you are using multiple threads, you can enjoy all the parallel execution -+ * and interactive response benefits of threads, and you get to enjoy all the -+ * benefits of race conditions and locking bugs, too. Ensure that any code used -+ * in the callback function of fuse_operations is also thread-safe. -+ * -+ * @param f the FUSE handle -+ * @param config loop configuration -+ * @return see fuse_session_loop() -+ * -+ * See also: fuse_loop() -+ */ -+#if FUSE_USE_VERSION < 32 -+int fuse_loop_mt_31(struct fuse *f, int clone_fd); -+#define fuse_loop_mt(f, clone_fd) fuse_loop_mt_31(f, clone_fd) -+#else -+int fuse_loop_mt(struct fuse *f, struct fuse_loop_config *config); -+#endif -+ -+/** -+ * Get the current context -+ * -+ * The context is only valid for the duration of a filesystem -+ * operation, and thus must not be stored and used later. -+ * -+ * @return the context -+ */ -+struct fuse_context *fuse_get_context(void); -+ -+/** -+ * Get the current supplementary group IDs for the current request -+ * -+ * Similar to the getgroups(2) system call, except the return value is -+ * always the total number of group IDs, even if it is larger than the -+ * specified size. -+ * -+ * The current fuse kernel module in linux (as of 2.6.30) doesn't pass -+ * the group list to userspace, hence this function needs to parse -+ * "/proc/$TID/task/$TID/status" to get the group IDs. -+ * -+ * This feature may not be supported on all operating systems. In -+ * such a case this function will return -ENOSYS. -+ * -+ * @param size size of given array -+ * @param list array of group IDs to be filled in -+ * @return the total number of supplementary group IDs or -errno on failure -+ */ -+int fuse_getgroups(int size, gid_t list[]); -+ -+/** -+ * Check if the current request has already been interrupted -+ * -+ * @return 1 if the request has been interrupted, 0 otherwise -+ */ -+int fuse_interrupted(void); -+ -+/** -+ * Invalidates cache for the given path. -+ * -+ * This calls fuse_lowlevel_notify_inval_inode internally. -+ * -+ * @return 0 on successful invalidation, negative error value otherwise. -+ * This routine may return -ENOENT to indicate that there was -+ * no entry to be invalidated, e.g., because the path has not -+ * been seen before or has been forgotten; this should not be -+ * considered to be an error. -+ */ -+int fuse_invalidate_path(struct fuse *f, const char *path); -+ -+/** -+ * The real main function -+ * -+ * Do not call this directly, use fuse_main() -+ */ -+int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, -+ size_t op_size, void *private_data); -+ -+/** -+ * Start the cleanup thread when using option "remember". -+ * -+ * This is done automatically by fuse_loop_mt() -+ * @param fuse struct fuse pointer for fuse instance -+ * @return 0 on success and -1 on error -+ */ -+int fuse_start_cleanup_thread(struct fuse *fuse); -+ -+/** -+ * Stop the cleanup thread when using option "remember". -+ * -+ * This is done automatically by fuse_loop_mt() -+ * @param fuse struct fuse pointer for fuse instance -+ */ -+void fuse_stop_cleanup_thread(struct fuse *fuse); -+ -+/** -+ * Iterate over cache removing stale entries -+ * use in conjunction with "-oremember" -+ * -+ * NOTE: This is already done for the standard sessions -+ * -+ * @param fuse struct fuse pointer for fuse instance -+ * @return the number of seconds until the next cleanup -+ */ -+int fuse_clean_cache(struct fuse *fuse); -+ -+/* -+ * Stacking API -+ */ -+ -+/** -+ * Fuse filesystem object -+ * -+ * This is opaque object represents a filesystem layer -+ */ -+struct fuse_fs; -+ -+/* -+ * These functions call the relevant filesystem operation, and return -+ * the result. -+ * -+ * If the operation is not defined, they return -ENOSYS, with the -+ * exception of fuse_fs_open, fuse_fs_release, fuse_fs_opendir, -+ * fuse_fs_releasedir and fuse_fs_statfs, which return 0. -+ */ -+ -+int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf, -+ struct fuse_file_info *fi); -+int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, -+ const char *newpath, unsigned int flags); -+int fuse_fs_unlink(struct fuse_fs *fs, const char *path); -+int fuse_fs_rmdir(struct fuse_fs *fs, const char *path); -+int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, -+ const char *path); -+int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath); -+int fuse_fs_release(struct fuse_fs *fs, const char *path, -+ struct fuse_file_info *fi); -+int fuse_fs_open(struct fuse_fs *fs, const char *path, -+ struct fuse_file_info *fi); -+int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size, -+ off_t off, struct fuse_file_info *fi); -+int fuse_fs_read_buf(struct fuse_fs *fs, const char *path, -+ struct fuse_bufvec **bufp, size_t size, off_t off, -+ struct fuse_file_info *fi); -+int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf, -+ size_t size, off_t off, struct fuse_file_info *fi); -+int fuse_fs_write_buf(struct fuse_fs *fs, const char *path, -+ struct fuse_bufvec *buf, off_t off, -+ struct fuse_file_info *fi); -+int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync, -+ struct fuse_file_info *fi); -+int fuse_fs_flush(struct fuse_fs *fs, const char *path, -+ struct fuse_file_info *fi); -+int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf); -+int fuse_fs_opendir(struct fuse_fs *fs, const char *path, -+ struct fuse_file_info *fi); -+int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf, -+ fuse_fill_dir_t filler, off_t off, -+ struct fuse_file_info *fi, enum fuse_readdir_flags flags); -+int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync, -+ struct fuse_file_info *fi); -+int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, -+ struct fuse_file_info *fi); -+int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode, -+ struct fuse_file_info *fi); -+int fuse_fs_lock(struct fuse_fs *fs, const char *path, -+ struct fuse_file_info *fi, int cmd, struct flock *lock); -+int fuse_fs_flock(struct fuse_fs *fs, const char *path, -+ struct fuse_file_info *fi, int op); -+int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode, -+ struct fuse_file_info *fi); -+int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid, -+ struct fuse_file_info *fi); -+int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size, -+ struct fuse_file_info *fi); -+int fuse_fs_utimens(struct fuse_fs *fs, const char *path, -+ const struct timespec tv[2], struct fuse_file_info *fi); -+int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask); -+int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf, -+ size_t len); -+int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode, -+ dev_t rdev); -+int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode); -+int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name, -+ const char *value, size_t size, int flags); -+int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name, -+ char *value, size_t size); -+int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list, -+ size_t size); -+int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, -+ const char *name); -+int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize, -+ uint64_t *idx); -+int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd, -+ void *arg, struct fuse_file_info *fi, unsigned int flags, -+ void *data); -+int fuse_fs_poll(struct fuse_fs *fs, const char *path, -+ struct fuse_file_info *fi, struct fuse_pollhandle *ph, -+ unsigned *reventsp); -+int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode, -+ off_t offset, off_t length, struct fuse_file_info *fi); -+ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in, -+ struct fuse_file_info *fi_in, off_t off_in, -+ const char *path_out, -+ struct fuse_file_info *fi_out, off_t off_out, -+ size_t len, int flags); -+off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence, -+ struct fuse_file_info *fi); -+void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn, -+ struct fuse_config *cfg); -+void fuse_fs_destroy(struct fuse_fs *fs); -+ -+int fuse_notify_poll(struct fuse_pollhandle *ph); -+ -+/** -+ * Create a new fuse filesystem object -+ * -+ * This is usually called from the factory of a fuse module to create -+ * a new instance of a filesystem. -+ * -+ * @param op the filesystem operations -+ * @param op_size the size of the fuse_operations structure -+ * @param private_data Initial value for the `private_data` -+ * field of `struct fuse_context`. May be overridden by the -+ * `struct fuse_operations.init` handler. -+ * @return a new filesystem object -+ */ -+struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, -+ void *private_data); -+ -+/** -+ * Factory for creating filesystem objects -+ * -+ * The function may use and remove options from 'args' that belong -+ * to this module. -+ * -+ * For now the 'fs' vector always contains exactly one filesystem. -+ * This is the filesystem which will be below the newly created -+ * filesystem in the stack. -+ * -+ * @param args the command line arguments -+ * @param fs NULL terminated filesystem object vector -+ * @return the new filesystem object -+ */ -+typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args, -+ struct fuse_fs *fs[]); -+/** -+ * Register filesystem module -+ * -+ * If the "-omodules=*name*_:..." option is present, filesystem -+ * objects are created and pushed onto the stack with the *factory_* -+ * function. -+ * -+ * @param name_ the name of this filesystem module -+ * @param factory_ the factory function for this filesystem module -+ */ -+#define FUSE_REGISTER_MODULE(name_, factory_) \ -+ fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_ -+ -+/** Get session from fuse object */ -+struct fuse_session *fuse_get_session(struct fuse *f); -+ -+/** -+ * Open a FUSE file descriptor and set up the mount for the given -+ * mountpoint and flags. -+ * -+ * @param mountpoint reference to the mount in the file system -+ * @param options mount options -+ * @return the FUSE file descriptor or -1 upon error -+ */ -+int fuse_open_channel(const char *mountpoint, const char *options); -+ -+#ifdef __cplusplus -+} -+#endif -+ -+#endif /* FUSE_H_ */ -diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h -new file mode 100644 -index 0000000000..2d686b2ac4 ---- /dev/null -+++ b/tools/virtiofsd/fuse_common.h -@@ -0,0 +1,823 @@ -+/* FUSE: Filesystem in Userspace -+ Copyright (C) 2001-2007 Miklos Szeredi -+ -+ This program can be distributed under the terms of the GNU LGPLv2. -+ See the file COPYING.LIB. -+*/ -+ -+/** @file */ -+ -+#if !defined(FUSE_H_) && !defined(FUSE_LOWLEVEL_H_) -+#error "Never include directly; use or instead." -+#endif -+ -+#ifndef FUSE_COMMON_H_ -+#define FUSE_COMMON_H_ -+ -+#include "fuse_opt.h" -+#include "fuse_log.h" -+#include -+#include -+ -+/** Major version of FUSE library interface */ -+#define FUSE_MAJOR_VERSION 3 -+ -+/** Minor version of FUSE library interface */ -+#define FUSE_MINOR_VERSION 2 -+ -+#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) -+#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+/** -+ * Information about an open file. -+ * -+ * File Handles are created by the open, opendir, and create methods and closed -+ * by the release and releasedir methods. Multiple file handles may be -+ * concurrently open for the same file. Generally, a client will create one -+ * file handle per file descriptor, though in some cases multiple file -+ * descriptors can share a single file handle. -+ */ -+struct fuse_file_info { -+ /** Open flags. Available in open() and release() */ -+ int flags; -+ -+ /** In case of a write operation indicates if this was caused -+ by a delayed write from the page cache. If so, then the -+ context's pid, uid, and gid fields will not be valid, and -+ the *fh* value may not match the *fh* value that would -+ have been sent with the corresponding individual write -+ requests if write caching had been disabled. */ -+ unsigned int writepage : 1; -+ -+ /** Can be filled in by open, to use direct I/O on this file. */ -+ unsigned int direct_io : 1; -+ -+ /** Can be filled in by open. It signals the kernel that any -+ currently cached file data (ie., data that the filesystem -+ provided the last time the file was open) need not be -+ invalidated. Has no effect when set in other contexts (in -+ particular it does nothing when set by opendir()). */ -+ unsigned int keep_cache : 1; -+ -+ /** Indicates a flush operation. Set in flush operation, also -+ maybe set in highlevel lock operation and lowlevel release -+ operation. */ -+ unsigned int flush : 1; -+ -+ /** Can be filled in by open, to indicate that the file is not -+ seekable. */ -+ unsigned int nonseekable : 1; -+ -+ /* Indicates that flock locks for this file should be -+ released. If set, lock_owner shall contain a valid value. -+ May only be set in ->release(). */ -+ unsigned int flock_release : 1; -+ -+ /** Can be filled in by opendir. It signals the kernel to -+ enable caching of entries returned by readdir(). Has no -+ effect when set in other contexts (in particular it does -+ nothing when set by open()). */ -+ unsigned int cache_readdir : 1; -+ -+ /** Padding. Reserved for future use*/ -+ unsigned int padding : 25; -+ unsigned int padding2 : 32; -+ -+ /** File handle id. May be filled in by filesystem in create, -+ * open, and opendir(). Available in most other file operations on the -+ * same file handle. */ -+ uint64_t fh; -+ -+ /** Lock owner id. Available in locking operations and flush */ -+ uint64_t lock_owner; -+ -+ /** Requested poll events. Available in ->poll. Only set on kernels -+ which support it. If unsupported, this field is set to zero. */ -+ uint32_t poll_events; -+}; -+ -+/** -+ * Configuration parameters passed to fuse_session_loop_mt() and -+ * fuse_loop_mt(). -+ */ -+struct fuse_loop_config { -+ /** -+ * whether to use separate device fds for each thread -+ * (may increase performance) -+ */ -+ int clone_fd; -+ -+ /** -+ * The maximum number of available worker threads before they -+ * start to get deleted when they become idle. If not -+ * specified, the default is 10. -+ * -+ * Adjusting this has performance implications; a very small number -+ * of threads in the pool will cause a lot of thread creation and -+ * deletion overhead and performance may suffer. When set to 0, a new -+ * thread will be created to service every operation. -+ */ -+ unsigned int max_idle_threads; -+}; -+ -+/************************************************************************** -+ * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' * -+ **************************************************************************/ -+ -+/** -+ * Indicates that the filesystem supports asynchronous read requests. -+ * -+ * If this capability is not requested/available, the kernel will -+ * ensure that there is at most one pending read request per -+ * file-handle at any time, and will attempt to order read requests by -+ * increasing offset. -+ * -+ * This feature is enabled by default when supported by the kernel. -+ */ -+#define FUSE_CAP_ASYNC_READ (1 << 0) -+ -+/** -+ * Indicates that the filesystem supports "remote" locking. -+ * -+ * This feature is enabled by default when supported by the kernel, -+ * and if getlk() and setlk() handlers are implemented. -+ */ -+#define FUSE_CAP_POSIX_LOCKS (1 << 1) -+ -+/** -+ * Indicates that the filesystem supports the O_TRUNC open flag. If -+ * disabled, and an application specifies O_TRUNC, fuse first calls -+ * truncate() and then open() with O_TRUNC filtered out. -+ * -+ * This feature is enabled by default when supported by the kernel. -+ */ -+#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3) -+ -+/** -+ * Indicates that the filesystem supports lookups of "." and "..". -+ * -+ * This feature is disabled by default. -+ */ -+#define FUSE_CAP_EXPORT_SUPPORT (1 << 4) -+ -+/** -+ * Indicates that the kernel should not apply the umask to the -+ * file mode on create operations. -+ * -+ * This feature is disabled by default. -+ */ -+#define FUSE_CAP_DONT_MASK (1 << 6) -+ -+/** -+ * Indicates that libfuse should try to use splice() when writing to -+ * the fuse device. This may improve performance. -+ * -+ * This feature is disabled by default. -+ */ -+#define FUSE_CAP_SPLICE_WRITE (1 << 7) -+ -+/** -+ * Indicates that libfuse should try to move pages instead of copying when -+ * writing to / reading from the fuse device. This may improve performance. -+ * -+ * This feature is disabled by default. -+ */ -+#define FUSE_CAP_SPLICE_MOVE (1 << 8) -+ -+/** -+ * Indicates that libfuse should try to use splice() when reading from -+ * the fuse device. This may improve performance. -+ * -+ * This feature is enabled by default when supported by the kernel and -+ * if the filesystem implements a write_buf() handler. -+ */ -+#define FUSE_CAP_SPLICE_READ (1 << 9) -+ -+/** -+ * If set, the calls to flock(2) will be emulated using POSIX locks and must -+ * then be handled by the filesystem's setlock() handler. -+ * -+ * If not set, flock(2) calls will be handled by the FUSE kernel module -+ * internally (so any access that does not go through the kernel cannot be taken -+ * into account). -+ * -+ * This feature is enabled by default when supported by the kernel and -+ * if the filesystem implements a flock() handler. -+ */ -+#define FUSE_CAP_FLOCK_LOCKS (1 << 10) -+ -+/** -+ * Indicates that the filesystem supports ioctl's on directories. -+ * -+ * This feature is enabled by default when supported by the kernel. -+ */ -+#define FUSE_CAP_IOCTL_DIR (1 << 11) -+ -+/** -+ * Traditionally, while a file is open the FUSE kernel module only -+ * asks the filesystem for an update of the file's attributes when a -+ * client attempts to read beyond EOF. This is unsuitable for -+ * e.g. network filesystems, where the file contents may change -+ * without the kernel knowing about it. -+ * -+ * If this flag is set, FUSE will check the validity of the attributes -+ * on every read. If the attributes are no longer valid (i.e., if the -+ * *attr_timeout* passed to fuse_reply_attr() or set in `struct -+ * fuse_entry_param` has passed), it will first issue a `getattr` -+ * request. If the new mtime differs from the previous value, any -+ * cached file *contents* will be invalidated as well. -+ * -+ * This flag should always be set when available. If all file changes -+ * go through the kernel, *attr_timeout* should be set to a very large -+ * number to avoid unnecessary getattr() calls. -+ * -+ * This feature is enabled by default when supported by the kernel. -+ */ -+#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12) -+ -+/** -+ * Indicates that the filesystem supports readdirplus. -+ * -+ * This feature is enabled by default when supported by the kernel and if the -+ * filesystem implements a readdirplus() handler. -+ */ -+#define FUSE_CAP_READDIRPLUS (1 << 13) -+ -+/** -+ * Indicates that the filesystem supports adaptive readdirplus. -+ * -+ * If FUSE_CAP_READDIRPLUS is not set, this flag has no effect. -+ * -+ * If FUSE_CAP_READDIRPLUS is set and this flag is not set, the kernel -+ * will always issue readdirplus() requests to retrieve directory -+ * contents. -+ * -+ * If FUSE_CAP_READDIRPLUS is set and this flag is set, the kernel -+ * will issue both readdir() and readdirplus() requests, depending on -+ * how much information is expected to be required. -+ * -+ * As of Linux 4.20, the algorithm is as follows: when userspace -+ * starts to read directory entries, issue a READDIRPLUS request to -+ * the filesystem. If any entry attributes have been looked up by the -+ * time userspace requests the next batch of entries continue with -+ * READDIRPLUS, otherwise switch to plain READDIR. This will reasult -+ * in eg plain "ls" triggering READDIRPLUS first then READDIR after -+ * that because it doesn't do lookups. "ls -l" should result in all -+ * READDIRPLUS, except if dentries are already cached. -+ * -+ * This feature is enabled by default when supported by the kernel and -+ * if the filesystem implements both a readdirplus() and a readdir() -+ * handler. -+ */ -+#define FUSE_CAP_READDIRPLUS_AUTO (1 << 14) -+ -+/** -+ * Indicates that the filesystem supports asynchronous direct I/O submission. -+ * -+ * If this capability is not requested/available, the kernel will ensure that -+ * there is at most one pending read and one pending write request per direct -+ * I/O file-handle at any time. -+ * -+ * This feature is enabled by default when supported by the kernel. -+ */ -+#define FUSE_CAP_ASYNC_DIO (1 << 15) -+ -+/** -+ * Indicates that writeback caching should be enabled. This means that -+ * individual write request may be buffered and merged in the kernel -+ * before they are send to the filesystem. -+ * -+ * This feature is disabled by default. -+ */ -+#define FUSE_CAP_WRITEBACK_CACHE (1 << 16) -+ -+/** -+ * Indicates support for zero-message opens. If this flag is set in -+ * the `capable` field of the `fuse_conn_info` structure, then the -+ * filesystem may return `ENOSYS` from the open() handler to indicate -+ * success. Further attempts to open files will be handled in the -+ * kernel. (If this flag is not set, returning ENOSYS will be treated -+ * as an error and signaled to the caller). -+ * -+ * Setting (or unsetting) this flag in the `want` field has *no -+ * effect*. -+ */ -+#define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17) -+ -+/** -+ * Indicates support for parallel directory operations. If this flag -+ * is unset, the FUSE kernel module will ensure that lookup() and -+ * readdir() requests are never issued concurrently for the same -+ * directory. -+ * -+ * This feature is enabled by default when supported by the kernel. -+ */ -+#define FUSE_CAP_PARALLEL_DIROPS (1 << 18) -+ -+/** -+ * Indicates support for POSIX ACLs. -+ * -+ * If this feature is enabled, the kernel will cache and have -+ * responsibility for enforcing ACLs. ACL will be stored as xattrs and -+ * passed to userspace, which is responsible for updating the ACLs in -+ * the filesystem, keeping the file mode in sync with the ACL, and -+ * ensuring inheritance of default ACLs when new filesystem nodes are -+ * created. Note that this requires that the file system is able to -+ * parse and interpret the xattr representation of ACLs. -+ * -+ * Enabling this feature implicitly turns on the -+ * ``default_permissions`` mount option (even if it was not passed to -+ * mount(2)). -+ * -+ * This feature is disabled by default. -+ */ -+#define FUSE_CAP_POSIX_ACL (1 << 19) -+ -+/** -+ * Indicates that the filesystem is responsible for unsetting -+ * setuid and setgid bits when a file is written, truncated, or -+ * its owner is changed. -+ * -+ * This feature is enabled by default when supported by the kernel. -+ */ -+#define FUSE_CAP_HANDLE_KILLPRIV (1 << 20) -+ -+/** -+ * Indicates support for zero-message opendirs. If this flag is set in -+ * the `capable` field of the `fuse_conn_info` structure, then the filesystem -+ * may return `ENOSYS` from the opendir() handler to indicate success. Further -+ * opendir and releasedir messages will be handled in the kernel. (If this -+ * flag is not set, returning ENOSYS will be treated as an error and signalled -+ * to the caller.) -+ * -+ * Setting (or unsetting) this flag in the `want` field has *no effect*. -+ */ -+#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24) -+ -+/** -+ * Ioctl flags -+ * -+ * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine -+ * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed -+ * FUSE_IOCTL_RETRY: retry with new iovecs -+ * FUSE_IOCTL_DIR: is a directory -+ * -+ * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs -+ */ -+#define FUSE_IOCTL_COMPAT (1 << 0) -+#define FUSE_IOCTL_UNRESTRICTED (1 << 1) -+#define FUSE_IOCTL_RETRY (1 << 2) -+#define FUSE_IOCTL_DIR (1 << 4) -+ -+#define FUSE_IOCTL_MAX_IOV 256 -+ -+/** -+ * Connection information, passed to the ->init() method -+ * -+ * Some of the elements are read-write, these can be changed to -+ * indicate the value requested by the filesystem. The requested -+ * value must usually be smaller than the indicated value. -+ */ -+struct fuse_conn_info { -+ /** -+ * Major version of the protocol (read-only) -+ */ -+ unsigned proto_major; -+ -+ /** -+ * Minor version of the protocol (read-only) -+ */ -+ unsigned proto_minor; -+ -+ /** -+ * Maximum size of the write buffer -+ */ -+ unsigned max_write; -+ -+ /** -+ * Maximum size of read requests. A value of zero indicates no -+ * limit. However, even if the filesystem does not specify a -+ * limit, the maximum size of read requests will still be -+ * limited by the kernel. -+ * -+ * NOTE: For the time being, the maximum size of read requests -+ * must be set both here *and* passed to fuse_session_new() -+ * using the ``-o max_read=`` mount option. At some point -+ * in the future, specifying the mount option will no longer -+ * be necessary. -+ */ -+ unsigned max_read; -+ -+ /** -+ * Maximum readahead -+ */ -+ unsigned max_readahead; -+ -+ /** -+ * Capability flags that the kernel supports (read-only) -+ */ -+ unsigned capable; -+ -+ /** -+ * Capability flags that the filesystem wants to enable. -+ * -+ * libfuse attempts to initialize this field with -+ * reasonable default values before calling the init() handler. -+ */ -+ unsigned want; -+ -+ /** -+ * Maximum number of pending "background" requests. A -+ * background request is any type of request for which the -+ * total number is not limited by other means. As of kernel -+ * 4.8, only two types of requests fall into this category: -+ * -+ * 1. Read-ahead requests -+ * 2. Asynchronous direct I/O requests -+ * -+ * Read-ahead requests are generated (if max_readahead is -+ * non-zero) by the kernel to preemptively fill its caches -+ * when it anticipates that userspace will soon read more -+ * data. -+ * -+ * Asynchronous direct I/O requests are generated if -+ * FUSE_CAP_ASYNC_DIO is enabled and userspace submits a large -+ * direct I/O request. In this case the kernel will internally -+ * split it up into multiple smaller requests and submit them -+ * to the filesystem concurrently. -+ * -+ * Note that the following requests are *not* background -+ * requests: writeback requests (limited by the kernel's -+ * flusher algorithm), regular (i.e., synchronous and -+ * buffered) userspace read/write requests (limited to one per -+ * thread), asynchronous read requests (Linux's io_submit(2) -+ * call actually blocks, so these are also limited to one per -+ * thread). -+ */ -+ unsigned max_background; -+ -+ /** -+ * Kernel congestion threshold parameter. If the number of pending -+ * background requests exceeds this number, the FUSE kernel module will -+ * mark the filesystem as "congested". This instructs the kernel to -+ * expect that queued requests will take some time to complete, and to -+ * adjust its algorithms accordingly (e.g. by putting a waiting thread -+ * to sleep instead of using a busy-loop). -+ */ -+ unsigned congestion_threshold; -+ -+ /** -+ * When FUSE_CAP_WRITEBACK_CACHE is enabled, the kernel is responsible -+ * for updating mtime and ctime when write requests are received. The -+ * updated values are passed to the filesystem with setattr() requests. -+ * However, if the filesystem does not support the full resolution of -+ * the kernel timestamps (nanoseconds), the mtime and ctime values used -+ * by kernel and filesystem will differ (and result in an apparent -+ * change of times after a cache flush). -+ * -+ * To prevent this problem, this variable can be used to inform the -+ * kernel about the timestamp granularity supported by the file-system. -+ * The value should be power of 10. The default is 1, i.e. full -+ * nano-second resolution. Filesystems supporting only second resolution -+ * should set this to 1000000000. -+ */ -+ unsigned time_gran; -+ -+ /** -+ * For future use. -+ */ -+ unsigned reserved[22]; -+}; -+ -+struct fuse_session; -+struct fuse_pollhandle; -+struct fuse_conn_info_opts; -+ -+/** -+ * This function parses several command-line options that can be used -+ * to override elements of struct fuse_conn_info. The pointer returned -+ * by this function should be passed to the -+ * fuse_apply_conn_info_opts() method by the file system's init() -+ * handler. -+ * -+ * Before using this function, think twice if you really want these -+ * parameters to be adjustable from the command line. In most cases, -+ * they should be determined by the file system internally. -+ * -+ * The following options are recognized: -+ * -+ * -o max_write=N sets conn->max_write -+ * -o max_readahead=N sets conn->max_readahead -+ * -o max_background=N sets conn->max_background -+ * -o congestion_threshold=N sets conn->congestion_threshold -+ * -o async_read sets FUSE_CAP_ASYNC_READ in conn->want -+ * -o sync_read unsets FUSE_CAP_ASYNC_READ in conn->want -+ * -o atomic_o_trunc sets FUSE_CAP_ATOMIC_O_TRUNC in conn->want -+ * -o no_remote_lock Equivalent to -o no_remote_flock,no_remote_posix_lock -+ * -o no_remote_flock Unsets FUSE_CAP_FLOCK_LOCKS in conn->want -+ * -o no_remote_posix_lock Unsets FUSE_CAP_POSIX_LOCKS in conn->want -+ * -o [no_]splice_write (un-)sets FUSE_CAP_SPLICE_WRITE in conn->want -+ * -o [no_]splice_move (un-)sets FUSE_CAP_SPLICE_MOVE in conn->want -+ * -o [no_]splice_read (un-)sets FUSE_CAP_SPLICE_READ in conn->want -+ * -o [no_]auto_inval_data (un-)sets FUSE_CAP_AUTO_INVAL_DATA in conn->want -+ * -o readdirplus=no unsets FUSE_CAP_READDIRPLUS in conn->want -+ * -o readdirplus=yes sets FUSE_CAP_READDIRPLUS and unsets -+ * FUSE_CAP_READDIRPLUS_AUTO in conn->want -+ * -o readdirplus=auto sets FUSE_CAP_READDIRPLUS and -+ * FUSE_CAP_READDIRPLUS_AUTO in conn->want -+ * -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in conn->want -+ * -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in conn->want -+ * -o time_gran=N sets conn->time_gran -+ * -+ * Known options will be removed from *args*, unknown options will be -+ * passed through unchanged. -+ * -+ * @param args argument vector (input+output) -+ * @return parsed options -+ **/ -+struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args); -+ -+/** -+ * This function applies the (parsed) parameters in *opts* to the -+ * *conn* pointer. It may modify the following fields: wants, -+ * max_write, max_readahead, congestion_threshold, max_background, -+ * time_gran. A field is only set (or unset) if the corresponding -+ * option has been explicitly set. -+ */ -+void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, -+ struct fuse_conn_info *conn); -+ -+/** -+ * Go into the background -+ * -+ * @param foreground if true, stay in the foreground -+ * @return 0 on success, -1 on failure -+ */ -+int fuse_daemonize(int foreground); -+ -+/** -+ * Get the version of the library -+ * -+ * @return the version -+ */ -+int fuse_version(void); -+ -+/** -+ * Get the full package version string of the library -+ * -+ * @return the package version -+ */ -+const char *fuse_pkgversion(void); -+ -+/** -+ * Destroy poll handle -+ * -+ * @param ph the poll handle -+ */ -+void fuse_pollhandle_destroy(struct fuse_pollhandle *ph); -+ -+/* ----------------------------------------------------------- * -+ * Data buffer * -+ * ----------------------------------------------------------- */ -+ -+/** -+ * Buffer flags -+ */ -+enum fuse_buf_flags { -+ /** -+ * Buffer contains a file descriptor -+ * -+ * If this flag is set, the .fd field is valid, otherwise the -+ * .mem fields is valid. -+ */ -+ FUSE_BUF_IS_FD = (1 << 1), -+ -+ /** -+ * Seek on the file descriptor -+ * -+ * If this flag is set then the .pos field is valid and is -+ * used to seek to the given offset before performing -+ * operation on file descriptor. -+ */ -+ FUSE_BUF_FD_SEEK = (1 << 2), -+ -+ /** -+ * Retry operation on file descriptor -+ * -+ * If this flag is set then retry operation on file descriptor -+ * until .size bytes have been copied or an error or EOF is -+ * detected. -+ */ -+ FUSE_BUF_FD_RETRY = (1 << 3), -+}; -+ -+/** -+ * Buffer copy flags -+ */ -+enum fuse_buf_copy_flags { -+ /** -+ * Don't use splice(2) -+ * -+ * Always fall back to using read and write instead of -+ * splice(2) to copy data from one file descriptor to another. -+ * -+ * If this flag is not set, then only fall back if splice is -+ * unavailable. -+ */ -+ FUSE_BUF_NO_SPLICE = (1 << 1), -+ -+ /** -+ * Force splice -+ * -+ * Always use splice(2) to copy data from one file descriptor -+ * to another. If splice is not available, return -EINVAL. -+ */ -+ FUSE_BUF_FORCE_SPLICE = (1 << 2), -+ -+ /** -+ * Try to move data with splice. -+ * -+ * If splice is used, try to move pages from the source to the -+ * destination instead of copying. See documentation of -+ * SPLICE_F_MOVE in splice(2) man page. -+ */ -+ FUSE_BUF_SPLICE_MOVE = (1 << 3), -+ -+ /** -+ * Don't block on the pipe when copying data with splice -+ * -+ * Makes the operations on the pipe non-blocking (if the pipe -+ * is full or empty). See SPLICE_F_NONBLOCK in the splice(2) -+ * man page. -+ */ -+ FUSE_BUF_SPLICE_NONBLOCK= (1 << 4), -+}; -+ -+/** -+ * Single data buffer -+ * -+ * Generic data buffer for I/O, extended attributes, etc... Data may -+ * be supplied as a memory pointer or as a file descriptor -+ */ -+struct fuse_buf { -+ /** -+ * Size of data in bytes -+ */ -+ size_t size; -+ -+ /** -+ * Buffer flags -+ */ -+ enum fuse_buf_flags flags; -+ -+ /** -+ * Memory pointer -+ * -+ * Used unless FUSE_BUF_IS_FD flag is set. -+ */ -+ void *mem; -+ -+ /** -+ * File descriptor -+ * -+ * Used if FUSE_BUF_IS_FD flag is set. -+ */ -+ int fd; -+ -+ /** -+ * File position -+ * -+ * Used if FUSE_BUF_FD_SEEK flag is set. -+ */ -+ off_t pos; -+}; -+ -+/** -+ * Data buffer vector -+ * -+ * An array of data buffers, each containing a memory pointer or a -+ * file descriptor. -+ * -+ * Allocate dynamically to add more than one buffer. -+ */ -+struct fuse_bufvec { -+ /** -+ * Number of buffers in the array -+ */ -+ size_t count; -+ -+ /** -+ * Index of current buffer within the array -+ */ -+ size_t idx; -+ -+ /** -+ * Current offset within the current buffer -+ */ -+ size_t off; -+ -+ /** -+ * Array of buffers -+ */ -+ struct fuse_buf buf[1]; -+}; -+ -+/* Initialize bufvec with a single buffer of given size */ -+#define FUSE_BUFVEC_INIT(size__) \ -+ ((struct fuse_bufvec) { \ -+ /* .count= */ 1, \ -+ /* .idx = */ 0, \ -+ /* .off = */ 0, \ -+ /* .buf = */ { /* [0] = */ { \ -+ /* .size = */ (size__), \ -+ /* .flags = */ (enum fuse_buf_flags) 0, \ -+ /* .mem = */ NULL, \ -+ /* .fd = */ -1, \ -+ /* .pos = */ 0, \ -+ } } \ -+ } ) -+ -+/** -+ * Get total size of data in a fuse buffer vector -+ * -+ * @param bufv buffer vector -+ * @return size of data -+ */ -+size_t fuse_buf_size(const struct fuse_bufvec *bufv); -+ -+/** -+ * Copy data from one buffer vector to another -+ * -+ * @param dst destination buffer vector -+ * @param src source buffer vector -+ * @param flags flags controlling the copy -+ * @return actual number of bytes copied or -errno on error -+ */ -+ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, -+ enum fuse_buf_copy_flags flags); -+ -+/* ----------------------------------------------------------- * -+ * Signal handling * -+ * ----------------------------------------------------------- */ -+ -+/** -+ * Exit session on HUP, TERM and INT signals and ignore PIPE signal -+ * -+ * Stores session in a global variable. May only be called once per -+ * process until fuse_remove_signal_handlers() is called. -+ * -+ * Once either of the POSIX signals arrives, the signal handler calls -+ * fuse_session_exit(). -+ * -+ * @param se the session to exit -+ * @return 0 on success, -1 on failure -+ * -+ * See also: -+ * fuse_remove_signal_handlers() -+ */ -+int fuse_set_signal_handlers(struct fuse_session *se); -+ -+/** -+ * Restore default signal handlers -+ * -+ * Resets global session. After this fuse_set_signal_handlers() may -+ * be called again. -+ * -+ * @param se the same session as given in fuse_set_signal_handlers() -+ * -+ * See also: -+ * fuse_set_signal_handlers() -+ */ -+void fuse_remove_signal_handlers(struct fuse_session *se); -+ -+/* ----------------------------------------------------------- * -+ * Compatibility stuff * -+ * ----------------------------------------------------------- */ -+ -+#if !defined(FUSE_USE_VERSION) || FUSE_USE_VERSION < 30 -+# error only API version 30 or greater is supported -+#endif -+ -+#ifdef __cplusplus -+} -+#endif -+ -+ -+/* -+ * This interface uses 64 bit off_t. -+ * -+ * On 32bit systems please add -D_FILE_OFFSET_BITS=64 to your compile flags! -+ */ -+ -+#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus -+_Static_assert(sizeof(off_t) == 8, "fuse: off_t must be 64bit"); -+#else -+struct _fuse_off_t_must_be_64bit_dummy_struct \ -+ { unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); }; -+#endif -+ -+#endif /* FUSE_COMMON_H_ */ -diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h -new file mode 100644 -index 0000000000..d38b630ac5 ---- /dev/null -+++ b/tools/virtiofsd/fuse_i.h -@@ -0,0 +1,139 @@ -+/* -+ FUSE: Filesystem in Userspace -+ Copyright (C) 2001-2007 Miklos Szeredi -+ -+ This program can be distributed under the terms of the GNU LGPLv2. -+ See the file COPYING.LIB -+*/ -+ -+#include "fuse.h" -+#include "fuse_lowlevel.h" -+ -+struct mount_opts; -+ -+struct fuse_req { -+ struct fuse_session *se; -+ uint64_t unique; -+ int ctr; -+ pthread_mutex_t lock; -+ struct fuse_ctx ctx; -+ struct fuse_chan *ch; -+ int interrupted; -+ unsigned int ioctl_64bit : 1; -+ union { -+ struct { -+ uint64_t unique; -+ } i; -+ struct { -+ fuse_interrupt_func_t func; -+ void *data; -+ } ni; -+ } u; -+ struct fuse_req *next; -+ struct fuse_req *prev; -+}; -+ -+struct fuse_notify_req { -+ uint64_t unique; -+ void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t, -+ const void *, const struct fuse_buf *); -+ struct fuse_notify_req *next; -+ struct fuse_notify_req *prev; -+}; -+ -+struct fuse_session { -+ char *mountpoint; -+ volatile int exited; -+ int fd; -+ struct mount_opts *mo; -+ int debug; -+ int deny_others; -+ struct fuse_lowlevel_ops op; -+ int got_init; -+ struct cuse_data *cuse_data; -+ void *userdata; -+ uid_t owner; -+ struct fuse_conn_info conn; -+ struct fuse_req list; -+ struct fuse_req interrupts; -+ pthread_mutex_t lock; -+ int got_destroy; -+ pthread_key_t pipe_key; -+ int broken_splice_nonblock; -+ uint64_t notify_ctr; -+ struct fuse_notify_req notify_list; -+ size_t bufsize; -+ int error; -+}; -+ -+struct fuse_chan { -+ pthread_mutex_t lock; -+ int ctr; -+ int fd; -+}; -+ -+/** -+ * Filesystem module -+ * -+ * Filesystem modules are registered with the FUSE_REGISTER_MODULE() -+ * macro. -+ * -+ */ -+struct fuse_module { -+ char *name; -+ fuse_module_factory_t factory; -+ struct fuse_module *next; -+ struct fusemod_so *so; -+ int ctr; -+}; -+ -+/* ----------------------------------------------------------- * -+ * Channel interface (when using -o clone_fd) * -+ * ----------------------------------------------------------- */ -+ -+/** -+ * Obtain counted reference to the channel -+ * -+ * @param ch the channel -+ * @return the channel -+ */ -+struct fuse_chan *fuse_chan_get(struct fuse_chan *ch); -+ -+/** -+ * Drop counted reference to a channel -+ * -+ * @param ch the channel -+ */ -+void fuse_chan_put(struct fuse_chan *ch); -+ -+struct mount_opts *parse_mount_opts(struct fuse_args *args); -+void destroy_mount_opts(struct mount_opts *mo); -+void fuse_mount_version(void); -+unsigned get_max_read(struct mount_opts *o); -+void fuse_kern_unmount(const char *mountpoint, int fd); -+int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo); -+ -+int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, -+ int count); -+void fuse_free_req(fuse_req_t req); -+ -+void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg); -+ -+int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg); -+ -+int fuse_session_receive_buf_int(struct fuse_session *se, struct fuse_buf *buf, -+ struct fuse_chan *ch); -+void fuse_session_process_buf_int(struct fuse_session *se, -+ const struct fuse_buf *buf, struct fuse_chan *ch); -+ -+struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op, -+ size_t op_size, void *private_data); -+int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config); -+int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config); -+ -+#define FUSE_MAX_MAX_PAGES 256 -+#define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32 -+ -+/* room needed in buffer to accommodate header */ -+#define FUSE_BUFFER_HEADER_SIZE 0x1000 -+ -diff --git a/tools/virtiofsd/fuse_log.h b/tools/virtiofsd/fuse_log.h -new file mode 100644 -index 0000000000..5e112e0f53 ---- /dev/null -+++ b/tools/virtiofsd/fuse_log.h -@@ -0,0 +1,82 @@ -+/* -+ FUSE: Filesystem in Userspace -+ Copyright (C) 2019 Red Hat, Inc. -+ -+ This program can be distributed under the terms of the GNU LGPLv2. -+ See the file COPYING.LIB. -+*/ -+ -+#ifndef FUSE_LOG_H_ -+#define FUSE_LOG_H_ -+ -+/** @file -+ * -+ * This file defines the logging interface of FUSE -+ */ -+ -+#include -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+/** -+ * Log severity level -+ * -+ * These levels correspond to syslog(2) log levels since they are widely used. -+ */ -+enum fuse_log_level { -+ FUSE_LOG_EMERG, -+ FUSE_LOG_ALERT, -+ FUSE_LOG_CRIT, -+ FUSE_LOG_ERR, -+ FUSE_LOG_WARNING, -+ FUSE_LOG_NOTICE, -+ FUSE_LOG_INFO, -+ FUSE_LOG_DEBUG -+}; -+ -+/** -+ * Log message handler function. -+ * -+ * This function must be thread-safe. It may be called from any libfuse -+ * function, including fuse_parse_cmdline() and other functions invoked before -+ * a FUSE filesystem is created. -+ * -+ * Install a custom log message handler function using fuse_set_log_func(). -+ * -+ * @param level log severity level -+ * @param fmt sprintf-style format string including newline -+ * @param ap format string arguments -+ */ -+typedef void (*fuse_log_func_t)(enum fuse_log_level level, -+ const char *fmt, va_list ap); -+ -+/** -+ * Install a custom log handler function. -+ * -+ * Log messages are emitted by libfuse functions to report errors and debug -+ * information. Messages are printed to stderr by default but this can be -+ * overridden by installing a custom log message handler function. -+ * -+ * The log message handler function is global and affects all FUSE filesystems -+ * created within this process. -+ * -+ * @param func a custom log message handler function or NULL to revert to -+ * the default -+ */ -+void fuse_set_log_func(fuse_log_func_t func); -+ -+/** -+ * Emit a log message -+ * -+ * @param level severity level (FUSE_LOG_ERR, FUSE_LOG_DEBUG, etc) -+ * @param fmt sprintf-style format string including newline -+ */ -+void fuse_log(enum fuse_log_level level, const char *fmt, ...); -+ -+#ifdef __cplusplus -+} -+#endif -+ -+#endif /* FUSE_LOG_H_ */ -diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h -new file mode 100644 -index 0000000000..18c6363f07 ---- /dev/null -+++ b/tools/virtiofsd/fuse_lowlevel.h -@@ -0,0 +1,2089 @@ -+/* -+ FUSE: Filesystem in Userspace -+ Copyright (C) 2001-2007 Miklos Szeredi -+ -+ This program can be distributed under the terms of the GNU LGPLv2. -+ See the file COPYING.LIB. -+*/ -+ -+#ifndef FUSE_LOWLEVEL_H_ -+#define FUSE_LOWLEVEL_H_ -+ -+/** @file -+ * -+ * Low level API -+ * -+ * IMPORTANT: you should define FUSE_USE_VERSION before including this -+ * header. To use the newest API define it to 31 (recommended for any -+ * new application). -+ */ -+ -+#ifndef FUSE_USE_VERSION -+#error FUSE_USE_VERSION not defined -+#endif -+ -+#include "fuse_common.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+/* ----------------------------------------------------------- * -+ * Miscellaneous definitions * -+ * ----------------------------------------------------------- */ -+ -+/** The node ID of the root inode */ -+#define FUSE_ROOT_ID 1 -+ -+/** Inode number type */ -+typedef uint64_t fuse_ino_t; -+ -+/** Request pointer type */ -+typedef struct fuse_req *fuse_req_t; -+ -+/** -+ * Session -+ * -+ * This provides hooks for processing requests, and exiting -+ */ -+struct fuse_session; -+ -+/** Directory entry parameters supplied to fuse_reply_entry() */ -+struct fuse_entry_param { -+ /** Unique inode number -+ * -+ * In lookup, zero means negative entry (from version 2.5) -+ * Returning ENOENT also means negative entry, but by setting zero -+ * ino the kernel may cache negative entries for entry_timeout -+ * seconds. -+ */ -+ fuse_ino_t ino; -+ -+ /** Generation number for this entry. -+ * -+ * If the file system will be exported over NFS, the -+ * ino/generation pairs need to be unique over the file -+ * system's lifetime (rather than just the mount time). So if -+ * the file system reuses an inode after it has been deleted, -+ * it must assign a new, previously unused generation number -+ * to the inode at the same time. -+ * -+ */ -+ uint64_t generation; -+ -+ /** Inode attributes. -+ * -+ * Even if attr_timeout == 0, attr must be correct. For example, -+ * for open(), FUSE uses attr.st_size from lookup() to determine -+ * how many bytes to request. If this value is not correct, -+ * incorrect data will be returned. -+ */ -+ struct stat attr; -+ -+ /** Validity timeout (in seconds) for inode attributes. If -+ attributes only change as a result of requests that come -+ through the kernel, this should be set to a very large -+ value. */ -+ double attr_timeout; -+ -+ /** Validity timeout (in seconds) for the name. If directory -+ entries are changed/deleted only as a result of requests -+ that come through the kernel, this should be set to a very -+ large value. */ -+ double entry_timeout; -+}; -+ -+/** -+ * Additional context associated with requests. -+ * -+ * Note that the reported client uid, gid and pid may be zero in some -+ * situations. For example, if the FUSE file system is running in a -+ * PID or user namespace but then accessed from outside the namespace, -+ * there is no valid uid/pid/gid that could be reported. -+ */ -+struct fuse_ctx { -+ /** User ID of the calling process */ -+ uid_t uid; -+ -+ /** Group ID of the calling process */ -+ gid_t gid; -+ -+ /** Thread ID of the calling process */ -+ pid_t pid; -+ -+ /** Umask of the calling process */ -+ mode_t umask; -+}; -+ -+struct fuse_forget_data { -+ fuse_ino_t ino; -+ uint64_t nlookup; -+}; -+ -+/* 'to_set' flags in setattr */ -+#define FUSE_SET_ATTR_MODE (1 << 0) -+#define FUSE_SET_ATTR_UID (1 << 1) -+#define FUSE_SET_ATTR_GID (1 << 2) -+#define FUSE_SET_ATTR_SIZE (1 << 3) -+#define FUSE_SET_ATTR_ATIME (1 << 4) -+#define FUSE_SET_ATTR_MTIME (1 << 5) -+#define FUSE_SET_ATTR_ATIME_NOW (1 << 7) -+#define FUSE_SET_ATTR_MTIME_NOW (1 << 8) -+#define FUSE_SET_ATTR_CTIME (1 << 10) -+ -+/* ----------------------------------------------------------- * -+ * Request methods and replies * -+ * ----------------------------------------------------------- */ -+ -+/** -+ * Low level filesystem operations -+ * -+ * Most of the methods (with the exception of init and destroy) -+ * receive a request handle (fuse_req_t) as their first argument. -+ * This handle must be passed to one of the specified reply functions. -+ * -+ * This may be done inside the method invocation, or after the call -+ * has returned. The request handle is valid until one of the reply -+ * functions is called. -+ * -+ * Other pointer arguments (name, fuse_file_info, etc) are not valid -+ * after the call has returned, so if they are needed later, their -+ * contents have to be copied. -+ * -+ * In general, all methods are expected to perform any necessary -+ * permission checking. However, a filesystem may delegate this task -+ * to the kernel by passing the `default_permissions` mount option to -+ * `fuse_session_new()`. In this case, methods will only be called if -+ * the kernel's permission check has succeeded. -+ * -+ * The filesystem sometimes needs to handle a return value of -ENOENT -+ * from the reply function, which means, that the request was -+ * interrupted, and the reply discarded. For example if -+ * fuse_reply_open() return -ENOENT means, that the release method for -+ * this file will not be called. -+ */ -+struct fuse_lowlevel_ops { -+ /** -+ * Initialize filesystem -+ * -+ * This function is called when libfuse establishes -+ * communication with the FUSE kernel module. The file system -+ * should use this module to inspect and/or modify the -+ * connection parameters provided in the `conn` structure. -+ * -+ * Note that some parameters may be overwritten by options -+ * passed to fuse_session_new() which take precedence over the -+ * values set in this handler. -+ * -+ * There's no reply to this function -+ * -+ * @param userdata the user data passed to fuse_session_new() -+ */ -+ void (*init) (void *userdata, struct fuse_conn_info *conn); -+ -+ /** -+ * Clean up filesystem. -+ * -+ * Called on filesystem exit. When this method is called, the -+ * connection to the kernel may be gone already, so that eg. calls -+ * to fuse_lowlevel_notify_* will fail. -+ * -+ * There's no reply to this function -+ * -+ * @param userdata the user data passed to fuse_session_new() -+ */ -+ void (*destroy) (void *userdata); -+ -+ /** -+ * Look up a directory entry by name and get its attributes. -+ * -+ * Valid replies: -+ * fuse_reply_entry -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param parent inode number of the parent directory -+ * @param name the name to look up -+ */ -+ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name); -+ -+ /** -+ * Forget about an inode -+ * -+ * This function is called when the kernel removes an inode -+ * from its internal caches. -+ * -+ * The inode's lookup count increases by one for every call to -+ * fuse_reply_entry and fuse_reply_create. The nlookup parameter -+ * indicates by how much the lookup count should be decreased. -+ * -+ * Inodes with a non-zero lookup count may receive request from -+ * the kernel even after calls to unlink, rmdir or (when -+ * overwriting an existing file) rename. Filesystems must handle -+ * such requests properly and it is recommended to defer removal -+ * of the inode until the lookup count reaches zero. Calls to -+ * unlink, rmdir or rename will be followed closely by forget -+ * unless the file or directory is open, in which case the -+ * kernel issues forget only after the release or releasedir -+ * calls. -+ * -+ * Note that if a file system will be exported over NFS the -+ * inodes lifetime must extend even beyond forget. See the -+ * generation field in struct fuse_entry_param above. -+ * -+ * On unmount the lookup count for all inodes implicitly drops -+ * to zero. It is not guaranteed that the file system will -+ * receive corresponding forget messages for the affected -+ * inodes. -+ * -+ * Valid replies: -+ * fuse_reply_none -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param nlookup the number of lookups to forget -+ */ -+ void (*forget) (fuse_req_t req, fuse_ino_t ino, uint64_t nlookup); -+ -+ /** -+ * Get file attributes. -+ * -+ * If writeback caching is enabled, the kernel may have a -+ * better idea of a file's length than the FUSE file system -+ * (eg if there has been a write that extended the file size, -+ * but that has not yet been passed to the filesystem.n -+ * -+ * In this case, the st_size value provided by the file system -+ * will be ignored. -+ * -+ * Valid replies: -+ * fuse_reply_attr -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi for future use, currently always NULL -+ */ -+ void (*getattr) (fuse_req_t req, fuse_ino_t ino, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Set file attributes -+ * -+ * In the 'attr' argument only members indicated by the 'to_set' -+ * bitmask contain valid values. Other members contain undefined -+ * values. -+ * -+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -+ * expected to reset the setuid and setgid bits if the file -+ * size or owner is being changed. -+ * -+ * If the setattr was invoked from the ftruncate() system call -+ * under Linux kernel versions 2.6.15 or later, the fi->fh will -+ * contain the value set by the open method or will be undefined -+ * if the open method didn't set any value. Otherwise (not -+ * ftruncate call, or kernel version earlier than 2.6.15) the fi -+ * parameter will be NULL. -+ * -+ * Valid replies: -+ * fuse_reply_attr -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param attr the attributes -+ * @param to_set bit mask of attributes which should be set -+ * @param fi file information, or NULL -+ */ -+ void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr, -+ int to_set, struct fuse_file_info *fi); -+ -+ /** -+ * Read symbolic link -+ * -+ * Valid replies: -+ * fuse_reply_readlink -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ */ -+ void (*readlink) (fuse_req_t req, fuse_ino_t ino); -+ -+ /** -+ * Create file node -+ * -+ * Create a regular file, character device, block device, fifo or -+ * socket node. -+ * -+ * Valid replies: -+ * fuse_reply_entry -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param parent inode number of the parent directory -+ * @param name to create -+ * @param mode file type and mode with which to create the new file -+ * @param rdev the device number (only valid if created file is a device) -+ */ -+ void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name, -+ mode_t mode, dev_t rdev); -+ -+ /** -+ * Create a directory -+ * -+ * Valid replies: -+ * fuse_reply_entry -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param parent inode number of the parent directory -+ * @param name to create -+ * @param mode with which to create the new file -+ */ -+ void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name, -+ mode_t mode); -+ -+ /** -+ * Remove a file -+ * -+ * If the file's inode's lookup count is non-zero, the file -+ * system is expected to postpone any removal of the inode -+ * until the lookup count reaches zero (see description of the -+ * forget function). -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param parent inode number of the parent directory -+ * @param name to remove -+ */ -+ void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name); -+ -+ /** -+ * Remove a directory -+ * -+ * If the directory's inode's lookup count is non-zero, the -+ * file system is expected to postpone any removal of the -+ * inode until the lookup count reaches zero (see description -+ * of the forget function). -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param parent inode number of the parent directory -+ * @param name to remove -+ */ -+ void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name); -+ -+ /** -+ * Create a symbolic link -+ * -+ * Valid replies: -+ * fuse_reply_entry -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param link the contents of the symbolic link -+ * @param parent inode number of the parent directory -+ * @param name to create -+ */ -+ void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent, -+ const char *name); -+ -+ /** Rename a file -+ * -+ * If the target exists it should be atomically replaced. If -+ * the target's inode's lookup count is non-zero, the file -+ * system is expected to postpone any removal of the inode -+ * until the lookup count reaches zero (see description of the -+ * forget function). -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure with error code EINVAL, i.e. all -+ * future bmap requests will fail with EINVAL without being -+ * send to the filesystem process. -+ * -+ * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If -+ * RENAME_NOREPLACE is specified, the filesystem must not -+ * overwrite *newname* if it exists and return an error -+ * instead. If `RENAME_EXCHANGE` is specified, the filesystem -+ * must atomically exchange the two files, i.e. both must -+ * exist and neither may be deleted. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param parent inode number of the old parent directory -+ * @param name old name -+ * @param newparent inode number of the new parent directory -+ * @param newname new name -+ */ -+ void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name, -+ fuse_ino_t newparent, const char *newname, -+ unsigned int flags); -+ -+ /** -+ * Create a hard link -+ * -+ * Valid replies: -+ * fuse_reply_entry -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the old inode number -+ * @param newparent inode number of the new parent directory -+ * @param newname new name to create -+ */ -+ void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, -+ const char *newname); -+ -+ /** -+ * Open a file -+ * -+ * Open flags are available in fi->flags. The following rules -+ * apply. -+ * -+ * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be -+ * filtered out / handled by the kernel. -+ * -+ * - Access modes (O_RDONLY, O_WRONLY, O_RDWR) should be used -+ * by the filesystem to check if the operation is -+ * permitted. If the ``-o default_permissions`` mount -+ * option is given, this check is already done by the -+ * kernel before calling open() and may thus be omitted by -+ * the filesystem. -+ * -+ * - When writeback caching is enabled, the kernel may send -+ * read requests even for files opened with O_WRONLY. The -+ * filesystem should be prepared to handle this. -+ * -+ * - When writeback caching is disabled, the filesystem is -+ * expected to properly handle the O_APPEND flag and ensure -+ * that each write is appending to the end of the file. -+ * -+ * - When writeback caching is enabled, the kernel will -+ * handle O_APPEND. However, unless all changes to the file -+ * come through the kernel this will not work reliably. The -+ * filesystem should thus either ignore the O_APPEND flag -+ * (and let the kernel handle it), or return an error -+ * (indicating that reliably O_APPEND is not available). -+ * -+ * Filesystem may store an arbitrary file handle (pointer, -+ * index, etc) in fi->fh, and use this in other all other file -+ * operations (read, write, flush, release, fsync). -+ * -+ * Filesystem may also implement stateless file I/O and not store -+ * anything in fi->fh. -+ * -+ * There are also some flags (direct_io, keep_cache) which the -+ * filesystem may set in fi, to change the way the file is opened. -+ * See fuse_file_info structure in for more details. -+ * -+ * If this request is answered with an error code of ENOSYS -+ * and FUSE_CAP_NO_OPEN_SUPPORT is set in -+ * `fuse_conn_info.capable`, this is treated as success and -+ * future calls to open and release will also succeed without being -+ * sent to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_open -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ */ -+ void (*open) (fuse_req_t req, fuse_ino_t ino, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Read data -+ * -+ * Read should send exactly the number of bytes requested except -+ * on EOF or error, otherwise the rest of the data will be -+ * substituted with zeroes. An exception to this is when the file -+ * has been opened in 'direct_io' mode, in which case the return -+ * value of the read system call will reflect the return value of -+ * this operation. -+ * -+ * fi->fh will contain the value set by the open method, or will -+ * be undefined if the open method didn't set any value. -+ * -+ * Valid replies: -+ * fuse_reply_buf -+ * fuse_reply_iov -+ * fuse_reply_data -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param size number of bytes to read -+ * @param off offset to read from -+ * @param fi file information -+ */ -+ void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Write data -+ * -+ * Write should return exactly the number of bytes requested -+ * except on error. An exception to this is when the file has -+ * been opened in 'direct_io' mode, in which case the return value -+ * of the write system call will reflect the return value of this -+ * operation. -+ * -+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -+ * expected to reset the setuid and setgid bits. -+ * -+ * fi->fh will contain the value set by the open method, or will -+ * be undefined if the open method didn't set any value. -+ * -+ * Valid replies: -+ * fuse_reply_write -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param buf data to write -+ * @param size number of bytes to write -+ * @param off offset to write to -+ * @param fi file information -+ */ -+ void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf, -+ size_t size, off_t off, struct fuse_file_info *fi); -+ -+ /** -+ * Flush method -+ * -+ * This is called on each close() of the opened file. -+ * -+ * Since file descriptors can be duplicated (dup, dup2, fork), for -+ * one open call there may be many flush calls. -+ * -+ * Filesystems shouldn't assume that flush will always be called -+ * after some writes, or that if will be called at all. -+ * -+ * fi->fh will contain the value set by the open method, or will -+ * be undefined if the open method didn't set any value. -+ * -+ * NOTE: the name of the method is misleading, since (unlike -+ * fsync) the filesystem is not forced to flush pending writes. -+ * One reason to flush data is if the filesystem wants to return -+ * write errors during close. However, such use is non-portable -+ * because POSIX does not require [close] to wait for delayed I/O to -+ * complete. -+ * -+ * If the filesystem supports file locking operations (setlk, -+ * getlk) it should remove all locks belonging to 'fi->owner'. -+ * -+ * If this request is answered with an error code of ENOSYS, -+ * this is treated as success and future calls to flush() will -+ * succeed automatically without being send to the filesystem -+ * process. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ * -+ * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html -+ */ -+ void (*flush) (fuse_req_t req, fuse_ino_t ino, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Release an open file -+ * -+ * Release is called when there are no more references to an open -+ * file: all file descriptors are closed and all memory mappings -+ * are unmapped. -+ * -+ * For every open call there will be exactly one release call (unless -+ * the filesystem is force-unmounted). -+ * -+ * The filesystem may reply with an error, but error values are -+ * not returned to close() or munmap() which triggered the -+ * release. -+ * -+ * fi->fh will contain the value set by the open method, or will -+ * be undefined if the open method didn't set any value. -+ * fi->flags will contain the same flags as for open. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ */ -+ void (*release) (fuse_req_t req, fuse_ino_t ino, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Synchronize file contents -+ * -+ * If the datasync parameter is non-zero, then only the user data -+ * should be flushed, not the meta data. -+ * -+ * If this request is answered with an error code of ENOSYS, -+ * this is treated as success and future calls to fsync() will -+ * succeed automatically without being send to the filesystem -+ * process. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param datasync flag indicating if only data should be flushed -+ * @param fi file information -+ */ -+ void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Open a directory -+ * -+ * Filesystem may store an arbitrary file handle (pointer, index, -+ * etc) in fi->fh, and use this in other all other directory -+ * stream operations (readdir, releasedir, fsyncdir). -+ * -+ * If this request is answered with an error code of ENOSYS and -+ * FUSE_CAP_NO_OPENDIR_SUPPORT is set in `fuse_conn_info.capable`, -+ * this is treated as success and future calls to opendir and -+ * releasedir will also succeed without being sent to the filesystem -+ * process. In addition, the kernel will cache readdir results -+ * as if opendir returned FOPEN_KEEP_CACHE | FOPEN_CACHE_DIR. -+ * -+ * Valid replies: -+ * fuse_reply_open -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ */ -+ void (*opendir) (fuse_req_t req, fuse_ino_t ino, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Read directory -+ * -+ * Send a buffer filled using fuse_add_direntry(), with size not -+ * exceeding the requested size. Send an empty buffer on end of -+ * stream. -+ * -+ * fi->fh will contain the value set by the opendir method, or -+ * will be undefined if the opendir method didn't set any value. -+ * -+ * Returning a directory entry from readdir() does not affect -+ * its lookup count. -+ * -+ * If off_t is non-zero, then it will correspond to one of the off_t -+ * values that was previously returned by readdir() for the same -+ * directory handle. In this case, readdir() should skip over entries -+ * coming before the position defined by the off_t value. If entries -+ * are added or removed while the directory handle is open, they filesystem -+ * may still include the entries that have been removed, and may not -+ * report the entries that have been created. However, addition or -+ * removal of entries must never cause readdir() to skip over unrelated -+ * entries or to report them more than once. This means -+ * that off_t can not be a simple index that enumerates the entries -+ * that have been returned but must contain sufficient information to -+ * uniquely determine the next directory entry to return even when the -+ * set of entries is changing. -+ * -+ * The function does not have to report the '.' and '..' -+ * entries, but is allowed to do so. Note that, if readdir does -+ * not return '.' or '..', they will not be implicitly returned, -+ * and this behavior is observable by the caller. -+ * -+ * Valid replies: -+ * fuse_reply_buf -+ * fuse_reply_data -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param size maximum number of bytes to send -+ * @param off offset to continue reading the directory stream -+ * @param fi file information -+ */ -+ void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Release an open directory -+ * -+ * For every opendir call there will be exactly one releasedir -+ * call (unless the filesystem is force-unmounted). -+ * -+ * fi->fh will contain the value set by the opendir method, or -+ * will be undefined if the opendir method didn't set any value. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ */ -+ void (*releasedir) (fuse_req_t req, fuse_ino_t ino, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Synchronize directory contents -+ * -+ * If the datasync parameter is non-zero, then only the directory -+ * contents should be flushed, not the meta data. -+ * -+ * fi->fh will contain the value set by the opendir method, or -+ * will be undefined if the opendir method didn't set any value. -+ * -+ * If this request is answered with an error code of ENOSYS, -+ * this is treated as success and future calls to fsyncdir() will -+ * succeed automatically without being send to the filesystem -+ * process. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param datasync flag indicating if only data should be flushed -+ * @param fi file information -+ */ -+ void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Get file system statistics -+ * -+ * Valid replies: -+ * fuse_reply_statfs -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number, zero means "undefined" -+ */ -+ void (*statfs) (fuse_req_t req, fuse_ino_t ino); -+ -+ /** -+ * Set an extended attribute -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -+ * future setxattr() requests will fail with EOPNOTSUPP without being -+ * send to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ */ -+ void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, -+ const char *value, size_t size, int flags); -+ -+ /** -+ * Get an extended attribute -+ * -+ * If size is zero, the size of the value should be sent with -+ * fuse_reply_xattr. -+ * -+ * If the size is non-zero, and the value fits in the buffer, the -+ * value should be sent with fuse_reply_buf. -+ * -+ * If the size is too small for the value, the ERANGE error should -+ * be sent. -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -+ * future getxattr() requests will fail with EOPNOTSUPP without being -+ * send to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_buf -+ * fuse_reply_data -+ * fuse_reply_xattr -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param name of the extended attribute -+ * @param size maximum size of the value to send -+ */ -+ void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, -+ size_t size); -+ -+ /** -+ * List extended attribute names -+ * -+ * If size is zero, the total size of the attribute list should be -+ * sent with fuse_reply_xattr. -+ * -+ * If the size is non-zero, and the null character separated -+ * attribute list fits in the buffer, the list should be sent with -+ * fuse_reply_buf. -+ * -+ * If the size is too small for the list, the ERANGE error should -+ * be sent. -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -+ * future listxattr() requests will fail with EOPNOTSUPP without being -+ * send to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_buf -+ * fuse_reply_data -+ * fuse_reply_xattr -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param size maximum size of the list to send -+ */ -+ void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size); -+ -+ /** -+ * Remove an extended attribute -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -+ * future removexattr() requests will fail with EOPNOTSUPP without being -+ * send to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param name of the extended attribute -+ */ -+ void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name); -+ -+ /** -+ * Check file access permissions -+ * -+ * This will be called for the access() and chdir() system -+ * calls. If the 'default_permissions' mount option is given, -+ * this method is not called. -+ * -+ * This method is not called under Linux kernel versions 2.4.x -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent success, i.e. this and all future access() -+ * requests will succeed without being send to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param mask requested access mode -+ */ -+ void (*access) (fuse_req_t req, fuse_ino_t ino, int mask); -+ -+ /** -+ * Create and open a file -+ * -+ * If the file does not exist, first create it with the specified -+ * mode, and then open it. -+ * -+ * See the description of the open handler for more -+ * information. -+ * -+ * If this method is not implemented or under Linux kernel -+ * versions earlier than 2.6.15, the mknod() and open() methods -+ * will be called instead. -+ * -+ * If this request is answered with an error code of ENOSYS, the handler -+ * is treated as not implemented (i.e., for this and future requests the -+ * mknod() and open() handlers will be called instead). -+ * -+ * Valid replies: -+ * fuse_reply_create -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param parent inode number of the parent directory -+ * @param name to create -+ * @param mode file type and mode with which to create the new file -+ * @param fi file information -+ */ -+ void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name, -+ mode_t mode, struct fuse_file_info *fi); -+ -+ /** -+ * Test for a POSIX file lock -+ * -+ * Valid replies: -+ * fuse_reply_lock -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ * @param lock the region/type to test -+ */ -+ void (*getlk) (fuse_req_t req, fuse_ino_t ino, -+ struct fuse_file_info *fi, struct flock *lock); -+ -+ /** -+ * Acquire, modify or release a POSIX file lock -+ * -+ * For POSIX threads (NPTL) there's a 1-1 relation between pid and -+ * owner, but otherwise this is not always the case. For checking -+ * lock ownership, 'fi->owner' must be used. The l_pid field in -+ * 'struct flock' should only be used to fill in this field in -+ * getlk(). -+ * -+ * Note: if the locking methods are not implemented, the kernel -+ * will still allow file locking to work locally. Hence these are -+ * only interesting for network filesystems and similar. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ * @param lock the region/type to set -+ * @param sleep locking operation may sleep -+ */ -+ void (*setlk) (fuse_req_t req, fuse_ino_t ino, -+ struct fuse_file_info *fi, -+ struct flock *lock, int sleep); -+ -+ /** -+ * Map block index within file to block index within device -+ * -+ * Note: This makes sense only for block device backed filesystems -+ * mounted with the 'blkdev' option -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure, i.e. all future bmap() requests will -+ * fail with the same error code without being send to the filesystem -+ * process. -+ * -+ * Valid replies: -+ * fuse_reply_bmap -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param blocksize unit of block index -+ * @param idx block index within file -+ */ -+ void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize, -+ uint64_t idx); -+ -+ /** -+ * Ioctl -+ * -+ * Note: For unrestricted ioctls (not allowed for FUSE -+ * servers), data in and out areas can be discovered by giving -+ * iovs and setting FUSE_IOCTL_RETRY in *flags*. For -+ * restricted ioctls, kernel prepares in/out data area -+ * according to the information encoded in cmd. -+ * -+ * Valid replies: -+ * fuse_reply_ioctl_retry -+ * fuse_reply_ioctl -+ * fuse_reply_ioctl_iov -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param cmd ioctl command -+ * @param arg ioctl argument -+ * @param fi file information -+ * @param flags for FUSE_IOCTL_* flags -+ * @param in_buf data fetched from the caller -+ * @param in_bufsz number of fetched bytes -+ * @param out_bufsz maximum size of output data -+ * -+ * Note : the unsigned long request submitted by the application -+ * is truncated to 32 bits. -+ */ -+ void (*ioctl) (fuse_req_t req, fuse_ino_t ino, unsigned int cmd, -+ void *arg, struct fuse_file_info *fi, unsigned flags, -+ const void *in_buf, size_t in_bufsz, size_t out_bufsz); -+ -+ /** -+ * Poll for IO readiness -+ * -+ * Note: If ph is non-NULL, the client should notify -+ * when IO readiness events occur by calling -+ * fuse_lowlevel_notify_poll() with the specified ph. -+ * -+ * Regardless of the number of times poll with a non-NULL ph -+ * is received, single notification is enough to clear all. -+ * Notifying more times incurs overhead but doesn't harm -+ * correctness. -+ * -+ * The callee is responsible for destroying ph with -+ * fuse_pollhandle_destroy() when no longer in use. -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as success (with a kernel-defined default poll-mask) and -+ * future calls to pull() will succeed the same way without being send -+ * to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_poll -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ * @param ph poll handle to be used for notification -+ */ -+ void (*poll) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, -+ struct fuse_pollhandle *ph); -+ -+ /** -+ * Write data made available in a buffer -+ * -+ * This is a more generic version of the ->write() method. If -+ * FUSE_CAP_SPLICE_READ is set in fuse_conn_info.want and the -+ * kernel supports splicing from the fuse device, then the -+ * data will be made available in pipe for supporting zero -+ * copy data transfer. -+ * -+ * buf->count is guaranteed to be one (and thus buf->idx is -+ * always zero). The write_buf handler must ensure that -+ * bufv->off is correctly updated (reflecting the number of -+ * bytes read from bufv->buf[0]). -+ * -+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -+ * expected to reset the setuid and setgid bits. -+ * -+ * Valid replies: -+ * fuse_reply_write -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param bufv buffer containing the data -+ * @param off offset to write to -+ * @param fi file information -+ */ -+ void (*write_buf) (fuse_req_t req, fuse_ino_t ino, -+ struct fuse_bufvec *bufv, off_t off, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Callback function for the retrieve request -+ * -+ * Valid replies: -+ * fuse_reply_none -+ * -+ * @param req request handle -+ * @param cookie user data supplied to fuse_lowlevel_notify_retrieve() -+ * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve() -+ * @param offset the offset supplied to fuse_lowlevel_notify_retrieve() -+ * @param bufv the buffer containing the returned data -+ */ -+ void (*retrieve_reply) (fuse_req_t req, void *cookie, fuse_ino_t ino, -+ off_t offset, struct fuse_bufvec *bufv); -+ -+ /** -+ * Forget about multiple inodes -+ * -+ * See description of the forget function for more -+ * information. -+ * -+ * Valid replies: -+ * fuse_reply_none -+ * -+ * @param req request handle -+ */ -+ void (*forget_multi) (fuse_req_t req, size_t count, -+ struct fuse_forget_data *forgets); -+ -+ /** -+ * Acquire, modify or release a BSD file lock -+ * -+ * Note: if the locking methods are not implemented, the kernel -+ * will still allow file locking to work locally. Hence these are -+ * only interesting for network filesystems and similar. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ * @param op the locking operation, see flock(2) -+ */ -+ void (*flock) (fuse_req_t req, fuse_ino_t ino, -+ struct fuse_file_info *fi, int op); -+ -+ /** -+ * Allocate requested space. If this function returns success then -+ * subsequent writes to the specified range shall not fail due to the lack -+ * of free space on the file system storage media. -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -+ * future fallocate() requests will fail with EOPNOTSUPP without being -+ * send to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param offset starting point for allocated region -+ * @param length size of allocated region -+ * @param mode determines the operation to be performed on the given range, -+ * see fallocate(2) -+ */ -+ void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode, -+ off_t offset, off_t length, struct fuse_file_info *fi); -+ -+ /** -+ * Read directory with attributes -+ * -+ * Send a buffer filled using fuse_add_direntry_plus(), with size not -+ * exceeding the requested size. Send an empty buffer on end of -+ * stream. -+ * -+ * fi->fh will contain the value set by the opendir method, or -+ * will be undefined if the opendir method didn't set any value. -+ * -+ * In contrast to readdir() (which does not affect the lookup counts), -+ * the lookup count of every entry returned by readdirplus(), except "." -+ * and "..", is incremented by one. -+ * -+ * Valid replies: -+ * fuse_reply_buf -+ * fuse_reply_data -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param size maximum number of bytes to send -+ * @param off offset to continue reading the directory stream -+ * @param fi file information -+ */ -+ void (*readdirplus) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Copy a range of data from one file to another -+ * -+ * Performs an optimized copy between two file descriptors without the -+ * additional cost of transferring data through the FUSE kernel module -+ * to user space (glibc) and then back into the FUSE filesystem again. -+ * -+ * In case this method is not implemented, glibc falls back to reading -+ * data from the source and writing to the destination. Effectively -+ * doing an inefficient copy of the data. -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -+ * future copy_file_range() requests will fail with EOPNOTSUPP without -+ * being send to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_write -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino_in the inode number or the source file -+ * @param off_in starting point from were the data should be read -+ * @param fi_in file information of the source file -+ * @param ino_out the inode number or the destination file -+ * @param off_out starting point where the data should be written -+ * @param fi_out file information of the destination file -+ * @param len maximum size of the data to copy -+ * @param flags passed along with the copy_file_range() syscall -+ */ -+ void (*copy_file_range) (fuse_req_t req, fuse_ino_t ino_in, -+ off_t off_in, struct fuse_file_info *fi_in, -+ fuse_ino_t ino_out, off_t off_out, -+ struct fuse_file_info *fi_out, size_t len, -+ int flags); -+ -+ /** -+ * Find next data or hole after the specified offset -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure, i.e. all future lseek() requests will -+ * fail with the same error code without being send to the filesystem -+ * process. -+ * -+ * Valid replies: -+ * fuse_reply_lseek -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param off offset to start search from -+ * @param whence either SEEK_DATA or SEEK_HOLE -+ * @param fi file information -+ */ -+ void (*lseek) (fuse_req_t req, fuse_ino_t ino, off_t off, int whence, -+ struct fuse_file_info *fi); -+}; -+ -+/** -+ * Reply with an error code or success. -+ * -+ * Possible requests: -+ * all except forget -+ * -+ * Whereever possible, error codes should be chosen from the list of -+ * documented error conditions in the corresponding system calls -+ * manpage. -+ * -+ * An error code of ENOSYS is sometimes treated specially. This is -+ * indicated in the documentation of the affected handler functions. -+ * -+ * The following requests may be answered with a zero error code: -+ * unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr, -+ * removexattr, setlk. -+ * -+ * @param req request handle -+ * @param err the positive error value, or zero for success -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_err(fuse_req_t req, int err); -+ -+/** -+ * Don't send reply -+ * -+ * Possible requests: -+ * forget -+ * forget_multi -+ * retrieve_reply -+ * -+ * @param req request handle -+ */ -+void fuse_reply_none(fuse_req_t req); -+ -+/** -+ * Reply with a directory entry -+ * -+ * Possible requests: -+ * lookup, mknod, mkdir, symlink, link -+ * -+ * Side effects: -+ * increments the lookup count on success -+ * -+ * @param req request handle -+ * @param e the entry parameters -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e); -+ -+/** -+ * Reply with a directory entry and open parameters -+ * -+ * currently the following members of 'fi' are used: -+ * fh, direct_io, keep_cache -+ * -+ * Possible requests: -+ * create -+ * -+ * Side effects: -+ * increments the lookup count on success -+ * -+ * @param req request handle -+ * @param e the entry parameters -+ * @param fi file information -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, -+ const struct fuse_file_info *fi); -+ -+/** -+ * Reply with attributes -+ * -+ * Possible requests: -+ * getattr, setattr -+ * -+ * @param req request handle -+ * @param attr the attributes -+ * @param attr_timeout validity timeout (in seconds) for the attributes -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_attr(fuse_req_t req, const struct stat *attr, -+ double attr_timeout); -+ -+/** -+ * Reply with the contents of a symbolic link -+ * -+ * Possible requests: -+ * readlink -+ * -+ * @param req request handle -+ * @param link symbolic link contents -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_readlink(fuse_req_t req, const char *link); -+ -+/** -+ * Reply with open parameters -+ * -+ * currently the following members of 'fi' are used: -+ * fh, direct_io, keep_cache -+ * -+ * Possible requests: -+ * open, opendir -+ * -+ * @param req request handle -+ * @param fi file information -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi); -+ -+/** -+ * Reply with number of bytes written -+ * -+ * Possible requests: -+ * write -+ * -+ * @param req request handle -+ * @param count the number of bytes written -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_write(fuse_req_t req, size_t count); -+ -+/** -+ * Reply with data -+ * -+ * Possible requests: -+ * read, readdir, getxattr, listxattr -+ * -+ * @param req request handle -+ * @param buf buffer containing data -+ * @param size the size of data in bytes -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size); -+ -+/** -+ * Reply with data copied/moved from buffer(s) -+ * -+ * Zero copy data transfer ("splicing") will be used under -+ * the following circumstances: -+ * -+ * 1. FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.want, and -+ * 2. the kernel supports splicing from the fuse device -+ * (FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.capable), and -+ * 3. *flags* does not contain FUSE_BUF_NO_SPLICE -+ * 4. The amount of data that is provided in file-descriptor backed -+ * buffers (i.e., buffers for which bufv[n].flags == FUSE_BUF_FD) -+ * is at least twice the page size. -+ * -+ * In order for SPLICE_F_MOVE to be used, the following additional -+ * conditions have to be fulfilled: -+ * -+ * 1. FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.want, and -+ * 2. the kernel supports it (i.e, FUSE_CAP_SPLICE_MOVE is set in -+ fuse_conn_info.capable), and -+ * 3. *flags* contains FUSE_BUF_SPLICE_MOVE -+ * -+ * Note that, if splice is used, the data is actually spliced twice: -+ * once into a temporary pipe (to prepend header data), and then again -+ * into the kernel. If some of the provided buffers are memory-backed, -+ * the data in them is copied in step one and spliced in step two. -+ * -+ * The FUSE_BUF_SPLICE_FORCE_SPLICE and FUSE_BUF_SPLICE_NONBLOCK flags -+ * are silently ignored. -+ * -+ * Possible requests: -+ * read, readdir, getxattr, listxattr -+ * -+ * Side effects: -+ * when used to return data from a readdirplus() (but not readdir()) -+ * call, increments the lookup count of each returned entry by one -+ * on success. -+ * -+ * @param req request handle -+ * @param bufv buffer vector -+ * @param flags flags controlling the copy -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, -+ enum fuse_buf_copy_flags flags); -+ -+/** -+ * Reply with data vector -+ * -+ * Possible requests: -+ * read, readdir, getxattr, listxattr -+ * -+ * @param req request handle -+ * @param iov the vector containing the data -+ * @param count the size of vector -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count); -+ -+/** -+ * Reply with filesystem statistics -+ * -+ * Possible requests: -+ * statfs -+ * -+ * @param req request handle -+ * @param stbuf filesystem statistics -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf); -+ -+/** -+ * Reply with needed buffer size -+ * -+ * Possible requests: -+ * getxattr, listxattr -+ * -+ * @param req request handle -+ * @param count the buffer size needed in bytes -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_xattr(fuse_req_t req, size_t count); -+ -+/** -+ * Reply with file lock information -+ * -+ * Possible requests: -+ * getlk -+ * -+ * @param req request handle -+ * @param lock the lock information -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_lock(fuse_req_t req, const struct flock *lock); -+ -+/** -+ * Reply with block index -+ * -+ * Possible requests: -+ * bmap -+ * -+ * @param req request handle -+ * @param idx block index within device -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_bmap(fuse_req_t req, uint64_t idx); -+ -+/* ----------------------------------------------------------- * -+ * Filling a buffer in readdir * -+ * ----------------------------------------------------------- */ -+ -+/** -+ * Add a directory entry to the buffer -+ * -+ * Buffer needs to be large enough to hold the entry. If it's not, -+ * then the entry is not filled in but the size of the entry is still -+ * returned. The caller can check this by comparing the bufsize -+ * parameter with the returned entry size. If the entry size is -+ * larger than the buffer size, the operation failed. -+ * -+ * From the 'stbuf' argument the st_ino field and bits 12-15 of the -+ * st_mode field are used. The other fields are ignored. -+ * -+ * *off* should be any non-zero value that the filesystem can use to -+ * identify the current point in the directory stream. It does not -+ * need to be the actual physical position. A value of zero is -+ * reserved to mean "from the beginning", and should therefore never -+ * be used (the first call to fuse_add_direntry should be passed the -+ * offset of the second directory entry). -+ * -+ * @param req request handle -+ * @param buf the point where the new entry will be added to the buffer -+ * @param bufsize remaining size of the buffer -+ * @param name the name of the entry -+ * @param stbuf the file attributes -+ * @param off the offset of the next entry -+ * @return the space needed for the entry -+ */ -+size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, -+ const char *name, const struct stat *stbuf, -+ off_t off); -+ -+/** -+ * Add a directory entry to the buffer with the attributes -+ * -+ * See documentation of `fuse_add_direntry()` for more details. -+ * -+ * @param req request handle -+ * @param buf the point where the new entry will be added to the buffer -+ * @param bufsize remaining size of the buffer -+ * @param name the name of the entry -+ * @param e the directory entry -+ * @param off the offset of the next entry -+ * @return the space needed for the entry -+ */ -+size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, -+ const char *name, -+ const struct fuse_entry_param *e, off_t off); -+ -+/** -+ * Reply to ask for data fetch and output buffer preparation. ioctl -+ * will be retried with the specified input data fetched and output -+ * buffer prepared. -+ * -+ * Possible requests: -+ * ioctl -+ * -+ * @param req request handle -+ * @param in_iov iovec specifying data to fetch from the caller -+ * @param in_count number of entries in in_iov -+ * @param out_iov iovec specifying addresses to write output to -+ * @param out_count number of entries in out_iov -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_ioctl_retry(fuse_req_t req, -+ const struct iovec *in_iov, size_t in_count, -+ const struct iovec *out_iov, size_t out_count); -+ -+/** -+ * Reply to finish ioctl -+ * -+ * Possible requests: -+ * ioctl -+ * -+ * @param req request handle -+ * @param result result to be passed to the caller -+ * @param buf buffer containing output data -+ * @param size length of output data -+ */ -+int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size); -+ -+/** -+ * Reply to finish ioctl with iov buffer -+ * -+ * Possible requests: -+ * ioctl -+ * -+ * @param req request handle -+ * @param result result to be passed to the caller -+ * @param iov the vector containing the data -+ * @param count the size of vector -+ */ -+int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, -+ int count); -+ -+/** -+ * Reply with poll result event mask -+ * -+ * @param req request handle -+ * @param revents poll result event mask -+ */ -+int fuse_reply_poll(fuse_req_t req, unsigned revents); -+ -+/** -+ * Reply with offset -+ * -+ * Possible requests: -+ * lseek -+ * -+ * @param req request handle -+ * @param off offset of next data or hole -+ * @return zero for success, -errno for failure to send reply -+ */ -+int fuse_reply_lseek(fuse_req_t req, off_t off); -+ -+/* ----------------------------------------------------------- * -+ * Notification * -+ * ----------------------------------------------------------- */ -+ -+/** -+ * Notify IO readiness event -+ * -+ * For more information, please read comment for poll operation. -+ * -+ * @param ph poll handle to notify IO readiness event for -+ */ -+int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph); -+ -+/** -+ * Notify to invalidate cache for an inode. -+ * -+ * Added in FUSE protocol version 7.12. If the kernel does not support -+ * this (or a newer) version, the function will return -ENOSYS and do -+ * nothing. -+ * -+ * If the filesystem has writeback caching enabled, invalidating an -+ * inode will first trigger a writeback of all dirty pages. The call -+ * will block until all writeback requests have completed and the -+ * inode has been invalidated. It will, however, not wait for -+ * completion of pending writeback requests that have been issued -+ * before. -+ * -+ * If there are no dirty pages, this function will never block. -+ * -+ * @param se the session object -+ * @param ino the inode number -+ * @param off the offset in the inode where to start invalidating -+ * or negative to invalidate attributes only -+ * @param len the amount of cache to invalidate or 0 for all -+ * @return zero for success, -errno for failure -+ */ -+int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, -+ off_t off, off_t len); -+ -+/** -+ * Notify to invalidate parent attributes and the dentry matching -+ * parent/name -+ * -+ * To avoid a deadlock this function must not be called in the -+ * execution path of a related filesytem operation or within any code -+ * that could hold a lock that could be needed to execute such an -+ * operation. As of kernel 4.18, a "related operation" is a lookup(), -+ * symlink(), mknod(), mkdir(), unlink(), rename(), link() or create() -+ * request for the parent, and a setattr(), unlink(), rmdir(), -+ * rename(), setxattr(), removexattr(), readdir() or readdirplus() -+ * request for the inode itself. -+ * -+ * When called correctly, this function will never block. -+ * -+ * Added in FUSE protocol version 7.12. If the kernel does not support -+ * this (or a newer) version, the function will return -ENOSYS and do -+ * nothing. -+ * -+ * @param se the session object -+ * @param parent inode number -+ * @param name file name -+ * @param namelen strlen() of file name -+ * @return zero for success, -errno for failure -+ */ -+int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, -+ const char *name, size_t namelen); -+ -+/** -+ * This function behaves like fuse_lowlevel_notify_inval_entry() with -+ * the following additional effect (at least as of Linux kernel 4.8): -+ * -+ * If the provided *child* inode matches the inode that is currently -+ * associated with the cached dentry, and if there are any inotify -+ * watches registered for the dentry, then the watchers are informed -+ * that the dentry has been deleted. -+ * -+ * To avoid a deadlock this function must not be called while -+ * executing a related filesytem operation or while holding a lock -+ * that could be needed to execute such an operation (see the -+ * description of fuse_lowlevel_notify_inval_entry() for more -+ * details). -+ * -+ * When called correctly, this function will never block. -+ * -+ * Added in FUSE protocol version 7.18. If the kernel does not support -+ * this (or a newer) version, the function will return -ENOSYS and do -+ * nothing. -+ * -+ * @param se the session object -+ * @param parent inode number -+ * @param child inode number -+ * @param name file name -+ * @param namelen strlen() of file name -+ * @return zero for success, -errno for failure -+ */ -+int fuse_lowlevel_notify_delete(struct fuse_session *se, -+ fuse_ino_t parent, fuse_ino_t child, -+ const char *name, size_t namelen); -+ -+/** -+ * Store data to the kernel buffers -+ * -+ * Synchronously store data in the kernel buffers belonging to the -+ * given inode. The stored data is marked up-to-date (no read will be -+ * performed against it, unless it's invalidated or evicted from the -+ * cache). -+ * -+ * If the stored data overflows the current file size, then the size -+ * is extended, similarly to a write(2) on the filesystem. -+ * -+ * If this function returns an error, then the store wasn't fully -+ * completed, but it may have been partially completed. -+ * -+ * Added in FUSE protocol version 7.15. If the kernel does not support -+ * this (or a newer) version, the function will return -ENOSYS and do -+ * nothing. -+ * -+ * @param se the session object -+ * @param ino the inode number -+ * @param offset the starting offset into the file to store to -+ * @param bufv buffer vector -+ * @param flags flags controlling the copy -+ * @return zero for success, -errno for failure -+ */ -+int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, -+ off_t offset, struct fuse_bufvec *bufv, -+ enum fuse_buf_copy_flags flags); -+/** -+ * Retrieve data from the kernel buffers -+ * -+ * Retrieve data in the kernel buffers belonging to the given inode. -+ * If successful then the retrieve_reply() method will be called with -+ * the returned data. -+ * -+ * Only present pages are returned in the retrieve reply. Retrieving -+ * stops when it finds a non-present page and only data prior to that -+ * is returned. -+ * -+ * If this function returns an error, then the retrieve will not be -+ * completed and no reply will be sent. -+ * -+ * This function doesn't change the dirty state of pages in the kernel -+ * buffer. For dirty pages the write() method will be called -+ * regardless of having been retrieved previously. -+ * -+ * Added in FUSE protocol version 7.15. If the kernel does not support -+ * this (or a newer) version, the function will return -ENOSYS and do -+ * nothing. -+ * -+ * @param se the session object -+ * @param ino the inode number -+ * @param size the number of bytes to retrieve -+ * @param offset the starting offset into the file to retrieve from -+ * @param cookie user data to supply to the reply callback -+ * @return zero for success, -errno for failure -+ */ -+int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, -+ size_t size, off_t offset, void *cookie); -+ -+ -+/* ----------------------------------------------------------- * -+ * Utility functions * -+ * ----------------------------------------------------------- */ -+ -+/** -+ * Get the userdata from the request -+ * -+ * @param req request handle -+ * @return the user data passed to fuse_session_new() -+ */ -+void *fuse_req_userdata(fuse_req_t req); -+ -+/** -+ * Get the context from the request -+ * -+ * The pointer returned by this function will only be valid for the -+ * request's lifetime -+ * -+ * @param req request handle -+ * @return the context structure -+ */ -+const struct fuse_ctx *fuse_req_ctx(fuse_req_t req); -+ -+/** -+ * Get the current supplementary group IDs for the specified request -+ * -+ * Similar to the getgroups(2) system call, except the return value is -+ * always the total number of group IDs, even if it is larger than the -+ * specified size. -+ * -+ * The current fuse kernel module in linux (as of 2.6.30) doesn't pass -+ * the group list to userspace, hence this function needs to parse -+ * "/proc/$TID/task/$TID/status" to get the group IDs. -+ * -+ * This feature may not be supported on all operating systems. In -+ * such a case this function will return -ENOSYS. -+ * -+ * @param req request handle -+ * @param size size of given array -+ * @param list array of group IDs to be filled in -+ * @return the total number of supplementary group IDs or -errno on failure -+ */ -+int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]); -+ -+/** -+ * Callback function for an interrupt -+ * -+ * @param req interrupted request -+ * @param data user data -+ */ -+typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data); -+ -+/** -+ * Register/unregister callback for an interrupt -+ * -+ * If an interrupt has already happened, then the callback function is -+ * called from within this function, hence it's not possible for -+ * interrupts to be lost. -+ * -+ * @param req request handle -+ * @param func the callback function or NULL for unregister -+ * @param data user data passed to the callback function -+ */ -+void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, -+ void *data); -+ -+/** -+ * Check if a request has already been interrupted -+ * -+ * @param req request handle -+ * @return 1 if the request has been interrupted, 0 otherwise -+ */ -+int fuse_req_interrupted(fuse_req_t req); -+ -+ -+/* ----------------------------------------------------------- * -+ * Inquiry functions * -+ * ----------------------------------------------------------- */ -+ -+/** -+ * Print low-level version information to stdout. -+ */ -+void fuse_lowlevel_version(void); -+ -+/** -+ * Print available low-level options to stdout. This is not an -+ * exhaustive list, but includes only those options that may be of -+ * interest to an end-user of a file system. -+ */ -+void fuse_lowlevel_help(void); -+ -+/** -+ * Print available options for `fuse_parse_cmdline()`. -+ */ -+void fuse_cmdline_help(void); -+ -+/* ----------------------------------------------------------- * -+ * Filesystem setup & teardown * -+ * ----------------------------------------------------------- */ -+ -+struct fuse_cmdline_opts { -+ int singlethread; -+ int foreground; -+ int debug; -+ int nodefault_subtype; -+ char *mountpoint; -+ int show_version; -+ int show_help; -+ int clone_fd; -+ unsigned int max_idle_threads; -+}; -+ -+/** -+ * Utility function to parse common options for simple file systems -+ * using the low-level API. A help text that describes the available -+ * options can be printed with `fuse_cmdline_help`. A single -+ * non-option argument is treated as the mountpoint. Multiple -+ * non-option arguments will result in an error. -+ * -+ * If neither -o subtype= or -o fsname= options are given, a new -+ * subtype option will be added and set to the basename of the program -+ * (the fsname will remain unset, and then defaults to "fuse"). -+ * -+ * Known options will be removed from *args*, unknown options will -+ * remain. -+ * -+ * @param args argument vector (input+output) -+ * @param opts output argument for parsed options -+ * @return 0 on success, -1 on failure -+ */ -+int fuse_parse_cmdline(struct fuse_args *args, -+ struct fuse_cmdline_opts *opts); -+ -+/** -+ * Create a low level session. -+ * -+ * Returns a session structure suitable for passing to -+ * fuse_session_mount() and fuse_session_loop(). -+ * -+ * This function accepts most file-system independent mount options -+ * (like context, nodev, ro - see mount(8)), as well as the general -+ * fuse mount options listed in mount.fuse(8) (e.g. -o allow_root and -+ * -o default_permissions, but not ``-o use_ino``). Instead of `-o -+ * debug`, debugging may also enabled with `-d` or `--debug`. -+ * -+ * If not all options are known, an error message is written to stderr -+ * and the function returns NULL. -+ * -+ * Option parsing skips argv[0], which is assumed to contain the -+ * program name. To prevent accidentally passing an option in -+ * argv[0], this element must always be present (even if no options -+ * are specified). It may be set to the empty string ('\0') if no -+ * reasonable value can be provided. -+ * -+ * @param args argument vector -+ * @param op the (low-level) filesystem operations -+ * @param op_size sizeof(struct fuse_lowlevel_ops) -+ * @param userdata user data -+ * -+ * @return the fuse session on success, NULL on failure -+ **/ -+struct fuse_session *fuse_session_new(struct fuse_args *args, -+ const struct fuse_lowlevel_ops *op, -+ size_t op_size, void *userdata); -+ -+/** -+ * Mount a FUSE file system. -+ * -+ * @param mountpoint the mount point path -+ * @param se session object -+ * -+ * @return 0 on success, -1 on failure. -+ **/ -+int fuse_session_mount(struct fuse_session *se, const char *mountpoint); -+ -+/** -+ * Enter a single threaded, blocking event loop. -+ * -+ * When the event loop terminates because the connection to the FUSE -+ * kernel module has been closed, this function returns zero. This -+ * happens when the filesystem is unmounted regularly (by the -+ * filesystem owner or root running the umount(8) or fusermount(1) -+ * command), or if connection is explicitly severed by writing ``1`` -+ * to the``abort`` file in ``/sys/fs/fuse/connections/NNN``. The only -+ * way to distinguish between these two conditions is to check if the -+ * filesystem is still mounted after the session loop returns. -+ * -+ * When some error occurs during request processing, the function -+ * returns a negated errno(3) value. -+ * -+ * If the loop has been terminated because of a signal handler -+ * installed by fuse_set_signal_handlers(), this function returns the -+ * (positive) signal value that triggered the exit. -+ * -+ * @param se the session -+ * @return 0, -errno, or a signal value -+ */ -+int fuse_session_loop(struct fuse_session *se); -+ -+/** -+ * Enter a multi-threaded event loop. -+ * -+ * For a description of the return value and the conditions when the -+ * event loop exits, refer to the documentation of -+ * fuse_session_loop(). -+ * -+ * @param se the session -+ * @param config session loop configuration -+ * @return see fuse_session_loop() -+ */ -+#if FUSE_USE_VERSION < 32 -+int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd); -+#define fuse_session_loop_mt(se, clone_fd) fuse_session_loop_mt_31(se, clone_fd) -+#else -+int fuse_session_loop_mt(struct fuse_session *se, struct fuse_loop_config *config); -+#endif -+ -+/** -+ * Flag a session as terminated. -+ * -+ * This function is invoked by the POSIX signal handlers, when -+ * registered using fuse_set_signal_handlers(). It will cause any -+ * running event loops to terminate on the next opportunity. -+ * -+ * @param se the session -+ */ -+void fuse_session_exit(struct fuse_session *se); -+ -+/** -+ * Reset the terminated flag of a session -+ * -+ * @param se the session -+ */ -+void fuse_session_reset(struct fuse_session *se); -+ -+/** -+ * Query the terminated flag of a session -+ * -+ * @param se the session -+ * @return 1 if exited, 0 if not exited -+ */ -+int fuse_session_exited(struct fuse_session *se); -+ -+/** -+ * Ensure that file system is unmounted. -+ * -+ * In regular operation, the file system is typically unmounted by the -+ * user calling umount(8) or fusermount(1), which then terminates the -+ * FUSE session loop. However, the session loop may also terminate as -+ * a result of an explicit call to fuse_session_exit() (e.g. by a -+ * signal handler installed by fuse_set_signal_handler()). In this -+ * case the filesystem remains mounted, but any attempt to access it -+ * will block (while the filesystem process is still running) or give -+ * an ESHUTDOWN error (after the filesystem process has terminated). -+ * -+ * If the communication channel with the FUSE kernel module is still -+ * open (i.e., if the session loop was terminated by an explicit call -+ * to fuse_session_exit()), this function will close it and unmount -+ * the filesystem. If the communication channel has been closed by the -+ * kernel, this method will do (almost) nothing. -+ * -+ * NOTE: The above semantics mean that if the connection to the kernel -+ * is terminated via the ``/sys/fs/fuse/connections/NNN/abort`` file, -+ * this method will *not* unmount the filesystem. -+ * -+ * @param se the session -+ */ -+void fuse_session_unmount(struct fuse_session *se); -+ -+/** -+ * Destroy a session -+ * -+ * @param se the session -+ */ -+void fuse_session_destroy(struct fuse_session *se); -+ -+/* ----------------------------------------------------------- * -+ * Custom event loop support * -+ * ----------------------------------------------------------- */ -+ -+/** -+ * Return file descriptor for communication with kernel. -+ * -+ * The file selector can be used to integrate FUSE with a custom event -+ * loop. Whenever data is available for reading on the provided fd, -+ * the event loop should call `fuse_session_receive_buf` followed by -+ * `fuse_session_process_buf` to process the request. -+ * -+ * The returned file descriptor is valid until `fuse_session_unmount` -+ * is called. -+ * -+ * @param se the session -+ * @return a file descriptor -+ */ -+int fuse_session_fd(struct fuse_session *se); -+ -+/** -+ * Process a raw request supplied in a generic buffer -+ * -+ * The fuse_buf may contain a memory buffer or a pipe file descriptor. -+ * -+ * @param se the session -+ * @param buf the fuse_buf containing the request -+ */ -+void fuse_session_process_buf(struct fuse_session *se, -+ const struct fuse_buf *buf); -+ -+/** -+ * Read a raw request from the kernel into the supplied buffer. -+ * -+ * Depending on file system options, system capabilities, and request -+ * size the request is either read into a memory buffer or spliced -+ * into a temporary pipe. -+ * -+ * @param se the session -+ * @param buf the fuse_buf to store the request in -+ * @return the actual size of the raw request, or -errno on error -+ */ -+int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf); -+ -+#ifdef __cplusplus -+} -+#endif -+ -+#endif /* FUSE_LOWLEVEL_H_ */ -diff --git a/tools/virtiofsd/fuse_misc.h b/tools/virtiofsd/fuse_misc.h -new file mode 100644 -index 0000000000..2f6663ed7d ---- /dev/null -+++ b/tools/virtiofsd/fuse_misc.h -@@ -0,0 +1,59 @@ -+/* -+ FUSE: Filesystem in Userspace -+ Copyright (C) 2001-2007 Miklos Szeredi -+ -+ This program can be distributed under the terms of the GNU LGPLv2. -+ See the file COPYING.LIB -+*/ -+ -+#include -+ -+/* -+ Versioned symbols cannot be used in some cases because it -+ - confuse the dynamic linker in uClibc -+ - not supported on MacOSX (in MachO binary format) -+*/ -+#if (!defined(__UCLIBC__) && !defined(__APPLE__)) -+#define FUSE_SYMVER(x) __asm__(x) -+#else -+#define FUSE_SYMVER(x) -+#endif -+ -+#ifndef USE_UCLIBC -+#define fuse_mutex_init(mut) pthread_mutex_init(mut, NULL) -+#else -+/* Is this hack still needed? */ -+static inline void fuse_mutex_init(pthread_mutex_t *mut) -+{ -+ pthread_mutexattr_t attr; -+ pthread_mutexattr_init(&attr); -+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); -+ pthread_mutex_init(mut, &attr); -+ pthread_mutexattr_destroy(&attr); -+} -+#endif -+ -+#ifdef HAVE_STRUCT_STAT_ST_ATIM -+/* Linux */ -+#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atim.tv_nsec) -+#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctim.tv_nsec) -+#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtim.tv_nsec) -+#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atim.tv_nsec = (val) -+#define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctim.tv_nsec = (val) -+#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtim.tv_nsec = (val) -+#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC) -+/* FreeBSD */ -+#define ST_ATIM_NSEC(stbuf) ((stbuf)->st_atimespec.tv_nsec) -+#define ST_CTIM_NSEC(stbuf) ((stbuf)->st_ctimespec.tv_nsec) -+#define ST_MTIM_NSEC(stbuf) ((stbuf)->st_mtimespec.tv_nsec) -+#define ST_ATIM_NSEC_SET(stbuf, val) (stbuf)->st_atimespec.tv_nsec = (val) -+#define ST_CTIM_NSEC_SET(stbuf, val) (stbuf)->st_ctimespec.tv_nsec = (val) -+#define ST_MTIM_NSEC_SET(stbuf, val) (stbuf)->st_mtimespec.tv_nsec = (val) -+#else -+#define ST_ATIM_NSEC(stbuf) 0 -+#define ST_CTIM_NSEC(stbuf) 0 -+#define ST_MTIM_NSEC(stbuf) 0 -+#define ST_ATIM_NSEC_SET(stbuf, val) do { } while (0) -+#define ST_CTIM_NSEC_SET(stbuf, val) do { } while (0) -+#define ST_MTIM_NSEC_SET(stbuf, val) do { } while (0) -+#endif -diff --git a/tools/virtiofsd/fuse_opt.h b/tools/virtiofsd/fuse_opt.h -new file mode 100644 -index 0000000000..d8573e74fd ---- /dev/null -+++ b/tools/virtiofsd/fuse_opt.h -@@ -0,0 +1,271 @@ -+/* -+ FUSE: Filesystem in Userspace -+ Copyright (C) 2001-2007 Miklos Szeredi -+ -+ This program can be distributed under the terms of the GNU LGPLv2. -+ See the file COPYING.LIB. -+*/ -+ -+#ifndef FUSE_OPT_H_ -+#define FUSE_OPT_H_ -+ -+/** @file -+ * -+ * This file defines the option parsing interface of FUSE -+ */ -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+/** -+ * Option description -+ * -+ * This structure describes a single option, and action associated -+ * with it, in case it matches. -+ * -+ * More than one such match may occur, in which case the action for -+ * each match is executed. -+ * -+ * There are three possible actions in case of a match: -+ * -+ * i) An integer (int or unsigned) variable determined by 'offset' is -+ * set to 'value' -+ * -+ * ii) The processing function is called, with 'value' as the key -+ * -+ * iii) An integer (any) or string (char *) variable determined by -+ * 'offset' is set to the value of an option parameter -+ * -+ * 'offset' should normally be either set to -+ * -+ * - 'offsetof(struct foo, member)' actions i) and iii) -+ * -+ * - -1 action ii) -+ * -+ * The 'offsetof()' macro is defined in the header. -+ * -+ * The template determines which options match, and also have an -+ * effect on the action. Normally the action is either i) or ii), but -+ * if a format is present in the template, then action iii) is -+ * performed. -+ * -+ * The types of templates are: -+ * -+ * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only -+ * themselves. Invalid values are "--" and anything beginning -+ * with "-o" -+ * -+ * 2) "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or -+ * the relevant option in a comma separated option list -+ * -+ * 3) "bar=", "--foo=", etc. These are variations of 1) and 2) -+ * which have a parameter -+ * -+ * 4) "bar=%s", "--foo=%lu", etc. Same matching as above but perform -+ * action iii). -+ * -+ * 5) "-x ", etc. Matches either "-xparam" or "-x param" as -+ * two separate arguments -+ * -+ * 6) "-x %s", etc. Combination of 4) and 5) -+ * -+ * If the format is "%s", memory is allocated for the string unlike with -+ * scanf(). The previous value (if non-NULL) stored at the this location is -+ * freed. -+ */ -+struct fuse_opt { -+ /** Matching template and optional parameter formatting */ -+ const char *templ; -+ -+ /** -+ * Offset of variable within 'data' parameter of fuse_opt_parse() -+ * or -1 -+ */ -+ unsigned long offset; -+ -+ /** -+ * Value to set the variable to, or to be passed as 'key' to the -+ * processing function. Ignored if template has a format -+ */ -+ int value; -+}; -+ -+/** -+ * Key option. In case of a match, the processing function will be -+ * called with the specified key. -+ */ -+#define FUSE_OPT_KEY(templ, key) { templ, -1U, key } -+ -+/** -+ * Last option. An array of 'struct fuse_opt' must end with a NULL -+ * template value -+ */ -+#define FUSE_OPT_END { NULL, 0, 0 } -+ -+/** -+ * Argument list -+ */ -+struct fuse_args { -+ /** Argument count */ -+ int argc; -+ -+ /** Argument vector. NULL terminated */ -+ char **argv; -+ -+ /** Is 'argv' allocated? */ -+ int allocated; -+}; -+ -+/** -+ * Initializer for 'struct fuse_args' -+ */ -+#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 } -+ -+/** -+ * Key value passed to the processing function if an option did not -+ * match any template -+ */ -+#define FUSE_OPT_KEY_OPT -1 -+ -+/** -+ * Key value passed to the processing function for all non-options -+ * -+ * Non-options are the arguments beginning with a character other than -+ * '-' or all arguments after the special '--' option -+ */ -+#define FUSE_OPT_KEY_NONOPT -2 -+ -+/** -+ * Special key value for options to keep -+ * -+ * Argument is not passed to processing function, but behave as if the -+ * processing function returned 1 -+ */ -+#define FUSE_OPT_KEY_KEEP -3 -+ -+/** -+ * Special key value for options to discard -+ * -+ * Argument is not passed to processing function, but behave as if the -+ * processing function returned zero -+ */ -+#define FUSE_OPT_KEY_DISCARD -4 -+ -+/** -+ * Processing function -+ * -+ * This function is called if -+ * - option did not match any 'struct fuse_opt' -+ * - argument is a non-option -+ * - option did match and offset was set to -1 -+ * -+ * The 'arg' parameter will always contain the whole argument or -+ * option including the parameter if exists. A two-argument option -+ * ("-x foo") is always converted to single argument option of the -+ * form "-xfoo" before this function is called. -+ * -+ * Options of the form '-ofoo' are passed to this function without the -+ * '-o' prefix. -+ * -+ * The return value of this function determines whether this argument -+ * is to be inserted into the output argument vector, or discarded. -+ * -+ * @param data is the user data passed to the fuse_opt_parse() function -+ * @param arg is the whole argument or option -+ * @param key determines why the processing function was called -+ * @param outargs the current output argument list -+ * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept -+ */ -+typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, -+ struct fuse_args *outargs); -+ -+/** -+ * Option parsing function -+ * -+ * If 'args' was returned from a previous call to fuse_opt_parse() or -+ * it was constructed from -+ * -+ * A NULL 'args' is equivalent to an empty argument vector -+ * -+ * A NULL 'opts' is equivalent to an 'opts' array containing a single -+ * end marker -+ * -+ * A NULL 'proc' is equivalent to a processing function always -+ * returning '1' -+ * -+ * @param args is the input and output argument list -+ * @param data is the user data -+ * @param opts is the option description array -+ * @param proc is the processing function -+ * @return -1 on error, 0 on success -+ */ -+int fuse_opt_parse(struct fuse_args *args, void *data, -+ const struct fuse_opt opts[], fuse_opt_proc_t proc); -+ -+/** -+ * Add an option to a comma separated option list -+ * -+ * @param opts is a pointer to an option list, may point to a NULL value -+ * @param opt is the option to add -+ * @return -1 on allocation error, 0 on success -+ */ -+int fuse_opt_add_opt(char **opts, const char *opt); -+ -+/** -+ * Add an option, escaping commas, to a comma separated option list -+ * -+ * @param opts is a pointer to an option list, may point to a NULL value -+ * @param opt is the option to add -+ * @return -1 on allocation error, 0 on success -+ */ -+int fuse_opt_add_opt_escaped(char **opts, const char *opt); -+ -+/** -+ * Add an argument to a NULL terminated argument vector -+ * -+ * @param args is the structure containing the current argument list -+ * @param arg is the new argument to add -+ * @return -1 on allocation error, 0 on success -+ */ -+int fuse_opt_add_arg(struct fuse_args *args, const char *arg); -+ -+/** -+ * Add an argument at the specified position in a NULL terminated -+ * argument vector -+ * -+ * Adds the argument to the N-th position. This is useful for adding -+ * options at the beginning of the array which must not come after the -+ * special '--' option. -+ * -+ * @param args is the structure containing the current argument list -+ * @param pos is the position at which to add the argument -+ * @param arg is the new argument to add -+ * @return -1 on allocation error, 0 on success -+ */ -+int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg); -+ -+/** -+ * Free the contents of argument list -+ * -+ * The structure itself is not freed -+ * -+ * @param args is the structure containing the argument list -+ */ -+void fuse_opt_free_args(struct fuse_args *args); -+ -+ -+/** -+ * Check if an option matches -+ * -+ * @param opts is the option description array -+ * @param opt is the option to match -+ * @return 1 if a match is found, 0 if not -+ */ -+int fuse_opt_match(const struct fuse_opt opts[], const char *opt); -+ -+#ifdef __cplusplus -+} -+#endif -+ -+#endif /* FUSE_OPT_H_ */ -diff --git a/tools/virtiofsd/passthrough_helpers.h b/tools/virtiofsd/passthrough_helpers.h -new file mode 100644 -index 0000000000..6b77c33600 ---- /dev/null -+++ b/tools/virtiofsd/passthrough_helpers.h -@@ -0,0 +1,76 @@ -+/* -+ * FUSE: Filesystem in Userspace -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions and the following disclaimer. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -+ * SUCH DAMAGE -+ */ -+ -+/* -+ * Creates files on the underlying file system in response to a FUSE_MKNOD -+ * operation -+ */ -+static int mknod_wrapper(int dirfd, const char *path, const char *link, -+ int mode, dev_t rdev) -+{ -+ int res; -+ -+ if (S_ISREG(mode)) { -+ res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode); -+ if (res >= 0) -+ res = close(res); -+ } else if (S_ISDIR(mode)) { -+ res = mkdirat(dirfd, path, mode); -+ } else if (S_ISLNK(mode) && link != NULL) { -+ res = symlinkat(link, dirfd, path); -+ } else if (S_ISFIFO(mode)) { -+ res = mkfifoat(dirfd, path, mode); -+#ifdef __FreeBSD__ -+ } else if (S_ISSOCK(mode)) { -+ struct sockaddr_un su; -+ int fd; -+ -+ if (strlen(path) >= sizeof(su.sun_path)) { -+ errno = ENAMETOOLONG; -+ return -1; -+ } -+ fd = socket(AF_UNIX, SOCK_STREAM, 0); -+ if (fd >= 0) { -+ /* -+ * We must bind the socket to the underlying file -+ * system to create the socket file, even though -+ * we'll never listen on this socket. -+ */ -+ su.sun_family = AF_UNIX; -+ strncpy(su.sun_path, path, sizeof(su.sun_path)); -+ res = bindat(dirfd, fd, (struct sockaddr*)&su, -+ sizeof(su)); -+ if (res == 0) -+ close(fd); -+ } else { -+ res = -1; -+ } -+#endif -+ } else { -+ res = mknodat(dirfd, path, mode, rdev); -+ } -+ -+ return res; -+} diff --git a/0011-virtiofsd-Pull-in-kernel-s-fuse.h.patch b/0011-virtiofsd-Pull-in-kernel-s-fuse.h.patch deleted file mode 100644 index fbf4e25..0000000 --- a/0011-virtiofsd-Pull-in-kernel-s-fuse.h.patch +++ /dev/null @@ -1,929 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:00:40 +0000 -Subject: [PATCH] virtiofsd: Pull in kernel's fuse.h -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Update scripts/update-linux-headers.sh to add fuse.h and -use it to pull in fuse.h from the kernel; from v5.5-rc1 - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit a62a9e192bc5f0aa0bc076b51db5a069add87c78) ---- - include/standard-headers/linux/fuse.h | 891 ++++++++++++++++++++++++++ - scripts/update-linux-headers.sh | 1 + - 2 files changed, 892 insertions(+) - create mode 100644 include/standard-headers/linux/fuse.h - -diff --git a/include/standard-headers/linux/fuse.h b/include/standard-headers/linux/fuse.h -new file mode 100644 -index 0000000000..f4df0a40f6 ---- /dev/null -+++ b/include/standard-headers/linux/fuse.h -@@ -0,0 +1,891 @@ -+/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */ -+/* -+ This file defines the kernel interface of FUSE -+ Copyright (C) 2001-2008 Miklos Szeredi -+ -+ This program can be distributed under the terms of the GNU GPL. -+ See the file COPYING. -+ -+ This -- and only this -- header file may also be distributed under -+ the terms of the BSD Licence as follows: -+ -+ Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved. -+ -+ Redistribution and use in source and binary forms, with or without -+ modification, are permitted provided that the following conditions -+ are met: -+ 1. Redistributions of source code must retain the above copyright -+ notice, this list of conditions and the following disclaimer. -+ 2. Redistributions in binary form must reproduce the above copyright -+ notice, this list of conditions and the following disclaimer in the -+ documentation and/or other materials provided with the distribution. -+ -+ THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND -+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -+ ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE -+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -+ SUCH DAMAGE. -+*/ -+ -+/* -+ * This file defines the kernel interface of FUSE -+ * -+ * Protocol changelog: -+ * -+ * 7.1: -+ * - add the following messages: -+ * FUSE_SETATTR, FUSE_SYMLINK, FUSE_MKNOD, FUSE_MKDIR, FUSE_UNLINK, -+ * FUSE_RMDIR, FUSE_RENAME, FUSE_LINK, FUSE_OPEN, FUSE_READ, FUSE_WRITE, -+ * FUSE_RELEASE, FUSE_FSYNC, FUSE_FLUSH, FUSE_SETXATTR, FUSE_GETXATTR, -+ * FUSE_LISTXATTR, FUSE_REMOVEXATTR, FUSE_OPENDIR, FUSE_READDIR, -+ * FUSE_RELEASEDIR -+ * - add padding to messages to accommodate 32-bit servers on 64-bit kernels -+ * -+ * 7.2: -+ * - add FOPEN_DIRECT_IO and FOPEN_KEEP_CACHE flags -+ * - add FUSE_FSYNCDIR message -+ * -+ * 7.3: -+ * - add FUSE_ACCESS message -+ * - add FUSE_CREATE message -+ * - add filehandle to fuse_setattr_in -+ * -+ * 7.4: -+ * - add frsize to fuse_kstatfs -+ * - clean up request size limit checking -+ * -+ * 7.5: -+ * - add flags and max_write to fuse_init_out -+ * -+ * 7.6: -+ * - add max_readahead to fuse_init_in and fuse_init_out -+ * -+ * 7.7: -+ * - add FUSE_INTERRUPT message -+ * - add POSIX file lock support -+ * -+ * 7.8: -+ * - add lock_owner and flags fields to fuse_release_in -+ * - add FUSE_BMAP message -+ * - add FUSE_DESTROY message -+ * -+ * 7.9: -+ * - new fuse_getattr_in input argument of GETATTR -+ * - add lk_flags in fuse_lk_in -+ * - add lock_owner field to fuse_setattr_in, fuse_read_in and fuse_write_in -+ * - add blksize field to fuse_attr -+ * - add file flags field to fuse_read_in and fuse_write_in -+ * - Add ATIME_NOW and MTIME_NOW flags to fuse_setattr_in -+ * -+ * 7.10 -+ * - add nonseekable open flag -+ * -+ * 7.11 -+ * - add IOCTL message -+ * - add unsolicited notification support -+ * - add POLL message and NOTIFY_POLL notification -+ * -+ * 7.12 -+ * - add umask flag to input argument of create, mknod and mkdir -+ * - add notification messages for invalidation of inodes and -+ * directory entries -+ * -+ * 7.13 -+ * - make max number of background requests and congestion threshold -+ * tunables -+ * -+ * 7.14 -+ * - add splice support to fuse device -+ * -+ * 7.15 -+ * - add store notify -+ * - add retrieve notify -+ * -+ * 7.16 -+ * - add BATCH_FORGET request -+ * - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct -+ * fuse_ioctl_iovec' instead of ambiguous 'struct iovec' -+ * - add FUSE_IOCTL_32BIT flag -+ * -+ * 7.17 -+ * - add FUSE_FLOCK_LOCKS and FUSE_RELEASE_FLOCK_UNLOCK -+ * -+ * 7.18 -+ * - add FUSE_IOCTL_DIR flag -+ * - add FUSE_NOTIFY_DELETE -+ * -+ * 7.19 -+ * - add FUSE_FALLOCATE -+ * -+ * 7.20 -+ * - add FUSE_AUTO_INVAL_DATA -+ * -+ * 7.21 -+ * - add FUSE_READDIRPLUS -+ * - send the requested events in POLL request -+ * -+ * 7.22 -+ * - add FUSE_ASYNC_DIO -+ * -+ * 7.23 -+ * - add FUSE_WRITEBACK_CACHE -+ * - add time_gran to fuse_init_out -+ * - add reserved space to fuse_init_out -+ * - add FATTR_CTIME -+ * - add ctime and ctimensec to fuse_setattr_in -+ * - add FUSE_RENAME2 request -+ * - add FUSE_NO_OPEN_SUPPORT flag -+ * -+ * 7.24 -+ * - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support -+ * -+ * 7.25 -+ * - add FUSE_PARALLEL_DIROPS -+ * -+ * 7.26 -+ * - add FUSE_HANDLE_KILLPRIV -+ * - add FUSE_POSIX_ACL -+ * -+ * 7.27 -+ * - add FUSE_ABORT_ERROR -+ * -+ * 7.28 -+ * - add FUSE_COPY_FILE_RANGE -+ * - add FOPEN_CACHE_DIR -+ * - add FUSE_MAX_PAGES, add max_pages to init_out -+ * - add FUSE_CACHE_SYMLINKS -+ * -+ * 7.29 -+ * - add FUSE_NO_OPENDIR_SUPPORT flag -+ * -+ * 7.30 -+ * - add FUSE_EXPLICIT_INVAL_DATA -+ * - add FUSE_IOCTL_COMPAT_X32 -+ * -+ * 7.31 -+ * - add FUSE_WRITE_KILL_PRIV flag -+ * - add FUSE_SETUPMAPPING and FUSE_REMOVEMAPPING -+ * - add map_alignment to fuse_init_out, add FUSE_MAP_ALIGNMENT flag -+ */ -+ -+#ifndef _LINUX_FUSE_H -+#define _LINUX_FUSE_H -+ -+#include -+ -+/* -+ * Version negotiation: -+ * -+ * Both the kernel and userspace send the version they support in the -+ * INIT request and reply respectively. -+ * -+ * If the major versions match then both shall use the smallest -+ * of the two minor versions for communication. -+ * -+ * If the kernel supports a larger major version, then userspace shall -+ * reply with the major version it supports, ignore the rest of the -+ * INIT message and expect a new INIT message from the kernel with a -+ * matching major version. -+ * -+ * If the library supports a larger major version, then it shall fall -+ * back to the major protocol version sent by the kernel for -+ * communication and reply with that major version (and an arbitrary -+ * supported minor version). -+ */ -+ -+/** Version number of this interface */ -+#define FUSE_KERNEL_VERSION 7 -+ -+/** Minor version number of this interface */ -+#define FUSE_KERNEL_MINOR_VERSION 31 -+ -+/** The node ID of the root inode */ -+#define FUSE_ROOT_ID 1 -+ -+/* Make sure all structures are padded to 64bit boundary, so 32bit -+ userspace works under 64bit kernels */ -+ -+struct fuse_attr { -+ uint64_t ino; -+ uint64_t size; -+ uint64_t blocks; -+ uint64_t atime; -+ uint64_t mtime; -+ uint64_t ctime; -+ uint32_t atimensec; -+ uint32_t mtimensec; -+ uint32_t ctimensec; -+ uint32_t mode; -+ uint32_t nlink; -+ uint32_t uid; -+ uint32_t gid; -+ uint32_t rdev; -+ uint32_t blksize; -+ uint32_t padding; -+}; -+ -+struct fuse_kstatfs { -+ uint64_t blocks; -+ uint64_t bfree; -+ uint64_t bavail; -+ uint64_t files; -+ uint64_t ffree; -+ uint32_t bsize; -+ uint32_t namelen; -+ uint32_t frsize; -+ uint32_t padding; -+ uint32_t spare[6]; -+}; -+ -+struct fuse_file_lock { -+ uint64_t start; -+ uint64_t end; -+ uint32_t type; -+ uint32_t pid; /* tgid */ -+}; -+ -+/** -+ * Bitmasks for fuse_setattr_in.valid -+ */ -+#define FATTR_MODE (1 << 0) -+#define FATTR_UID (1 << 1) -+#define FATTR_GID (1 << 2) -+#define FATTR_SIZE (1 << 3) -+#define FATTR_ATIME (1 << 4) -+#define FATTR_MTIME (1 << 5) -+#define FATTR_FH (1 << 6) -+#define FATTR_ATIME_NOW (1 << 7) -+#define FATTR_MTIME_NOW (1 << 8) -+#define FATTR_LOCKOWNER (1 << 9) -+#define FATTR_CTIME (1 << 10) -+ -+/** -+ * Flags returned by the OPEN request -+ * -+ * FOPEN_DIRECT_IO: bypass page cache for this open file -+ * FOPEN_KEEP_CACHE: don't invalidate the data cache on open -+ * FOPEN_NONSEEKABLE: the file is not seekable -+ * FOPEN_CACHE_DIR: allow caching this directory -+ * FOPEN_STREAM: the file is stream-like (no file position at all) -+ */ -+#define FOPEN_DIRECT_IO (1 << 0) -+#define FOPEN_KEEP_CACHE (1 << 1) -+#define FOPEN_NONSEEKABLE (1 << 2) -+#define FOPEN_CACHE_DIR (1 << 3) -+#define FOPEN_STREAM (1 << 4) -+ -+/** -+ * INIT request/reply flags -+ * -+ * FUSE_ASYNC_READ: asynchronous read requests -+ * FUSE_POSIX_LOCKS: remote locking for POSIX file locks -+ * FUSE_FILE_OPS: kernel sends file handle for fstat, etc... (not yet supported) -+ * FUSE_ATOMIC_O_TRUNC: handles the O_TRUNC open flag in the filesystem -+ * FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".." -+ * FUSE_BIG_WRITES: filesystem can handle write size larger than 4kB -+ * FUSE_DONT_MASK: don't apply umask to file mode on create operations -+ * FUSE_SPLICE_WRITE: kernel supports splice write on the device -+ * FUSE_SPLICE_MOVE: kernel supports splice move on the device -+ * FUSE_SPLICE_READ: kernel supports splice read on the device -+ * FUSE_FLOCK_LOCKS: remote locking for BSD style file locks -+ * FUSE_HAS_IOCTL_DIR: kernel supports ioctl on directories -+ * FUSE_AUTO_INVAL_DATA: automatically invalidate cached pages -+ * FUSE_DO_READDIRPLUS: do READDIRPLUS (READDIR+LOOKUP in one) -+ * FUSE_READDIRPLUS_AUTO: adaptive readdirplus -+ * FUSE_ASYNC_DIO: asynchronous direct I/O submission -+ * FUSE_WRITEBACK_CACHE: use writeback cache for buffered writes -+ * FUSE_NO_OPEN_SUPPORT: kernel supports zero-message opens -+ * FUSE_PARALLEL_DIROPS: allow parallel lookups and readdir -+ * FUSE_HANDLE_KILLPRIV: fs handles killing suid/sgid/cap on write/chown/trunc -+ * FUSE_POSIX_ACL: filesystem supports posix acls -+ * FUSE_ABORT_ERROR: reading the device after abort returns ECONNABORTED -+ * FUSE_MAX_PAGES: init_out.max_pages contains the max number of req pages -+ * FUSE_CACHE_SYMLINKS: cache READLINK responses -+ * FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir -+ * FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request -+ * FUSE_MAP_ALIGNMENT: map_alignment field is valid -+ */ -+#define FUSE_ASYNC_READ (1 << 0) -+#define FUSE_POSIX_LOCKS (1 << 1) -+#define FUSE_FILE_OPS (1 << 2) -+#define FUSE_ATOMIC_O_TRUNC (1 << 3) -+#define FUSE_EXPORT_SUPPORT (1 << 4) -+#define FUSE_BIG_WRITES (1 << 5) -+#define FUSE_DONT_MASK (1 << 6) -+#define FUSE_SPLICE_WRITE (1 << 7) -+#define FUSE_SPLICE_MOVE (1 << 8) -+#define FUSE_SPLICE_READ (1 << 9) -+#define FUSE_FLOCK_LOCKS (1 << 10) -+#define FUSE_HAS_IOCTL_DIR (1 << 11) -+#define FUSE_AUTO_INVAL_DATA (1 << 12) -+#define FUSE_DO_READDIRPLUS (1 << 13) -+#define FUSE_READDIRPLUS_AUTO (1 << 14) -+#define FUSE_ASYNC_DIO (1 << 15) -+#define FUSE_WRITEBACK_CACHE (1 << 16) -+#define FUSE_NO_OPEN_SUPPORT (1 << 17) -+#define FUSE_PARALLEL_DIROPS (1 << 18) -+#define FUSE_HANDLE_KILLPRIV (1 << 19) -+#define FUSE_POSIX_ACL (1 << 20) -+#define FUSE_ABORT_ERROR (1 << 21) -+#define FUSE_MAX_PAGES (1 << 22) -+#define FUSE_CACHE_SYMLINKS (1 << 23) -+#define FUSE_NO_OPENDIR_SUPPORT (1 << 24) -+#define FUSE_EXPLICIT_INVAL_DATA (1 << 25) -+#define FUSE_MAP_ALIGNMENT (1 << 26) -+ -+/** -+ * CUSE INIT request/reply flags -+ * -+ * CUSE_UNRESTRICTED_IOCTL: use unrestricted ioctl -+ */ -+#define CUSE_UNRESTRICTED_IOCTL (1 << 0) -+ -+/** -+ * Release flags -+ */ -+#define FUSE_RELEASE_FLUSH (1 << 0) -+#define FUSE_RELEASE_FLOCK_UNLOCK (1 << 1) -+ -+/** -+ * Getattr flags -+ */ -+#define FUSE_GETATTR_FH (1 << 0) -+ -+/** -+ * Lock flags -+ */ -+#define FUSE_LK_FLOCK (1 << 0) -+ -+/** -+ * WRITE flags -+ * -+ * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed -+ * FUSE_WRITE_LOCKOWNER: lock_owner field is valid -+ * FUSE_WRITE_KILL_PRIV: kill suid and sgid bits -+ */ -+#define FUSE_WRITE_CACHE (1 << 0) -+#define FUSE_WRITE_LOCKOWNER (1 << 1) -+#define FUSE_WRITE_KILL_PRIV (1 << 2) -+ -+/** -+ * Read flags -+ */ -+#define FUSE_READ_LOCKOWNER (1 << 1) -+ -+/** -+ * Ioctl flags -+ * -+ * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine -+ * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed -+ * FUSE_IOCTL_RETRY: retry with new iovecs -+ * FUSE_IOCTL_32BIT: 32bit ioctl -+ * FUSE_IOCTL_DIR: is a directory -+ * FUSE_IOCTL_COMPAT_X32: x32 compat ioctl on 64bit machine (64bit time_t) -+ * -+ * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs -+ */ -+#define FUSE_IOCTL_COMPAT (1 << 0) -+#define FUSE_IOCTL_UNRESTRICTED (1 << 1) -+#define FUSE_IOCTL_RETRY (1 << 2) -+#define FUSE_IOCTL_32BIT (1 << 3) -+#define FUSE_IOCTL_DIR (1 << 4) -+#define FUSE_IOCTL_COMPAT_X32 (1 << 5) -+ -+#define FUSE_IOCTL_MAX_IOV 256 -+ -+/** -+ * Poll flags -+ * -+ * FUSE_POLL_SCHEDULE_NOTIFY: request poll notify -+ */ -+#define FUSE_POLL_SCHEDULE_NOTIFY (1 << 0) -+ -+/** -+ * Fsync flags -+ * -+ * FUSE_FSYNC_FDATASYNC: Sync data only, not metadata -+ */ -+#define FUSE_FSYNC_FDATASYNC (1 << 0) -+ -+enum fuse_opcode { -+ FUSE_LOOKUP = 1, -+ FUSE_FORGET = 2, /* no reply */ -+ FUSE_GETATTR = 3, -+ FUSE_SETATTR = 4, -+ FUSE_READLINK = 5, -+ FUSE_SYMLINK = 6, -+ FUSE_MKNOD = 8, -+ FUSE_MKDIR = 9, -+ FUSE_UNLINK = 10, -+ FUSE_RMDIR = 11, -+ FUSE_RENAME = 12, -+ FUSE_LINK = 13, -+ FUSE_OPEN = 14, -+ FUSE_READ = 15, -+ FUSE_WRITE = 16, -+ FUSE_STATFS = 17, -+ FUSE_RELEASE = 18, -+ FUSE_FSYNC = 20, -+ FUSE_SETXATTR = 21, -+ FUSE_GETXATTR = 22, -+ FUSE_LISTXATTR = 23, -+ FUSE_REMOVEXATTR = 24, -+ FUSE_FLUSH = 25, -+ FUSE_INIT = 26, -+ FUSE_OPENDIR = 27, -+ FUSE_READDIR = 28, -+ FUSE_RELEASEDIR = 29, -+ FUSE_FSYNCDIR = 30, -+ FUSE_GETLK = 31, -+ FUSE_SETLK = 32, -+ FUSE_SETLKW = 33, -+ FUSE_ACCESS = 34, -+ FUSE_CREATE = 35, -+ FUSE_INTERRUPT = 36, -+ FUSE_BMAP = 37, -+ FUSE_DESTROY = 38, -+ FUSE_IOCTL = 39, -+ FUSE_POLL = 40, -+ FUSE_NOTIFY_REPLY = 41, -+ FUSE_BATCH_FORGET = 42, -+ FUSE_FALLOCATE = 43, -+ FUSE_READDIRPLUS = 44, -+ FUSE_RENAME2 = 45, -+ FUSE_LSEEK = 46, -+ FUSE_COPY_FILE_RANGE = 47, -+ FUSE_SETUPMAPPING = 48, -+ FUSE_REMOVEMAPPING = 49, -+ -+ /* CUSE specific operations */ -+ CUSE_INIT = 4096, -+ -+ /* Reserved opcodes: helpful to detect structure endian-ness */ -+ CUSE_INIT_BSWAP_RESERVED = 1048576, /* CUSE_INIT << 8 */ -+ FUSE_INIT_BSWAP_RESERVED = 436207616, /* FUSE_INIT << 24 */ -+}; -+ -+enum fuse_notify_code { -+ FUSE_NOTIFY_POLL = 1, -+ FUSE_NOTIFY_INVAL_INODE = 2, -+ FUSE_NOTIFY_INVAL_ENTRY = 3, -+ FUSE_NOTIFY_STORE = 4, -+ FUSE_NOTIFY_RETRIEVE = 5, -+ FUSE_NOTIFY_DELETE = 6, -+ FUSE_NOTIFY_CODE_MAX, -+}; -+ -+/* The read buffer is required to be at least 8k, but may be much larger */ -+#define FUSE_MIN_READ_BUFFER 8192 -+ -+#define FUSE_COMPAT_ENTRY_OUT_SIZE 120 -+ -+struct fuse_entry_out { -+ uint64_t nodeid; /* Inode ID */ -+ uint64_t generation; /* Inode generation: nodeid:gen must -+ be unique for the fs's lifetime */ -+ uint64_t entry_valid; /* Cache timeout for the name */ -+ uint64_t attr_valid; /* Cache timeout for the attributes */ -+ uint32_t entry_valid_nsec; -+ uint32_t attr_valid_nsec; -+ struct fuse_attr attr; -+}; -+ -+struct fuse_forget_in { -+ uint64_t nlookup; -+}; -+ -+struct fuse_forget_one { -+ uint64_t nodeid; -+ uint64_t nlookup; -+}; -+ -+struct fuse_batch_forget_in { -+ uint32_t count; -+ uint32_t dummy; -+}; -+ -+struct fuse_getattr_in { -+ uint32_t getattr_flags; -+ uint32_t dummy; -+ uint64_t fh; -+}; -+ -+#define FUSE_COMPAT_ATTR_OUT_SIZE 96 -+ -+struct fuse_attr_out { -+ uint64_t attr_valid; /* Cache timeout for the attributes */ -+ uint32_t attr_valid_nsec; -+ uint32_t dummy; -+ struct fuse_attr attr; -+}; -+ -+#define FUSE_COMPAT_MKNOD_IN_SIZE 8 -+ -+struct fuse_mknod_in { -+ uint32_t mode; -+ uint32_t rdev; -+ uint32_t umask; -+ uint32_t padding; -+}; -+ -+struct fuse_mkdir_in { -+ uint32_t mode; -+ uint32_t umask; -+}; -+ -+struct fuse_rename_in { -+ uint64_t newdir; -+}; -+ -+struct fuse_rename2_in { -+ uint64_t newdir; -+ uint32_t flags; -+ uint32_t padding; -+}; -+ -+struct fuse_link_in { -+ uint64_t oldnodeid; -+}; -+ -+struct fuse_setattr_in { -+ uint32_t valid; -+ uint32_t padding; -+ uint64_t fh; -+ uint64_t size; -+ uint64_t lock_owner; -+ uint64_t atime; -+ uint64_t mtime; -+ uint64_t ctime; -+ uint32_t atimensec; -+ uint32_t mtimensec; -+ uint32_t ctimensec; -+ uint32_t mode; -+ uint32_t unused4; -+ uint32_t uid; -+ uint32_t gid; -+ uint32_t unused5; -+}; -+ -+struct fuse_open_in { -+ uint32_t flags; -+ uint32_t unused; -+}; -+ -+struct fuse_create_in { -+ uint32_t flags; -+ uint32_t mode; -+ uint32_t umask; -+ uint32_t padding; -+}; -+ -+struct fuse_open_out { -+ uint64_t fh; -+ uint32_t open_flags; -+ uint32_t padding; -+}; -+ -+struct fuse_release_in { -+ uint64_t fh; -+ uint32_t flags; -+ uint32_t release_flags; -+ uint64_t lock_owner; -+}; -+ -+struct fuse_flush_in { -+ uint64_t fh; -+ uint32_t unused; -+ uint32_t padding; -+ uint64_t lock_owner; -+}; -+ -+struct fuse_read_in { -+ uint64_t fh; -+ uint64_t offset; -+ uint32_t size; -+ uint32_t read_flags; -+ uint64_t lock_owner; -+ uint32_t flags; -+ uint32_t padding; -+}; -+ -+#define FUSE_COMPAT_WRITE_IN_SIZE 24 -+ -+struct fuse_write_in { -+ uint64_t fh; -+ uint64_t offset; -+ uint32_t size; -+ uint32_t write_flags; -+ uint64_t lock_owner; -+ uint32_t flags; -+ uint32_t padding; -+}; -+ -+struct fuse_write_out { -+ uint32_t size; -+ uint32_t padding; -+}; -+ -+#define FUSE_COMPAT_STATFS_SIZE 48 -+ -+struct fuse_statfs_out { -+ struct fuse_kstatfs st; -+}; -+ -+struct fuse_fsync_in { -+ uint64_t fh; -+ uint32_t fsync_flags; -+ uint32_t padding; -+}; -+ -+struct fuse_setxattr_in { -+ uint32_t size; -+ uint32_t flags; -+}; -+ -+struct fuse_getxattr_in { -+ uint32_t size; -+ uint32_t padding; -+}; -+ -+struct fuse_getxattr_out { -+ uint32_t size; -+ uint32_t padding; -+}; -+ -+struct fuse_lk_in { -+ uint64_t fh; -+ uint64_t owner; -+ struct fuse_file_lock lk; -+ uint32_t lk_flags; -+ uint32_t padding; -+}; -+ -+struct fuse_lk_out { -+ struct fuse_file_lock lk; -+}; -+ -+struct fuse_access_in { -+ uint32_t mask; -+ uint32_t padding; -+}; -+ -+struct fuse_init_in { -+ uint32_t major; -+ uint32_t minor; -+ uint32_t max_readahead; -+ uint32_t flags; -+}; -+ -+#define FUSE_COMPAT_INIT_OUT_SIZE 8 -+#define FUSE_COMPAT_22_INIT_OUT_SIZE 24 -+ -+struct fuse_init_out { -+ uint32_t major; -+ uint32_t minor; -+ uint32_t max_readahead; -+ uint32_t flags; -+ uint16_t max_background; -+ uint16_t congestion_threshold; -+ uint32_t max_write; -+ uint32_t time_gran; -+ uint16_t max_pages; -+ uint16_t map_alignment; -+ uint32_t unused[8]; -+}; -+ -+#define CUSE_INIT_INFO_MAX 4096 -+ -+struct cuse_init_in { -+ uint32_t major; -+ uint32_t minor; -+ uint32_t unused; -+ uint32_t flags; -+}; -+ -+struct cuse_init_out { -+ uint32_t major; -+ uint32_t minor; -+ uint32_t unused; -+ uint32_t flags; -+ uint32_t max_read; -+ uint32_t max_write; -+ uint32_t dev_major; /* chardev major */ -+ uint32_t dev_minor; /* chardev minor */ -+ uint32_t spare[10]; -+}; -+ -+struct fuse_interrupt_in { -+ uint64_t unique; -+}; -+ -+struct fuse_bmap_in { -+ uint64_t block; -+ uint32_t blocksize; -+ uint32_t padding; -+}; -+ -+struct fuse_bmap_out { -+ uint64_t block; -+}; -+ -+struct fuse_ioctl_in { -+ uint64_t fh; -+ uint32_t flags; -+ uint32_t cmd; -+ uint64_t arg; -+ uint32_t in_size; -+ uint32_t out_size; -+}; -+ -+struct fuse_ioctl_iovec { -+ uint64_t base; -+ uint64_t len; -+}; -+ -+struct fuse_ioctl_out { -+ int32_t result; -+ uint32_t flags; -+ uint32_t in_iovs; -+ uint32_t out_iovs; -+}; -+ -+struct fuse_poll_in { -+ uint64_t fh; -+ uint64_t kh; -+ uint32_t flags; -+ uint32_t events; -+}; -+ -+struct fuse_poll_out { -+ uint32_t revents; -+ uint32_t padding; -+}; -+ -+struct fuse_notify_poll_wakeup_out { -+ uint64_t kh; -+}; -+ -+struct fuse_fallocate_in { -+ uint64_t fh; -+ uint64_t offset; -+ uint64_t length; -+ uint32_t mode; -+ uint32_t padding; -+}; -+ -+struct fuse_in_header { -+ uint32_t len; -+ uint32_t opcode; -+ uint64_t unique; -+ uint64_t nodeid; -+ uint32_t uid; -+ uint32_t gid; -+ uint32_t pid; -+ uint32_t padding; -+}; -+ -+struct fuse_out_header { -+ uint32_t len; -+ int32_t error; -+ uint64_t unique; -+}; -+ -+struct fuse_dirent { -+ uint64_t ino; -+ uint64_t off; -+ uint32_t namelen; -+ uint32_t type; -+ char name[]; -+}; -+ -+#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name) -+#define FUSE_DIRENT_ALIGN(x) \ -+ (((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1)) -+#define FUSE_DIRENT_SIZE(d) \ -+ FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen) -+ -+struct fuse_direntplus { -+ struct fuse_entry_out entry_out; -+ struct fuse_dirent dirent; -+}; -+ -+#define FUSE_NAME_OFFSET_DIRENTPLUS \ -+ offsetof(struct fuse_direntplus, dirent.name) -+#define FUSE_DIRENTPLUS_SIZE(d) \ -+ FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen) -+ -+struct fuse_notify_inval_inode_out { -+ uint64_t ino; -+ int64_t off; -+ int64_t len; -+}; -+ -+struct fuse_notify_inval_entry_out { -+ uint64_t parent; -+ uint32_t namelen; -+ uint32_t padding; -+}; -+ -+struct fuse_notify_delete_out { -+ uint64_t parent; -+ uint64_t child; -+ uint32_t namelen; -+ uint32_t padding; -+}; -+ -+struct fuse_notify_store_out { -+ uint64_t nodeid; -+ uint64_t offset; -+ uint32_t size; -+ uint32_t padding; -+}; -+ -+struct fuse_notify_retrieve_out { -+ uint64_t notify_unique; -+ uint64_t nodeid; -+ uint64_t offset; -+ uint32_t size; -+ uint32_t padding; -+}; -+ -+/* Matches the size of fuse_write_in */ -+struct fuse_notify_retrieve_in { -+ uint64_t dummy1; -+ uint64_t offset; -+ uint32_t size; -+ uint32_t dummy2; -+ uint64_t dummy3; -+ uint64_t dummy4; -+}; -+ -+/* Device ioctls: */ -+#define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t) -+ -+struct fuse_lseek_in { -+ uint64_t fh; -+ uint64_t offset; -+ uint32_t whence; -+ uint32_t padding; -+}; -+ -+struct fuse_lseek_out { -+ uint64_t offset; -+}; -+ -+struct fuse_copy_file_range_in { -+ uint64_t fh_in; -+ uint64_t off_in; -+ uint64_t nodeid_out; -+ uint64_t fh_out; -+ uint64_t off_out; -+ uint64_t len; -+ uint64_t flags; -+}; -+ -+#endif /* _LINUX_FUSE_H */ -diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh -index f76d77363b..29c27f4681 100755 ---- a/scripts/update-linux-headers.sh -+++ b/scripts/update-linux-headers.sh -@@ -186,6 +186,7 @@ rm -rf "$output/include/standard-headers/linux" - mkdir -p "$output/include/standard-headers/linux" - for i in "$tmpdir"/include/linux/*virtio*.h \ - "$tmpdir/include/linux/qemu_fw_cfg.h" \ -+ "$tmpdir/include/linux/fuse.h" \ - "$tmpdir/include/linux/input.h" \ - "$tmpdir/include/linux/input-event-codes.h" \ - "$tmpdir/include/linux/pci_regs.h" \ diff --git a/0012-virtiofsd-Add-auxiliary-.c-s.patch b/0012-virtiofsd-Add-auxiliary-.c-s.patch deleted file mode 100644 index 645e280..0000000 --- a/0012-virtiofsd-Add-auxiliary-.c-s.patch +++ /dev/null @@ -1,1371 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:00:41 +0000 -Subject: [PATCH] virtiofsd: Add auxiliary .c's -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add most of the non-main .c files we need from upstream fuse-3.8.0 - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit ffcf8d9f8649c6e56b1193bbbc9c9f7388920043) ---- - tools/virtiofsd/buffer.c | 321 ++++++++++++++++++++++++ - tools/virtiofsd/fuse_log.c | 40 +++ - tools/virtiofsd/fuse_opt.c | 423 +++++++++++++++++++++++++++++++ - tools/virtiofsd/fuse_signals.c | 91 +++++++ - tools/virtiofsd/helper.c | 440 +++++++++++++++++++++++++++++++++ - 5 files changed, 1315 insertions(+) - create mode 100644 tools/virtiofsd/buffer.c - create mode 100644 tools/virtiofsd/fuse_log.c - create mode 100644 tools/virtiofsd/fuse_opt.c - create mode 100644 tools/virtiofsd/fuse_signals.c - create mode 100644 tools/virtiofsd/helper.c - -diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c -new file mode 100644 -index 0000000000..5ab9b87455 ---- /dev/null -+++ b/tools/virtiofsd/buffer.c -@@ -0,0 +1,321 @@ -+/* -+ FUSE: Filesystem in Userspace -+ Copyright (C) 2010 Miklos Szeredi -+ -+ Functions for dealing with `struct fuse_buf` and `struct -+ fuse_bufvec`. -+ -+ This program can be distributed under the terms of the GNU LGPLv2. -+ See the file COPYING.LIB -+*/ -+ -+#define _GNU_SOURCE -+ -+#include "config.h" -+#include "fuse_i.h" -+#include "fuse_lowlevel.h" -+#include -+#include -+#include -+#include -+ -+size_t fuse_buf_size(const struct fuse_bufvec *bufv) -+{ -+ size_t i; -+ size_t size = 0; -+ -+ for (i = 0; i < bufv->count; i++) { -+ if (bufv->buf[i].size == SIZE_MAX) -+ size = SIZE_MAX; -+ else -+ size += bufv->buf[i].size; -+ } -+ -+ return size; -+} -+ -+static size_t min_size(size_t s1, size_t s2) -+{ -+ return s1 < s2 ? s1 : s2; -+} -+ -+static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off, -+ const struct fuse_buf *src, size_t src_off, -+ size_t len) -+{ -+ ssize_t res = 0; -+ size_t copied = 0; -+ -+ while (len) { -+ if (dst->flags & FUSE_BUF_FD_SEEK) { -+ res = pwrite(dst->fd, (char *)src->mem + src_off, len, -+ dst->pos + dst_off); -+ } else { -+ res = write(dst->fd, (char *)src->mem + src_off, len); -+ } -+ if (res == -1) { -+ if (!copied) -+ return -errno; -+ break; -+ } -+ if (res == 0) -+ break; -+ -+ copied += res; -+ if (!(dst->flags & FUSE_BUF_FD_RETRY)) -+ break; -+ -+ src_off += res; -+ dst_off += res; -+ len -= res; -+ } -+ -+ return copied; -+} -+ -+static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off, -+ const struct fuse_buf *src, size_t src_off, -+ size_t len) -+{ -+ ssize_t res = 0; -+ size_t copied = 0; -+ -+ while (len) { -+ if (src->flags & FUSE_BUF_FD_SEEK) { -+ res = pread(src->fd, (char *)dst->mem + dst_off, len, -+ src->pos + src_off); -+ } else { -+ res = read(src->fd, (char *)dst->mem + dst_off, len); -+ } -+ if (res == -1) { -+ if (!copied) -+ return -errno; -+ break; -+ } -+ if (res == 0) -+ break; -+ -+ copied += res; -+ if (!(src->flags & FUSE_BUF_FD_RETRY)) -+ break; -+ -+ dst_off += res; -+ src_off += res; -+ len -= res; -+ } -+ -+ return copied; -+} -+ -+static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off, -+ const struct fuse_buf *src, size_t src_off, -+ size_t len) -+{ -+ char buf[4096]; -+ struct fuse_buf tmp = { -+ .size = sizeof(buf), -+ .flags = 0, -+ }; -+ ssize_t res; -+ size_t copied = 0; -+ -+ tmp.mem = buf; -+ -+ while (len) { -+ size_t this_len = min_size(tmp.size, len); -+ size_t read_len; -+ -+ res = fuse_buf_read(&tmp, 0, src, src_off, this_len); -+ if (res < 0) { -+ if (!copied) -+ return res; -+ break; -+ } -+ if (res == 0) -+ break; -+ -+ read_len = res; -+ res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len); -+ if (res < 0) { -+ if (!copied) -+ return res; -+ break; -+ } -+ if (res == 0) -+ break; -+ -+ copied += res; -+ -+ if (res < this_len) -+ break; -+ -+ dst_off += res; -+ src_off += res; -+ len -= res; -+ } -+ -+ return copied; -+} -+ -+#ifdef HAVE_SPLICE -+static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off, -+ const struct fuse_buf *src, size_t src_off, -+ size_t len, enum fuse_buf_copy_flags flags) -+{ -+ int splice_flags = 0; -+ off_t *srcpos = NULL; -+ off_t *dstpos = NULL; -+ off_t srcpos_val; -+ off_t dstpos_val; -+ ssize_t res; -+ size_t copied = 0; -+ -+ if (flags & FUSE_BUF_SPLICE_MOVE) -+ splice_flags |= SPLICE_F_MOVE; -+ if (flags & FUSE_BUF_SPLICE_NONBLOCK) -+ splice_flags |= SPLICE_F_NONBLOCK; -+ -+ if (src->flags & FUSE_BUF_FD_SEEK) { -+ srcpos_val = src->pos + src_off; -+ srcpos = &srcpos_val; -+ } -+ if (dst->flags & FUSE_BUF_FD_SEEK) { -+ dstpos_val = dst->pos + dst_off; -+ dstpos = &dstpos_val; -+ } -+ -+ while (len) { -+ res = splice(src->fd, srcpos, dst->fd, dstpos, len, -+ splice_flags); -+ if (res == -1) { -+ if (copied) -+ break; -+ -+ if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE)) -+ return -errno; -+ -+ /* Maybe splice is not supported for this combination */ -+ return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, -+ len); -+ } -+ if (res == 0) -+ break; -+ -+ copied += res; -+ if (!(src->flags & FUSE_BUF_FD_RETRY) && -+ !(dst->flags & FUSE_BUF_FD_RETRY)) { -+ break; -+ } -+ -+ len -= res; -+ } -+ -+ return copied; -+} -+#else -+static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off, -+ const struct fuse_buf *src, size_t src_off, -+ size_t len, enum fuse_buf_copy_flags flags) -+{ -+ (void) flags; -+ -+ return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); -+} -+#endif -+ -+ -+static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off, -+ const struct fuse_buf *src, size_t src_off, -+ size_t len, enum fuse_buf_copy_flags flags) -+{ -+ int src_is_fd = src->flags & FUSE_BUF_IS_FD; -+ int dst_is_fd = dst->flags & FUSE_BUF_IS_FD; -+ -+ if (!src_is_fd && !dst_is_fd) { -+ char *dstmem = (char *)dst->mem + dst_off; -+ char *srcmem = (char *)src->mem + src_off; -+ -+ if (dstmem != srcmem) { -+ if (dstmem + len <= srcmem || srcmem + len <= dstmem) -+ memcpy(dstmem, srcmem, len); -+ else -+ memmove(dstmem, srcmem, len); -+ } -+ -+ return len; -+ } else if (!src_is_fd) { -+ return fuse_buf_write(dst, dst_off, src, src_off, len); -+ } else if (!dst_is_fd) { -+ return fuse_buf_read(dst, dst_off, src, src_off, len); -+ } else if (flags & FUSE_BUF_NO_SPLICE) { -+ return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); -+ } else { -+ return fuse_buf_splice(dst, dst_off, src, src_off, len, flags); -+ } -+} -+ -+static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv) -+{ -+ if (bufv->idx < bufv->count) -+ return &bufv->buf[bufv->idx]; -+ else -+ return NULL; -+} -+ -+static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len) -+{ -+ const struct fuse_buf *buf = fuse_bufvec_current(bufv); -+ -+ bufv->off += len; -+ assert(bufv->off <= buf->size); -+ if (bufv->off == buf->size) { -+ assert(bufv->idx < bufv->count); -+ bufv->idx++; -+ if (bufv->idx == bufv->count) -+ return 0; -+ bufv->off = 0; -+ } -+ return 1; -+} -+ -+ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv, -+ enum fuse_buf_copy_flags flags) -+{ -+ size_t copied = 0; -+ -+ if (dstv == srcv) -+ return fuse_buf_size(dstv); -+ -+ for (;;) { -+ const struct fuse_buf *src = fuse_bufvec_current(srcv); -+ const struct fuse_buf *dst = fuse_bufvec_current(dstv); -+ size_t src_len; -+ size_t dst_len; -+ size_t len; -+ ssize_t res; -+ -+ if (src == NULL || dst == NULL) -+ break; -+ -+ src_len = src->size - srcv->off; -+ dst_len = dst->size - dstv->off; -+ len = min_size(src_len, dst_len); -+ -+ res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags); -+ if (res < 0) { -+ if (!copied) -+ return res; -+ break; -+ } -+ copied += res; -+ -+ if (!fuse_bufvec_advance(srcv, res) || -+ !fuse_bufvec_advance(dstv, res)) -+ break; -+ -+ if (res < len) -+ break; -+ } -+ -+ return copied; -+} -diff --git a/tools/virtiofsd/fuse_log.c b/tools/virtiofsd/fuse_log.c -new file mode 100644 -index 0000000000..0d268ab014 ---- /dev/null -+++ b/tools/virtiofsd/fuse_log.c -@@ -0,0 +1,40 @@ -+/* -+ FUSE: Filesystem in Userspace -+ Copyright (C) 2019 Red Hat, Inc. -+ -+ Logging API. -+ -+ This program can be distributed under the terms of the GNU LGPLv2. -+ See the file COPYING.LIB -+*/ -+ -+#include "fuse_log.h" -+ -+#include -+#include -+ -+static void default_log_func( -+ __attribute__(( unused )) enum fuse_log_level level, -+ const char *fmt, va_list ap) -+{ -+ vfprintf(stderr, fmt, ap); -+} -+ -+static fuse_log_func_t log_func = default_log_func; -+ -+void fuse_set_log_func(fuse_log_func_t func) -+{ -+ if (!func) -+ func = default_log_func; -+ -+ log_func = func; -+} -+ -+void fuse_log(enum fuse_log_level level, const char *fmt, ...) -+{ -+ va_list ap; -+ -+ va_start(ap, fmt); -+ log_func(level, fmt, ap); -+ va_end(ap); -+} -diff --git a/tools/virtiofsd/fuse_opt.c b/tools/virtiofsd/fuse_opt.c -new file mode 100644 -index 0000000000..93066b926e ---- /dev/null -+++ b/tools/virtiofsd/fuse_opt.c -@@ -0,0 +1,423 @@ -+/* -+ FUSE: Filesystem in Userspace -+ Copyright (C) 2001-2007 Miklos Szeredi -+ -+ Implementation of option parsing routines (dealing with `struct -+ fuse_args`). -+ -+ This program can be distributed under the terms of the GNU LGPLv2. -+ See the file COPYING.LIB -+*/ -+ -+#include "config.h" -+#include "fuse_i.h" -+#include "fuse_opt.h" -+#include "fuse_misc.h" -+ -+#include -+#include -+#include -+#include -+ -+struct fuse_opt_context { -+ void *data; -+ const struct fuse_opt *opt; -+ fuse_opt_proc_t proc; -+ int argctr; -+ int argc; -+ char **argv; -+ struct fuse_args outargs; -+ char *opts; -+ int nonopt; -+}; -+ -+void fuse_opt_free_args(struct fuse_args *args) -+{ -+ if (args) { -+ if (args->argv && args->allocated) { -+ int i; -+ for (i = 0; i < args->argc; i++) -+ free(args->argv[i]); -+ free(args->argv); -+ } -+ args->argc = 0; -+ args->argv = NULL; -+ args->allocated = 0; -+ } -+} -+ -+static int alloc_failed(void) -+{ -+ fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); -+ return -1; -+} -+ -+int fuse_opt_add_arg(struct fuse_args *args, const char *arg) -+{ -+ char **newargv; -+ char *newarg; -+ -+ assert(!args->argv || args->allocated); -+ -+ newarg = strdup(arg); -+ if (!newarg) -+ return alloc_failed(); -+ -+ newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); -+ if (!newargv) { -+ free(newarg); -+ return alloc_failed(); -+ } -+ -+ args->argv = newargv; -+ args->allocated = 1; -+ args->argv[args->argc++] = newarg; -+ args->argv[args->argc] = NULL; -+ return 0; -+} -+ -+static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos, -+ const char *arg) -+{ -+ assert(pos <= args->argc); -+ if (fuse_opt_add_arg(args, arg) == -1) -+ return -1; -+ -+ if (pos != args->argc - 1) { -+ char *newarg = args->argv[args->argc - 1]; -+ memmove(&args->argv[pos + 1], &args->argv[pos], -+ sizeof(char *) * (args->argc - pos - 1)); -+ args->argv[pos] = newarg; -+ } -+ return 0; -+} -+ -+int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) -+{ -+ return fuse_opt_insert_arg_common(args, pos, arg); -+} -+ -+static int next_arg(struct fuse_opt_context *ctx, const char *opt) -+{ -+ if (ctx->argctr + 1 >= ctx->argc) { -+ fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt); -+ return -1; -+ } -+ ctx->argctr++; -+ return 0; -+} -+ -+static int add_arg(struct fuse_opt_context *ctx, const char *arg) -+{ -+ return fuse_opt_add_arg(&ctx->outargs, arg); -+} -+ -+static int add_opt_common(char **opts, const char *opt, int esc) -+{ -+ unsigned oldlen = *opts ? strlen(*opts) : 0; -+ char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1); -+ -+ if (!d) -+ return alloc_failed(); -+ -+ *opts = d; -+ if (oldlen) { -+ d += oldlen; -+ *d++ = ','; -+ } -+ -+ for (; *opt; opt++) { -+ if (esc && (*opt == ',' || *opt == '\\')) -+ *d++ = '\\'; -+ *d++ = *opt; -+ } -+ *d = '\0'; -+ -+ return 0; -+} -+ -+int fuse_opt_add_opt(char **opts, const char *opt) -+{ -+ return add_opt_common(opts, opt, 0); -+} -+ -+int fuse_opt_add_opt_escaped(char **opts, const char *opt) -+{ -+ return add_opt_common(opts, opt, 1); -+} -+ -+static int add_opt(struct fuse_opt_context *ctx, const char *opt) -+{ -+ return add_opt_common(&ctx->opts, opt, 1); -+} -+ -+static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key, -+ int iso) -+{ -+ if (key == FUSE_OPT_KEY_DISCARD) -+ return 0; -+ -+ if (key != FUSE_OPT_KEY_KEEP && ctx->proc) { -+ int res = ctx->proc(ctx->data, arg, key, &ctx->outargs); -+ if (res == -1 || !res) -+ return res; -+ } -+ if (iso) -+ return add_opt(ctx, arg); -+ else -+ return add_arg(ctx, arg); -+} -+ -+static int match_template(const char *t, const char *arg, unsigned *sepp) -+{ -+ int arglen = strlen(arg); -+ const char *sep = strchr(t, '='); -+ sep = sep ? sep : strchr(t, ' '); -+ if (sep && (!sep[1] || sep[1] == '%')) { -+ int tlen = sep - t; -+ if (sep[0] == '=') -+ tlen ++; -+ if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { -+ *sepp = sep - t; -+ return 1; -+ } -+ } -+ if (strcmp(t, arg) == 0) { -+ *sepp = 0; -+ return 1; -+ } -+ return 0; -+} -+ -+static const struct fuse_opt *find_opt(const struct fuse_opt *opt, -+ const char *arg, unsigned *sepp) -+{ -+ for (; opt && opt->templ; opt++) -+ if (match_template(opt->templ, arg, sepp)) -+ return opt; -+ return NULL; -+} -+ -+int fuse_opt_match(const struct fuse_opt *opts, const char *opt) -+{ -+ unsigned dummy; -+ return find_opt(opts, opt, &dummy) ? 1 : 0; -+} -+ -+static int process_opt_param(void *var, const char *format, const char *param, -+ const char *arg) -+{ -+ assert(format[0] == '%'); -+ if (format[1] == 's') { -+ char **s = var; -+ char *copy = strdup(param); -+ if (!copy) -+ return alloc_failed(); -+ -+ free(*s); -+ *s = copy; -+ } else { -+ if (sscanf(param, format, var) != 1) { -+ fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n", arg); -+ return -1; -+ } -+ } -+ return 0; -+} -+ -+static int process_opt(struct fuse_opt_context *ctx, -+ const struct fuse_opt *opt, unsigned sep, -+ const char *arg, int iso) -+{ -+ if (opt->offset == -1U) { -+ if (call_proc(ctx, arg, opt->value, iso) == -1) -+ return -1; -+ } else { -+ void *var = (char *)ctx->data + opt->offset; -+ if (sep && opt->templ[sep + 1]) { -+ const char *param = arg + sep; -+ if (opt->templ[sep] == '=') -+ param ++; -+ if (process_opt_param(var, opt->templ + sep + 1, -+ param, arg) == -1) -+ return -1; -+ } else -+ *(int *)var = opt->value; -+ } -+ return 0; -+} -+ -+static int process_opt_sep_arg(struct fuse_opt_context *ctx, -+ const struct fuse_opt *opt, unsigned sep, -+ const char *arg, int iso) -+{ -+ int res; -+ char *newarg; -+ char *param; -+ -+ if (next_arg(ctx, arg) == -1) -+ return -1; -+ -+ param = ctx->argv[ctx->argctr]; -+ newarg = malloc(sep + strlen(param) + 1); -+ if (!newarg) -+ return alloc_failed(); -+ -+ memcpy(newarg, arg, sep); -+ strcpy(newarg + sep, param); -+ res = process_opt(ctx, opt, sep, newarg, iso); -+ free(newarg); -+ -+ return res; -+} -+ -+static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso) -+{ -+ unsigned sep; -+ const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); -+ if (opt) { -+ for (; opt; opt = find_opt(opt + 1, arg, &sep)) { -+ int res; -+ if (sep && opt->templ[sep] == ' ' && !arg[sep]) -+ res = process_opt_sep_arg(ctx, opt, sep, arg, -+ iso); -+ else -+ res = process_opt(ctx, opt, sep, arg, iso); -+ if (res == -1) -+ return -1; -+ } -+ return 0; -+ } else -+ return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); -+} -+ -+static int process_real_option_group(struct fuse_opt_context *ctx, char *opts) -+{ -+ char *s = opts; -+ char *d = s; -+ int end = 0; -+ -+ while (!end) { -+ if (*s == '\0') -+ end = 1; -+ if (*s == ',' || end) { -+ int res; -+ -+ *d = '\0'; -+ res = process_gopt(ctx, opts, 1); -+ if (res == -1) -+ return -1; -+ d = opts; -+ } else { -+ if (s[0] == '\\' && s[1] != '\0') { -+ s++; -+ if (s[0] >= '0' && s[0] <= '3' && -+ s[1] >= '0' && s[1] <= '7' && -+ s[2] >= '0' && s[2] <= '7') { -+ *d++ = (s[0] - '0') * 0100 + -+ (s[1] - '0') * 0010 + -+ (s[2] - '0'); -+ s += 2; -+ } else { -+ *d++ = *s; -+ } -+ } else { -+ *d++ = *s; -+ } -+ } -+ s++; -+ } -+ -+ return 0; -+} -+ -+static int process_option_group(struct fuse_opt_context *ctx, const char *opts) -+{ -+ int res; -+ char *copy = strdup(opts); -+ -+ if (!copy) { -+ fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); -+ return -1; -+ } -+ res = process_real_option_group(ctx, copy); -+ free(copy); -+ return res; -+} -+ -+static int process_one(struct fuse_opt_context *ctx, const char *arg) -+{ -+ if (ctx->nonopt || arg[0] != '-') -+ return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); -+ else if (arg[1] == 'o') { -+ if (arg[2]) -+ return process_option_group(ctx, arg + 2); -+ else { -+ if (next_arg(ctx, arg) == -1) -+ return -1; -+ -+ return process_option_group(ctx, -+ ctx->argv[ctx->argctr]); -+ } -+ } else if (arg[1] == '-' && !arg[2]) { -+ if (add_arg(ctx, arg) == -1) -+ return -1; -+ ctx->nonopt = ctx->outargs.argc; -+ return 0; -+ } else -+ return process_gopt(ctx, arg, 0); -+} -+ -+static int opt_parse(struct fuse_opt_context *ctx) -+{ -+ if (ctx->argc) { -+ if (add_arg(ctx, ctx->argv[0]) == -1) -+ return -1; -+ } -+ -+ for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) -+ if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) -+ return -1; -+ -+ if (ctx->opts) { -+ if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 || -+ fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) -+ return -1; -+ } -+ -+ /* If option separator ("--") is the last argument, remove it */ -+ if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc && -+ strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) { -+ free(ctx->outargs.argv[ctx->outargs.argc - 1]); -+ ctx->outargs.argv[--ctx->outargs.argc] = NULL; -+ } -+ -+ return 0; -+} -+ -+int fuse_opt_parse(struct fuse_args *args, void *data, -+ const struct fuse_opt opts[], fuse_opt_proc_t proc) -+{ -+ int res; -+ struct fuse_opt_context ctx = { -+ .data = data, -+ .opt = opts, -+ .proc = proc, -+ }; -+ -+ if (!args || !args->argv || !args->argc) -+ return 0; -+ -+ ctx.argc = args->argc; -+ ctx.argv = args->argv; -+ -+ res = opt_parse(&ctx); -+ if (res != -1) { -+ struct fuse_args tmp = *args; -+ *args = ctx.outargs; -+ ctx.outargs = tmp; -+ } -+ free(ctx.opts); -+ fuse_opt_free_args(&ctx.outargs); -+ return res; -+} -diff --git a/tools/virtiofsd/fuse_signals.c b/tools/virtiofsd/fuse_signals.c -new file mode 100644 -index 0000000000..4271947bd4 ---- /dev/null -+++ b/tools/virtiofsd/fuse_signals.c -@@ -0,0 +1,91 @@ -+/* -+ FUSE: Filesystem in Userspace -+ Copyright (C) 2001-2007 Miklos Szeredi -+ -+ Utility functions for setting signal handlers. -+ -+ This program can be distributed under the terms of the GNU LGPLv2. -+ See the file COPYING.LIB -+*/ -+ -+#include "config.h" -+#include "fuse_lowlevel.h" -+#include "fuse_i.h" -+ -+#include -+#include -+#include -+#include -+ -+static struct fuse_session *fuse_instance; -+ -+static void exit_handler(int sig) -+{ -+ if (fuse_instance) { -+ fuse_session_exit(fuse_instance); -+ if(sig <= 0) { -+ fuse_log(FUSE_LOG_ERR, "assertion error: signal value <= 0\n"); -+ abort(); -+ } -+ fuse_instance->error = sig; -+ } -+} -+ -+static void do_nothing(int sig) -+{ -+ (void) sig; -+} -+ -+static int set_one_signal_handler(int sig, void (*handler)(int), int remove) -+{ -+ struct sigaction sa; -+ struct sigaction old_sa; -+ -+ memset(&sa, 0, sizeof(struct sigaction)); -+ sa.sa_handler = remove ? SIG_DFL : handler; -+ sigemptyset(&(sa.sa_mask)); -+ sa.sa_flags = 0; -+ -+ if (sigaction(sig, NULL, &old_sa) == -1) { -+ perror("fuse: cannot get old signal handler"); -+ return -1; -+ } -+ -+ if (old_sa.sa_handler == (remove ? handler : SIG_DFL) && -+ sigaction(sig, &sa, NULL) == -1) { -+ perror("fuse: cannot set signal handler"); -+ return -1; -+ } -+ return 0; -+} -+ -+int fuse_set_signal_handlers(struct fuse_session *se) -+{ -+ /* If we used SIG_IGN instead of the do_nothing function, -+ then we would be unable to tell if we set SIG_IGN (and -+ thus should reset to SIG_DFL in fuse_remove_signal_handlers) -+ or if it was already set to SIG_IGN (and should be left -+ untouched. */ -+ if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 || -+ set_one_signal_handler(SIGINT, exit_handler, 0) == -1 || -+ set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 || -+ set_one_signal_handler(SIGPIPE, do_nothing, 0) == -1) -+ return -1; -+ -+ fuse_instance = se; -+ return 0; -+} -+ -+void fuse_remove_signal_handlers(struct fuse_session *se) -+{ -+ if (fuse_instance != se) -+ fuse_log(FUSE_LOG_ERR, -+ "fuse: fuse_remove_signal_handlers: unknown session\n"); -+ else -+ fuse_instance = NULL; -+ -+ set_one_signal_handler(SIGHUP, exit_handler, 1); -+ set_one_signal_handler(SIGINT, exit_handler, 1); -+ set_one_signal_handler(SIGTERM, exit_handler, 1); -+ set_one_signal_handler(SIGPIPE, do_nothing, 1); -+} -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -new file mode 100644 -index 0000000000..64ff7ad6d5 ---- /dev/null -+++ b/tools/virtiofsd/helper.c -@@ -0,0 +1,440 @@ -+/* -+ FUSE: Filesystem in Userspace -+ Copyright (C) 2001-2007 Miklos Szeredi -+ -+ Helper functions to create (simple) standalone programs. With the -+ aid of these functions it should be possible to create full FUSE -+ file system by implementing nothing but the request handlers. -+ -+ This program can be distributed under the terms of the GNU LGPLv2. -+ See the file COPYING.LIB. -+*/ -+ -+#include "config.h" -+#include "fuse_i.h" -+#include "fuse_misc.h" -+#include "fuse_opt.h" -+#include "fuse_lowlevel.h" -+#include "mount_util.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#define FUSE_HELPER_OPT(t, p) \ -+ { t, offsetof(struct fuse_cmdline_opts, p), 1 } -+ -+static const struct fuse_opt fuse_helper_opts[] = { -+ FUSE_HELPER_OPT("-h", show_help), -+ FUSE_HELPER_OPT("--help", show_help), -+ FUSE_HELPER_OPT("-V", show_version), -+ FUSE_HELPER_OPT("--version", show_version), -+ FUSE_HELPER_OPT("-d", debug), -+ FUSE_HELPER_OPT("debug", debug), -+ FUSE_HELPER_OPT("-d", foreground), -+ FUSE_HELPER_OPT("debug", foreground), -+ FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), -+ FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), -+ FUSE_HELPER_OPT("-f", foreground), -+ FUSE_HELPER_OPT("-s", singlethread), -+ FUSE_HELPER_OPT("fsname=", nodefault_subtype), -+ FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), -+#ifndef __FreeBSD__ -+ FUSE_HELPER_OPT("subtype=", nodefault_subtype), -+ FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), -+#endif -+ FUSE_HELPER_OPT("clone_fd", clone_fd), -+ FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads), -+ FUSE_OPT_END -+}; -+ -+struct fuse_conn_info_opts { -+ int atomic_o_trunc; -+ int no_remote_posix_lock; -+ int no_remote_flock; -+ int splice_write; -+ int splice_move; -+ int splice_read; -+ int no_splice_write; -+ int no_splice_move; -+ int no_splice_read; -+ int auto_inval_data; -+ int no_auto_inval_data; -+ int no_readdirplus; -+ int no_readdirplus_auto; -+ int async_dio; -+ int no_async_dio; -+ int writeback_cache; -+ int no_writeback_cache; -+ int async_read; -+ int sync_read; -+ unsigned max_write; -+ unsigned max_readahead; -+ unsigned max_background; -+ unsigned congestion_threshold; -+ unsigned time_gran; -+ int set_max_write; -+ int set_max_readahead; -+ int set_max_background; -+ int set_congestion_threshold; -+ int set_time_gran; -+}; -+ -+#define CONN_OPTION(t, p, v) \ -+ { t, offsetof(struct fuse_conn_info_opts, p), v } -+static const struct fuse_opt conn_info_opt_spec[] = { -+ CONN_OPTION("max_write=%u", max_write, 0), -+ CONN_OPTION("max_write=", set_max_write, 1), -+ CONN_OPTION("max_readahead=%u", max_readahead, 0), -+ CONN_OPTION("max_readahead=", set_max_readahead, 1), -+ CONN_OPTION("max_background=%u", max_background, 0), -+ CONN_OPTION("max_background=", set_max_background, 1), -+ CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0), -+ CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1), -+ CONN_OPTION("sync_read", sync_read, 1), -+ CONN_OPTION("async_read", async_read, 1), -+ CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1), -+ CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1), -+ CONN_OPTION("no_remote_lock", no_remote_flock, 1), -+ CONN_OPTION("no_remote_flock", no_remote_flock, 1), -+ CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1), -+ CONN_OPTION("splice_write", splice_write, 1), -+ CONN_OPTION("no_splice_write", no_splice_write, 1), -+ CONN_OPTION("splice_move", splice_move, 1), -+ CONN_OPTION("no_splice_move", no_splice_move, 1), -+ CONN_OPTION("splice_read", splice_read, 1), -+ CONN_OPTION("no_splice_read", no_splice_read, 1), -+ CONN_OPTION("auto_inval_data", auto_inval_data, 1), -+ CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1), -+ CONN_OPTION("readdirplus=no", no_readdirplus, 1), -+ CONN_OPTION("readdirplus=yes", no_readdirplus, 0), -+ CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1), -+ CONN_OPTION("readdirplus=auto", no_readdirplus, 0), -+ CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0), -+ CONN_OPTION("async_dio", async_dio, 1), -+ CONN_OPTION("no_async_dio", no_async_dio, 1), -+ CONN_OPTION("writeback_cache", writeback_cache, 1), -+ CONN_OPTION("no_writeback_cache", no_writeback_cache, 1), -+ CONN_OPTION("time_gran=%u", time_gran, 0), -+ CONN_OPTION("time_gran=", set_time_gran, 1), -+ FUSE_OPT_END -+}; -+ -+ -+void fuse_cmdline_help(void) -+{ -+ printf(" -h --help print help\n" -+ " -V --version print version\n" -+ " -d -o debug enable debug output (implies -f)\n" -+ " -f foreground operation\n" -+ " -s disable multi-threaded operation\n" -+ " -o clone_fd use separate fuse device fd for each thread\n" -+ " (may improve performance)\n" -+ " -o max_idle_threads the maximum number of idle worker threads\n" -+ " allowed (default: 10)\n"); -+} -+ -+static int fuse_helper_opt_proc(void *data, const char *arg, int key, -+ struct fuse_args *outargs) -+{ -+ (void) outargs; -+ struct fuse_cmdline_opts *opts = data; -+ -+ switch (key) { -+ case FUSE_OPT_KEY_NONOPT: -+ if (!opts->mountpoint) { -+ if (fuse_mnt_parse_fuse_fd(arg) != -1) { -+ return fuse_opt_add_opt(&opts->mountpoint, arg); -+ } -+ -+ char mountpoint[PATH_MAX] = ""; -+ if (realpath(arg, mountpoint) == NULL) { -+ fuse_log(FUSE_LOG_ERR, -+ "fuse: bad mount point `%s': %s\n", -+ arg, strerror(errno)); -+ return -1; -+ } -+ return fuse_opt_add_opt(&opts->mountpoint, mountpoint); -+ } else { -+ fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg); -+ return -1; -+ } -+ -+ default: -+ /* Pass through unknown options */ -+ return 1; -+ } -+} -+ -+/* Under FreeBSD, there is no subtype option so this -+ function actually sets the fsname */ -+static int add_default_subtype(const char *progname, struct fuse_args *args) -+{ -+ int res; -+ char *subtype_opt; -+ -+ const char *basename = strrchr(progname, '/'); -+ if (basename == NULL) -+ basename = progname; -+ else if (basename[1] != '\0') -+ basename++; -+ -+ subtype_opt = (char *) malloc(strlen(basename) + 64); -+ if (subtype_opt == NULL) { -+ fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); -+ return -1; -+ } -+#ifdef __FreeBSD__ -+ sprintf(subtype_opt, "-ofsname=%s", basename); -+#else -+ sprintf(subtype_opt, "-osubtype=%s", basename); -+#endif -+ res = fuse_opt_add_arg(args, subtype_opt); -+ free(subtype_opt); -+ return res; -+} -+ -+int fuse_parse_cmdline(struct fuse_args *args, -+ struct fuse_cmdline_opts *opts) -+{ -+ memset(opts, 0, sizeof(struct fuse_cmdline_opts)); -+ -+ opts->max_idle_threads = 10; -+ -+ if (fuse_opt_parse(args, opts, fuse_helper_opts, -+ fuse_helper_opt_proc) == -1) -+ return -1; -+ -+ /* *Linux*: if neither -o subtype nor -o fsname are specified, -+ set subtype to program's basename. -+ *FreeBSD*: if fsname is not specified, set to program's -+ basename. */ -+ if (!opts->nodefault_subtype) -+ if (add_default_subtype(args->argv[0], args) == -1) -+ return -1; -+ -+ return 0; -+} -+ -+ -+int fuse_daemonize(int foreground) -+{ -+ if (!foreground) { -+ int nullfd; -+ int waiter[2]; -+ char completed; -+ -+ if (pipe(waiter)) { -+ perror("fuse_daemonize: pipe"); -+ return -1; -+ } -+ -+ /* -+ * demonize current process by forking it and killing the -+ * parent. This makes current process as a child of 'init'. -+ */ -+ switch(fork()) { -+ case -1: -+ perror("fuse_daemonize: fork"); -+ return -1; -+ case 0: -+ break; -+ default: -+ (void) read(waiter[0], &completed, sizeof(completed)); -+ _exit(0); -+ } -+ -+ if (setsid() == -1) { -+ perror("fuse_daemonize: setsid"); -+ return -1; -+ } -+ -+ (void) chdir("/"); -+ -+ nullfd = open("/dev/null", O_RDWR, 0); -+ if (nullfd != -1) { -+ (void) dup2(nullfd, 0); -+ (void) dup2(nullfd, 1); -+ (void) dup2(nullfd, 2); -+ if (nullfd > 2) -+ close(nullfd); -+ } -+ -+ /* Propagate completion of daemon initialization */ -+ completed = 1; -+ (void) write(waiter[1], &completed, sizeof(completed)); -+ close(waiter[0]); -+ close(waiter[1]); -+ } else { -+ (void) chdir("/"); -+ } -+ return 0; -+} -+ -+int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, -+ size_t op_size, void *user_data) -+{ -+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv); -+ struct fuse *fuse; -+ struct fuse_cmdline_opts opts; -+ int res; -+ -+ if (fuse_parse_cmdline(&args, &opts) != 0) -+ return 1; -+ -+ if (opts.show_version) { -+ printf("FUSE library version %s\n", PACKAGE_VERSION); -+ fuse_lowlevel_version(); -+ res = 0; -+ goto out1; -+ } -+ -+ if (opts.show_help) { -+ if(args.argv[0][0] != '\0') -+ printf("usage: %s [options] \n\n", -+ args.argv[0]); -+ printf("FUSE options:\n"); -+ fuse_cmdline_help(); -+ fuse_lib_help(&args); -+ res = 0; -+ goto out1; -+ } -+ -+ if (!opts.show_help && -+ !opts.mountpoint) { -+ fuse_log(FUSE_LOG_ERR, "error: no mountpoint specified\n"); -+ res = 2; -+ goto out1; -+ } -+ -+ -+ fuse = fuse_new_31(&args, op, op_size, user_data); -+ if (fuse == NULL) { -+ res = 3; -+ goto out1; -+ } -+ -+ if (fuse_mount(fuse,opts.mountpoint) != 0) { -+ res = 4; -+ goto out2; -+ } -+ -+ if (fuse_daemonize(opts.foreground) != 0) { -+ res = 5; -+ goto out3; -+ } -+ -+ struct fuse_session *se = fuse_get_session(fuse); -+ if (fuse_set_signal_handlers(se) != 0) { -+ res = 6; -+ goto out3; -+ } -+ -+ if (opts.singlethread) -+ res = fuse_loop(fuse); -+ else { -+ struct fuse_loop_config loop_config; -+ loop_config.clone_fd = opts.clone_fd; -+ loop_config.max_idle_threads = opts.max_idle_threads; -+ res = fuse_loop_mt_32(fuse, &loop_config); -+ } -+ if (res) -+ res = 7; -+ -+ fuse_remove_signal_handlers(se); -+out3: -+ fuse_unmount(fuse); -+out2: -+ fuse_destroy(fuse); -+out1: -+ free(opts.mountpoint); -+ fuse_opt_free_args(&args); -+ return res; -+} -+ -+ -+void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, -+ struct fuse_conn_info *conn) -+{ -+ if(opts->set_max_write) -+ conn->max_write = opts->max_write; -+ if(opts->set_max_background) -+ conn->max_background = opts->max_background; -+ if(opts->set_congestion_threshold) -+ conn->congestion_threshold = opts->congestion_threshold; -+ if(opts->set_time_gran) -+ conn->time_gran = opts->time_gran; -+ if(opts->set_max_readahead) -+ conn->max_readahead = opts->max_readahead; -+ -+#define LL_ENABLE(cond,cap) \ -+ if (cond) conn->want |= (cap) -+#define LL_DISABLE(cond,cap) \ -+ if (cond) conn->want &= ~(cap) -+ -+ LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ); -+ LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ); -+ -+ LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE); -+ LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE); -+ -+ LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE); -+ LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE); -+ -+ LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); -+ LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); -+ -+ LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS); -+ LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO); -+ -+ LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO); -+ LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO); -+ -+ LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE); -+ LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE); -+ -+ LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ); -+ LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ); -+ -+ LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS); -+ LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS); -+} -+ -+struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args) -+{ -+ struct fuse_conn_info_opts *opts; -+ -+ opts = calloc(1, sizeof(struct fuse_conn_info_opts)); -+ if(opts == NULL) { -+ fuse_log(FUSE_LOG_ERR, "calloc failed\n"); -+ return NULL; -+ } -+ if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) { -+ free(opts); -+ return NULL; -+ } -+ return opts; -+} -+ -+int fuse_open_channel(const char *mountpoint, const char* options) -+{ -+ struct mount_opts *opts = NULL; -+ int fd = -1; -+ const char *argv[] = { "", "-o", options }; -+ int argc = sizeof(argv) / sizeof(argv[0]); -+ struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv); -+ -+ opts = parse_mount_opts(&args); -+ if (opts == NULL) -+ return -1; -+ -+ fd = fuse_kern_mount(mountpoint, opts); -+ destroy_mount_opts(opts); -+ -+ return fd; -+} diff --git a/0013-virtiofsd-Add-fuse_lowlevel.c.patch b/0013-virtiofsd-Add-fuse_lowlevel.c.patch deleted file mode 100644 index c4a16e4..0000000 --- a/0013-virtiofsd-Add-fuse_lowlevel.c.patch +++ /dev/null @@ -1,3156 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:00:42 +0000 -Subject: [PATCH] virtiofsd: Add fuse_lowlevel.c -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -fuse_lowlevel is one of the largest files from the library -and does most of the work. Add it separately to keep the diff -sizes small. -Again this is from upstream fuse-3.8.0 - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 2de121f01e37e2fe98a4362f4abf7c0848697f76) ---- - tools/virtiofsd/fuse_lowlevel.c | 3129 +++++++++++++++++++++++++++++++ - 1 file changed, 3129 insertions(+) - create mode 100644 tools/virtiofsd/fuse_lowlevel.c - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -new file mode 100644 -index 0000000000..f2d7038e34 ---- /dev/null -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -0,0 +1,3129 @@ -+/* -+ FUSE: Filesystem in Userspace -+ Copyright (C) 2001-2007 Miklos Szeredi -+ -+ Implementation of (most of) the low-level FUSE API. The session loop -+ functions are implemented in separate files. -+ -+ This program can be distributed under the terms of the GNU LGPLv2. -+ See the file COPYING.LIB -+*/ -+ -+#define _GNU_SOURCE -+ -+#include "config.h" -+#include "fuse_i.h" -+#include "fuse_kernel.h" -+#include "fuse_opt.h" -+#include "fuse_misc.h" -+#include "mount_util.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#ifndef F_LINUX_SPECIFIC_BASE -+#define F_LINUX_SPECIFIC_BASE 1024 -+#endif -+#ifndef F_SETPIPE_SZ -+#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) -+#endif -+ -+ -+#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) -+#define OFFSET_MAX 0x7fffffffffffffffLL -+ -+#define container_of(ptr, type, member) ({ \ -+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ -+ (type *)( (char *)__mptr - offsetof(type,member) );}) -+ -+struct fuse_pollhandle { -+ uint64_t kh; -+ struct fuse_session *se; -+}; -+ -+static size_t pagesize; -+ -+static __attribute__((constructor)) void fuse_ll_init_pagesize(void) -+{ -+ pagesize = getpagesize(); -+} -+ -+static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr) -+{ -+ attr->ino = stbuf->st_ino; -+ attr->mode = stbuf->st_mode; -+ attr->nlink = stbuf->st_nlink; -+ attr->uid = stbuf->st_uid; -+ attr->gid = stbuf->st_gid; -+ attr->rdev = stbuf->st_rdev; -+ attr->size = stbuf->st_size; -+ attr->blksize = stbuf->st_blksize; -+ attr->blocks = stbuf->st_blocks; -+ attr->atime = stbuf->st_atime; -+ attr->mtime = stbuf->st_mtime; -+ attr->ctime = stbuf->st_ctime; -+ attr->atimensec = ST_ATIM_NSEC(stbuf); -+ attr->mtimensec = ST_MTIM_NSEC(stbuf); -+ attr->ctimensec = ST_CTIM_NSEC(stbuf); -+} -+ -+static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf) -+{ -+ stbuf->st_mode = attr->mode; -+ stbuf->st_uid = attr->uid; -+ stbuf->st_gid = attr->gid; -+ stbuf->st_size = attr->size; -+ stbuf->st_atime = attr->atime; -+ stbuf->st_mtime = attr->mtime; -+ stbuf->st_ctime = attr->ctime; -+ ST_ATIM_NSEC_SET(stbuf, attr->atimensec); -+ ST_MTIM_NSEC_SET(stbuf, attr->mtimensec); -+ ST_CTIM_NSEC_SET(stbuf, attr->ctimensec); -+} -+ -+static size_t iov_length(const struct iovec *iov, size_t count) -+{ -+ size_t seg; -+ size_t ret = 0; -+ -+ for (seg = 0; seg < count; seg++) -+ ret += iov[seg].iov_len; -+ return ret; -+} -+ -+static void list_init_req(struct fuse_req *req) -+{ -+ req->next = req; -+ req->prev = req; -+} -+ -+static void list_del_req(struct fuse_req *req) -+{ -+ struct fuse_req *prev = req->prev; -+ struct fuse_req *next = req->next; -+ prev->next = next; -+ next->prev = prev; -+} -+ -+static void list_add_req(struct fuse_req *req, struct fuse_req *next) -+{ -+ struct fuse_req *prev = next->prev; -+ req->next = next; -+ req->prev = prev; -+ prev->next = req; -+ next->prev = req; -+} -+ -+static void destroy_req(fuse_req_t req) -+{ -+ pthread_mutex_destroy(&req->lock); -+ free(req); -+} -+ -+void fuse_free_req(fuse_req_t req) -+{ -+ int ctr; -+ struct fuse_session *se = req->se; -+ -+ pthread_mutex_lock(&se->lock); -+ req->u.ni.func = NULL; -+ req->u.ni.data = NULL; -+ list_del_req(req); -+ ctr = --req->ctr; -+ fuse_chan_put(req->ch); -+ req->ch = NULL; -+ pthread_mutex_unlock(&se->lock); -+ if (!ctr) -+ destroy_req(req); -+} -+ -+static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se) -+{ -+ struct fuse_req *req; -+ -+ req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req)); -+ if (req == NULL) { -+ fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n"); -+ } else { -+ req->se = se; -+ req->ctr = 1; -+ list_init_req(req); -+ fuse_mutex_init(&req->lock); -+ } -+ -+ return req; -+} -+ -+/* Send data. If *ch* is NULL, send via session master fd */ -+static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, -+ struct iovec *iov, int count) -+{ -+ struct fuse_out_header *out = iov[0].iov_base; -+ -+ out->len = iov_length(iov, count); -+ if (se->debug) { -+ if (out->unique == 0) { -+ fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n", -+ out->error, out->len); -+ } else if (out->error) { -+ fuse_log(FUSE_LOG_DEBUG, -+ " unique: %llu, error: %i (%s), outsize: %i\n", -+ (unsigned long long) out->unique, out->error, -+ strerror(-out->error), out->len); -+ } else { -+ fuse_log(FUSE_LOG_DEBUG, -+ " unique: %llu, success, outsize: %i\n", -+ (unsigned long long) out->unique, out->len); -+ } -+ } -+ -+ ssize_t res = writev(ch ? ch->fd : se->fd, -+ iov, count); -+ int err = errno; -+ -+ if (res == -1) { -+ assert(se != NULL); -+ -+ /* ENOENT means the operation was interrupted */ -+ if (!fuse_session_exited(se) && err != ENOENT) -+ perror("fuse: writing device"); -+ return -err; -+ } -+ -+ return 0; -+} -+ -+ -+int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, -+ int count) -+{ -+ struct fuse_out_header out; -+ -+ if (error <= -1000 || error > 0) { -+ fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error); -+ error = -ERANGE; -+ } -+ -+ out.unique = req->unique; -+ out.error = error; -+ -+ iov[0].iov_base = &out; -+ iov[0].iov_len = sizeof(struct fuse_out_header); -+ -+ return fuse_send_msg(req->se, req->ch, iov, count); -+} -+ -+static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov, -+ int count) -+{ -+ int res; -+ -+ res = fuse_send_reply_iov_nofree(req, error, iov, count); -+ fuse_free_req(req); -+ return res; -+} -+ -+static int send_reply(fuse_req_t req, int error, const void *arg, -+ size_t argsize) -+{ -+ struct iovec iov[2]; -+ int count = 1; -+ if (argsize) { -+ iov[1].iov_base = (void *) arg; -+ iov[1].iov_len = argsize; -+ count++; -+ } -+ return send_reply_iov(req, error, iov, count); -+} -+ -+int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count) -+{ -+ int res; -+ struct iovec *padded_iov; -+ -+ padded_iov = malloc((count + 1) * sizeof(struct iovec)); -+ if (padded_iov == NULL) -+ return fuse_reply_err(req, ENOMEM); -+ -+ memcpy(padded_iov + 1, iov, count * sizeof(struct iovec)); -+ count++; -+ -+ res = send_reply_iov(req, 0, padded_iov, count); -+ free(padded_iov); -+ -+ return res; -+} -+ -+ -+/* `buf` is allowed to be empty so that the proper size may be -+ allocated by the caller */ -+size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, -+ const char *name, const struct stat *stbuf, off_t off) -+{ -+ (void)req; -+ size_t namelen; -+ size_t entlen; -+ size_t entlen_padded; -+ struct fuse_dirent *dirent; -+ -+ namelen = strlen(name); -+ entlen = FUSE_NAME_OFFSET + namelen; -+ entlen_padded = FUSE_DIRENT_ALIGN(entlen); -+ -+ if ((buf == NULL) || (entlen_padded > bufsize)) -+ return entlen_padded; -+ -+ dirent = (struct fuse_dirent*) buf; -+ dirent->ino = stbuf->st_ino; -+ dirent->off = off; -+ dirent->namelen = namelen; -+ dirent->type = (stbuf->st_mode & S_IFMT) >> 12; -+ memcpy(dirent->name, name, namelen); -+ memset(dirent->name + namelen, 0, entlen_padded - entlen); -+ -+ return entlen_padded; -+} -+ -+static void convert_statfs(const struct statvfs *stbuf, -+ struct fuse_kstatfs *kstatfs) -+{ -+ kstatfs->bsize = stbuf->f_bsize; -+ kstatfs->frsize = stbuf->f_frsize; -+ kstatfs->blocks = stbuf->f_blocks; -+ kstatfs->bfree = stbuf->f_bfree; -+ kstatfs->bavail = stbuf->f_bavail; -+ kstatfs->files = stbuf->f_files; -+ kstatfs->ffree = stbuf->f_ffree; -+ kstatfs->namelen = stbuf->f_namemax; -+} -+ -+static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize) -+{ -+ return send_reply(req, 0, arg, argsize); -+} -+ -+int fuse_reply_err(fuse_req_t req, int err) -+{ -+ return send_reply(req, -err, NULL, 0); -+} -+ -+void fuse_reply_none(fuse_req_t req) -+{ -+ fuse_free_req(req); -+} -+ -+static unsigned long calc_timeout_sec(double t) -+{ -+ if (t > (double) ULONG_MAX) -+ return ULONG_MAX; -+ else if (t < 0.0) -+ return 0; -+ else -+ return (unsigned long) t; -+} -+ -+static unsigned int calc_timeout_nsec(double t) -+{ -+ double f = t - (double) calc_timeout_sec(t); -+ if (f < 0.0) -+ return 0; -+ else if (f >= 0.999999999) -+ return 999999999; -+ else -+ return (unsigned int) (f * 1.0e9); -+} -+ -+static void fill_entry(struct fuse_entry_out *arg, -+ const struct fuse_entry_param *e) -+{ -+ arg->nodeid = e->ino; -+ arg->generation = e->generation; -+ arg->entry_valid = calc_timeout_sec(e->entry_timeout); -+ arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout); -+ arg->attr_valid = calc_timeout_sec(e->attr_timeout); -+ arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout); -+ convert_stat(&e->attr, &arg->attr); -+} -+ -+/* `buf` is allowed to be empty so that the proper size may be -+ allocated by the caller */ -+size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, -+ const char *name, -+ const struct fuse_entry_param *e, off_t off) -+{ -+ (void)req; -+ size_t namelen; -+ size_t entlen; -+ size_t entlen_padded; -+ -+ namelen = strlen(name); -+ entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen; -+ entlen_padded = FUSE_DIRENT_ALIGN(entlen); -+ if ((buf == NULL) || (entlen_padded > bufsize)) -+ return entlen_padded; -+ -+ struct fuse_direntplus *dp = (struct fuse_direntplus *) buf; -+ memset(&dp->entry_out, 0, sizeof(dp->entry_out)); -+ fill_entry(&dp->entry_out, e); -+ -+ struct fuse_dirent *dirent = &dp->dirent; -+ dirent->ino = e->attr.st_ino; -+ dirent->off = off; -+ dirent->namelen = namelen; -+ dirent->type = (e->attr.st_mode & S_IFMT) >> 12; -+ memcpy(dirent->name, name, namelen); -+ memset(dirent->name + namelen, 0, entlen_padded - entlen); -+ -+ return entlen_padded; -+} -+ -+static void fill_open(struct fuse_open_out *arg, -+ const struct fuse_file_info *f) -+{ -+ arg->fh = f->fh; -+ if (f->direct_io) -+ arg->open_flags |= FOPEN_DIRECT_IO; -+ if (f->keep_cache) -+ arg->open_flags |= FOPEN_KEEP_CACHE; -+ if (f->cache_readdir) -+ arg->open_flags |= FOPEN_CACHE_DIR; -+ if (f->nonseekable) -+ arg->open_flags |= FOPEN_NONSEEKABLE; -+} -+ -+int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e) -+{ -+ struct fuse_entry_out arg; -+ size_t size = req->se->conn.proto_minor < 9 ? -+ FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg); -+ -+ /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant -+ negative entry */ -+ if (!e->ino && req->se->conn.proto_minor < 4) -+ return fuse_reply_err(req, ENOENT); -+ -+ memset(&arg, 0, sizeof(arg)); -+ fill_entry(&arg, e); -+ return send_reply_ok(req, &arg, size); -+} -+ -+int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, -+ const struct fuse_file_info *f) -+{ -+ char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)]; -+ size_t entrysize = req->se->conn.proto_minor < 9 ? -+ FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out); -+ struct fuse_entry_out *earg = (struct fuse_entry_out *) buf; -+ struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize); -+ -+ memset(buf, 0, sizeof(buf)); -+ fill_entry(earg, e); -+ fill_open(oarg, f); -+ return send_reply_ok(req, buf, -+ entrysize + sizeof(struct fuse_open_out)); -+} -+ -+int fuse_reply_attr(fuse_req_t req, const struct stat *attr, -+ double attr_timeout) -+{ -+ struct fuse_attr_out arg; -+ size_t size = req->se->conn.proto_minor < 9 ? -+ FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg); -+ -+ memset(&arg, 0, sizeof(arg)); -+ arg.attr_valid = calc_timeout_sec(attr_timeout); -+ arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout); -+ convert_stat(attr, &arg.attr); -+ -+ return send_reply_ok(req, &arg, size); -+} -+ -+int fuse_reply_readlink(fuse_req_t req, const char *linkname) -+{ -+ return send_reply_ok(req, linkname, strlen(linkname)); -+} -+ -+int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f) -+{ -+ struct fuse_open_out arg; -+ -+ memset(&arg, 0, sizeof(arg)); -+ fill_open(&arg, f); -+ return send_reply_ok(req, &arg, sizeof(arg)); -+} -+ -+int fuse_reply_write(fuse_req_t req, size_t count) -+{ -+ struct fuse_write_out arg; -+ -+ memset(&arg, 0, sizeof(arg)); -+ arg.size = count; -+ -+ return send_reply_ok(req, &arg, sizeof(arg)); -+} -+ -+int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size) -+{ -+ return send_reply_ok(req, buf, size); -+} -+ -+static int fuse_send_data_iov_fallback(struct fuse_session *se, -+ struct fuse_chan *ch, -+ struct iovec *iov, int iov_count, -+ struct fuse_bufvec *buf, -+ size_t len) -+{ -+ struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len); -+ void *mbuf; -+ int res; -+ -+ /* Optimize common case */ -+ if (buf->count == 1 && buf->idx == 0 && buf->off == 0 && -+ !(buf->buf[0].flags & FUSE_BUF_IS_FD)) { -+ /* FIXME: also avoid memory copy if there are multiple buffers -+ but none of them contain an fd */ -+ -+ iov[iov_count].iov_base = buf->buf[0].mem; -+ iov[iov_count].iov_len = len; -+ iov_count++; -+ return fuse_send_msg(se, ch, iov, iov_count); -+ } -+ -+ res = posix_memalign(&mbuf, pagesize, len); -+ if (res != 0) -+ return res; -+ -+ mem_buf.buf[0].mem = mbuf; -+ res = fuse_buf_copy(&mem_buf, buf, 0); -+ if (res < 0) { -+ free(mbuf); -+ return -res; -+ } -+ len = res; -+ -+ iov[iov_count].iov_base = mbuf; -+ iov[iov_count].iov_len = len; -+ iov_count++; -+ res = fuse_send_msg(se, ch, iov, iov_count); -+ free(mbuf); -+ -+ return res; -+} -+ -+struct fuse_ll_pipe { -+ size_t size; -+ int can_grow; -+ int pipe[2]; -+}; -+ -+static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp) -+{ -+ close(llp->pipe[0]); -+ close(llp->pipe[1]); -+ free(llp); -+} -+ -+#ifdef HAVE_SPLICE -+#if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC) -+static int fuse_pipe(int fds[2]) -+{ -+ int rv = pipe(fds); -+ -+ if (rv == -1) -+ return rv; -+ -+ if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 || -+ fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1 || -+ fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || -+ fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { -+ close(fds[0]); -+ close(fds[1]); -+ rv = -1; -+ } -+ return rv; -+} -+#else -+static int fuse_pipe(int fds[2]) -+{ -+ return pipe2(fds, O_CLOEXEC | O_NONBLOCK); -+} -+#endif -+ -+static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se) -+{ -+ struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key); -+ if (llp == NULL) { -+ int res; -+ -+ llp = malloc(sizeof(struct fuse_ll_pipe)); -+ if (llp == NULL) -+ return NULL; -+ -+ res = fuse_pipe(llp->pipe); -+ if (res == -1) { -+ free(llp); -+ return NULL; -+ } -+ -+ /* -+ *the default size is 16 pages on linux -+ */ -+ llp->size = pagesize * 16; -+ llp->can_grow = 1; -+ -+ pthread_setspecific(se->pipe_key, llp); -+ } -+ -+ return llp; -+} -+#endif -+ -+static void fuse_ll_clear_pipe(struct fuse_session *se) -+{ -+ struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key); -+ if (llp) { -+ pthread_setspecific(se->pipe_key, NULL); -+ fuse_ll_pipe_free(llp); -+ } -+} -+ -+#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE) -+static int read_back(int fd, char *buf, size_t len) -+{ -+ int res; -+ -+ res = read(fd, buf, len); -+ if (res == -1) { -+ fuse_log(FUSE_LOG_ERR, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno)); -+ return -EIO; -+ } -+ if (res != len) { -+ fuse_log(FUSE_LOG_ERR, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len); -+ return -EIO; -+ } -+ return 0; -+} -+ -+static int grow_pipe_to_max(int pipefd) -+{ -+ int max; -+ int res; -+ int maxfd; -+ char buf[32]; -+ -+ maxfd = open("/proc/sys/fs/pipe-max-size", O_RDONLY); -+ if (maxfd < 0) -+ return -errno; -+ -+ res = read(maxfd, buf, sizeof(buf) - 1); -+ if (res < 0) { -+ int saved_errno; -+ -+ saved_errno = errno; -+ close(maxfd); -+ return -saved_errno; -+ } -+ close(maxfd); -+ buf[res] = '\0'; -+ -+ max = atoi(buf); -+ res = fcntl(pipefd, F_SETPIPE_SZ, max); -+ if (res < 0) -+ return -errno; -+ return max; -+} -+ -+static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, -+ struct iovec *iov, int iov_count, -+ struct fuse_bufvec *buf, unsigned int flags) -+{ -+ int res; -+ size_t len = fuse_buf_size(buf); -+ struct fuse_out_header *out = iov[0].iov_base; -+ struct fuse_ll_pipe *llp; -+ int splice_flags; -+ size_t pipesize; -+ size_t total_fd_size; -+ size_t idx; -+ size_t headerlen; -+ struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len); -+ -+ if (se->broken_splice_nonblock) -+ goto fallback; -+ -+ if (flags & FUSE_BUF_NO_SPLICE) -+ goto fallback; -+ -+ total_fd_size = 0; -+ for (idx = buf->idx; idx < buf->count; idx++) { -+ if (buf->buf[idx].flags & FUSE_BUF_IS_FD) { -+ total_fd_size = buf->buf[idx].size; -+ if (idx == buf->idx) -+ total_fd_size -= buf->off; -+ } -+ } -+ if (total_fd_size < 2 * pagesize) -+ goto fallback; -+ -+ if (se->conn.proto_minor < 14 || -+ !(se->conn.want & FUSE_CAP_SPLICE_WRITE)) -+ goto fallback; -+ -+ llp = fuse_ll_get_pipe(se); -+ if (llp == NULL) -+ goto fallback; -+ -+ -+ headerlen = iov_length(iov, iov_count); -+ -+ out->len = headerlen + len; -+ -+ /* -+ * Heuristic for the required pipe size, does not work if the -+ * source contains less than page size fragments -+ */ -+ pipesize = pagesize * (iov_count + buf->count + 1) + out->len; -+ -+ if (llp->size < pipesize) { -+ if (llp->can_grow) { -+ res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize); -+ if (res == -1) { -+ res = grow_pipe_to_max(llp->pipe[0]); -+ if (res > 0) -+ llp->size = res; -+ llp->can_grow = 0; -+ goto fallback; -+ } -+ llp->size = res; -+ } -+ if (llp->size < pipesize) -+ goto fallback; -+ } -+ -+ -+ res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK); -+ if (res == -1) -+ goto fallback; -+ -+ if (res != headerlen) { -+ res = -EIO; -+ fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res, -+ headerlen); -+ goto clear_pipe; -+ } -+ -+ pipe_buf.buf[0].flags = FUSE_BUF_IS_FD; -+ pipe_buf.buf[0].fd = llp->pipe[1]; -+ -+ res = fuse_buf_copy(&pipe_buf, buf, -+ FUSE_BUF_FORCE_SPLICE | FUSE_BUF_SPLICE_NONBLOCK); -+ if (res < 0) { -+ if (res == -EAGAIN || res == -EINVAL) { -+ /* -+ * Should only get EAGAIN on kernels with -+ * broken SPLICE_F_NONBLOCK support (<= -+ * 2.6.35) where this error or a short read is -+ * returned even if the pipe itself is not -+ * full -+ * -+ * EINVAL might mean that splice can't handle -+ * this combination of input and output. -+ */ -+ if (res == -EAGAIN) -+ se->broken_splice_nonblock = 1; -+ -+ pthread_setspecific(se->pipe_key, NULL); -+ fuse_ll_pipe_free(llp); -+ goto fallback; -+ } -+ res = -res; -+ goto clear_pipe; -+ } -+ -+ if (res != 0 && res < len) { -+ struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len); -+ void *mbuf; -+ size_t now_len = res; -+ /* -+ * For regular files a short count is either -+ * 1) due to EOF, or -+ * 2) because of broken SPLICE_F_NONBLOCK (see above) -+ * -+ * For other inputs it's possible that we overflowed -+ * the pipe because of small buffer fragments. -+ */ -+ -+ res = posix_memalign(&mbuf, pagesize, len); -+ if (res != 0) -+ goto clear_pipe; -+ -+ mem_buf.buf[0].mem = mbuf; -+ mem_buf.off = now_len; -+ res = fuse_buf_copy(&mem_buf, buf, 0); -+ if (res > 0) { -+ char *tmpbuf; -+ size_t extra_len = res; -+ /* -+ * Trickiest case: got more data. Need to get -+ * back the data from the pipe and then fall -+ * back to regular write. -+ */ -+ tmpbuf = malloc(headerlen); -+ if (tmpbuf == NULL) { -+ free(mbuf); -+ res = ENOMEM; -+ goto clear_pipe; -+ } -+ res = read_back(llp->pipe[0], tmpbuf, headerlen); -+ free(tmpbuf); -+ if (res != 0) { -+ free(mbuf); -+ goto clear_pipe; -+ } -+ res = read_back(llp->pipe[0], mbuf, now_len); -+ if (res != 0) { -+ free(mbuf); -+ goto clear_pipe; -+ } -+ len = now_len + extra_len; -+ iov[iov_count].iov_base = mbuf; -+ iov[iov_count].iov_len = len; -+ iov_count++; -+ res = fuse_send_msg(se, ch, iov, iov_count); -+ free(mbuf); -+ return res; -+ } -+ free(mbuf); -+ res = now_len; -+ } -+ len = res; -+ out->len = headerlen + len; -+ -+ if (se->debug) { -+ fuse_log(FUSE_LOG_DEBUG, -+ " unique: %llu, success, outsize: %i (splice)\n", -+ (unsigned long long) out->unique, out->len); -+ } -+ -+ splice_flags = 0; -+ if ((flags & FUSE_BUF_SPLICE_MOVE) && -+ (se->conn.want & FUSE_CAP_SPLICE_MOVE)) -+ splice_flags |= SPLICE_F_MOVE; -+ -+ res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, -+ NULL, out->len, splice_flags); -+ if (res == -1) { -+ res = -errno; -+ perror("fuse: splice from pipe"); -+ goto clear_pipe; -+ } -+ if (res != out->len) { -+ res = -EIO; -+ fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n", -+ res, out->len); -+ goto clear_pipe; -+ } -+ return 0; -+ -+clear_pipe: -+ fuse_ll_clear_pipe(se); -+ return res; -+ -+fallback: -+ return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); -+} -+#else -+static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, -+ struct iovec *iov, int iov_count, -+ struct fuse_bufvec *buf, unsigned int flags) -+{ -+ size_t len = fuse_buf_size(buf); -+ (void) flags; -+ -+ return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); -+} -+#endif -+ -+int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, -+ enum fuse_buf_copy_flags flags) -+{ -+ struct iovec iov[2]; -+ struct fuse_out_header out; -+ int res; -+ -+ iov[0].iov_base = &out; -+ iov[0].iov_len = sizeof(struct fuse_out_header); -+ -+ out.unique = req->unique; -+ out.error = 0; -+ -+ res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags); -+ if (res <= 0) { -+ fuse_free_req(req); -+ return res; -+ } else { -+ return fuse_reply_err(req, res); -+ } -+} -+ -+int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf) -+{ -+ struct fuse_statfs_out arg; -+ size_t size = req->se->conn.proto_minor < 4 ? -+ FUSE_COMPAT_STATFS_SIZE : sizeof(arg); -+ -+ memset(&arg, 0, sizeof(arg)); -+ convert_statfs(stbuf, &arg.st); -+ -+ return send_reply_ok(req, &arg, size); -+} -+ -+int fuse_reply_xattr(fuse_req_t req, size_t count) -+{ -+ struct fuse_getxattr_out arg; -+ -+ memset(&arg, 0, sizeof(arg)); -+ arg.size = count; -+ -+ return send_reply_ok(req, &arg, sizeof(arg)); -+} -+ -+int fuse_reply_lock(fuse_req_t req, const struct flock *lock) -+{ -+ struct fuse_lk_out arg; -+ -+ memset(&arg, 0, sizeof(arg)); -+ arg.lk.type = lock->l_type; -+ if (lock->l_type != F_UNLCK) { -+ arg.lk.start = lock->l_start; -+ if (lock->l_len == 0) -+ arg.lk.end = OFFSET_MAX; -+ else -+ arg.lk.end = lock->l_start + lock->l_len - 1; -+ } -+ arg.lk.pid = lock->l_pid; -+ return send_reply_ok(req, &arg, sizeof(arg)); -+} -+ -+int fuse_reply_bmap(fuse_req_t req, uint64_t idx) -+{ -+ struct fuse_bmap_out arg; -+ -+ memset(&arg, 0, sizeof(arg)); -+ arg.block = idx; -+ -+ return send_reply_ok(req, &arg, sizeof(arg)); -+} -+ -+static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov, -+ size_t count) -+{ -+ struct fuse_ioctl_iovec *fiov; -+ size_t i; -+ -+ fiov = malloc(sizeof(fiov[0]) * count); -+ if (!fiov) -+ return NULL; -+ -+ for (i = 0; i < count; i++) { -+ fiov[i].base = (uintptr_t) iov[i].iov_base; -+ fiov[i].len = iov[i].iov_len; -+ } -+ -+ return fiov; -+} -+ -+int fuse_reply_ioctl_retry(fuse_req_t req, -+ const struct iovec *in_iov, size_t in_count, -+ const struct iovec *out_iov, size_t out_count) -+{ -+ struct fuse_ioctl_out arg; -+ struct fuse_ioctl_iovec *in_fiov = NULL; -+ struct fuse_ioctl_iovec *out_fiov = NULL; -+ struct iovec iov[4]; -+ size_t count = 1; -+ int res; -+ -+ memset(&arg, 0, sizeof(arg)); -+ arg.flags |= FUSE_IOCTL_RETRY; -+ arg.in_iovs = in_count; -+ arg.out_iovs = out_count; -+ iov[count].iov_base = &arg; -+ iov[count].iov_len = sizeof(arg); -+ count++; -+ -+ if (req->se->conn.proto_minor < 16) { -+ if (in_count) { -+ iov[count].iov_base = (void *)in_iov; -+ iov[count].iov_len = sizeof(in_iov[0]) * in_count; -+ count++; -+ } -+ -+ if (out_count) { -+ iov[count].iov_base = (void *)out_iov; -+ iov[count].iov_len = sizeof(out_iov[0]) * out_count; -+ count++; -+ } -+ } else { -+ /* Can't handle non-compat 64bit ioctls on 32bit */ -+ if (sizeof(void *) == 4 && req->ioctl_64bit) { -+ res = fuse_reply_err(req, EINVAL); -+ goto out; -+ } -+ -+ if (in_count) { -+ in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count); -+ if (!in_fiov) -+ goto enomem; -+ -+ iov[count].iov_base = (void *)in_fiov; -+ iov[count].iov_len = sizeof(in_fiov[0]) * in_count; -+ count++; -+ } -+ if (out_count) { -+ out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count); -+ if (!out_fiov) -+ goto enomem; -+ -+ iov[count].iov_base = (void *)out_fiov; -+ iov[count].iov_len = sizeof(out_fiov[0]) * out_count; -+ count++; -+ } -+ } -+ -+ res = send_reply_iov(req, 0, iov, count); -+out: -+ free(in_fiov); -+ free(out_fiov); -+ -+ return res; -+ -+enomem: -+ res = fuse_reply_err(req, ENOMEM); -+ goto out; -+} -+ -+int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size) -+{ -+ struct fuse_ioctl_out arg; -+ struct iovec iov[3]; -+ size_t count = 1; -+ -+ memset(&arg, 0, sizeof(arg)); -+ arg.result = result; -+ iov[count].iov_base = &arg; -+ iov[count].iov_len = sizeof(arg); -+ count++; -+ -+ if (size) { -+ iov[count].iov_base = (char *) buf; -+ iov[count].iov_len = size; -+ count++; -+ } -+ -+ return send_reply_iov(req, 0, iov, count); -+} -+ -+int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, -+ int count) -+{ -+ struct iovec *padded_iov; -+ struct fuse_ioctl_out arg; -+ int res; -+ -+ padded_iov = malloc((count + 2) * sizeof(struct iovec)); -+ if (padded_iov == NULL) -+ return fuse_reply_err(req, ENOMEM); -+ -+ memset(&arg, 0, sizeof(arg)); -+ arg.result = result; -+ padded_iov[1].iov_base = &arg; -+ padded_iov[1].iov_len = sizeof(arg); -+ -+ memcpy(&padded_iov[2], iov, count * sizeof(struct iovec)); -+ -+ res = send_reply_iov(req, 0, padded_iov, count + 2); -+ free(padded_iov); -+ -+ return res; -+} -+ -+int fuse_reply_poll(fuse_req_t req, unsigned revents) -+{ -+ struct fuse_poll_out arg; -+ -+ memset(&arg, 0, sizeof(arg)); -+ arg.revents = revents; -+ -+ return send_reply_ok(req, &arg, sizeof(arg)); -+} -+ -+int fuse_reply_lseek(fuse_req_t req, off_t off) -+{ -+ struct fuse_lseek_out arg; -+ -+ memset(&arg, 0, sizeof(arg)); -+ arg.offset = off; -+ -+ return send_reply_ok(req, &arg, sizeof(arg)); -+} -+ -+static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ char *name = (char *) inarg; -+ -+ if (req->se->op.lookup) -+ req->se->op.lookup(req, nodeid, name); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg; -+ -+ if (req->se->op.forget) -+ req->se->op.forget(req, nodeid, arg->nlookup); -+ else -+ fuse_reply_none(req); -+} -+ -+static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid, -+ const void *inarg) -+{ -+ struct fuse_batch_forget_in *arg = (void *) inarg; -+ struct fuse_forget_one *param = (void *) PARAM(arg); -+ unsigned int i; -+ -+ (void) nodeid; -+ -+ if (req->se->op.forget_multi) { -+ req->se->op.forget_multi(req, arg->count, -+ (struct fuse_forget_data *) param); -+ } else if (req->se->op.forget) { -+ for (i = 0; i < arg->count; i++) { -+ struct fuse_forget_one *forget = ¶m[i]; -+ struct fuse_req *dummy_req; -+ -+ dummy_req = fuse_ll_alloc_req(req->se); -+ if (dummy_req == NULL) -+ break; -+ -+ dummy_req->unique = req->unique; -+ dummy_req->ctx = req->ctx; -+ dummy_req->ch = NULL; -+ -+ req->se->op.forget(dummy_req, forget->nodeid, -+ forget->nlookup); -+ } -+ fuse_reply_none(req); -+ } else { -+ fuse_reply_none(req); -+ } -+} -+ -+static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_file_info *fip = NULL; -+ struct fuse_file_info fi; -+ -+ if (req->se->conn.proto_minor >= 9) { -+ struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg; -+ -+ if (arg->getattr_flags & FUSE_GETATTR_FH) { -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ fip = &fi; -+ } -+ } -+ -+ if (req->se->op.getattr) -+ req->se->op.getattr(req, nodeid, fip); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg; -+ -+ if (req->se->op.setattr) { -+ struct fuse_file_info *fi = NULL; -+ struct fuse_file_info fi_store; -+ struct stat stbuf; -+ memset(&stbuf, 0, sizeof(stbuf)); -+ convert_attr(arg, &stbuf); -+ if (arg->valid & FATTR_FH) { -+ arg->valid &= ~FATTR_FH; -+ memset(&fi_store, 0, sizeof(fi_store)); -+ fi = &fi_store; -+ fi->fh = arg->fh; -+ } -+ arg->valid &= -+ FUSE_SET_ATTR_MODE | -+ FUSE_SET_ATTR_UID | -+ FUSE_SET_ATTR_GID | -+ FUSE_SET_ATTR_SIZE | -+ FUSE_SET_ATTR_ATIME | -+ FUSE_SET_ATTR_MTIME | -+ FUSE_SET_ATTR_ATIME_NOW | -+ FUSE_SET_ATTR_MTIME_NOW | -+ FUSE_SET_ATTR_CTIME; -+ -+ req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi); -+ } else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_access_in *arg = (struct fuse_access_in *) inarg; -+ -+ if (req->se->op.access) -+ req->se->op.access(req, nodeid, arg->mask); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ (void) inarg; -+ -+ if (req->se->op.readlink) -+ req->se->op.readlink(req, nodeid); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg; -+ char *name = PARAM(arg); -+ -+ if (req->se->conn.proto_minor >= 12) -+ req->ctx.umask = arg->umask; -+ else -+ name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE; -+ -+ if (req->se->op.mknod) -+ req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg; -+ -+ if (req->se->conn.proto_minor >= 12) -+ req->ctx.umask = arg->umask; -+ -+ if (req->se->op.mkdir) -+ req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ char *name = (char *) inarg; -+ -+ if (req->se->op.unlink) -+ req->se->op.unlink(req, nodeid, name); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ char *name = (char *) inarg; -+ -+ if (req->se->op.rmdir) -+ req->se->op.rmdir(req, nodeid, name); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ char *name = (char *) inarg; -+ char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1; -+ -+ if (req->se->op.symlink) -+ req->se->op.symlink(req, linkname, nodeid, name); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg; -+ char *oldname = PARAM(arg); -+ char *newname = oldname + strlen(oldname) + 1; -+ -+ if (req->se->op.rename) -+ req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, -+ 0); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_rename2_in *arg = (struct fuse_rename2_in *) inarg; -+ char *oldname = PARAM(arg); -+ char *newname = oldname + strlen(oldname) + 1; -+ -+ if (req->se->op.rename) -+ req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, -+ arg->flags); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_link_in *arg = (struct fuse_link_in *) inarg; -+ -+ if (req->se->op.link) -+ req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg)); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_create_in *arg = (struct fuse_create_in *) inarg; -+ -+ if (req->se->op.create) { -+ struct fuse_file_info fi; -+ char *name = PARAM(arg); -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.flags = arg->flags; -+ -+ if (req->se->conn.proto_minor >= 12) -+ req->ctx.umask = arg->umask; -+ else -+ name = (char *) inarg + sizeof(struct fuse_open_in); -+ -+ req->se->op.create(req, nodeid, name, arg->mode, &fi); -+ } else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_open_in *arg = (struct fuse_open_in *) inarg; -+ struct fuse_file_info fi; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.flags = arg->flags; -+ -+ if (req->se->op.open) -+ req->se->op.open(req, nodeid, &fi); -+ else -+ fuse_reply_open(req, &fi); -+} -+ -+static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_read_in *arg = (struct fuse_read_in *) inarg; -+ -+ if (req->se->op.read) { -+ struct fuse_file_info fi; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ if (req->se->conn.proto_minor >= 9) { -+ fi.lock_owner = arg->lock_owner; -+ fi.flags = arg->flags; -+ } -+ req->se->op.read(req, nodeid, arg->size, arg->offset, &fi); -+ } else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_write_in *arg = (struct fuse_write_in *) inarg; -+ struct fuse_file_info fi; -+ char *param; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0; -+ -+ if (req->se->conn.proto_minor < 9) { -+ param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE; -+ } else { -+ fi.lock_owner = arg->lock_owner; -+ fi.flags = arg->flags; -+ param = PARAM(arg); -+ } -+ -+ if (req->se->op.write) -+ req->se->op.write(req, nodeid, param, arg->size, -+ arg->offset, &fi); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, -+ const struct fuse_buf *ibuf) -+{ -+ struct fuse_session *se = req->se; -+ struct fuse_bufvec bufv = { -+ .buf[0] = *ibuf, -+ .count = 1, -+ }; -+ struct fuse_write_in *arg = (struct fuse_write_in *) inarg; -+ struct fuse_file_info fi; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ fi.writepage = arg->write_flags & FUSE_WRITE_CACHE; -+ -+ if (se->conn.proto_minor < 9) { -+ bufv.buf[0].mem = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE; -+ bufv.buf[0].size -= sizeof(struct fuse_in_header) + -+ FUSE_COMPAT_WRITE_IN_SIZE; -+ assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD)); -+ } else { -+ fi.lock_owner = arg->lock_owner; -+ fi.flags = arg->flags; -+ if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) -+ bufv.buf[0].mem = PARAM(arg); -+ -+ bufv.buf[0].size -= sizeof(struct fuse_in_header) + -+ sizeof(struct fuse_write_in); -+ } -+ if (bufv.buf[0].size < arg->size) { -+ fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n"); -+ fuse_reply_err(req, EIO); -+ goto out; -+ } -+ bufv.buf[0].size = arg->size; -+ -+ se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi); -+ -+out: -+ /* Need to reset the pipe if ->write_buf() didn't consume all data */ -+ if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count) -+ fuse_ll_clear_pipe(se); -+} -+ -+static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg; -+ struct fuse_file_info fi; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ fi.flush = 1; -+ if (req->se->conn.proto_minor >= 7) -+ fi.lock_owner = arg->lock_owner; -+ -+ if (req->se->op.flush) -+ req->se->op.flush(req, nodeid, &fi); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_release_in *arg = (struct fuse_release_in *) inarg; -+ struct fuse_file_info fi; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.flags = arg->flags; -+ fi.fh = arg->fh; -+ if (req->se->conn.proto_minor >= 8) { -+ fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; -+ fi.lock_owner = arg->lock_owner; -+ } -+ if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) { -+ fi.flock_release = 1; -+ fi.lock_owner = arg->lock_owner; -+ } -+ -+ if (req->se->op.release) -+ req->se->op.release(req, nodeid, &fi); -+ else -+ fuse_reply_err(req, 0); -+} -+ -+static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; -+ struct fuse_file_info fi; -+ int datasync = arg->fsync_flags & 1; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ -+ if (req->se->op.fsync) -+ req->se->op.fsync(req, nodeid, datasync, &fi); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_open_in *arg = (struct fuse_open_in *) inarg; -+ struct fuse_file_info fi; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.flags = arg->flags; -+ -+ if (req->se->op.opendir) -+ req->se->op.opendir(req, nodeid, &fi); -+ else -+ fuse_reply_open(req, &fi); -+} -+ -+static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_read_in *arg = (struct fuse_read_in *) inarg; -+ struct fuse_file_info fi; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ -+ if (req->se->op.readdir) -+ req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_read_in *arg = (struct fuse_read_in *) inarg; -+ struct fuse_file_info fi; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ -+ if (req->se->op.readdirplus) -+ req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_release_in *arg = (struct fuse_release_in *) inarg; -+ struct fuse_file_info fi; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.flags = arg->flags; -+ fi.fh = arg->fh; -+ -+ if (req->se->op.releasedir) -+ req->se->op.releasedir(req, nodeid, &fi); -+ else -+ fuse_reply_err(req, 0); -+} -+ -+static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; -+ struct fuse_file_info fi; -+ int datasync = arg->fsync_flags & 1; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ -+ if (req->se->op.fsyncdir) -+ req->se->op.fsyncdir(req, nodeid, datasync, &fi); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ (void) nodeid; -+ (void) inarg; -+ -+ if (req->se->op.statfs) -+ req->se->op.statfs(req, nodeid); -+ else { -+ struct statvfs buf = { -+ .f_namemax = 255, -+ .f_bsize = 512, -+ }; -+ fuse_reply_statfs(req, &buf); -+ } -+} -+ -+static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg; -+ char *name = PARAM(arg); -+ char *value = name + strlen(name) + 1; -+ -+ if (req->se->op.setxattr) -+ req->se->op.setxattr(req, nodeid, name, value, arg->size, -+ arg->flags); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; -+ -+ if (req->se->op.getxattr) -+ req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; -+ -+ if (req->se->op.listxattr) -+ req->se->op.listxattr(req, nodeid, arg->size); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ char *name = (char *) inarg; -+ -+ if (req->se->op.removexattr) -+ req->se->op.removexattr(req, nodeid, name); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void convert_fuse_file_lock(struct fuse_file_lock *fl, -+ struct flock *flock) -+{ -+ memset(flock, 0, sizeof(struct flock)); -+ flock->l_type = fl->type; -+ flock->l_whence = SEEK_SET; -+ flock->l_start = fl->start; -+ if (fl->end == OFFSET_MAX) -+ flock->l_len = 0; -+ else -+ flock->l_len = fl->end - fl->start + 1; -+ flock->l_pid = fl->pid; -+} -+ -+static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg; -+ struct fuse_file_info fi; -+ struct flock flock; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ fi.lock_owner = arg->owner; -+ -+ convert_fuse_file_lock(&arg->lk, &flock); -+ if (req->se->op.getlk) -+ req->se->op.getlk(req, nodeid, &fi, &flock); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, -+ const void *inarg, int sleep) -+{ -+ struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg; -+ struct fuse_file_info fi; -+ struct flock flock; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ fi.lock_owner = arg->owner; -+ -+ if (arg->lk_flags & FUSE_LK_FLOCK) { -+ int op = 0; -+ -+ switch (arg->lk.type) { -+ case F_RDLCK: -+ op = LOCK_SH; -+ break; -+ case F_WRLCK: -+ op = LOCK_EX; -+ break; -+ case F_UNLCK: -+ op = LOCK_UN; -+ break; -+ } -+ if (!sleep) -+ op |= LOCK_NB; -+ -+ if (req->se->op.flock) -+ req->se->op.flock(req, nodeid, &fi, op); -+ else -+ fuse_reply_err(req, ENOSYS); -+ } else { -+ convert_fuse_file_lock(&arg->lk, &flock); -+ if (req->se->op.setlk) -+ req->se->op.setlk(req, nodeid, &fi, &flock, sleep); -+ else -+ fuse_reply_err(req, ENOSYS); -+ } -+} -+ -+static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ do_setlk_common(req, nodeid, inarg, 0); -+} -+ -+static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ do_setlk_common(req, nodeid, inarg, 1); -+} -+ -+static int find_interrupted(struct fuse_session *se, struct fuse_req *req) -+{ -+ struct fuse_req *curr; -+ -+ for (curr = se->list.next; curr != &se->list; curr = curr->next) { -+ if (curr->unique == req->u.i.unique) { -+ fuse_interrupt_func_t func; -+ void *data; -+ -+ curr->ctr++; -+ pthread_mutex_unlock(&se->lock); -+ -+ /* Ugh, ugly locking */ -+ pthread_mutex_lock(&curr->lock); -+ pthread_mutex_lock(&se->lock); -+ curr->interrupted = 1; -+ func = curr->u.ni.func; -+ data = curr->u.ni.data; -+ pthread_mutex_unlock(&se->lock); -+ if (func) -+ func(curr, data); -+ pthread_mutex_unlock(&curr->lock); -+ -+ pthread_mutex_lock(&se->lock); -+ curr->ctr--; -+ if (!curr->ctr) -+ destroy_req(curr); -+ -+ return 1; -+ } -+ } -+ for (curr = se->interrupts.next; curr != &se->interrupts; -+ curr = curr->next) { -+ if (curr->u.i.unique == req->u.i.unique) -+ return 1; -+ } -+ return 0; -+} -+ -+static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg; -+ struct fuse_session *se = req->se; -+ -+ (void) nodeid; -+ if (se->debug) -+ fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n", -+ (unsigned long long) arg->unique); -+ -+ req->u.i.unique = arg->unique; -+ -+ pthread_mutex_lock(&se->lock); -+ if (find_interrupted(se, req)) -+ destroy_req(req); -+ else -+ list_add_req(req, &se->interrupts); -+ pthread_mutex_unlock(&se->lock); -+} -+ -+static struct fuse_req *check_interrupt(struct fuse_session *se, -+ struct fuse_req *req) -+{ -+ struct fuse_req *curr; -+ -+ for (curr = se->interrupts.next; curr != &se->interrupts; -+ curr = curr->next) { -+ if (curr->u.i.unique == req->unique) { -+ req->interrupted = 1; -+ list_del_req(curr); -+ free(curr); -+ return NULL; -+ } -+ } -+ curr = se->interrupts.next; -+ if (curr != &se->interrupts) { -+ list_del_req(curr); -+ list_init_req(curr); -+ return curr; -+ } else -+ return NULL; -+} -+ -+static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg; -+ -+ if (req->se->op.bmap) -+ req->se->op.bmap(req, nodeid, arg->blocksize, arg->block); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg; -+ unsigned int flags = arg->flags; -+ void *in_buf = arg->in_size ? PARAM(arg) : NULL; -+ struct fuse_file_info fi; -+ -+ if (flags & FUSE_IOCTL_DIR && -+ !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) { -+ fuse_reply_err(req, ENOTTY); -+ return; -+ } -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ -+ if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 && -+ !(flags & FUSE_IOCTL_32BIT)) { -+ req->ioctl_64bit = 1; -+ } -+ -+ if (req->se->op.ioctl) -+ req->se->op.ioctl(req, nodeid, arg->cmd, -+ (void *)(uintptr_t)arg->arg, &fi, flags, -+ in_buf, arg->in_size, arg->out_size); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+void fuse_pollhandle_destroy(struct fuse_pollhandle *ph) -+{ -+ free(ph); -+} -+ -+static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg; -+ struct fuse_file_info fi; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ fi.poll_events = arg->events; -+ -+ if (req->se->op.poll) { -+ struct fuse_pollhandle *ph = NULL; -+ -+ if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) { -+ ph = malloc(sizeof(struct fuse_pollhandle)); -+ if (ph == NULL) { -+ fuse_reply_err(req, ENOMEM); -+ return; -+ } -+ ph->kh = arg->kh; -+ ph->se = req->se; -+ } -+ -+ req->se->op.poll(req, nodeid, &fi, ph); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } -+} -+ -+static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg; -+ struct fuse_file_info fi; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ -+ if (req->se->op.fallocate) -+ req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void *inarg) -+{ -+ struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in *) inarg; -+ struct fuse_file_info fi_in, fi_out; -+ -+ memset(&fi_in, 0, sizeof(fi_in)); -+ fi_in.fh = arg->fh_in; -+ -+ memset(&fi_out, 0, sizeof(fi_out)); -+ fi_out.fh = arg->fh_out; -+ -+ -+ if (req->se->op.copy_file_range) -+ req->se->op.copy_file_range(req, nodeid_in, arg->off_in, -+ &fi_in, arg->nodeid_out, -+ arg->off_out, &fi_out, arg->len, -+ arg->flags); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_lseek_in *arg = (struct fuse_lseek_in *) inarg; -+ struct fuse_file_info fi; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ -+ if (req->se->op.lseek) -+ req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi); -+ else -+ fuse_reply_err(req, ENOSYS); -+} -+ -+static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_init_in *arg = (struct fuse_init_in *) inarg; -+ struct fuse_init_out outarg; -+ struct fuse_session *se = req->se; -+ size_t bufsize = se->bufsize; -+ size_t outargsize = sizeof(outarg); -+ -+ (void) nodeid; -+ if (se->debug) { -+ fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor); -+ if (arg->major == 7 && arg->minor >= 6) { -+ fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags); -+ fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n", -+ arg->max_readahead); -+ } -+ } -+ se->conn.proto_major = arg->major; -+ se->conn.proto_minor = arg->minor; -+ se->conn.capable = 0; -+ se->conn.want = 0; -+ -+ memset(&outarg, 0, sizeof(outarg)); -+ outarg.major = FUSE_KERNEL_VERSION; -+ outarg.minor = FUSE_KERNEL_MINOR_VERSION; -+ -+ if (arg->major < 7) { -+ fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n", -+ arg->major, arg->minor); -+ fuse_reply_err(req, EPROTO); -+ return; -+ } -+ -+ if (arg->major > 7) { -+ /* Wait for a second INIT request with a 7.X version */ -+ send_reply_ok(req, &outarg, sizeof(outarg)); -+ return; -+ } -+ -+ if (arg->minor >= 6) { -+ if (arg->max_readahead < se->conn.max_readahead) -+ se->conn.max_readahead = arg->max_readahead; -+ if (arg->flags & FUSE_ASYNC_READ) -+ se->conn.capable |= FUSE_CAP_ASYNC_READ; -+ if (arg->flags & FUSE_POSIX_LOCKS) -+ se->conn.capable |= FUSE_CAP_POSIX_LOCKS; -+ if (arg->flags & FUSE_ATOMIC_O_TRUNC) -+ se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC; -+ if (arg->flags & FUSE_EXPORT_SUPPORT) -+ se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT; -+ if (arg->flags & FUSE_DONT_MASK) -+ se->conn.capable |= FUSE_CAP_DONT_MASK; -+ if (arg->flags & FUSE_FLOCK_LOCKS) -+ se->conn.capable |= FUSE_CAP_FLOCK_LOCKS; -+ if (arg->flags & FUSE_AUTO_INVAL_DATA) -+ se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA; -+ if (arg->flags & FUSE_DO_READDIRPLUS) -+ se->conn.capable |= FUSE_CAP_READDIRPLUS; -+ if (arg->flags & FUSE_READDIRPLUS_AUTO) -+ se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO; -+ if (arg->flags & FUSE_ASYNC_DIO) -+ se->conn.capable |= FUSE_CAP_ASYNC_DIO; -+ if (arg->flags & FUSE_WRITEBACK_CACHE) -+ se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE; -+ if (arg->flags & FUSE_NO_OPEN_SUPPORT) -+ se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT; -+ if (arg->flags & FUSE_PARALLEL_DIROPS) -+ se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS; -+ if (arg->flags & FUSE_POSIX_ACL) -+ se->conn.capable |= FUSE_CAP_POSIX_ACL; -+ if (arg->flags & FUSE_HANDLE_KILLPRIV) -+ se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV; -+ if (arg->flags & FUSE_NO_OPENDIR_SUPPORT) -+ se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT; -+ if (!(arg->flags & FUSE_MAX_PAGES)) { -+ size_t max_bufsize = -+ FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() -+ + FUSE_BUFFER_HEADER_SIZE; -+ if (bufsize > max_bufsize) { -+ bufsize = max_bufsize; -+ } -+ } -+ } else { -+ se->conn.max_readahead = 0; -+ } -+ -+ if (se->conn.proto_minor >= 14) { -+#ifdef HAVE_SPLICE -+#ifdef HAVE_VMSPLICE -+ se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; -+#endif -+ se->conn.capable |= FUSE_CAP_SPLICE_READ; -+#endif -+ } -+ if (se->conn.proto_minor >= 18) -+ se->conn.capable |= FUSE_CAP_IOCTL_DIR; -+ -+ /* Default settings for modern filesystems. -+ * -+ * Most of these capabilities were disabled by default in -+ * libfuse2 for backwards compatibility reasons. In libfuse3, -+ * we can finally enable them by default (as long as they're -+ * supported by the kernel). -+ */ -+#define LL_SET_DEFAULT(cond, cap) \ -+ if ((cond) && (se->conn.capable & (cap))) \ -+ se->conn.want |= (cap) -+ LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ); -+ LL_SET_DEFAULT(1, FUSE_CAP_PARALLEL_DIROPS); -+ LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA); -+ LL_SET_DEFAULT(1, FUSE_CAP_HANDLE_KILLPRIV); -+ LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO); -+ LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR); -+ LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC); -+ LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ); -+ LL_SET_DEFAULT(se->op.getlk && se->op.setlk, -+ FUSE_CAP_POSIX_LOCKS); -+ LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS); -+ LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS); -+ LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir, -+ FUSE_CAP_READDIRPLUS_AUTO); -+ se->conn.time_gran = 1; -+ -+ if (bufsize < FUSE_MIN_READ_BUFFER) { -+ fuse_log(FUSE_LOG_ERR, "fuse: warning: buffer size too small: %zu\n", -+ bufsize); -+ bufsize = FUSE_MIN_READ_BUFFER; -+ } -+ se->bufsize = bufsize; -+ -+ if (se->conn.max_write > bufsize - FUSE_BUFFER_HEADER_SIZE) -+ se->conn.max_write = bufsize - FUSE_BUFFER_HEADER_SIZE; -+ -+ se->got_init = 1; -+ if (se->op.init) -+ se->op.init(se->userdata, &se->conn); -+ -+ if (se->conn.want & (~se->conn.capable)) { -+ fuse_log(FUSE_LOG_ERR, "fuse: error: filesystem requested capabilities " -+ "0x%x that are not supported by kernel, aborting.\n", -+ se->conn.want & (~se->conn.capable)); -+ fuse_reply_err(req, EPROTO); -+ se->error = -EPROTO; -+ fuse_session_exit(se); -+ return; -+ } -+ -+ unsigned max_read_mo = get_max_read(se->mo); -+ if (se->conn.max_read != max_read_mo) { -+ fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() " -+ "requested different maximum read size (%u vs %u)\n", -+ se->conn.max_read, max_read_mo); -+ fuse_reply_err(req, EPROTO); -+ se->error = -EPROTO; -+ fuse_session_exit(se); -+ return; -+ } -+ -+ if (se->conn.max_write < bufsize - FUSE_BUFFER_HEADER_SIZE) { -+ se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE; -+ } -+ if (arg->flags & FUSE_MAX_PAGES) { -+ outarg.flags |= FUSE_MAX_PAGES; -+ outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1; -+ } -+ -+ /* Always enable big writes, this is superseded -+ by the max_write option */ -+ outarg.flags |= FUSE_BIG_WRITES; -+ -+ if (se->conn.want & FUSE_CAP_ASYNC_READ) -+ outarg.flags |= FUSE_ASYNC_READ; -+ if (se->conn.want & FUSE_CAP_POSIX_LOCKS) -+ outarg.flags |= FUSE_POSIX_LOCKS; -+ if (se->conn.want & FUSE_CAP_ATOMIC_O_TRUNC) -+ outarg.flags |= FUSE_ATOMIC_O_TRUNC; -+ if (se->conn.want & FUSE_CAP_EXPORT_SUPPORT) -+ outarg.flags |= FUSE_EXPORT_SUPPORT; -+ if (se->conn.want & FUSE_CAP_DONT_MASK) -+ outarg.flags |= FUSE_DONT_MASK; -+ if (se->conn.want & FUSE_CAP_FLOCK_LOCKS) -+ outarg.flags |= FUSE_FLOCK_LOCKS; -+ if (se->conn.want & FUSE_CAP_AUTO_INVAL_DATA) -+ outarg.flags |= FUSE_AUTO_INVAL_DATA; -+ if (se->conn.want & FUSE_CAP_READDIRPLUS) -+ outarg.flags |= FUSE_DO_READDIRPLUS; -+ if (se->conn.want & FUSE_CAP_READDIRPLUS_AUTO) -+ outarg.flags |= FUSE_READDIRPLUS_AUTO; -+ if (se->conn.want & FUSE_CAP_ASYNC_DIO) -+ outarg.flags |= FUSE_ASYNC_DIO; -+ if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE) -+ outarg.flags |= FUSE_WRITEBACK_CACHE; -+ if (se->conn.want & FUSE_CAP_POSIX_ACL) -+ outarg.flags |= FUSE_POSIX_ACL; -+ outarg.max_readahead = se->conn.max_readahead; -+ outarg.max_write = se->conn.max_write; -+ if (se->conn.proto_minor >= 13) { -+ if (se->conn.max_background >= (1 << 16)) -+ se->conn.max_background = (1 << 16) - 1; -+ if (se->conn.congestion_threshold > se->conn.max_background) -+ se->conn.congestion_threshold = se->conn.max_background; -+ if (!se->conn.congestion_threshold) { -+ se->conn.congestion_threshold = -+ se->conn.max_background * 3 / 4; -+ } -+ -+ outarg.max_background = se->conn.max_background; -+ outarg.congestion_threshold = se->conn.congestion_threshold; -+ } -+ if (se->conn.proto_minor >= 23) -+ outarg.time_gran = se->conn.time_gran; -+ -+ if (se->debug) { -+ fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor); -+ fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); -+ fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n", -+ outarg.max_readahead); -+ fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write); -+ fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n", -+ outarg.max_background); -+ fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n", -+ outarg.congestion_threshold); -+ fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", -+ outarg.time_gran); -+ } -+ if (arg->minor < 5) -+ outargsize = FUSE_COMPAT_INIT_OUT_SIZE; -+ else if (arg->minor < 23) -+ outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE; -+ -+ send_reply_ok(req, &outarg, outargsize); -+} -+ -+static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+{ -+ struct fuse_session *se = req->se; -+ -+ (void) nodeid; -+ (void) inarg; -+ -+ se->got_destroy = 1; -+ if (se->op.destroy) -+ se->op.destroy(se->userdata); -+ -+ send_reply_ok(req, NULL, 0); -+} -+ -+static void list_del_nreq(struct fuse_notify_req *nreq) -+{ -+ struct fuse_notify_req *prev = nreq->prev; -+ struct fuse_notify_req *next = nreq->next; -+ prev->next = next; -+ next->prev = prev; -+} -+ -+static void list_add_nreq(struct fuse_notify_req *nreq, -+ struct fuse_notify_req *next) -+{ -+ struct fuse_notify_req *prev = next->prev; -+ nreq->next = next; -+ nreq->prev = prev; -+ prev->next = nreq; -+ next->prev = nreq; -+} -+ -+static void list_init_nreq(struct fuse_notify_req *nreq) -+{ -+ nreq->next = nreq; -+ nreq->prev = nreq; -+} -+ -+static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid, -+ const void *inarg, const struct fuse_buf *buf) -+{ -+ struct fuse_session *se = req->se; -+ struct fuse_notify_req *nreq; -+ struct fuse_notify_req *head; -+ -+ pthread_mutex_lock(&se->lock); -+ head = &se->notify_list; -+ for (nreq = head->next; nreq != head; nreq = nreq->next) { -+ if (nreq->unique == req->unique) { -+ list_del_nreq(nreq); -+ break; -+ } -+ } -+ pthread_mutex_unlock(&se->lock); -+ -+ if (nreq != head) -+ nreq->reply(nreq, req, nodeid, inarg, buf); -+} -+ -+static int send_notify_iov(struct fuse_session *se, int notify_code, -+ struct iovec *iov, int count) -+{ -+ struct fuse_out_header out; -+ -+ if (!se->got_init) -+ return -ENOTCONN; -+ -+ out.unique = 0; -+ out.error = notify_code; -+ iov[0].iov_base = &out; -+ iov[0].iov_len = sizeof(struct fuse_out_header); -+ -+ return fuse_send_msg(se, NULL, iov, count); -+} -+ -+int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph) -+{ -+ if (ph != NULL) { -+ struct fuse_notify_poll_wakeup_out outarg; -+ struct iovec iov[2]; -+ -+ outarg.kh = ph->kh; -+ -+ iov[1].iov_base = &outarg; -+ iov[1].iov_len = sizeof(outarg); -+ -+ return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2); -+ } else { -+ return 0; -+ } -+} -+ -+int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, -+ off_t off, off_t len) -+{ -+ struct fuse_notify_inval_inode_out outarg; -+ struct iovec iov[2]; -+ -+ if (!se) -+ return -EINVAL; -+ -+ if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) -+ return -ENOSYS; -+ -+ outarg.ino = ino; -+ outarg.off = off; -+ outarg.len = len; -+ -+ iov[1].iov_base = &outarg; -+ iov[1].iov_len = sizeof(outarg); -+ -+ return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2); -+} -+ -+int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, -+ const char *name, size_t namelen) -+{ -+ struct fuse_notify_inval_entry_out outarg; -+ struct iovec iov[3]; -+ -+ if (!se) -+ return -EINVAL; -+ -+ if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) -+ return -ENOSYS; -+ -+ outarg.parent = parent; -+ outarg.namelen = namelen; -+ outarg.padding = 0; -+ -+ iov[1].iov_base = &outarg; -+ iov[1].iov_len = sizeof(outarg); -+ iov[2].iov_base = (void *)name; -+ iov[2].iov_len = namelen + 1; -+ -+ return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3); -+} -+ -+int fuse_lowlevel_notify_delete(struct fuse_session *se, -+ fuse_ino_t parent, fuse_ino_t child, -+ const char *name, size_t namelen) -+{ -+ struct fuse_notify_delete_out outarg; -+ struct iovec iov[3]; -+ -+ if (!se) -+ return -EINVAL; -+ -+ if (se->conn.proto_major < 6 || se->conn.proto_minor < 18) -+ return -ENOSYS; -+ -+ outarg.parent = parent; -+ outarg.child = child; -+ outarg.namelen = namelen; -+ outarg.padding = 0; -+ -+ iov[1].iov_base = &outarg; -+ iov[1].iov_len = sizeof(outarg); -+ iov[2].iov_base = (void *)name; -+ iov[2].iov_len = namelen + 1; -+ -+ return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3); -+} -+ -+int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, -+ off_t offset, struct fuse_bufvec *bufv, -+ enum fuse_buf_copy_flags flags) -+{ -+ struct fuse_out_header out; -+ struct fuse_notify_store_out outarg; -+ struct iovec iov[3]; -+ size_t size = fuse_buf_size(bufv); -+ int res; -+ -+ if (!se) -+ return -EINVAL; -+ -+ if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) -+ return -ENOSYS; -+ -+ out.unique = 0; -+ out.error = FUSE_NOTIFY_STORE; -+ -+ outarg.nodeid = ino; -+ outarg.offset = offset; -+ outarg.size = size; -+ outarg.padding = 0; -+ -+ iov[0].iov_base = &out; -+ iov[0].iov_len = sizeof(out); -+ iov[1].iov_base = &outarg; -+ iov[1].iov_len = sizeof(outarg); -+ -+ res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags); -+ if (res > 0) -+ res = -res; -+ -+ return res; -+} -+ -+struct fuse_retrieve_req { -+ struct fuse_notify_req nreq; -+ void *cookie; -+}; -+ -+static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq, -+ fuse_req_t req, fuse_ino_t ino, -+ const void *inarg, -+ const struct fuse_buf *ibuf) -+{ -+ struct fuse_session *se = req->se; -+ struct fuse_retrieve_req *rreq = -+ container_of(nreq, struct fuse_retrieve_req, nreq); -+ const struct fuse_notify_retrieve_in *arg = inarg; -+ struct fuse_bufvec bufv = { -+ .buf[0] = *ibuf, -+ .count = 1, -+ }; -+ -+ if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) -+ bufv.buf[0].mem = PARAM(arg); -+ -+ bufv.buf[0].size -= sizeof(struct fuse_in_header) + -+ sizeof(struct fuse_notify_retrieve_in); -+ -+ if (bufv.buf[0].size < arg->size) { -+ fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n"); -+ fuse_reply_none(req); -+ goto out; -+ } -+ bufv.buf[0].size = arg->size; -+ -+ if (se->op.retrieve_reply) { -+ se->op.retrieve_reply(req, rreq->cookie, ino, -+ arg->offset, &bufv); -+ } else { -+ fuse_reply_none(req); -+ } -+out: -+ free(rreq); -+ if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count) -+ fuse_ll_clear_pipe(se); -+} -+ -+int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, -+ size_t size, off_t offset, void *cookie) -+{ -+ struct fuse_notify_retrieve_out outarg; -+ struct iovec iov[2]; -+ struct fuse_retrieve_req *rreq; -+ int err; -+ -+ if (!se) -+ return -EINVAL; -+ -+ if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) -+ return -ENOSYS; -+ -+ rreq = malloc(sizeof(*rreq)); -+ if (rreq == NULL) -+ return -ENOMEM; -+ -+ pthread_mutex_lock(&se->lock); -+ rreq->cookie = cookie; -+ rreq->nreq.unique = se->notify_ctr++; -+ rreq->nreq.reply = fuse_ll_retrieve_reply; -+ list_add_nreq(&rreq->nreq, &se->notify_list); -+ pthread_mutex_unlock(&se->lock); -+ -+ outarg.notify_unique = rreq->nreq.unique; -+ outarg.nodeid = ino; -+ outarg.offset = offset; -+ outarg.size = size; -+ outarg.padding = 0; -+ -+ iov[1].iov_base = &outarg; -+ iov[1].iov_len = sizeof(outarg); -+ -+ err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2); -+ if (err) { -+ pthread_mutex_lock(&se->lock); -+ list_del_nreq(&rreq->nreq); -+ pthread_mutex_unlock(&se->lock); -+ free(rreq); -+ } -+ -+ return err; -+} -+ -+void *fuse_req_userdata(fuse_req_t req) -+{ -+ return req->se->userdata; -+} -+ -+const struct fuse_ctx *fuse_req_ctx(fuse_req_t req) -+{ -+ return &req->ctx; -+} -+ -+void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, -+ void *data) -+{ -+ pthread_mutex_lock(&req->lock); -+ pthread_mutex_lock(&req->se->lock); -+ req->u.ni.func = func; -+ req->u.ni.data = data; -+ pthread_mutex_unlock(&req->se->lock); -+ if (req->interrupted && func) -+ func(req, data); -+ pthread_mutex_unlock(&req->lock); -+} -+ -+int fuse_req_interrupted(fuse_req_t req) -+{ -+ int interrupted; -+ -+ pthread_mutex_lock(&req->se->lock); -+ interrupted = req->interrupted; -+ pthread_mutex_unlock(&req->se->lock); -+ -+ return interrupted; -+} -+ -+static struct { -+ void (*func)(fuse_req_t, fuse_ino_t, const void *); -+ const char *name; -+} fuse_ll_ops[] = { -+ [FUSE_LOOKUP] = { do_lookup, "LOOKUP" }, -+ [FUSE_FORGET] = { do_forget, "FORGET" }, -+ [FUSE_GETATTR] = { do_getattr, "GETATTR" }, -+ [FUSE_SETATTR] = { do_setattr, "SETATTR" }, -+ [FUSE_READLINK] = { do_readlink, "READLINK" }, -+ [FUSE_SYMLINK] = { do_symlink, "SYMLINK" }, -+ [FUSE_MKNOD] = { do_mknod, "MKNOD" }, -+ [FUSE_MKDIR] = { do_mkdir, "MKDIR" }, -+ [FUSE_UNLINK] = { do_unlink, "UNLINK" }, -+ [FUSE_RMDIR] = { do_rmdir, "RMDIR" }, -+ [FUSE_RENAME] = { do_rename, "RENAME" }, -+ [FUSE_LINK] = { do_link, "LINK" }, -+ [FUSE_OPEN] = { do_open, "OPEN" }, -+ [FUSE_READ] = { do_read, "READ" }, -+ [FUSE_WRITE] = { do_write, "WRITE" }, -+ [FUSE_STATFS] = { do_statfs, "STATFS" }, -+ [FUSE_RELEASE] = { do_release, "RELEASE" }, -+ [FUSE_FSYNC] = { do_fsync, "FSYNC" }, -+ [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" }, -+ [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" }, -+ [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" }, -+ [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" }, -+ [FUSE_FLUSH] = { do_flush, "FLUSH" }, -+ [FUSE_INIT] = { do_init, "INIT" }, -+ [FUSE_OPENDIR] = { do_opendir, "OPENDIR" }, -+ [FUSE_READDIR] = { do_readdir, "READDIR" }, -+ [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" }, -+ [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" }, -+ [FUSE_GETLK] = { do_getlk, "GETLK" }, -+ [FUSE_SETLK] = { do_setlk, "SETLK" }, -+ [FUSE_SETLKW] = { do_setlkw, "SETLKW" }, -+ [FUSE_ACCESS] = { do_access, "ACCESS" }, -+ [FUSE_CREATE] = { do_create, "CREATE" }, -+ [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" }, -+ [FUSE_BMAP] = { do_bmap, "BMAP" }, -+ [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, -+ [FUSE_POLL] = { do_poll, "POLL" }, -+ [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" }, -+ [FUSE_DESTROY] = { do_destroy, "DESTROY" }, -+ [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" }, -+ [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" }, -+ [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"}, -+ [FUSE_RENAME2] = { do_rename2, "RENAME2" }, -+ [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" }, -+ [FUSE_LSEEK] = { do_lseek, "LSEEK" }, -+ [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" }, -+}; -+ -+#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) -+ -+static const char *opname(enum fuse_opcode opcode) -+{ -+ if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name) -+ return "???"; -+ else -+ return fuse_ll_ops[opcode].name; -+} -+ -+static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst, -+ struct fuse_bufvec *src) -+{ -+ ssize_t res = fuse_buf_copy(dst, src, 0); -+ if (res < 0) { -+ fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res)); -+ return res; -+ } -+ if ((size_t)res < fuse_buf_size(dst)) { -+ fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n"); -+ return -1; -+ } -+ return 0; -+} -+ -+void fuse_session_process_buf(struct fuse_session *se, -+ const struct fuse_buf *buf) -+{ -+ fuse_session_process_buf_int(se, buf, NULL); -+} -+ -+void fuse_session_process_buf_int(struct fuse_session *se, -+ const struct fuse_buf *buf, struct fuse_chan *ch) -+{ -+ const size_t write_header_size = sizeof(struct fuse_in_header) + -+ sizeof(struct fuse_write_in); -+ struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 }; -+ struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size); -+ struct fuse_in_header *in; -+ const void *inarg; -+ struct fuse_req *req; -+ void *mbuf = NULL; -+ int err; -+ int res; -+ -+ if (buf->flags & FUSE_BUF_IS_FD) { -+ if (buf->size < tmpbuf.buf[0].size) -+ tmpbuf.buf[0].size = buf->size; -+ -+ mbuf = malloc(tmpbuf.buf[0].size); -+ if (mbuf == NULL) { -+ fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n"); -+ goto clear_pipe; -+ } -+ tmpbuf.buf[0].mem = mbuf; -+ -+ res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv); -+ if (res < 0) -+ goto clear_pipe; -+ -+ in = mbuf; -+ } else { -+ in = buf->mem; -+ } -+ -+ if (se->debug) { -+ fuse_log(FUSE_LOG_DEBUG, -+ "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n", -+ (unsigned long long) in->unique, -+ opname((enum fuse_opcode) in->opcode), in->opcode, -+ (unsigned long long) in->nodeid, buf->size, in->pid); -+ } -+ -+ req = fuse_ll_alloc_req(se); -+ if (req == NULL) { -+ struct fuse_out_header out = { -+ .unique = in->unique, -+ .error = -ENOMEM, -+ }; -+ struct iovec iov = { -+ .iov_base = &out, -+ .iov_len = sizeof(struct fuse_out_header), -+ }; -+ -+ fuse_send_msg(se, ch, &iov, 1); -+ goto clear_pipe; -+ } -+ -+ req->unique = in->unique; -+ req->ctx.uid = in->uid; -+ req->ctx.gid = in->gid; -+ req->ctx.pid = in->pid; -+ req->ch = ch ? fuse_chan_get(ch) : NULL; -+ -+ err = EIO; -+ if (!se->got_init) { -+ enum fuse_opcode expected; -+ -+ expected = se->cuse_data ? CUSE_INIT : FUSE_INIT; -+ if (in->opcode != expected) -+ goto reply_err; -+ } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT) -+ goto reply_err; -+ -+ err = EACCES; -+ /* Implement -o allow_root */ -+ if (se->deny_others && in->uid != se->owner && in->uid != 0 && -+ in->opcode != FUSE_INIT && in->opcode != FUSE_READ && -+ in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC && -+ in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR && -+ in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR && -+ in->opcode != FUSE_NOTIFY_REPLY && -+ in->opcode != FUSE_READDIRPLUS) -+ goto reply_err; -+ -+ err = ENOSYS; -+ if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) -+ goto reply_err; -+ if (in->opcode != FUSE_INTERRUPT) { -+ struct fuse_req *intr; -+ pthread_mutex_lock(&se->lock); -+ intr = check_interrupt(se, req); -+ list_add_req(req, &se->list); -+ pthread_mutex_unlock(&se->lock); -+ if (intr) -+ fuse_reply_err(intr, EAGAIN); -+ } -+ -+ if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size && -+ (in->opcode != FUSE_WRITE || !se->op.write_buf) && -+ in->opcode != FUSE_NOTIFY_REPLY) { -+ void *newmbuf; -+ -+ err = ENOMEM; -+ newmbuf = realloc(mbuf, buf->size); -+ if (newmbuf == NULL) -+ goto reply_err; -+ mbuf = newmbuf; -+ -+ tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size); -+ tmpbuf.buf[0].mem = (char *)mbuf + write_header_size; -+ -+ res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv); -+ err = -res; -+ if (res < 0) -+ goto reply_err; -+ -+ in = mbuf; -+ } -+ -+ inarg = (void *) &in[1]; -+ if (in->opcode == FUSE_WRITE && se->op.write_buf) -+ do_write_buf(req, in->nodeid, inarg, buf); -+ else if (in->opcode == FUSE_NOTIFY_REPLY) -+ do_notify_reply(req, in->nodeid, inarg, buf); -+ else -+ fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); -+ -+out_free: -+ free(mbuf); -+ return; -+ -+reply_err: -+ fuse_reply_err(req, err); -+clear_pipe: -+ if (buf->flags & FUSE_BUF_IS_FD) -+ fuse_ll_clear_pipe(se); -+ goto out_free; -+} -+ -+#define LL_OPTION(n,o,v) \ -+ { n, offsetof(struct fuse_session, o), v } -+ -+static const struct fuse_opt fuse_ll_opts[] = { -+ LL_OPTION("debug", debug, 1), -+ LL_OPTION("-d", debug, 1), -+ LL_OPTION("--debug", debug, 1), -+ LL_OPTION("allow_root", deny_others, 1), -+ FUSE_OPT_END -+}; -+ -+void fuse_lowlevel_version(void) -+{ -+ printf("using FUSE kernel interface version %i.%i\n", -+ FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); -+ fuse_mount_version(); -+} -+ -+void fuse_lowlevel_help(void) -+{ -+ /* These are not all options, but the ones that are -+ potentially of interest to an end-user */ -+ printf( -+" -o allow_other allow access by all users\n" -+" -o allow_root allow access by root\n" -+" -o auto_unmount auto unmount on process termination\n"); -+} -+ -+void fuse_session_destroy(struct fuse_session *se) -+{ -+ struct fuse_ll_pipe *llp; -+ -+ if (se->got_init && !se->got_destroy) { -+ if (se->op.destroy) -+ se->op.destroy(se->userdata); -+ } -+ llp = pthread_getspecific(se->pipe_key); -+ if (llp != NULL) -+ fuse_ll_pipe_free(llp); -+ pthread_key_delete(se->pipe_key); -+ pthread_mutex_destroy(&se->lock); -+ free(se->cuse_data); -+ if (se->fd != -1) -+ close(se->fd); -+ destroy_mount_opts(se->mo); -+ free(se); -+} -+ -+ -+static void fuse_ll_pipe_destructor(void *data) -+{ -+ struct fuse_ll_pipe *llp = data; -+ fuse_ll_pipe_free(llp); -+} -+ -+int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf) -+{ -+ return fuse_session_receive_buf_int(se, buf, NULL); -+} -+ -+int fuse_session_receive_buf_int(struct fuse_session *se, struct fuse_buf *buf, -+ struct fuse_chan *ch) -+{ -+ int err; -+ ssize_t res; -+#ifdef HAVE_SPLICE -+ size_t bufsize = se->bufsize; -+ struct fuse_ll_pipe *llp; -+ struct fuse_buf tmpbuf; -+ -+ if (se->conn.proto_minor < 14 || !(se->conn.want & FUSE_CAP_SPLICE_READ)) -+ goto fallback; -+ -+ llp = fuse_ll_get_pipe(se); -+ if (llp == NULL) -+ goto fallback; -+ -+ if (llp->size < bufsize) { -+ if (llp->can_grow) { -+ res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize); -+ if (res == -1) { -+ llp->can_grow = 0; -+ res = grow_pipe_to_max(llp->pipe[0]); -+ if (res > 0) -+ llp->size = res; -+ goto fallback; -+ } -+ llp->size = res; -+ } -+ if (llp->size < bufsize) -+ goto fallback; -+ } -+ -+ res = splice(ch ? ch->fd : se->fd, -+ NULL, llp->pipe[1], NULL, bufsize, 0); -+ err = errno; -+ -+ if (fuse_session_exited(se)) -+ return 0; -+ -+ if (res == -1) { -+ if (err == ENODEV) { -+ /* Filesystem was unmounted, or connection was aborted -+ via /sys/fs/fuse/connections */ -+ fuse_session_exit(se); -+ return 0; -+ } -+ if (err != EINTR && err != EAGAIN) -+ perror("fuse: splice from device"); -+ return -err; -+ } -+ -+ if (res < sizeof(struct fuse_in_header)) { -+ fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n"); -+ return -EIO; -+ } -+ -+ tmpbuf = (struct fuse_buf) { -+ .size = res, -+ .flags = FUSE_BUF_IS_FD, -+ .fd = llp->pipe[0], -+ }; -+ -+ /* -+ * Don't bother with zero copy for small requests. -+ * fuse_loop_mt() needs to check for FORGET so this more than -+ * just an optimization. -+ */ -+ if (res < sizeof(struct fuse_in_header) + -+ sizeof(struct fuse_write_in) + pagesize) { -+ struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 }; -+ struct fuse_bufvec dst = { .count = 1 }; -+ -+ if (!buf->mem) { -+ buf->mem = malloc(se->bufsize); -+ if (!buf->mem) { -+ fuse_log(FUSE_LOG_ERR, -+ "fuse: failed to allocate read buffer\n"); -+ return -ENOMEM; -+ } -+ } -+ buf->size = se->bufsize; -+ buf->flags = 0; -+ dst.buf[0] = *buf; -+ -+ res = fuse_buf_copy(&dst, &src, 0); -+ if (res < 0) { -+ fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", -+ strerror(-res)); -+ fuse_ll_clear_pipe(se); -+ return res; -+ } -+ if (res < tmpbuf.size) { -+ fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n"); -+ fuse_ll_clear_pipe(se); -+ return -EIO; -+ } -+ assert(res == tmpbuf.size); -+ -+ } else { -+ /* Don't overwrite buf->mem, as that would cause a leak */ -+ buf->fd = tmpbuf.fd; -+ buf->flags = tmpbuf.flags; -+ } -+ buf->size = tmpbuf.size; -+ -+ return res; -+ -+fallback: -+#endif -+ if (!buf->mem) { -+ buf->mem = malloc(se->bufsize); -+ if (!buf->mem) { -+ fuse_log(FUSE_LOG_ERR, -+ "fuse: failed to allocate read buffer\n"); -+ return -ENOMEM; -+ } -+ } -+ -+restart: -+ res = read(ch ? ch->fd : se->fd, buf->mem, se->bufsize); -+ err = errno; -+ -+ if (fuse_session_exited(se)) -+ return 0; -+ if (res == -1) { -+ /* ENOENT means the operation was interrupted, it's safe -+ to restart */ -+ if (err == ENOENT) -+ goto restart; -+ -+ if (err == ENODEV) { -+ /* Filesystem was unmounted, or connection was aborted -+ via /sys/fs/fuse/connections */ -+ fuse_session_exit(se); -+ return 0; -+ } -+ /* Errors occurring during normal operation: EINTR (read -+ interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem -+ umounted) */ -+ if (err != EINTR && err != EAGAIN) -+ perror("fuse: reading device"); -+ return -err; -+ } -+ if ((size_t) res < sizeof(struct fuse_in_header)) { -+ fuse_log(FUSE_LOG_ERR, "short read on fuse device\n"); -+ return -EIO; -+ } -+ -+ buf->size = res; -+ -+ return res; -+} -+ -+struct fuse_session *fuse_session_new(struct fuse_args *args, -+ const struct fuse_lowlevel_ops *op, -+ size_t op_size, void *userdata) -+{ -+ int err; -+ struct fuse_session *se; -+ struct mount_opts *mo; -+ -+ if (sizeof(struct fuse_lowlevel_ops) < op_size) { -+ fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n"); -+ op_size = sizeof(struct fuse_lowlevel_ops); -+ } -+ -+ if (args->argc == 0) { -+ fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n"); -+ return NULL; -+ } -+ -+ se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session)); -+ if (se == NULL) { -+ fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n"); -+ goto out1; -+ } -+ se->fd = -1; -+ se->conn.max_write = UINT_MAX; -+ se->conn.max_readahead = UINT_MAX; -+ -+ /* Parse options */ -+ if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1) -+ goto out2; -+ if(se->deny_others) { -+ /* Allowing access only by root is done by instructing -+ * kernel to allow access by everyone, and then restricting -+ * access to root and mountpoint owner in libfuse. -+ */ -+ // We may be adding the option a second time, but -+ // that doesn't hurt. -+ if(fuse_opt_add_arg(args, "-oallow_other") == -1) -+ goto out2; -+ } -+ mo = parse_mount_opts(args); -+ if (mo == NULL) -+ goto out3; -+ -+ if(args->argc == 1 && -+ args->argv[0][0] == '-') { -+ fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but " -+ "will be ignored\n"); -+ } else if (args->argc != 1) { -+ int i; -+ fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `"); -+ for(i = 1; i < args->argc-1; i++) -+ fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]); -+ fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]); -+ goto out4; -+ } -+ -+ if (se->debug) -+ fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSION); -+ -+ se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() + -+ FUSE_BUFFER_HEADER_SIZE; -+ -+ list_init_req(&se->list); -+ list_init_req(&se->interrupts); -+ list_init_nreq(&se->notify_list); -+ se->notify_ctr = 1; -+ fuse_mutex_init(&se->lock); -+ -+ err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor); -+ if (err) { -+ fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n", -+ strerror(err)); -+ goto out5; -+ } -+ -+ memcpy(&se->op, op, op_size); -+ se->owner = getuid(); -+ se->userdata = userdata; -+ -+ se->mo = mo; -+ return se; -+ -+out5: -+ pthread_mutex_destroy(&se->lock); -+out4: -+ fuse_opt_free_args(args); -+out3: -+ free(mo); -+out2: -+ free(se); -+out1: -+ return NULL; -+} -+ -+int fuse_session_mount(struct fuse_session *se, const char *mountpoint) -+{ -+ int fd; -+ -+ /* -+ * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos -+ * would ensue. -+ */ -+ do { -+ fd = open("/dev/null", O_RDWR); -+ if (fd > 2) -+ close(fd); -+ } while (fd >= 0 && fd <= 2); -+ -+ /* -+ * To allow FUSE daemons to run without privileges, the caller may open -+ * /dev/fuse before launching the file system and pass on the file -+ * descriptor by specifying /dev/fd/N as the mount point. Note that the -+ * parent process takes care of performing the mount in this case. -+ */ -+ fd = fuse_mnt_parse_fuse_fd(mountpoint); -+ if (fd != -1) { -+ if (fcntl(fd, F_GETFD) == -1) { -+ fuse_log(FUSE_LOG_ERR, -+ "fuse: Invalid file descriptor /dev/fd/%u\n", -+ fd); -+ return -1; -+ } -+ se->fd = fd; -+ return 0; -+ } -+ -+ /* Open channel */ -+ fd = fuse_kern_mount(mountpoint, se->mo); -+ if (fd == -1) -+ return -1; -+ se->fd = fd; -+ -+ /* Save mountpoint */ -+ se->mountpoint = strdup(mountpoint); -+ if (se->mountpoint == NULL) -+ goto error_out; -+ -+ return 0; -+ -+error_out: -+ fuse_kern_unmount(mountpoint, fd); -+ return -1; -+} -+ -+int fuse_session_fd(struct fuse_session *se) -+{ -+ return se->fd; -+} -+ -+void fuse_session_unmount(struct fuse_session *se) -+{ -+ if (se->mountpoint != NULL) { -+ fuse_kern_unmount(se->mountpoint, se->fd); -+ free(se->mountpoint); -+ se->mountpoint = NULL; -+ } -+} -+ -+#ifdef linux -+int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) -+{ -+ char *buf; -+ size_t bufsize = 1024; -+ char path[128]; -+ int ret; -+ int fd; -+ unsigned long pid = req->ctx.pid; -+ char *s; -+ -+ sprintf(path, "/proc/%lu/task/%lu/status", pid, pid); -+ -+retry: -+ buf = malloc(bufsize); -+ if (buf == NULL) -+ return -ENOMEM; -+ -+ ret = -EIO; -+ fd = open(path, O_RDONLY); -+ if (fd == -1) -+ goto out_free; -+ -+ ret = read(fd, buf, bufsize); -+ close(fd); -+ if (ret < 0) { -+ ret = -EIO; -+ goto out_free; -+ } -+ -+ if ((size_t)ret == bufsize) { -+ free(buf); -+ bufsize *= 4; -+ goto retry; -+ } -+ -+ ret = -EIO; -+ s = strstr(buf, "\nGroups:"); -+ if (s == NULL) -+ goto out_free; -+ -+ s += 8; -+ ret = 0; -+ while (1) { -+ char *end; -+ unsigned long val = strtoul(s, &end, 0); -+ if (end == s) -+ break; -+ -+ s = end; -+ if (ret < size) -+ list[ret] = val; -+ ret++; -+ } -+ -+out_free: -+ free(buf); -+ return ret; -+} -+#else /* linux */ -+/* -+ * This is currently not implemented on other than Linux... -+ */ -+int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) -+{ -+ (void) req; (void) size; (void) list; -+ return -ENOSYS; -+} -+#endif -+ -+void fuse_session_exit(struct fuse_session *se) -+{ -+ se->exited = 1; -+} -+ -+void fuse_session_reset(struct fuse_session *se) -+{ -+ se->exited = 0; -+ se->error = 0; -+} -+ -+int fuse_session_exited(struct fuse_session *se) -+{ -+ return se->exited; -+} diff --git a/0014-virtiofsd-Add-passthrough_ll.patch b/0014-virtiofsd-Add-passthrough_ll.patch deleted file mode 100644 index 3e1f0b0..0000000 --- a/0014-virtiofsd-Add-passthrough_ll.patch +++ /dev/null @@ -1,1370 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:00:43 +0000 -Subject: [PATCH] virtiofsd: Add passthrough_ll -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -passthrough_ll is one of the examples in the upstream fuse project -and is the main part of our daemon here. It passes through requests -from fuse to the underlying filesystem, using syscalls as directly -as possible. - ->From libfuse fuse-3.8.0 - -Signed-off-by: Dr. David Alan Gilbert - Fixed up 'GPL' to 'GPLv2' as per Dan's comments and consistent - with the 'LICENSE' file in libfuse; patch sent to libfuse to fix - it upstream. -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 7c6b66027241f41720240fc6ee1021cdbd975b2e) ---- - tools/virtiofsd/passthrough_ll.c | 1338 ++++++++++++++++++++++++++++++ - 1 file changed, 1338 insertions(+) - create mode 100644 tools/virtiofsd/passthrough_ll.c - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -new file mode 100644 -index 0000000000..e1a605691a ---- /dev/null -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -0,0 +1,1338 @@ -+/* -+ FUSE: Filesystem in Userspace -+ Copyright (C) 2001-2007 Miklos Szeredi -+ -+ This program can be distributed under the terms of the GNU GPLv2. -+ See the file COPYING. -+*/ -+ -+/** @file -+ * -+ * This file system mirrors the existing file system hierarchy of the -+ * system, starting at the root file system. This is implemented by -+ * just "passing through" all requests to the corresponding user-space -+ * libc functions. In contrast to passthrough.c and passthrough_fh.c, -+ * this implementation uses the low-level API. Its performance should -+ * be the least bad among the three, but many operations are not -+ * implemented. In particular, it is not possible to remove files (or -+ * directories) because the code necessary to defer actual removal -+ * until the file is not opened anymore would make the example much -+ * more complicated. -+ * -+ * When writeback caching is enabled (-o writeback mount option), it -+ * is only possible to write to files for which the mounting user has -+ * read permissions. This is because the writeback cache requires the -+ * kernel to be able to issue read requests for all files (which the -+ * passthrough filesystem cannot satisfy if it can't read the file in -+ * the underlying filesystem). -+ * -+ * Compile with: -+ * -+ * gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o passthrough_ll -+ * -+ * ## Source code ## -+ * \include passthrough_ll.c -+ */ -+ -+#define _GNU_SOURCE -+#define FUSE_USE_VERSION 31 -+ -+#include "config.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "passthrough_helpers.h" -+ -+/* We are re-using pointers to our `struct lo_inode` and `struct -+ lo_dirp` elements as inodes. This means that we must be able to -+ store uintptr_t values in a fuse_ino_t variable. The following -+ incantation checks this condition at compile time. */ -+#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus -+_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t), -+ "fuse_ino_t too small to hold uintptr_t values!"); -+#else -+struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \ -+ { unsigned _uintptr_to_must_hold_fuse_ino_t: -+ ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); }; -+#endif -+ -+struct lo_inode { -+ struct lo_inode *next; /* protected by lo->mutex */ -+ struct lo_inode *prev; /* protected by lo->mutex */ -+ int fd; -+ bool is_symlink; -+ ino_t ino; -+ dev_t dev; -+ uint64_t refcount; /* protected by lo->mutex */ -+}; -+ -+enum { -+ CACHE_NEVER, -+ CACHE_NORMAL, -+ CACHE_ALWAYS, -+}; -+ -+struct lo_data { -+ pthread_mutex_t mutex; -+ int debug; -+ int writeback; -+ int flock; -+ int xattr; -+ const char *source; -+ double timeout; -+ int cache; -+ int timeout_set; -+ struct lo_inode root; /* protected by lo->mutex */ -+}; -+ -+static const struct fuse_opt lo_opts[] = { -+ { "writeback", -+ offsetof(struct lo_data, writeback), 1 }, -+ { "no_writeback", -+ offsetof(struct lo_data, writeback), 0 }, -+ { "source=%s", -+ offsetof(struct lo_data, source), 0 }, -+ { "flock", -+ offsetof(struct lo_data, flock), 1 }, -+ { "no_flock", -+ offsetof(struct lo_data, flock), 0 }, -+ { "xattr", -+ offsetof(struct lo_data, xattr), 1 }, -+ { "no_xattr", -+ offsetof(struct lo_data, xattr), 0 }, -+ { "timeout=%lf", -+ offsetof(struct lo_data, timeout), 0 }, -+ { "timeout=", -+ offsetof(struct lo_data, timeout_set), 1 }, -+ { "cache=never", -+ offsetof(struct lo_data, cache), CACHE_NEVER }, -+ { "cache=auto", -+ offsetof(struct lo_data, cache), CACHE_NORMAL }, -+ { "cache=always", -+ offsetof(struct lo_data, cache), CACHE_ALWAYS }, -+ -+ FUSE_OPT_END -+}; -+ -+static struct lo_data *lo_data(fuse_req_t req) -+{ -+ return (struct lo_data *) fuse_req_userdata(req); -+} -+ -+static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) -+{ -+ if (ino == FUSE_ROOT_ID) -+ return &lo_data(req)->root; -+ else -+ return (struct lo_inode *) (uintptr_t) ino; -+} -+ -+static int lo_fd(fuse_req_t req, fuse_ino_t ino) -+{ -+ return lo_inode(req, ino)->fd; -+} -+ -+static bool lo_debug(fuse_req_t req) -+{ -+ return lo_data(req)->debug != 0; -+} -+ -+static void lo_init(void *userdata, -+ struct fuse_conn_info *conn) -+{ -+ struct lo_data *lo = (struct lo_data*) userdata; -+ -+ if(conn->capable & FUSE_CAP_EXPORT_SUPPORT) -+ conn->want |= FUSE_CAP_EXPORT_SUPPORT; -+ -+ if (lo->writeback && -+ conn->capable & FUSE_CAP_WRITEBACK_CACHE) { -+ if (lo->debug) -+ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); -+ conn->want |= FUSE_CAP_WRITEBACK_CACHE; -+ } -+ if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { -+ if (lo->debug) -+ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); -+ conn->want |= FUSE_CAP_FLOCK_LOCKS; -+ } -+} -+ -+static void lo_getattr(fuse_req_t req, fuse_ino_t ino, -+ struct fuse_file_info *fi) -+{ -+ int res; -+ struct stat buf; -+ struct lo_data *lo = lo_data(req); -+ -+ (void) fi; -+ -+ res = fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -+ if (res == -1) -+ return (void) fuse_reply_err(req, errno); -+ -+ fuse_reply_attr(req, &buf, lo->timeout); -+} -+ -+static int utimensat_empty_nofollow(struct lo_inode *inode, -+ const struct timespec *tv) -+{ -+ int res; -+ char procname[64]; -+ -+ if (inode->is_symlink) { -+ res = utimensat(inode->fd, "", tv, -+ AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -+ if (res == -1 && errno == EINVAL) { -+ /* Sorry, no race free way to set times on symlink. */ -+ errno = EPERM; -+ } -+ return res; -+ } -+ sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ -+ return utimensat(AT_FDCWD, procname, tv, 0); -+} -+ -+static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, -+ int valid, struct fuse_file_info *fi) -+{ -+ int saverr; -+ char procname[64]; -+ struct lo_inode *inode = lo_inode(req, ino); -+ int ifd = inode->fd; -+ int res; -+ -+ if (valid & FUSE_SET_ATTR_MODE) { -+ if (fi) { -+ res = fchmod(fi->fh, attr->st_mode); -+ } else { -+ sprintf(procname, "/proc/self/fd/%i", ifd); -+ res = chmod(procname, attr->st_mode); -+ } -+ if (res == -1) -+ goto out_err; -+ } -+ if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) { -+ uid_t uid = (valid & FUSE_SET_ATTR_UID) ? -+ attr->st_uid : (uid_t) -1; -+ gid_t gid = (valid & FUSE_SET_ATTR_GID) ? -+ attr->st_gid : (gid_t) -1; -+ -+ res = fchownat(ifd, "", uid, gid, -+ AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -+ if (res == -1) -+ goto out_err; -+ } -+ if (valid & FUSE_SET_ATTR_SIZE) { -+ if (fi) { -+ res = ftruncate(fi->fh, attr->st_size); -+ } else { -+ sprintf(procname, "/proc/self/fd/%i", ifd); -+ res = truncate(procname, attr->st_size); -+ } -+ if (res == -1) -+ goto out_err; -+ } -+ if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { -+ struct timespec tv[2]; -+ -+ tv[0].tv_sec = 0; -+ tv[1].tv_sec = 0; -+ tv[0].tv_nsec = UTIME_OMIT; -+ tv[1].tv_nsec = UTIME_OMIT; -+ -+ if (valid & FUSE_SET_ATTR_ATIME_NOW) -+ tv[0].tv_nsec = UTIME_NOW; -+ else if (valid & FUSE_SET_ATTR_ATIME) -+ tv[0] = attr->st_atim; -+ -+ if (valid & FUSE_SET_ATTR_MTIME_NOW) -+ tv[1].tv_nsec = UTIME_NOW; -+ else if (valid & FUSE_SET_ATTR_MTIME) -+ tv[1] = attr->st_mtim; -+ -+ if (fi) -+ res = futimens(fi->fh, tv); -+ else -+ res = utimensat_empty_nofollow(inode, tv); -+ if (res == -1) -+ goto out_err; -+ } -+ -+ return lo_getattr(req, ino, fi); -+ -+out_err: -+ saverr = errno; -+ fuse_reply_err(req, saverr); -+} -+ -+static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st) -+{ -+ struct lo_inode *p; -+ struct lo_inode *ret = NULL; -+ -+ pthread_mutex_lock(&lo->mutex); -+ for (p = lo->root.next; p != &lo->root; p = p->next) { -+ if (p->ino == st->st_ino && p->dev == st->st_dev) { -+ assert(p->refcount > 0); -+ ret = p; -+ ret->refcount++; -+ break; -+ } -+ } -+ pthread_mutex_unlock(&lo->mutex); -+ return ret; -+} -+ -+static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, -+ struct fuse_entry_param *e) -+{ -+ int newfd; -+ int res; -+ int saverr; -+ struct lo_data *lo = lo_data(req); -+ struct lo_inode *inode; -+ -+ memset(e, 0, sizeof(*e)); -+ e->attr_timeout = lo->timeout; -+ e->entry_timeout = lo->timeout; -+ -+ newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW); -+ if (newfd == -1) -+ goto out_err; -+ -+ res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -+ if (res == -1) -+ goto out_err; -+ -+ inode = lo_find(lo_data(req), &e->attr); -+ if (inode) { -+ close(newfd); -+ newfd = -1; -+ } else { -+ struct lo_inode *prev, *next; -+ -+ saverr = ENOMEM; -+ inode = calloc(1, sizeof(struct lo_inode)); -+ if (!inode) -+ goto out_err; -+ -+ inode->is_symlink = S_ISLNK(e->attr.st_mode); -+ inode->refcount = 1; -+ inode->fd = newfd; -+ inode->ino = e->attr.st_ino; -+ inode->dev = e->attr.st_dev; -+ -+ pthread_mutex_lock(&lo->mutex); -+ prev = &lo->root; -+ next = prev->next; -+ next->prev = inode; -+ inode->next = next; -+ inode->prev = prev; -+ prev->next = inode; -+ pthread_mutex_unlock(&lo->mutex); -+ } -+ e->ino = (uintptr_t) inode; -+ -+ if (lo_debug(req)) -+ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", -+ (unsigned long long) parent, name, (unsigned long long) e->ino); -+ -+ return 0; -+ -+out_err: -+ saverr = errno; -+ if (newfd != -1) -+ close(newfd); -+ return saverr; -+} -+ -+static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) -+{ -+ struct fuse_entry_param e; -+ int err; -+ -+ if (lo_debug(req)) -+ fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", -+ parent, name); -+ -+ err = lo_do_lookup(req, parent, name, &e); -+ if (err) -+ fuse_reply_err(req, err); -+ else -+ fuse_reply_entry(req, &e); -+} -+ -+static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, -+ const char *name, mode_t mode, dev_t rdev, -+ const char *link) -+{ -+ int res; -+ int saverr; -+ struct lo_inode *dir = lo_inode(req, parent); -+ struct fuse_entry_param e; -+ -+ saverr = ENOMEM; -+ -+ res = mknod_wrapper(dir->fd, name, link, mode, rdev); -+ -+ saverr = errno; -+ if (res == -1) -+ goto out; -+ -+ saverr = lo_do_lookup(req, parent, name, &e); -+ if (saverr) -+ goto out; -+ -+ if (lo_debug(req)) -+ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", -+ (unsigned long long) parent, name, (unsigned long long) e.ino); -+ -+ fuse_reply_entry(req, &e); -+ return; -+ -+out: -+ fuse_reply_err(req, saverr); -+} -+ -+static void lo_mknod(fuse_req_t req, fuse_ino_t parent, -+ const char *name, mode_t mode, dev_t rdev) -+{ -+ lo_mknod_symlink(req, parent, name, mode, rdev, NULL); -+} -+ -+static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, -+ mode_t mode) -+{ -+ lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL); -+} -+ -+static void lo_symlink(fuse_req_t req, const char *link, -+ fuse_ino_t parent, const char *name) -+{ -+ lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link); -+} -+ -+static int linkat_empty_nofollow(struct lo_inode *inode, int dfd, -+ const char *name) -+{ -+ int res; -+ char procname[64]; -+ -+ if (inode->is_symlink) { -+ res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH); -+ if (res == -1 && (errno == ENOENT || errno == EINVAL)) { -+ /* Sorry, no race free way to hard-link a symlink. */ -+ errno = EPERM; -+ } -+ return res; -+ } -+ -+ sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ -+ return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW); -+} -+ -+static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, -+ const char *name) -+{ -+ int res; -+ struct lo_data *lo = lo_data(req); -+ struct lo_inode *inode = lo_inode(req, ino); -+ struct fuse_entry_param e; -+ int saverr; -+ -+ memset(&e, 0, sizeof(struct fuse_entry_param)); -+ e.attr_timeout = lo->timeout; -+ e.entry_timeout = lo->timeout; -+ -+ res = linkat_empty_nofollow(inode, lo_fd(req, parent), name); -+ if (res == -1) -+ goto out_err; -+ -+ res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -+ if (res == -1) -+ goto out_err; -+ -+ pthread_mutex_lock(&lo->mutex); -+ inode->refcount++; -+ pthread_mutex_unlock(&lo->mutex); -+ e.ino = (uintptr_t) inode; -+ -+ if (lo_debug(req)) -+ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", -+ (unsigned long long) parent, name, -+ (unsigned long long) e.ino); -+ -+ fuse_reply_entry(req, &e); -+ return; -+ -+out_err: -+ saverr = errno; -+ fuse_reply_err(req, saverr); -+} -+ -+static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) -+{ -+ int res; -+ -+ res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); -+ -+ fuse_reply_err(req, res == -1 ? errno : 0); -+} -+ -+static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, -+ fuse_ino_t newparent, const char *newname, -+ unsigned int flags) -+{ -+ int res; -+ -+ if (flags) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ -+ res = renameat(lo_fd(req, parent), name, -+ lo_fd(req, newparent), newname); -+ -+ fuse_reply_err(req, res == -1 ? errno : 0); -+} -+ -+static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) -+{ -+ int res; -+ -+ res = unlinkat(lo_fd(req, parent), name, 0); -+ -+ fuse_reply_err(req, res == -1 ? errno : 0); -+} -+ -+static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) -+{ -+ if (!inode) -+ return; -+ -+ pthread_mutex_lock(&lo->mutex); -+ assert(inode->refcount >= n); -+ inode->refcount -= n; -+ if (!inode->refcount) { -+ struct lo_inode *prev, *next; -+ -+ prev = inode->prev; -+ next = inode->next; -+ next->prev = prev; -+ prev->next = next; -+ -+ pthread_mutex_unlock(&lo->mutex); -+ close(inode->fd); -+ free(inode); -+ -+ } else { -+ pthread_mutex_unlock(&lo->mutex); -+ } -+} -+ -+static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) -+{ -+ struct lo_data *lo = lo_data(req); -+ struct lo_inode *inode = lo_inode(req, ino); -+ -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", -+ (unsigned long long) ino, -+ (unsigned long long) inode->refcount, -+ (unsigned long long) nlookup); -+ } -+ -+ unref_inode(lo, inode, nlookup); -+} -+ -+static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) -+{ -+ lo_forget_one(req, ino, nlookup); -+ fuse_reply_none(req); -+} -+ -+static void lo_forget_multi(fuse_req_t req, size_t count, -+ struct fuse_forget_data *forgets) -+{ -+ int i; -+ -+ for (i = 0; i < count; i++) -+ lo_forget_one(req, forgets[i].ino, forgets[i].nlookup); -+ fuse_reply_none(req); -+} -+ -+static void lo_readlink(fuse_req_t req, fuse_ino_t ino) -+{ -+ char buf[PATH_MAX + 1]; -+ int res; -+ -+ res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf)); -+ if (res == -1) -+ return (void) fuse_reply_err(req, errno); -+ -+ if (res == sizeof(buf)) -+ return (void) fuse_reply_err(req, ENAMETOOLONG); -+ -+ buf[res] = '\0'; -+ -+ fuse_reply_readlink(req, buf); -+} -+ -+struct lo_dirp { -+ DIR *dp; -+ struct dirent *entry; -+ off_t offset; -+}; -+ -+static struct lo_dirp *lo_dirp(struct fuse_file_info *fi) -+{ -+ return (struct lo_dirp *) (uintptr_t) fi->fh; -+} -+ -+static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) -+{ -+ int error = ENOMEM; -+ struct lo_data *lo = lo_data(req); -+ struct lo_dirp *d; -+ int fd; -+ -+ d = calloc(1, sizeof(struct lo_dirp)); -+ if (d == NULL) -+ goto out_err; -+ -+ fd = openat(lo_fd(req, ino), ".", O_RDONLY); -+ if (fd == -1) -+ goto out_errno; -+ -+ d->dp = fdopendir(fd); -+ if (d->dp == NULL) -+ goto out_errno; -+ -+ d->offset = 0; -+ d->entry = NULL; -+ -+ fi->fh = (uintptr_t) d; -+ if (lo->cache == CACHE_ALWAYS) -+ fi->keep_cache = 1; -+ fuse_reply_open(req, fi); -+ return; -+ -+out_errno: -+ error = errno; -+out_err: -+ if (d) { -+ if (fd != -1) -+ close(fd); -+ free(d); -+ } -+ fuse_reply_err(req, error); -+} -+ -+static int is_dot_or_dotdot(const char *name) -+{ -+ return name[0] == '.' && (name[1] == '\0' || -+ (name[1] == '.' && name[2] == '\0')); -+} -+ -+static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, -+ off_t offset, struct fuse_file_info *fi, int plus) -+{ -+ struct lo_dirp *d = lo_dirp(fi); -+ char *buf; -+ char *p; -+ size_t rem = size; -+ int err; -+ -+ (void) ino; -+ -+ buf = calloc(1, size); -+ if (!buf) { -+ err = ENOMEM; -+ goto error; -+ } -+ p = buf; -+ -+ if (offset != d->offset) { -+ seekdir(d->dp, offset); -+ d->entry = NULL; -+ d->offset = offset; -+ } -+ while (1) { -+ size_t entsize; -+ off_t nextoff; -+ const char *name; -+ -+ if (!d->entry) { -+ errno = 0; -+ d->entry = readdir(d->dp); -+ if (!d->entry) { -+ if (errno) { // Error -+ err = errno; -+ goto error; -+ } else { // End of stream -+ break; -+ } -+ } -+ } -+ nextoff = d->entry->d_off; -+ name = d->entry->d_name; -+ fuse_ino_t entry_ino = 0; -+ if (plus) { -+ struct fuse_entry_param e; -+ if (is_dot_or_dotdot(name)) { -+ e = (struct fuse_entry_param) { -+ .attr.st_ino = d->entry->d_ino, -+ .attr.st_mode = d->entry->d_type << 12, -+ }; -+ } else { -+ err = lo_do_lookup(req, ino, name, &e); -+ if (err) -+ goto error; -+ entry_ino = e.ino; -+ } -+ -+ entsize = fuse_add_direntry_plus(req, p, rem, name, -+ &e, nextoff); -+ } else { -+ struct stat st = { -+ .st_ino = d->entry->d_ino, -+ .st_mode = d->entry->d_type << 12, -+ }; -+ entsize = fuse_add_direntry(req, p, rem, name, -+ &st, nextoff); -+ } -+ if (entsize > rem) { -+ if (entry_ino != 0) -+ lo_forget_one(req, entry_ino, 1); -+ break; -+ } -+ -+ p += entsize; -+ rem -= entsize; -+ -+ d->entry = NULL; -+ d->offset = nextoff; -+ } -+ -+ err = 0; -+error: -+ // If there's an error, we can only signal it if we haven't stored -+ // any entries yet - otherwise we'd end up with wrong lookup -+ // counts for the entries that are already in the buffer. So we -+ // return what we've collected until that point. -+ if (err && rem == size) -+ fuse_reply_err(req, err); -+ else -+ fuse_reply_buf(req, buf, size - rem); -+ free(buf); -+} -+ -+static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, -+ off_t offset, struct fuse_file_info *fi) -+{ -+ lo_do_readdir(req, ino, size, offset, fi, 0); -+} -+ -+static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, -+ off_t offset, struct fuse_file_info *fi) -+{ -+ lo_do_readdir(req, ino, size, offset, fi, 1); -+} -+ -+static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) -+{ -+ struct lo_dirp *d = lo_dirp(fi); -+ (void) ino; -+ closedir(d->dp); -+ free(d); -+ fuse_reply_err(req, 0); -+} -+ -+static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, -+ mode_t mode, struct fuse_file_info *fi) -+{ -+ int fd; -+ struct lo_data *lo = lo_data(req); -+ struct fuse_entry_param e; -+ int err; -+ -+ if (lo_debug(req)) -+ fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", -+ parent, name); -+ -+ fd = openat(lo_fd(req, parent), name, -+ (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode); -+ if (fd == -1) -+ return (void) fuse_reply_err(req, errno); -+ -+ fi->fh = fd; -+ if (lo->cache == CACHE_NEVER) -+ fi->direct_io = 1; -+ else if (lo->cache == CACHE_ALWAYS) -+ fi->keep_cache = 1; -+ -+ err = lo_do_lookup(req, parent, name, &e); -+ if (err) -+ fuse_reply_err(req, err); -+ else -+ fuse_reply_create(req, &e, fi); -+} -+ -+static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, -+ struct fuse_file_info *fi) -+{ -+ int res; -+ int fd = dirfd(lo_dirp(fi)->dp); -+ (void) ino; -+ if (datasync) -+ res = fdatasync(fd); -+ else -+ res = fsync(fd); -+ fuse_reply_err(req, res == -1 ? errno : 0); -+} -+ -+static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) -+{ -+ int fd; -+ char buf[64]; -+ struct lo_data *lo = lo_data(req); -+ -+ if (lo_debug(req)) -+ fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", -+ ino, fi->flags); -+ -+ /* With writeback cache, kernel may send read requests even -+ when userspace opened write-only */ -+ if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) { -+ fi->flags &= ~O_ACCMODE; -+ fi->flags |= O_RDWR; -+ } -+ -+ /* With writeback cache, O_APPEND is handled by the kernel. -+ This breaks atomicity (since the file may change in the -+ underlying filesystem, so that the kernel's idea of the -+ end of the file isn't accurate anymore). In this example, -+ we just accept that. A more rigorous filesystem may want -+ to return an error here */ -+ if (lo->writeback && (fi->flags & O_APPEND)) -+ fi->flags &= ~O_APPEND; -+ -+ sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino)); -+ fd = open(buf, fi->flags & ~O_NOFOLLOW); -+ if (fd == -1) -+ return (void) fuse_reply_err(req, errno); -+ -+ fi->fh = fd; -+ if (lo->cache == CACHE_NEVER) -+ fi->direct_io = 1; -+ else if (lo->cache == CACHE_ALWAYS) -+ fi->keep_cache = 1; -+ fuse_reply_open(req, fi); -+} -+ -+static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) -+{ -+ (void) ino; -+ -+ close(fi->fh); -+ fuse_reply_err(req, 0); -+} -+ -+static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) -+{ -+ int res; -+ (void) ino; -+ res = close(dup(fi->fh)); -+ fuse_reply_err(req, res == -1 ? errno : 0); -+} -+ -+static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, -+ struct fuse_file_info *fi) -+{ -+ int res; -+ (void) ino; -+ if (datasync) -+ res = fdatasync(fi->fh); -+ else -+ res = fsync(fi->fh); -+ fuse_reply_err(req, res == -1 ? errno : 0); -+} -+ -+static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, -+ off_t offset, struct fuse_file_info *fi) -+{ -+ struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); -+ -+ if (lo_debug(req)) -+ fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, " -+ "off=%lu)\n", ino, size, (unsigned long) offset); -+ -+ buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; -+ buf.buf[0].fd = fi->fh; -+ buf.buf[0].pos = offset; -+ -+ fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE); -+} -+ -+static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, -+ struct fuse_bufvec *in_buf, off_t off, -+ struct fuse_file_info *fi) -+{ -+ (void) ino; -+ ssize_t res; -+ struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); -+ -+ out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; -+ out_buf.buf[0].fd = fi->fh; -+ out_buf.buf[0].pos = off; -+ -+ if (lo_debug(req)) -+ fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n", -+ ino, out_buf.buf[0].size, (unsigned long) off); -+ -+ res = fuse_buf_copy(&out_buf, in_buf, 0); -+ if(res < 0) -+ fuse_reply_err(req, -res); -+ else -+ fuse_reply_write(req, (size_t) res); -+} -+ -+static void lo_statfs(fuse_req_t req, fuse_ino_t ino) -+{ -+ int res; -+ struct statvfs stbuf; -+ -+ res = fstatvfs(lo_fd(req, ino), &stbuf); -+ if (res == -1) -+ fuse_reply_err(req, errno); -+ else -+ fuse_reply_statfs(req, &stbuf); -+} -+ -+static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, -+ off_t offset, off_t length, struct fuse_file_info *fi) -+{ -+ int err = EOPNOTSUPP; -+ (void) ino; -+ -+#ifdef HAVE_FALLOCATE -+ err = fallocate(fi->fh, mode, offset, length); -+ if (err < 0) -+ err = errno; -+ -+#elif defined(HAVE_POSIX_FALLOCATE) -+ if (mode) { -+ fuse_reply_err(req, EOPNOTSUPP); -+ return; -+ } -+ -+ err = posix_fallocate(fi->fh, offset, length); -+#endif -+ -+ fuse_reply_err(req, err); -+} -+ -+static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, -+ int op) -+{ -+ int res; -+ (void) ino; -+ -+ res = flock(fi->fh, op); -+ -+ fuse_reply_err(req, res == -1 ? errno : 0); -+} -+ -+static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, -+ size_t size) -+{ -+ char *value = NULL; -+ char procname[64]; -+ struct lo_inode *inode = lo_inode(req, ino); -+ ssize_t ret; -+ int saverr; -+ -+ saverr = ENOSYS; -+ if (!lo_data(req)->xattr) -+ goto out; -+ -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", -+ ino, name, size); -+ } -+ -+ if (inode->is_symlink) { -+ /* Sorry, no race free way to getxattr on symlink. */ -+ saverr = EPERM; -+ goto out; -+ } -+ -+ sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ -+ if (size) { -+ value = malloc(size); -+ if (!value) -+ goto out_err; -+ -+ ret = getxattr(procname, name, value, size); -+ if (ret == -1) -+ goto out_err; -+ saverr = 0; -+ if (ret == 0) -+ goto out; -+ -+ fuse_reply_buf(req, value, ret); -+ } else { -+ ret = getxattr(procname, name, NULL, 0); -+ if (ret == -1) -+ goto out_err; -+ -+ fuse_reply_xattr(req, ret); -+ } -+out_free: -+ free(value); -+ return; -+ -+out_err: -+ saverr = errno; -+out: -+ fuse_reply_err(req, saverr); -+ goto out_free; -+} -+ -+static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) -+{ -+ char *value = NULL; -+ char procname[64]; -+ struct lo_inode *inode = lo_inode(req, ino); -+ ssize_t ret; -+ int saverr; -+ -+ saverr = ENOSYS; -+ if (!lo_data(req)->xattr) -+ goto out; -+ -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n", -+ ino, size); -+ } -+ -+ if (inode->is_symlink) { -+ /* Sorry, no race free way to listxattr on symlink. */ -+ saverr = EPERM; -+ goto out; -+ } -+ -+ sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ -+ if (size) { -+ value = malloc(size); -+ if (!value) -+ goto out_err; -+ -+ ret = listxattr(procname, value, size); -+ if (ret == -1) -+ goto out_err; -+ saverr = 0; -+ if (ret == 0) -+ goto out; -+ -+ fuse_reply_buf(req, value, ret); -+ } else { -+ ret = listxattr(procname, NULL, 0); -+ if (ret == -1) -+ goto out_err; -+ -+ fuse_reply_xattr(req, ret); -+ } -+out_free: -+ free(value); -+ return; -+ -+out_err: -+ saverr = errno; -+out: -+ fuse_reply_err(req, saverr); -+ goto out_free; -+} -+ -+static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, -+ const char *value, size_t size, int flags) -+{ -+ char procname[64]; -+ struct lo_inode *inode = lo_inode(req, ino); -+ ssize_t ret; -+ int saverr; -+ -+ saverr = ENOSYS; -+ if (!lo_data(req)->xattr) -+ goto out; -+ -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n", -+ ino, name, value, size); -+ } -+ -+ if (inode->is_symlink) { -+ /* Sorry, no race free way to setxattr on symlink. */ -+ saverr = EPERM; -+ goto out; -+ } -+ -+ sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ -+ ret = setxattr(procname, name, value, size, flags); -+ saverr = ret == -1 ? errno : 0; -+ -+out: -+ fuse_reply_err(req, saverr); -+} -+ -+static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) -+{ -+ char procname[64]; -+ struct lo_inode *inode = lo_inode(req, ino); -+ ssize_t ret; -+ int saverr; -+ -+ saverr = ENOSYS; -+ if (!lo_data(req)->xattr) -+ goto out; -+ -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", -+ ino, name); -+ } -+ -+ if (inode->is_symlink) { -+ /* Sorry, no race free way to setxattr on symlink. */ -+ saverr = EPERM; -+ goto out; -+ } -+ -+ sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ -+ ret = removexattr(procname, name); -+ saverr = ret == -1 ? errno : 0; -+ -+out: -+ fuse_reply_err(req, saverr); -+} -+ -+#ifdef HAVE_COPY_FILE_RANGE -+static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, -+ struct fuse_file_info *fi_in, -+ fuse_ino_t ino_out, off_t off_out, -+ struct fuse_file_info *fi_out, size_t len, -+ int flags) -+{ -+ ssize_t res; -+ -+ if (lo_debug(req)) -+ fuse_log(FUSE_LOG_DEBUG, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, " -+ "off=%lu, ino=%" PRIu64 "/fd=%lu, " -+ "off=%lu, size=%zd, flags=0x%x)\n", -+ ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out, -+ len, flags); -+ -+ res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len, -+ flags); -+ if (res < 0) -+ fuse_reply_err(req, -errno); -+ else -+ fuse_reply_write(req, res); -+} -+#endif -+ -+static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, -+ struct fuse_file_info *fi) -+{ -+ off_t res; -+ -+ (void)ino; -+ res = lseek(fi->fh, off, whence); -+ if (res != -1) -+ fuse_reply_lseek(req, res); -+ else -+ fuse_reply_err(req, errno); -+} -+ -+static struct fuse_lowlevel_ops lo_oper = { -+ .init = lo_init, -+ .lookup = lo_lookup, -+ .mkdir = lo_mkdir, -+ .mknod = lo_mknod, -+ .symlink = lo_symlink, -+ .link = lo_link, -+ .unlink = lo_unlink, -+ .rmdir = lo_rmdir, -+ .rename = lo_rename, -+ .forget = lo_forget, -+ .forget_multi = lo_forget_multi, -+ .getattr = lo_getattr, -+ .setattr = lo_setattr, -+ .readlink = lo_readlink, -+ .opendir = lo_opendir, -+ .readdir = lo_readdir, -+ .readdirplus = lo_readdirplus, -+ .releasedir = lo_releasedir, -+ .fsyncdir = lo_fsyncdir, -+ .create = lo_create, -+ .open = lo_open, -+ .release = lo_release, -+ .flush = lo_flush, -+ .fsync = lo_fsync, -+ .read = lo_read, -+ .write_buf = lo_write_buf, -+ .statfs = lo_statfs, -+ .fallocate = lo_fallocate, -+ .flock = lo_flock, -+ .getxattr = lo_getxattr, -+ .listxattr = lo_listxattr, -+ .setxattr = lo_setxattr, -+ .removexattr = lo_removexattr, -+#ifdef HAVE_COPY_FILE_RANGE -+ .copy_file_range = lo_copy_file_range, -+#endif -+ .lseek = lo_lseek, -+}; -+ -+int main(int argc, char *argv[]) -+{ -+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv); -+ struct fuse_session *se; -+ struct fuse_cmdline_opts opts; -+ struct lo_data lo = { .debug = 0, -+ .writeback = 0 }; -+ int ret = -1; -+ -+ /* Don't mask creation mode, kernel already did that */ -+ umask(0); -+ -+ pthread_mutex_init(&lo.mutex, NULL); -+ lo.root.next = lo.root.prev = &lo.root; -+ lo.root.fd = -1; -+ lo.cache = CACHE_NORMAL; -+ -+ if (fuse_parse_cmdline(&args, &opts) != 0) -+ return 1; -+ if (opts.show_help) { -+ printf("usage: %s [options] \n\n", argv[0]); -+ fuse_cmdline_help(); -+ fuse_lowlevel_help(); -+ ret = 0; -+ goto err_out1; -+ } else if (opts.show_version) { -+ printf("FUSE library version %s\n", fuse_pkgversion()); -+ fuse_lowlevel_version(); -+ ret = 0; -+ goto err_out1; -+ } -+ -+ if(opts.mountpoint == NULL) { -+ printf("usage: %s [options] \n", argv[0]); -+ printf(" %s --help\n", argv[0]); -+ ret = 1; -+ goto err_out1; -+ } -+ -+ if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1) -+ return 1; -+ -+ lo.debug = opts.debug; -+ lo.root.refcount = 2; -+ if (lo.source) { -+ struct stat stat; -+ int res; -+ -+ res = lstat(lo.source, &stat); -+ if (res == -1) { -+ fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n", -+ lo.source); -+ exit(1); -+ } -+ if (!S_ISDIR(stat.st_mode)) { -+ fuse_log(FUSE_LOG_ERR, "source is not a directory\n"); -+ exit(1); -+ } -+ -+ } else { -+ lo.source = "/"; -+ } -+ lo.root.is_symlink = false; -+ if (!lo.timeout_set) { -+ switch (lo.cache) { -+ case CACHE_NEVER: -+ lo.timeout = 0.0; -+ break; -+ -+ case CACHE_NORMAL: -+ lo.timeout = 1.0; -+ break; -+ -+ case CACHE_ALWAYS: -+ lo.timeout = 86400.0; -+ break; -+ } -+ } else if (lo.timeout < 0) { -+ fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n", -+ lo.timeout); -+ exit(1); -+ } -+ -+ lo.root.fd = open(lo.source, O_PATH); -+ if (lo.root.fd == -1) { -+ fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", -+ lo.source); -+ exit(1); -+ } -+ -+ se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); -+ if (se == NULL) -+ goto err_out1; -+ -+ if (fuse_set_signal_handlers(se) != 0) -+ goto err_out2; -+ -+ if (fuse_session_mount(se, opts.mountpoint) != 0) -+ goto err_out3; -+ -+ fuse_daemonize(opts.foreground); -+ -+ /* Block until ctrl+c or fusermount -u */ -+ if (opts.singlethread) -+ ret = fuse_session_loop(se); -+ else -+ ret = fuse_session_loop_mt(se, opts.clone_fd); -+ -+ fuse_session_unmount(se); -+err_out3: -+ fuse_remove_signal_handlers(se); -+err_out2: -+ fuse_session_destroy(se); -+err_out1: -+ free(opts.mountpoint); -+ fuse_opt_free_args(&args); -+ -+ if (lo.root.fd >= 0) -+ close(lo.root.fd); -+ -+ return ret ? 1 : 0; -+} diff --git a/0015-virtiofsd-Trim-down-imported-files.patch b/0015-virtiofsd-Trim-down-imported-files.patch deleted file mode 100644 index 98ee88d..0000000 --- a/0015-virtiofsd-Trim-down-imported-files.patch +++ /dev/null @@ -1,1565 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:00:44 +0000 -Subject: [PATCH] virtiofsd: Trim down imported files -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -There's a lot of the original fuse code we don't need; trim them down. - -Signed-off-by: Dr. David Alan Gilbert -with additional trimming by: -Signed-off-by: Misono Tomohiro -Reviewed-by: Daniel P. Berrangé -Reviewed-by: Xiao Yang -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit a3e23f325439a290c504d6bbc48c2e742149ecab) ---- - tools/virtiofsd/buffer.c | 71 +-- - tools/virtiofsd/fuse.h | 46 -- - tools/virtiofsd/fuse_common.h | 32 -- - tools/virtiofsd/fuse_i.h | 41 -- - tools/virtiofsd/fuse_log.h | 8 - - tools/virtiofsd/fuse_lowlevel.c | 675 +------------------------- - tools/virtiofsd/fuse_lowlevel.h | 28 -- - tools/virtiofsd/fuse_opt.h | 8 - - tools/virtiofsd/helper.c | 143 ------ - tools/virtiofsd/passthrough_helpers.h | 26 - - tools/virtiofsd/passthrough_ll.c | 1 - - 11 files changed, 8 insertions(+), 1071 deletions(-) - -diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c -index 5ab9b87455..aefb7dbf15 100644 ---- a/tools/virtiofsd/buffer.c -+++ b/tools/virtiofsd/buffer.c -@@ -157,73 +157,6 @@ static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off, - return copied; - } - --#ifdef HAVE_SPLICE --static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off, -- const struct fuse_buf *src, size_t src_off, -- size_t len, enum fuse_buf_copy_flags flags) --{ -- int splice_flags = 0; -- off_t *srcpos = NULL; -- off_t *dstpos = NULL; -- off_t srcpos_val; -- off_t dstpos_val; -- ssize_t res; -- size_t copied = 0; -- -- if (flags & FUSE_BUF_SPLICE_MOVE) -- splice_flags |= SPLICE_F_MOVE; -- if (flags & FUSE_BUF_SPLICE_NONBLOCK) -- splice_flags |= SPLICE_F_NONBLOCK; -- -- if (src->flags & FUSE_BUF_FD_SEEK) { -- srcpos_val = src->pos + src_off; -- srcpos = &srcpos_val; -- } -- if (dst->flags & FUSE_BUF_FD_SEEK) { -- dstpos_val = dst->pos + dst_off; -- dstpos = &dstpos_val; -- } -- -- while (len) { -- res = splice(src->fd, srcpos, dst->fd, dstpos, len, -- splice_flags); -- if (res == -1) { -- if (copied) -- break; -- -- if (errno != EINVAL || (flags & FUSE_BUF_FORCE_SPLICE)) -- return -errno; -- -- /* Maybe splice is not supported for this combination */ -- return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, -- len); -- } -- if (res == 0) -- break; -- -- copied += res; -- if (!(src->flags & FUSE_BUF_FD_RETRY) && -- !(dst->flags & FUSE_BUF_FD_RETRY)) { -- break; -- } -- -- len -= res; -- } -- -- return copied; --} --#else --static ssize_t fuse_buf_splice(const struct fuse_buf *dst, size_t dst_off, -- const struct fuse_buf *src, size_t src_off, -- size_t len, enum fuse_buf_copy_flags flags) --{ -- (void) flags; -- -- return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); --} --#endif -- -- - static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off, - const struct fuse_buf *src, size_t src_off, - size_t len, enum fuse_buf_copy_flags flags) -@@ -247,10 +180,8 @@ static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off, - return fuse_buf_write(dst, dst_off, src, src_off, len); - } else if (!dst_is_fd) { - return fuse_buf_read(dst, dst_off, src, src_off, len); -- } else if (flags & FUSE_BUF_NO_SPLICE) { -- return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); - } else { -- return fuse_buf_splice(dst, dst_off, src, src_off, len, flags); -+ return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); - } - } - -diff --git a/tools/virtiofsd/fuse.h b/tools/virtiofsd/fuse.h -index 883f6e59fb..3202fba6bb 100644 ---- a/tools/virtiofsd/fuse.h -+++ b/tools/virtiofsd/fuse.h -@@ -25,10 +25,6 @@ - #include - #include - --#ifdef __cplusplus --extern "C" { --#endif -- - /* ----------------------------------------------------------- * - * Basic FUSE API * - * ----------------------------------------------------------- */ -@@ -978,44 +974,6 @@ int fuse_loop(struct fuse *f); - */ - void fuse_exit(struct fuse *f); - --/** -- * FUSE event loop with multiple threads -- * -- * Requests from the kernel are processed, and the appropriate -- * operations are called. Request are processed in parallel by -- * distributing them between multiple threads. -- * -- * For a description of the return value and the conditions when the -- * event loop exits, refer to the documentation of -- * fuse_session_loop(). -- * -- * Note: using fuse_loop() instead of fuse_loop_mt() means you are running in -- * single-threaded mode, and that you will not have to worry about reentrancy, -- * though you will have to worry about recursive lookups. In single-threaded -- * mode, FUSE will wait for one callback to return before calling another. -- * -- * Enabling multiple threads, by using fuse_loop_mt(), will cause FUSE to make -- * multiple simultaneous calls into the various callback functions given by your -- * fuse_operations record. -- * -- * If you are using multiple threads, you can enjoy all the parallel execution -- * and interactive response benefits of threads, and you get to enjoy all the -- * benefits of race conditions and locking bugs, too. Ensure that any code used -- * in the callback function of fuse_operations is also thread-safe. -- * -- * @param f the FUSE handle -- * @param config loop configuration -- * @return see fuse_session_loop() -- * -- * See also: fuse_loop() -- */ --#if FUSE_USE_VERSION < 32 --int fuse_loop_mt_31(struct fuse *f, int clone_fd); --#define fuse_loop_mt(f, clone_fd) fuse_loop_mt_31(f, clone_fd) --#else --int fuse_loop_mt(struct fuse *f, struct fuse_loop_config *config); --#endif -- - /** - * Get the current context - * -@@ -1268,8 +1226,4 @@ struct fuse_session *fuse_get_session(struct fuse *f); - */ - int fuse_open_channel(const char *mountpoint, const char *options); - --#ifdef __cplusplus --} --#endif -- - #endif /* FUSE_H_ */ -diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h -index 2d686b2ac4..bf8f8cc865 100644 ---- a/tools/virtiofsd/fuse_common.h -+++ b/tools/virtiofsd/fuse_common.h -@@ -28,10 +28,6 @@ - #define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) - #define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) - --#ifdef __cplusplus --extern "C" { --#endif -- - /** - * Information about an open file. - * -@@ -100,30 +96,6 @@ struct fuse_file_info { - uint32_t poll_events; - }; - --/** -- * Configuration parameters passed to fuse_session_loop_mt() and -- * fuse_loop_mt(). -- */ --struct fuse_loop_config { -- /** -- * whether to use separate device fds for each thread -- * (may increase performance) -- */ -- int clone_fd; -- -- /** -- * The maximum number of available worker threads before they -- * start to get deleted when they become idle. If not -- * specified, the default is 10. -- * -- * Adjusting this has performance implications; a very small number -- * of threads in the pool will cause a lot of thread creation and -- * deletion overhead and performance may suffer. When set to 0, a new -- * thread will be created to service every operation. -- */ -- unsigned int max_idle_threads; --}; -- - /************************************************************************** - * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' * - **************************************************************************/ -@@ -802,10 +774,6 @@ void fuse_remove_signal_handlers(struct fuse_session *se); - # error only API version 30 or greater is supported - #endif - --#ifdef __cplusplus --} --#endif -- - - /* - * This interface uses 64 bit off_t. -diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h -index d38b630ac5..b39522e3ca 100644 ---- a/tools/virtiofsd/fuse_i.h -+++ b/tools/virtiofsd/fuse_i.h -@@ -9,8 +9,6 @@ - #include "fuse.h" - #include "fuse_lowlevel.h" - --struct mount_opts; -- - struct fuse_req { - struct fuse_session *se; - uint64_t unique; -@@ -45,7 +43,6 @@ struct fuse_session { - char *mountpoint; - volatile int exited; - int fd; -- struct mount_opts *mo; - int debug; - int deny_others; - struct fuse_lowlevel_ops op; -@@ -58,7 +55,6 @@ struct fuse_session { - struct fuse_req interrupts; - pthread_mutex_t lock; - int got_destroy; -- pthread_key_t pipe_key; - int broken_splice_nonblock; - uint64_t notify_ctr; - struct fuse_notify_req notify_list; -@@ -87,53 +83,16 @@ struct fuse_module { - int ctr; - }; - --/* ----------------------------------------------------------- * -- * Channel interface (when using -o clone_fd) * -- * ----------------------------------------------------------- */ -- --/** -- * Obtain counted reference to the channel -- * -- * @param ch the channel -- * @return the channel -- */ --struct fuse_chan *fuse_chan_get(struct fuse_chan *ch); -- --/** -- * Drop counted reference to a channel -- * -- * @param ch the channel -- */ --void fuse_chan_put(struct fuse_chan *ch); -- --struct mount_opts *parse_mount_opts(struct fuse_args *args); --void destroy_mount_opts(struct mount_opts *mo); --void fuse_mount_version(void); --unsigned get_max_read(struct mount_opts *o); --void fuse_kern_unmount(const char *mountpoint, int fd); --int fuse_kern_mount(const char *mountpoint, struct mount_opts *mo); -- - int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, - int count); - void fuse_free_req(fuse_req_t req); - --void cuse_lowlevel_init(fuse_req_t req, fuse_ino_t nodeide, const void *inarg); -- --int fuse_start_thread(pthread_t *thread_id, void *(*func)(void *), void *arg); -- --int fuse_session_receive_buf_int(struct fuse_session *se, struct fuse_buf *buf, -- struct fuse_chan *ch); - void fuse_session_process_buf_int(struct fuse_session *se, - const struct fuse_buf *buf, struct fuse_chan *ch); - --struct fuse *fuse_new_31(struct fuse_args *args, const struct fuse_operations *op, -- size_t op_size, void *private_data); --int fuse_loop_mt_32(struct fuse *f, struct fuse_loop_config *config); --int fuse_session_loop_mt_32(struct fuse_session *se, struct fuse_loop_config *config); - - #define FUSE_MAX_MAX_PAGES 256 - #define FUSE_DEFAULT_MAX_PAGES_PER_REQ 32 - - /* room needed in buffer to accommodate header */ - #define FUSE_BUFFER_HEADER_SIZE 0x1000 -- -diff --git a/tools/virtiofsd/fuse_log.h b/tools/virtiofsd/fuse_log.h -index 5e112e0f53..0af700da6b 100644 ---- a/tools/virtiofsd/fuse_log.h -+++ b/tools/virtiofsd/fuse_log.h -@@ -16,10 +16,6 @@ - - #include - --#ifdef __cplusplus --extern "C" { --#endif -- - /** - * Log severity level - * -@@ -75,8 +71,4 @@ void fuse_set_log_func(fuse_log_func_t func); - */ - void fuse_log(enum fuse_log_level level, const char *fmt, ...); - --#ifdef __cplusplus --} --#endif -- - #endif /* FUSE_LOG_H_ */ -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index f2d7038e34..e6fa247924 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -16,7 +16,6 @@ - #include "fuse_kernel.h" - #include "fuse_opt.h" - #include "fuse_misc.h" --#include "mount_util.h" - - #include - #include -@@ -28,12 +27,6 @@ - #include - #include - --#ifndef F_LINUX_SPECIFIC_BASE --#define F_LINUX_SPECIFIC_BASE 1024 --#endif --#ifndef F_SETPIPE_SZ --#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) --#endif - - - #define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) -@@ -137,7 +130,6 @@ void fuse_free_req(fuse_req_t req) - req->u.ni.data = NULL; - list_del_req(req); - ctr = --req->ctr; -- fuse_chan_put(req->ch); - req->ch = NULL; - pthread_mutex_unlock(&se->lock); - if (!ctr) -@@ -184,19 +176,7 @@ static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, - } - } - -- ssize_t res = writev(ch ? ch->fd : se->fd, -- iov, count); -- int err = errno; -- -- if (res == -1) { -- assert(se != NULL); -- -- /* ENOENT means the operation was interrupted */ -- if (!fuse_session_exited(se) && err != ENOENT) -- perror("fuse: writing device"); -- return -err; -- } -- -+ abort(); /* virtio should have taken it before here */ - return 0; - } - -@@ -480,10 +460,6 @@ static int fuse_send_data_iov_fallback(struct fuse_session *se, - struct fuse_bufvec *buf, - size_t len) - { -- struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len); -- void *mbuf; -- int res; -- - /* Optimize common case */ - if (buf->count == 1 && buf->idx == 0 && buf->off == 0 && - !(buf->buf[0].flags & FUSE_BUF_IS_FD)) { -@@ -496,350 +472,10 @@ static int fuse_send_data_iov_fallback(struct fuse_session *se, - return fuse_send_msg(se, ch, iov, iov_count); - } - -- res = posix_memalign(&mbuf, pagesize, len); -- if (res != 0) -- return res; -- -- mem_buf.buf[0].mem = mbuf; -- res = fuse_buf_copy(&mem_buf, buf, 0); -- if (res < 0) { -- free(mbuf); -- return -res; -- } -- len = res; -- -- iov[iov_count].iov_base = mbuf; -- iov[iov_count].iov_len = len; -- iov_count++; -- res = fuse_send_msg(se, ch, iov, iov_count); -- free(mbuf); -- -- return res; --} -- --struct fuse_ll_pipe { -- size_t size; -- int can_grow; -- int pipe[2]; --}; -- --static void fuse_ll_pipe_free(struct fuse_ll_pipe *llp) --{ -- close(llp->pipe[0]); -- close(llp->pipe[1]); -- free(llp); --} -- --#ifdef HAVE_SPLICE --#if !defined(HAVE_PIPE2) || !defined(O_CLOEXEC) --static int fuse_pipe(int fds[2]) --{ -- int rv = pipe(fds); -- -- if (rv == -1) -- return rv; -- -- if (fcntl(fds[0], F_SETFL, O_NONBLOCK) == -1 || -- fcntl(fds[1], F_SETFL, O_NONBLOCK) == -1 || -- fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || -- fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { -- close(fds[0]); -- close(fds[1]); -- rv = -1; -- } -- return rv; --} --#else --static int fuse_pipe(int fds[2]) --{ -- return pipe2(fds, O_CLOEXEC | O_NONBLOCK); --} --#endif -- --static struct fuse_ll_pipe *fuse_ll_get_pipe(struct fuse_session *se) --{ -- struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key); -- if (llp == NULL) { -- int res; -- -- llp = malloc(sizeof(struct fuse_ll_pipe)); -- if (llp == NULL) -- return NULL; -- -- res = fuse_pipe(llp->pipe); -- if (res == -1) { -- free(llp); -- return NULL; -- } -- -- /* -- *the default size is 16 pages on linux -- */ -- llp->size = pagesize * 16; -- llp->can_grow = 1; -- -- pthread_setspecific(se->pipe_key, llp); -- } -- -- return llp; --} --#endif -- --static void fuse_ll_clear_pipe(struct fuse_session *se) --{ -- struct fuse_ll_pipe *llp = pthread_getspecific(se->pipe_key); -- if (llp) { -- pthread_setspecific(se->pipe_key, NULL); -- fuse_ll_pipe_free(llp); -- } --} -- --#if defined(HAVE_SPLICE) && defined(HAVE_VMSPLICE) --static int read_back(int fd, char *buf, size_t len) --{ -- int res; -- -- res = read(fd, buf, len); -- if (res == -1) { -- fuse_log(FUSE_LOG_ERR, "fuse: internal error: failed to read back from pipe: %s\n", strerror(errno)); -- return -EIO; -- } -- if (res != len) { -- fuse_log(FUSE_LOG_ERR, "fuse: internal error: short read back from pipe: %i from %zi\n", res, len); -- return -EIO; -- } -+ abort(); /* Will have taken vhost path */ - return 0; - } - --static int grow_pipe_to_max(int pipefd) --{ -- int max; -- int res; -- int maxfd; -- char buf[32]; -- -- maxfd = open("/proc/sys/fs/pipe-max-size", O_RDONLY); -- if (maxfd < 0) -- return -errno; -- -- res = read(maxfd, buf, sizeof(buf) - 1); -- if (res < 0) { -- int saved_errno; -- -- saved_errno = errno; -- close(maxfd); -- return -saved_errno; -- } -- close(maxfd); -- buf[res] = '\0'; -- -- max = atoi(buf); -- res = fcntl(pipefd, F_SETPIPE_SZ, max); -- if (res < 0) -- return -errno; -- return max; --} -- --static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, -- struct iovec *iov, int iov_count, -- struct fuse_bufvec *buf, unsigned int flags) --{ -- int res; -- size_t len = fuse_buf_size(buf); -- struct fuse_out_header *out = iov[0].iov_base; -- struct fuse_ll_pipe *llp; -- int splice_flags; -- size_t pipesize; -- size_t total_fd_size; -- size_t idx; -- size_t headerlen; -- struct fuse_bufvec pipe_buf = FUSE_BUFVEC_INIT(len); -- -- if (se->broken_splice_nonblock) -- goto fallback; -- -- if (flags & FUSE_BUF_NO_SPLICE) -- goto fallback; -- -- total_fd_size = 0; -- for (idx = buf->idx; idx < buf->count; idx++) { -- if (buf->buf[idx].flags & FUSE_BUF_IS_FD) { -- total_fd_size = buf->buf[idx].size; -- if (idx == buf->idx) -- total_fd_size -= buf->off; -- } -- } -- if (total_fd_size < 2 * pagesize) -- goto fallback; -- -- if (se->conn.proto_minor < 14 || -- !(se->conn.want & FUSE_CAP_SPLICE_WRITE)) -- goto fallback; -- -- llp = fuse_ll_get_pipe(se); -- if (llp == NULL) -- goto fallback; -- -- -- headerlen = iov_length(iov, iov_count); -- -- out->len = headerlen + len; -- -- /* -- * Heuristic for the required pipe size, does not work if the -- * source contains less than page size fragments -- */ -- pipesize = pagesize * (iov_count + buf->count + 1) + out->len; -- -- if (llp->size < pipesize) { -- if (llp->can_grow) { -- res = fcntl(llp->pipe[0], F_SETPIPE_SZ, pipesize); -- if (res == -1) { -- res = grow_pipe_to_max(llp->pipe[0]); -- if (res > 0) -- llp->size = res; -- llp->can_grow = 0; -- goto fallback; -- } -- llp->size = res; -- } -- if (llp->size < pipesize) -- goto fallback; -- } -- -- -- res = vmsplice(llp->pipe[1], iov, iov_count, SPLICE_F_NONBLOCK); -- if (res == -1) -- goto fallback; -- -- if (res != headerlen) { -- res = -EIO; -- fuse_log(FUSE_LOG_ERR, "fuse: short vmsplice to pipe: %u/%zu\n", res, -- headerlen); -- goto clear_pipe; -- } -- -- pipe_buf.buf[0].flags = FUSE_BUF_IS_FD; -- pipe_buf.buf[0].fd = llp->pipe[1]; -- -- res = fuse_buf_copy(&pipe_buf, buf, -- FUSE_BUF_FORCE_SPLICE | FUSE_BUF_SPLICE_NONBLOCK); -- if (res < 0) { -- if (res == -EAGAIN || res == -EINVAL) { -- /* -- * Should only get EAGAIN on kernels with -- * broken SPLICE_F_NONBLOCK support (<= -- * 2.6.35) where this error or a short read is -- * returned even if the pipe itself is not -- * full -- * -- * EINVAL might mean that splice can't handle -- * this combination of input and output. -- */ -- if (res == -EAGAIN) -- se->broken_splice_nonblock = 1; -- -- pthread_setspecific(se->pipe_key, NULL); -- fuse_ll_pipe_free(llp); -- goto fallback; -- } -- res = -res; -- goto clear_pipe; -- } -- -- if (res != 0 && res < len) { -- struct fuse_bufvec mem_buf = FUSE_BUFVEC_INIT(len); -- void *mbuf; -- size_t now_len = res; -- /* -- * For regular files a short count is either -- * 1) due to EOF, or -- * 2) because of broken SPLICE_F_NONBLOCK (see above) -- * -- * For other inputs it's possible that we overflowed -- * the pipe because of small buffer fragments. -- */ -- -- res = posix_memalign(&mbuf, pagesize, len); -- if (res != 0) -- goto clear_pipe; -- -- mem_buf.buf[0].mem = mbuf; -- mem_buf.off = now_len; -- res = fuse_buf_copy(&mem_buf, buf, 0); -- if (res > 0) { -- char *tmpbuf; -- size_t extra_len = res; -- /* -- * Trickiest case: got more data. Need to get -- * back the data from the pipe and then fall -- * back to regular write. -- */ -- tmpbuf = malloc(headerlen); -- if (tmpbuf == NULL) { -- free(mbuf); -- res = ENOMEM; -- goto clear_pipe; -- } -- res = read_back(llp->pipe[0], tmpbuf, headerlen); -- free(tmpbuf); -- if (res != 0) { -- free(mbuf); -- goto clear_pipe; -- } -- res = read_back(llp->pipe[0], mbuf, now_len); -- if (res != 0) { -- free(mbuf); -- goto clear_pipe; -- } -- len = now_len + extra_len; -- iov[iov_count].iov_base = mbuf; -- iov[iov_count].iov_len = len; -- iov_count++; -- res = fuse_send_msg(se, ch, iov, iov_count); -- free(mbuf); -- return res; -- } -- free(mbuf); -- res = now_len; -- } -- len = res; -- out->len = headerlen + len; -- -- if (se->debug) { -- fuse_log(FUSE_LOG_DEBUG, -- " unique: %llu, success, outsize: %i (splice)\n", -- (unsigned long long) out->unique, out->len); -- } -- -- splice_flags = 0; -- if ((flags & FUSE_BUF_SPLICE_MOVE) && -- (se->conn.want & FUSE_CAP_SPLICE_MOVE)) -- splice_flags |= SPLICE_F_MOVE; -- -- res = splice(llp->pipe[0], NULL, ch ? ch->fd : se->fd, -- NULL, out->len, splice_flags); -- if (res == -1) { -- res = -errno; -- perror("fuse: splice from pipe"); -- goto clear_pipe; -- } -- if (res != out->len) { -- res = -EIO; -- fuse_log(FUSE_LOG_ERR, "fuse: short splice from pipe: %u/%u\n", -- res, out->len); -- goto clear_pipe; -- } -- return 0; -- --clear_pipe: -- fuse_ll_clear_pipe(se); -- return res; -- --fallback: -- return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); --} --#else - static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, - struct iovec *iov, int iov_count, - struct fuse_bufvec *buf, unsigned int flags) -@@ -849,7 +485,6 @@ static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, - - return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); - } --#endif - - int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, - enum fuse_buf_copy_flags flags) -@@ -1408,16 +1043,11 @@ static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, - if (bufv.buf[0].size < arg->size) { - fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n"); - fuse_reply_err(req, EIO); -- goto out; -+ return; - } - bufv.buf[0].size = arg->size; - - se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi); -- --out: -- /* Need to reset the pipe if ->write_buf() didn't consume all data */ -- if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count) -- fuse_ll_clear_pipe(se); - } - - static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -@@ -2038,17 +1668,6 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - return; - } - -- unsigned max_read_mo = get_max_read(se->mo); -- if (se->conn.max_read != max_read_mo) { -- fuse_log(FUSE_LOG_ERR, "fuse: error: init() and fuse_session_new() " -- "requested different maximum read size (%u vs %u)\n", -- se->conn.max_read, max_read_mo); -- fuse_reply_err(req, EPROTO); -- se->error = -EPROTO; -- fuse_session_exit(se); -- return; -- } -- - if (se->conn.max_write < bufsize - FUSE_BUFFER_HEADER_SIZE) { - se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE; - } -@@ -2364,8 +1983,6 @@ static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq, - } - out: - free(rreq); -- if ((ibuf->flags & FUSE_BUF_IS_FD) && bufv.idx < bufv.count) -- fuse_ll_clear_pipe(se); - } - - int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, -@@ -2496,7 +2113,6 @@ static struct { - [FUSE_RENAME2] = { do_rename2, "RENAME2" }, - [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" }, - [FUSE_LSEEK] = { do_lseek, "LSEEK" }, -- [CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" }, - }; - - #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) -@@ -2509,21 +2125,6 @@ static const char *opname(enum fuse_opcode opcode) - return fuse_ll_ops[opcode].name; - } - --static int fuse_ll_copy_from_pipe(struct fuse_bufvec *dst, -- struct fuse_bufvec *src) --{ -- ssize_t res = fuse_buf_copy(dst, src, 0); -- if (res < 0) { -- fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", strerror(-res)); -- return res; -- } -- if ((size_t)res < fuse_buf_size(dst)) { -- fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n"); -- return -1; -- } -- return 0; --} -- - void fuse_session_process_buf(struct fuse_session *se, - const struct fuse_buf *buf) - { -@@ -2533,36 +2134,12 @@ void fuse_session_process_buf(struct fuse_session *se, - void fuse_session_process_buf_int(struct fuse_session *se, - const struct fuse_buf *buf, struct fuse_chan *ch) - { -- const size_t write_header_size = sizeof(struct fuse_in_header) + -- sizeof(struct fuse_write_in); -- struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 }; -- struct fuse_bufvec tmpbuf = FUSE_BUFVEC_INIT(write_header_size); - struct fuse_in_header *in; - const void *inarg; - struct fuse_req *req; -- void *mbuf = NULL; - int err; -- int res; -- -- if (buf->flags & FUSE_BUF_IS_FD) { -- if (buf->size < tmpbuf.buf[0].size) -- tmpbuf.buf[0].size = buf->size; - -- mbuf = malloc(tmpbuf.buf[0].size); -- if (mbuf == NULL) { -- fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate header\n"); -- goto clear_pipe; -- } -- tmpbuf.buf[0].mem = mbuf; -- -- res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv); -- if (res < 0) -- goto clear_pipe; -- -- in = mbuf; -- } else { -- in = buf->mem; -- } -+ in = buf->mem; - - if (se->debug) { - fuse_log(FUSE_LOG_DEBUG, -@@ -2584,14 +2161,14 @@ void fuse_session_process_buf_int(struct fuse_session *se, - }; - - fuse_send_msg(se, ch, &iov, 1); -- goto clear_pipe; -+ return; - } - - req->unique = in->unique; - req->ctx.uid = in->uid; - req->ctx.gid = in->gid; - req->ctx.pid = in->pid; -- req->ch = ch ? fuse_chan_get(ch) : NULL; -+ req->ch = ch; - - err = EIO; - if (!se->got_init) { -@@ -2627,28 +2204,6 @@ void fuse_session_process_buf_int(struct fuse_session *se, - fuse_reply_err(intr, EAGAIN); - } - -- if ((buf->flags & FUSE_BUF_IS_FD) && write_header_size < buf->size && -- (in->opcode != FUSE_WRITE || !se->op.write_buf) && -- in->opcode != FUSE_NOTIFY_REPLY) { -- void *newmbuf; -- -- err = ENOMEM; -- newmbuf = realloc(mbuf, buf->size); -- if (newmbuf == NULL) -- goto reply_err; -- mbuf = newmbuf; -- -- tmpbuf = FUSE_BUFVEC_INIT(buf->size - write_header_size); -- tmpbuf.buf[0].mem = (char *)mbuf + write_header_size; -- -- res = fuse_ll_copy_from_pipe(&tmpbuf, &bufv); -- err = -res; -- if (res < 0) -- goto reply_err; -- -- in = mbuf; -- } -- - inarg = (void *) &in[1]; - if (in->opcode == FUSE_WRITE && se->op.write_buf) - do_write_buf(req, in->nodeid, inarg, buf); -@@ -2657,16 +2212,10 @@ void fuse_session_process_buf_int(struct fuse_session *se, - else - fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); - --out_free: -- free(mbuf); - return; - - reply_err: - fuse_reply_err(req, err); --clear_pipe: -- if (buf->flags & FUSE_BUF_IS_FD) -- fuse_ll_clear_pipe(se); -- goto out_free; - } - - #define LL_OPTION(n,o,v) \ -@@ -2684,7 +2233,6 @@ void fuse_lowlevel_version(void) - { - printf("using FUSE kernel interface version %i.%i\n", - FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); -- fuse_mount_version(); - } - - void fuse_lowlevel_help(void) -@@ -2692,204 +2240,29 @@ void fuse_lowlevel_help(void) - /* These are not all options, but the ones that are - potentially of interest to an end-user */ - printf( --" -o allow_other allow access by all users\n" - " -o allow_root allow access by root\n" --" -o auto_unmount auto unmount on process termination\n"); -+); - } - - void fuse_session_destroy(struct fuse_session *se) - { -- struct fuse_ll_pipe *llp; -- - if (se->got_init && !se->got_destroy) { - if (se->op.destroy) - se->op.destroy(se->userdata); - } -- llp = pthread_getspecific(se->pipe_key); -- if (llp != NULL) -- fuse_ll_pipe_free(llp); -- pthread_key_delete(se->pipe_key); - pthread_mutex_destroy(&se->lock); - free(se->cuse_data); - if (se->fd != -1) - close(se->fd); -- destroy_mount_opts(se->mo); - free(se); - } - - --static void fuse_ll_pipe_destructor(void *data) --{ -- struct fuse_ll_pipe *llp = data; -- fuse_ll_pipe_free(llp); --} -- --int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf) --{ -- return fuse_session_receive_buf_int(se, buf, NULL); --} -- --int fuse_session_receive_buf_int(struct fuse_session *se, struct fuse_buf *buf, -- struct fuse_chan *ch) --{ -- int err; -- ssize_t res; --#ifdef HAVE_SPLICE -- size_t bufsize = se->bufsize; -- struct fuse_ll_pipe *llp; -- struct fuse_buf tmpbuf; -- -- if (se->conn.proto_minor < 14 || !(se->conn.want & FUSE_CAP_SPLICE_READ)) -- goto fallback; -- -- llp = fuse_ll_get_pipe(se); -- if (llp == NULL) -- goto fallback; -- -- if (llp->size < bufsize) { -- if (llp->can_grow) { -- res = fcntl(llp->pipe[0], F_SETPIPE_SZ, bufsize); -- if (res == -1) { -- llp->can_grow = 0; -- res = grow_pipe_to_max(llp->pipe[0]); -- if (res > 0) -- llp->size = res; -- goto fallback; -- } -- llp->size = res; -- } -- if (llp->size < bufsize) -- goto fallback; -- } -- -- res = splice(ch ? ch->fd : se->fd, -- NULL, llp->pipe[1], NULL, bufsize, 0); -- err = errno; -- -- if (fuse_session_exited(se)) -- return 0; -- -- if (res == -1) { -- if (err == ENODEV) { -- /* Filesystem was unmounted, or connection was aborted -- via /sys/fs/fuse/connections */ -- fuse_session_exit(se); -- return 0; -- } -- if (err != EINTR && err != EAGAIN) -- perror("fuse: splice from device"); -- return -err; -- } -- -- if (res < sizeof(struct fuse_in_header)) { -- fuse_log(FUSE_LOG_ERR, "short splice from fuse device\n"); -- return -EIO; -- } -- -- tmpbuf = (struct fuse_buf) { -- .size = res, -- .flags = FUSE_BUF_IS_FD, -- .fd = llp->pipe[0], -- }; -- -- /* -- * Don't bother with zero copy for small requests. -- * fuse_loop_mt() needs to check for FORGET so this more than -- * just an optimization. -- */ -- if (res < sizeof(struct fuse_in_header) + -- sizeof(struct fuse_write_in) + pagesize) { -- struct fuse_bufvec src = { .buf[0] = tmpbuf, .count = 1 }; -- struct fuse_bufvec dst = { .count = 1 }; -- -- if (!buf->mem) { -- buf->mem = malloc(se->bufsize); -- if (!buf->mem) { -- fuse_log(FUSE_LOG_ERR, -- "fuse: failed to allocate read buffer\n"); -- return -ENOMEM; -- } -- } -- buf->size = se->bufsize; -- buf->flags = 0; -- dst.buf[0] = *buf; -- -- res = fuse_buf_copy(&dst, &src, 0); -- if (res < 0) { -- fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: %s\n", -- strerror(-res)); -- fuse_ll_clear_pipe(se); -- return res; -- } -- if (res < tmpbuf.size) { -- fuse_log(FUSE_LOG_ERR, "fuse: copy from pipe: short read\n"); -- fuse_ll_clear_pipe(se); -- return -EIO; -- } -- assert(res == tmpbuf.size); -- -- } else { -- /* Don't overwrite buf->mem, as that would cause a leak */ -- buf->fd = tmpbuf.fd; -- buf->flags = tmpbuf.flags; -- } -- buf->size = tmpbuf.size; -- -- return res; -- --fallback: --#endif -- if (!buf->mem) { -- buf->mem = malloc(se->bufsize); -- if (!buf->mem) { -- fuse_log(FUSE_LOG_ERR, -- "fuse: failed to allocate read buffer\n"); -- return -ENOMEM; -- } -- } -- --restart: -- res = read(ch ? ch->fd : se->fd, buf->mem, se->bufsize); -- err = errno; -- -- if (fuse_session_exited(se)) -- return 0; -- if (res == -1) { -- /* ENOENT means the operation was interrupted, it's safe -- to restart */ -- if (err == ENOENT) -- goto restart; -- -- if (err == ENODEV) { -- /* Filesystem was unmounted, or connection was aborted -- via /sys/fs/fuse/connections */ -- fuse_session_exit(se); -- return 0; -- } -- /* Errors occurring during normal operation: EINTR (read -- interrupted), EAGAIN (nonblocking I/O), ENODEV (filesystem -- umounted) */ -- if (err != EINTR && err != EAGAIN) -- perror("fuse: reading device"); -- return -err; -- } -- if ((size_t) res < sizeof(struct fuse_in_header)) { -- fuse_log(FUSE_LOG_ERR, "short read on fuse device\n"); -- return -EIO; -- } -- -- buf->size = res; -- -- return res; --} -- - struct fuse_session *fuse_session_new(struct fuse_args *args, - const struct fuse_lowlevel_ops *op, - size_t op_size, void *userdata) - { -- int err; - struct fuse_session *se; -- struct mount_opts *mo; - - if (sizeof(struct fuse_lowlevel_ops) < op_size) { - fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n"); -@@ -2913,20 +2286,6 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, - /* Parse options */ - if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1) - goto out2; -- if(se->deny_others) { -- /* Allowing access only by root is done by instructing -- * kernel to allow access by everyone, and then restricting -- * access to root and mountpoint owner in libfuse. -- */ -- // We may be adding the option a second time, but -- // that doesn't hurt. -- if(fuse_opt_add_arg(args, "-oallow_other") == -1) -- goto out2; -- } -- mo = parse_mount_opts(args); -- if (mo == NULL) -- goto out3; -- - if(args->argc == 1 && - args->argv[0][0] == '-') { - fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but " -@@ -2940,9 +2299,6 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, - goto out4; - } - -- if (se->debug) -- fuse_log(FUSE_LOG_DEBUG, "FUSE library version: %s\n", PACKAGE_VERSION); -- - se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() + - FUSE_BUFFER_HEADER_SIZE; - -@@ -2952,26 +2308,14 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, - se->notify_ctr = 1; - fuse_mutex_init(&se->lock); - -- err = pthread_key_create(&se->pipe_key, fuse_ll_pipe_destructor); -- if (err) { -- fuse_log(FUSE_LOG_ERR, "fuse: failed to create thread specific key: %s\n", -- strerror(err)); -- goto out5; -- } -- - memcpy(&se->op, op, op_size); - se->owner = getuid(); - se->userdata = userdata; - -- se->mo = mo; - return se; - --out5: -- pthread_mutex_destroy(&se->lock); - out4: - fuse_opt_free_args(args); --out3: -- free(mo); - out2: - free(se); - out1: -@@ -3035,11 +2379,6 @@ int fuse_session_fd(struct fuse_session *se) - - void fuse_session_unmount(struct fuse_session *se) - { -- if (se->mountpoint != NULL) { -- fuse_kern_unmount(se->mountpoint, se->fd); -- free(se->mountpoint); -- se->mountpoint = NULL; -- } - } - - #ifdef linux -diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h -index 18c6363f07..6b1adfcfd1 100644 ---- a/tools/virtiofsd/fuse_lowlevel.h -+++ b/tools/virtiofsd/fuse_lowlevel.h -@@ -31,10 +31,6 @@ - #include - #include - --#ifdef __cplusplus --extern "C" { --#endif -- - /* ----------------------------------------------------------- * - * Miscellaneous definitions * - * ----------------------------------------------------------- */ -@@ -1863,14 +1859,12 @@ void fuse_cmdline_help(void); - * ----------------------------------------------------------- */ - - struct fuse_cmdline_opts { -- int singlethread; - int foreground; - int debug; - int nodefault_subtype; - char *mountpoint; - int show_version; - int show_help; -- int clone_fd; - unsigned int max_idle_threads; - }; - -@@ -1961,24 +1955,6 @@ int fuse_session_mount(struct fuse_session *se, const char *mountpoint); - */ - int fuse_session_loop(struct fuse_session *se); - --/** -- * Enter a multi-threaded event loop. -- * -- * For a description of the return value and the conditions when the -- * event loop exits, refer to the documentation of -- * fuse_session_loop(). -- * -- * @param se the session -- * @param config session loop configuration -- * @return see fuse_session_loop() -- */ --#if FUSE_USE_VERSION < 32 --int fuse_session_loop_mt_31(struct fuse_session *se, int clone_fd); --#define fuse_session_loop_mt(se, clone_fd) fuse_session_loop_mt_31(se, clone_fd) --#else --int fuse_session_loop_mt(struct fuse_session *se, struct fuse_loop_config *config); --#endif -- - /** - * Flag a session as terminated. - * -@@ -2082,8 +2058,4 @@ void fuse_session_process_buf(struct fuse_session *se, - */ - int fuse_session_receive_buf(struct fuse_session *se, struct fuse_buf *buf); - --#ifdef __cplusplus --} --#endif -- - #endif /* FUSE_LOWLEVEL_H_ */ -diff --git a/tools/virtiofsd/fuse_opt.h b/tools/virtiofsd/fuse_opt.h -index d8573e74fd..69102555be 100644 ---- a/tools/virtiofsd/fuse_opt.h -+++ b/tools/virtiofsd/fuse_opt.h -@@ -14,10 +14,6 @@ - * This file defines the option parsing interface of FUSE - */ - --#ifdef __cplusplus --extern "C" { --#endif -- - /** - * Option description - * -@@ -264,8 +260,4 @@ void fuse_opt_free_args(struct fuse_args *args); - */ - int fuse_opt_match(const struct fuse_opt opts[], const char *opt); - --#ifdef __cplusplus --} --#endif -- - #endif /* FUSE_OPT_H_ */ -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index 64ff7ad6d5..5a2e64c6d0 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -41,14 +41,10 @@ static const struct fuse_opt fuse_helper_opts[] = { - FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), - FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), - FUSE_HELPER_OPT("-f", foreground), -- FUSE_HELPER_OPT("-s", singlethread), - FUSE_HELPER_OPT("fsname=", nodefault_subtype), - FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), --#ifndef __FreeBSD__ - FUSE_HELPER_OPT("subtype=", nodefault_subtype), - FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), --#endif -- FUSE_HELPER_OPT("clone_fd", clone_fd), - FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads), - FUSE_OPT_END - }; -@@ -132,9 +128,6 @@ void fuse_cmdline_help(void) - " -V --version print version\n" - " -d -o debug enable debug output (implies -f)\n" - " -f foreground operation\n" -- " -s disable multi-threaded operation\n" -- " -o clone_fd use separate fuse device fd for each thread\n" -- " (may improve performance)\n" - " -o max_idle_threads the maximum number of idle worker threads\n" - " allowed (default: 10)\n"); - } -@@ -171,34 +164,6 @@ static int fuse_helper_opt_proc(void *data, const char *arg, int key, - } - } - --/* Under FreeBSD, there is no subtype option so this -- function actually sets the fsname */ --static int add_default_subtype(const char *progname, struct fuse_args *args) --{ -- int res; -- char *subtype_opt; -- -- const char *basename = strrchr(progname, '/'); -- if (basename == NULL) -- basename = progname; -- else if (basename[1] != '\0') -- basename++; -- -- subtype_opt = (char *) malloc(strlen(basename) + 64); -- if (subtype_opt == NULL) { -- fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); -- return -1; -- } --#ifdef __FreeBSD__ -- sprintf(subtype_opt, "-ofsname=%s", basename); --#else -- sprintf(subtype_opt, "-osubtype=%s", basename); --#endif -- res = fuse_opt_add_arg(args, subtype_opt); -- free(subtype_opt); -- return res; --} -- - int fuse_parse_cmdline(struct fuse_args *args, - struct fuse_cmdline_opts *opts) - { -@@ -210,14 +175,6 @@ int fuse_parse_cmdline(struct fuse_args *args, - fuse_helper_opt_proc) == -1) - return -1; - -- /* *Linux*: if neither -o subtype nor -o fsname are specified, -- set subtype to program's basename. -- *FreeBSD*: if fsname is not specified, set to program's -- basename. */ -- if (!opts->nodefault_subtype) -- if (add_default_subtype(args->argv[0], args) == -1) -- return -1; -- - return 0; - } - -@@ -276,88 +233,6 @@ int fuse_daemonize(int foreground) - return 0; - } - --int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, -- size_t op_size, void *user_data) --{ -- struct fuse_args args = FUSE_ARGS_INIT(argc, argv); -- struct fuse *fuse; -- struct fuse_cmdline_opts opts; -- int res; -- -- if (fuse_parse_cmdline(&args, &opts) != 0) -- return 1; -- -- if (opts.show_version) { -- printf("FUSE library version %s\n", PACKAGE_VERSION); -- fuse_lowlevel_version(); -- res = 0; -- goto out1; -- } -- -- if (opts.show_help) { -- if(args.argv[0][0] != '\0') -- printf("usage: %s [options] \n\n", -- args.argv[0]); -- printf("FUSE options:\n"); -- fuse_cmdline_help(); -- fuse_lib_help(&args); -- res = 0; -- goto out1; -- } -- -- if (!opts.show_help && -- !opts.mountpoint) { -- fuse_log(FUSE_LOG_ERR, "error: no mountpoint specified\n"); -- res = 2; -- goto out1; -- } -- -- -- fuse = fuse_new_31(&args, op, op_size, user_data); -- if (fuse == NULL) { -- res = 3; -- goto out1; -- } -- -- if (fuse_mount(fuse,opts.mountpoint) != 0) { -- res = 4; -- goto out2; -- } -- -- if (fuse_daemonize(opts.foreground) != 0) { -- res = 5; -- goto out3; -- } -- -- struct fuse_session *se = fuse_get_session(fuse); -- if (fuse_set_signal_handlers(se) != 0) { -- res = 6; -- goto out3; -- } -- -- if (opts.singlethread) -- res = fuse_loop(fuse); -- else { -- struct fuse_loop_config loop_config; -- loop_config.clone_fd = opts.clone_fd; -- loop_config.max_idle_threads = opts.max_idle_threads; -- res = fuse_loop_mt_32(fuse, &loop_config); -- } -- if (res) -- res = 7; -- -- fuse_remove_signal_handlers(se); --out3: -- fuse_unmount(fuse); --out2: -- fuse_destroy(fuse); --out1: -- free(opts.mountpoint); -- fuse_opt_free_args(&args); -- return res; --} -- -- - void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, - struct fuse_conn_info *conn) - { -@@ -420,21 +295,3 @@ struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args) - } - return opts; - } -- --int fuse_open_channel(const char *mountpoint, const char* options) --{ -- struct mount_opts *opts = NULL; -- int fd = -1; -- const char *argv[] = { "", "-o", options }; -- int argc = sizeof(argv) / sizeof(argv[0]); -- struct fuse_args args = FUSE_ARGS_INIT(argc, (char**) argv); -- -- opts = parse_mount_opts(&args); -- if (opts == NULL) -- return -1; -- -- fd = fuse_kern_mount(mountpoint, opts); -- destroy_mount_opts(opts); -- -- return fd; --} -diff --git a/tools/virtiofsd/passthrough_helpers.h b/tools/virtiofsd/passthrough_helpers.h -index 6b77c33600..7c5f561fbc 100644 ---- a/tools/virtiofsd/passthrough_helpers.h -+++ b/tools/virtiofsd/passthrough_helpers.h -@@ -42,32 +42,6 @@ static int mknod_wrapper(int dirfd, const char *path, const char *link, - res = symlinkat(link, dirfd, path); - } else if (S_ISFIFO(mode)) { - res = mkfifoat(dirfd, path, mode); --#ifdef __FreeBSD__ -- } else if (S_ISSOCK(mode)) { -- struct sockaddr_un su; -- int fd; -- -- if (strlen(path) >= sizeof(su.sun_path)) { -- errno = ENAMETOOLONG; -- return -1; -- } -- fd = socket(AF_UNIX, SOCK_STREAM, 0); -- if (fd >= 0) { -- /* -- * We must bind the socket to the underlying file -- * system to create the socket file, even though -- * we'll never listen on this socket. -- */ -- su.sun_family = AF_UNIX; -- strncpy(su.sun_path, path, sizeof(su.sun_path)); -- res = bindat(dirfd, fd, (struct sockaddr*)&su, -- sizeof(su)); -- if (res == 0) -- close(fd); -- } else { -- res = -1; -- } --#endif - } else { - res = mknodat(dirfd, path, mode, rdev); - } -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index e1a605691a..e5f7115bc1 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1240,7 +1240,6 @@ int main(int argc, char *argv[]) - ret = 0; - goto err_out1; - } else if (opts.show_version) { -- printf("FUSE library version %s\n", fuse_pkgversion()); - fuse_lowlevel_version(); - ret = 0; - goto err_out1; diff --git a/0016-virtiofsd-Format-imported-files-to-qemu-style.patch b/0016-virtiofsd-Format-imported-files-to-qemu-style.patch deleted file mode 100644 index e050d14..0000000 --- a/0016-virtiofsd-Format-imported-files-to-qemu-style.patch +++ /dev/null @@ -1,14727 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:00:45 +0000 -Subject: [PATCH] virtiofsd: Format imported files to qemu style -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Mostly using a set like: - -indent -nut -i 4 -nlp -br -cs -ce --no-space-after-function-call-names file -clang-format -style=file -i -- file -clang-tidy -fix-errors -checks=readability-braces-around-statements file -clang-format -style=file -i -- file - -With manual cleanups. - -The .clang-format used is below. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Reviewed by: Aleksandar Markovic - -Language: Cpp -AlignAfterOpenBracket: Align -AlignConsecutiveAssignments: false # although we like it, it creates churn -AlignConsecutiveDeclarations: false -AlignEscapedNewlinesLeft: true -AlignOperands: true -AlignTrailingComments: false # churn -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: false -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: None -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterReturnType: None # AlwaysBreakAfterDefinitionReturnType is taken into account -AlwaysBreakBeforeMultilineStrings: false -BinPackArguments: true -BinPackParameters: true -BraceWrapping: - AfterControlStatement: false - AfterEnum: false - AfterFunction: true - AfterStruct: false - AfterUnion: false - BeforeElse: false - IndentBraces: false -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Custom -BreakBeforeTernaryOperators: false -BreakStringLiterals: true -ColumnLimit: 80 -ContinuationIndentWidth: 4 -Cpp11BracedListStyle: false -DerivePointerAlignment: false -DisableFormat: false -ForEachMacros: [ - 'CPU_FOREACH', - 'CPU_FOREACH_REVERSE', - 'CPU_FOREACH_SAFE', - 'IOMMU_NOTIFIER_FOREACH', - 'QLIST_FOREACH', - 'QLIST_FOREACH_ENTRY', - 'QLIST_FOREACH_RCU', - 'QLIST_FOREACH_SAFE', - 'QLIST_FOREACH_SAFE_RCU', - 'QSIMPLEQ_FOREACH', - 'QSIMPLEQ_FOREACH_SAFE', - 'QSLIST_FOREACH', - 'QSLIST_FOREACH_SAFE', - 'QTAILQ_FOREACH', - 'QTAILQ_FOREACH_REVERSE', - 'QTAILQ_FOREACH_SAFE', - 'QTAILQ_RAW_FOREACH', - 'RAMBLOCK_FOREACH' -] -IncludeCategories: - - Regex: '^"qemu/osdep.h' - Priority: -3 - - Regex: '^"(block|chardev|crypto|disas|exec|fpu|hw|io|libdecnumber|migration|monitor|net|qapi|qemu|qom|standard-headers|sysemu|ui)/' - Priority: -2 - - Regex: '^"(elf.h|qemu-common.h|glib-compat.h|qemu-io.h|trace-tcg.h)' - Priority: -1 - - Regex: '.*' - Priority: 1 -IncludeIsMainRegex: '$' -IndentCaseLabels: false -IndentWidth: 4 -IndentWrappedFunctionNames: false -KeepEmptyLinesAtTheStartOfBlocks: false -MacroBlockBegin: '.*_BEGIN$' # only PREC_BEGIN ? -MacroBlockEnd: '.*_END$' -MaxEmptyLinesToKeep: 2 -PointerAlignment: Right -ReflowComments: true -SortIncludes: true -SpaceAfterCStyleCast: false -SpaceBeforeAssignmentOperators: true -SpaceBeforeParens: ControlStatements -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInContainerLiterals: true -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: Auto -UseTab: Never -... - -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 7387863d033e8028aa09a815736617a7c4490827) ---- - tools/virtiofsd/buffer.c | 434 +-- - tools/virtiofsd/fuse.h | 1572 +++++------ - tools/virtiofsd/fuse_common.h | 730 ++--- - tools/virtiofsd/fuse_i.h | 121 +- - tools/virtiofsd/fuse_log.c | 38 +- - tools/virtiofsd/fuse_log.h | 32 +- - tools/virtiofsd/fuse_lowlevel.c | 3638 +++++++++++++------------ - tools/virtiofsd/fuse_lowlevel.h | 2392 ++++++++-------- - tools/virtiofsd/fuse_misc.h | 30 +- - tools/virtiofsd/fuse_opt.c | 659 ++--- - tools/virtiofsd/fuse_opt.h | 79 +- - tools/virtiofsd/fuse_signals.c | 118 +- - tools/virtiofsd/helper.c | 506 ++-- - tools/virtiofsd/passthrough_helpers.h | 33 +- - tools/virtiofsd/passthrough_ll.c | 2061 +++++++------- - 15 files changed, 6382 insertions(+), 6061 deletions(-) - -diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c -index aefb7dbf15..5df946c82c 100644 ---- a/tools/virtiofsd/buffer.c -+++ b/tools/virtiofsd/buffer.c -@@ -1,252 +1,272 @@ - /* -- FUSE: Filesystem in Userspace -- Copyright (C) 2010 Miklos Szeredi -- -- Functions for dealing with `struct fuse_buf` and `struct -- fuse_bufvec`. -- -- This program can be distributed under the terms of the GNU LGPLv2. -- See the file COPYING.LIB --*/ -+ * FUSE: Filesystem in Userspace -+ * Copyright (C) 2010 Miklos Szeredi -+ * -+ * Functions for dealing with `struct fuse_buf` and `struct -+ * fuse_bufvec`. -+ * -+ * This program can be distributed under the terms of the GNU LGPLv2. -+ * See the file COPYING.LIB -+ */ - - #define _GNU_SOURCE - - #include "config.h" - #include "fuse_i.h" - #include "fuse_lowlevel.h" -+#include -+#include - #include - #include --#include --#include - - size_t fuse_buf_size(const struct fuse_bufvec *bufv) - { -- size_t i; -- size_t size = 0; -- -- for (i = 0; i < bufv->count; i++) { -- if (bufv->buf[i].size == SIZE_MAX) -- size = SIZE_MAX; -- else -- size += bufv->buf[i].size; -- } -- -- return size; -+ size_t i; -+ size_t size = 0; -+ -+ for (i = 0; i < bufv->count; i++) { -+ if (bufv->buf[i].size == SIZE_MAX) { -+ size = SIZE_MAX; -+ } else { -+ size += bufv->buf[i].size; -+ } -+ } -+ -+ return size; - } - - static size_t min_size(size_t s1, size_t s2) - { -- return s1 < s2 ? s1 : s2; -+ return s1 < s2 ? s1 : s2; - } - - static ssize_t fuse_buf_write(const struct fuse_buf *dst, size_t dst_off, -- const struct fuse_buf *src, size_t src_off, -- size_t len) -+ const struct fuse_buf *src, size_t src_off, -+ size_t len) - { -- ssize_t res = 0; -- size_t copied = 0; -- -- while (len) { -- if (dst->flags & FUSE_BUF_FD_SEEK) { -- res = pwrite(dst->fd, (char *)src->mem + src_off, len, -- dst->pos + dst_off); -- } else { -- res = write(dst->fd, (char *)src->mem + src_off, len); -- } -- if (res == -1) { -- if (!copied) -- return -errno; -- break; -- } -- if (res == 0) -- break; -- -- copied += res; -- if (!(dst->flags & FUSE_BUF_FD_RETRY)) -- break; -- -- src_off += res; -- dst_off += res; -- len -= res; -- } -- -- return copied; -+ ssize_t res = 0; -+ size_t copied = 0; -+ -+ while (len) { -+ if (dst->flags & FUSE_BUF_FD_SEEK) { -+ res = pwrite(dst->fd, (char *)src->mem + src_off, len, -+ dst->pos + dst_off); -+ } else { -+ res = write(dst->fd, (char *)src->mem + src_off, len); -+ } -+ if (res == -1) { -+ if (!copied) { -+ return -errno; -+ } -+ break; -+ } -+ if (res == 0) { -+ break; -+ } -+ -+ copied += res; -+ if (!(dst->flags & FUSE_BUF_FD_RETRY)) { -+ break; -+ } -+ -+ src_off += res; -+ dst_off += res; -+ len -= res; -+ } -+ -+ return copied; - } - - static ssize_t fuse_buf_read(const struct fuse_buf *dst, size_t dst_off, -- const struct fuse_buf *src, size_t src_off, -- size_t len) -+ const struct fuse_buf *src, size_t src_off, -+ size_t len) - { -- ssize_t res = 0; -- size_t copied = 0; -- -- while (len) { -- if (src->flags & FUSE_BUF_FD_SEEK) { -- res = pread(src->fd, (char *)dst->mem + dst_off, len, -- src->pos + src_off); -- } else { -- res = read(src->fd, (char *)dst->mem + dst_off, len); -- } -- if (res == -1) { -- if (!copied) -- return -errno; -- break; -- } -- if (res == 0) -- break; -- -- copied += res; -- if (!(src->flags & FUSE_BUF_FD_RETRY)) -- break; -- -- dst_off += res; -- src_off += res; -- len -= res; -- } -- -- return copied; -+ ssize_t res = 0; -+ size_t copied = 0; -+ -+ while (len) { -+ if (src->flags & FUSE_BUF_FD_SEEK) { -+ res = pread(src->fd, (char *)dst->mem + dst_off, len, -+ src->pos + src_off); -+ } else { -+ res = read(src->fd, (char *)dst->mem + dst_off, len); -+ } -+ if (res == -1) { -+ if (!copied) { -+ return -errno; -+ } -+ break; -+ } -+ if (res == 0) { -+ break; -+ } -+ -+ copied += res; -+ if (!(src->flags & FUSE_BUF_FD_RETRY)) { -+ break; -+ } -+ -+ dst_off += res; -+ src_off += res; -+ len -= res; -+ } -+ -+ return copied; - } - - static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off, -- const struct fuse_buf *src, size_t src_off, -- size_t len) -+ const struct fuse_buf *src, size_t src_off, -+ size_t len) - { -- char buf[4096]; -- struct fuse_buf tmp = { -- .size = sizeof(buf), -- .flags = 0, -- }; -- ssize_t res; -- size_t copied = 0; -- -- tmp.mem = buf; -- -- while (len) { -- size_t this_len = min_size(tmp.size, len); -- size_t read_len; -- -- res = fuse_buf_read(&tmp, 0, src, src_off, this_len); -- if (res < 0) { -- if (!copied) -- return res; -- break; -- } -- if (res == 0) -- break; -- -- read_len = res; -- res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len); -- if (res < 0) { -- if (!copied) -- return res; -- break; -- } -- if (res == 0) -- break; -- -- copied += res; -- -- if (res < this_len) -- break; -- -- dst_off += res; -- src_off += res; -- len -= res; -- } -- -- return copied; -+ char buf[4096]; -+ struct fuse_buf tmp = { -+ .size = sizeof(buf), -+ .flags = 0, -+ }; -+ ssize_t res; -+ size_t copied = 0; -+ -+ tmp.mem = buf; -+ -+ while (len) { -+ size_t this_len = min_size(tmp.size, len); -+ size_t read_len; -+ -+ res = fuse_buf_read(&tmp, 0, src, src_off, this_len); -+ if (res < 0) { -+ if (!copied) { -+ return res; -+ } -+ break; -+ } -+ if (res == 0) { -+ break; -+ } -+ -+ read_len = res; -+ res = fuse_buf_write(dst, dst_off, &tmp, 0, read_len); -+ if (res < 0) { -+ if (!copied) { -+ return res; -+ } -+ break; -+ } -+ if (res == 0) { -+ break; -+ } -+ -+ copied += res; -+ -+ if (res < this_len) { -+ break; -+ } -+ -+ dst_off += res; -+ src_off += res; -+ len -= res; -+ } -+ -+ return copied; - } - - static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off, -- const struct fuse_buf *src, size_t src_off, -- size_t len, enum fuse_buf_copy_flags flags) -+ const struct fuse_buf *src, size_t src_off, -+ size_t len, enum fuse_buf_copy_flags flags) - { -- int src_is_fd = src->flags & FUSE_BUF_IS_FD; -- int dst_is_fd = dst->flags & FUSE_BUF_IS_FD; -- -- if (!src_is_fd && !dst_is_fd) { -- char *dstmem = (char *)dst->mem + dst_off; -- char *srcmem = (char *)src->mem + src_off; -- -- if (dstmem != srcmem) { -- if (dstmem + len <= srcmem || srcmem + len <= dstmem) -- memcpy(dstmem, srcmem, len); -- else -- memmove(dstmem, srcmem, len); -- } -- -- return len; -- } else if (!src_is_fd) { -- return fuse_buf_write(dst, dst_off, src, src_off, len); -- } else if (!dst_is_fd) { -- return fuse_buf_read(dst, dst_off, src, src_off, len); -- } else { -- return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); -- } -+ int src_is_fd = src->flags & FUSE_BUF_IS_FD; -+ int dst_is_fd = dst->flags & FUSE_BUF_IS_FD; -+ -+ if (!src_is_fd && !dst_is_fd) { -+ char *dstmem = (char *)dst->mem + dst_off; -+ char *srcmem = (char *)src->mem + src_off; -+ -+ if (dstmem != srcmem) { -+ if (dstmem + len <= srcmem || srcmem + len <= dstmem) { -+ memcpy(dstmem, srcmem, len); -+ } else { -+ memmove(dstmem, srcmem, len); -+ } -+ } -+ -+ return len; -+ } else if (!src_is_fd) { -+ return fuse_buf_write(dst, dst_off, src, src_off, len); -+ } else if (!dst_is_fd) { -+ return fuse_buf_read(dst, dst_off, src, src_off, len); -+ } else { -+ return fuse_buf_fd_to_fd(dst, dst_off, src, src_off, len); -+ } - } - - static const struct fuse_buf *fuse_bufvec_current(struct fuse_bufvec *bufv) - { -- if (bufv->idx < bufv->count) -- return &bufv->buf[bufv->idx]; -- else -- return NULL; -+ if (bufv->idx < bufv->count) { -+ return &bufv->buf[bufv->idx]; -+ } else { -+ return NULL; -+ } - } - - static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len) - { -- const struct fuse_buf *buf = fuse_bufvec_current(bufv); -- -- bufv->off += len; -- assert(bufv->off <= buf->size); -- if (bufv->off == buf->size) { -- assert(bufv->idx < bufv->count); -- bufv->idx++; -- if (bufv->idx == bufv->count) -- return 0; -- bufv->off = 0; -- } -- return 1; -+ const struct fuse_buf *buf = fuse_bufvec_current(bufv); -+ -+ bufv->off += len; -+ assert(bufv->off <= buf->size); -+ if (bufv->off == buf->size) { -+ assert(bufv->idx < bufv->count); -+ bufv->idx++; -+ if (bufv->idx == bufv->count) { -+ return 0; -+ } -+ bufv->off = 0; -+ } -+ return 1; - } - - ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv, -- enum fuse_buf_copy_flags flags) -+ enum fuse_buf_copy_flags flags) - { -- size_t copied = 0; -- -- if (dstv == srcv) -- return fuse_buf_size(dstv); -- -- for (;;) { -- const struct fuse_buf *src = fuse_bufvec_current(srcv); -- const struct fuse_buf *dst = fuse_bufvec_current(dstv); -- size_t src_len; -- size_t dst_len; -- size_t len; -- ssize_t res; -- -- if (src == NULL || dst == NULL) -- break; -- -- src_len = src->size - srcv->off; -- dst_len = dst->size - dstv->off; -- len = min_size(src_len, dst_len); -- -- res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags); -- if (res < 0) { -- if (!copied) -- return res; -- break; -- } -- copied += res; -- -- if (!fuse_bufvec_advance(srcv, res) || -- !fuse_bufvec_advance(dstv, res)) -- break; -- -- if (res < len) -- break; -- } -- -- return copied; -+ size_t copied = 0; -+ -+ if (dstv == srcv) { -+ return fuse_buf_size(dstv); -+ } -+ -+ for (;;) { -+ const struct fuse_buf *src = fuse_bufvec_current(srcv); -+ const struct fuse_buf *dst = fuse_bufvec_current(dstv); -+ size_t src_len; -+ size_t dst_len; -+ size_t len; -+ ssize_t res; -+ -+ if (src == NULL || dst == NULL) { -+ break; -+ } -+ -+ src_len = src->size - srcv->off; -+ dst_len = dst->size - dstv->off; -+ len = min_size(src_len, dst_len); -+ -+ res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags); -+ if (res < 0) { -+ if (!copied) { -+ return res; -+ } -+ break; -+ } -+ copied += res; -+ -+ if (!fuse_bufvec_advance(srcv, res) || -+ !fuse_bufvec_advance(dstv, res)) { -+ break; -+ } -+ -+ if (res < len) { -+ break; -+ } -+ } -+ -+ return copied; - } -diff --git a/tools/virtiofsd/fuse.h b/tools/virtiofsd/fuse.h -index 3202fba6bb..7a4c713559 100644 ---- a/tools/virtiofsd/fuse.h -+++ b/tools/virtiofsd/fuse.h -@@ -1,15 +1,15 @@ - /* -- FUSE: Filesystem in Userspace -- Copyright (C) 2001-2007 Miklos Szeredi -- -- This program can be distributed under the terms of the GNU LGPLv2. -- See the file COPYING.LIB. --*/ -+ * FUSE: Filesystem in Userspace -+ * Copyright (C) 2001-2007 Miklos Szeredi -+ * -+ * This program can be distributed under the terms of the GNU LGPLv2. -+ * See the file COPYING.LIB. -+ */ - - #ifndef FUSE_H_ - #define FUSE_H_ - --/** @file -+/* - * - * This file defines the library interface of FUSE - * -@@ -19,15 +19,15 @@ - #include "fuse_common.h" - - #include --#include --#include - #include - #include -+#include - #include -+#include - --/* ----------------------------------------------------------- * -- * Basic FUSE API * -- * ----------------------------------------------------------- */ -+/* -+ * Basic FUSE API -+ */ - - /** Handle for a FUSE filesystem */ - struct fuse; -@@ -36,38 +36,39 @@ struct fuse; - * Readdir flags, passed to ->readdir() - */ - enum fuse_readdir_flags { -- /** -- * "Plus" mode. -- * -- * The kernel wants to prefill the inode cache during readdir. The -- * filesystem may honour this by filling in the attributes and setting -- * FUSE_FILL_DIR_FLAGS for the filler function. The filesystem may also -- * just ignore this flag completely. -- */ -- FUSE_READDIR_PLUS = (1 << 0), -+ /** -+ * "Plus" mode. -+ * -+ * The kernel wants to prefill the inode cache during readdir. The -+ * filesystem may honour this by filling in the attributes and setting -+ * FUSE_FILL_DIR_FLAGS for the filler function. The filesystem may also -+ * just ignore this flag completely. -+ */ -+ FUSE_READDIR_PLUS = (1 << 0), - }; - - enum fuse_fill_dir_flags { -- /** -- * "Plus" mode: all file attributes are valid -- * -- * The attributes are used by the kernel to prefill the inode cache -- * during a readdir. -- * -- * It is okay to set FUSE_FILL_DIR_PLUS if FUSE_READDIR_PLUS is not set -- * and vice versa. -- */ -- FUSE_FILL_DIR_PLUS = (1 << 1), -+ /** -+ * "Plus" mode: all file attributes are valid -+ * -+ * The attributes are used by the kernel to prefill the inode cache -+ * during a readdir. -+ * -+ * It is okay to set FUSE_FILL_DIR_PLUS if FUSE_READDIR_PLUS is not set -+ * and vice versa. -+ */ -+ FUSE_FILL_DIR_PLUS = (1 << 1), - }; - --/** Function to add an entry in a readdir() operation -+/** -+ * Function to add an entry in a readdir() operation - * - * The *off* parameter can be any non-zero value that enables the - * filesystem to identify the current point in the directory - * stream. It does not need to be the actual physical position. A - * value of zero is reserved to indicate that seeking in directories - * is not supported. -- * -+ * - * @param buf the buffer passed to the readdir() operation - * @param name the file name of the directory entry - * @param stat file attributes, can be NULL -@@ -75,9 +76,9 @@ enum fuse_fill_dir_flags { - * @param flags fill flags - * @return 1 if buffer is full, zero otherwise - */ --typedef int (*fuse_fill_dir_t) (void *buf, const char *name, -- const struct stat *stbuf, off_t off, -- enum fuse_fill_dir_flags flags); -+typedef int (*fuse_fill_dir_t)(void *buf, const char *name, -+ const struct stat *stbuf, off_t off, -+ enum fuse_fill_dir_flags flags); - /** - * Configuration of the high-level API - * -@@ -87,186 +88,186 @@ typedef int (*fuse_fill_dir_t) (void *buf, const char *name, - * file system implementation. - */ - struct fuse_config { -- /** -- * If `set_gid` is non-zero, the st_gid attribute of each file -- * is overwritten with the value of `gid`. -- */ -- int set_gid; -- unsigned int gid; -- -- /** -- * If `set_uid` is non-zero, the st_uid attribute of each file -- * is overwritten with the value of `uid`. -- */ -- int set_uid; -- unsigned int uid; -- -- /** -- * If `set_mode` is non-zero, the any permissions bits set in -- * `umask` are unset in the st_mode attribute of each file. -- */ -- int set_mode; -- unsigned int umask; -- -- /** -- * The timeout in seconds for which name lookups will be -- * cached. -- */ -- double entry_timeout; -- -- /** -- * The timeout in seconds for which a negative lookup will be -- * cached. This means, that if file did not exist (lookup -- * retuned ENOENT), the lookup will only be redone after the -- * timeout, and the file/directory will be assumed to not -- * exist until then. A value of zero means that negative -- * lookups are not cached. -- */ -- double negative_timeout; -- -- /** -- * The timeout in seconds for which file/directory attributes -- * (as returned by e.g. the `getattr` handler) are cached. -- */ -- double attr_timeout; -- -- /** -- * Allow requests to be interrupted -- */ -- int intr; -- -- /** -- * Specify which signal number to send to the filesystem when -- * a request is interrupted. The default is hardcoded to -- * USR1. -- */ -- int intr_signal; -- -- /** -- * Normally, FUSE assigns inodes to paths only for as long as -- * the kernel is aware of them. With this option inodes are -- * instead remembered for at least this many seconds. This -- * will require more memory, but may be necessary when using -- * applications that make use of inode numbers. -- * -- * A number of -1 means that inodes will be remembered for the -- * entire life-time of the file-system process. -- */ -- int remember; -- -- /** -- * The default behavior is that if an open file is deleted, -- * the file is renamed to a hidden file (.fuse_hiddenXXX), and -- * only removed when the file is finally released. This -- * relieves the filesystem implementation of having to deal -- * with this problem. This option disables the hiding -- * behavior, and files are removed immediately in an unlink -- * operation (or in a rename operation which overwrites an -- * existing file). -- * -- * It is recommended that you not use the hard_remove -- * option. When hard_remove is set, the following libc -- * functions fail on unlinked files (returning errno of -- * ENOENT): read(2), write(2), fsync(2), close(2), f*xattr(2), -- * ftruncate(2), fstat(2), fchmod(2), fchown(2) -- */ -- int hard_remove; -- -- /** -- * Honor the st_ino field in the functions getattr() and -- * fill_dir(). This value is used to fill in the st_ino field -- * in the stat(2), lstat(2), fstat(2) functions and the d_ino -- * field in the readdir(2) function. The filesystem does not -- * have to guarantee uniqueness, however some applications -- * rely on this value being unique for the whole filesystem. -- * -- * Note that this does *not* affect the inode that libfuse -- * and the kernel use internally (also called the "nodeid"). -- */ -- int use_ino; -- -- /** -- * If use_ino option is not given, still try to fill in the -- * d_ino field in readdir(2). If the name was previously -- * looked up, and is still in the cache, the inode number -- * found there will be used. Otherwise it will be set to -1. -- * If use_ino option is given, this option is ignored. -- */ -- int readdir_ino; -- -- /** -- * This option disables the use of page cache (file content cache) -- * in the kernel for this filesystem. This has several affects: -- * -- * 1. Each read(2) or write(2) system call will initiate one -- * or more read or write operations, data will not be -- * cached in the kernel. -- * -- * 2. The return value of the read() and write() system calls -- * will correspond to the return values of the read and -- * write operations. This is useful for example if the -- * file size is not known in advance (before reading it). -- * -- * Internally, enabling this option causes fuse to set the -- * `direct_io` field of `struct fuse_file_info` - overwriting -- * any value that was put there by the file system. -- */ -- int direct_io; -- -- /** -- * This option disables flushing the cache of the file -- * contents on every open(2). This should only be enabled on -- * filesystems where the file data is never changed -- * externally (not through the mounted FUSE filesystem). Thus -- * it is not suitable for network filesystems and other -- * intermediate filesystems. -- * -- * NOTE: if this option is not specified (and neither -- * direct_io) data is still cached after the open(2), so a -- * read(2) system call will not always initiate a read -- * operation. -- * -- * Internally, enabling this option causes fuse to set the -- * `keep_cache` field of `struct fuse_file_info` - overwriting -- * any value that was put there by the file system. -- */ -- int kernel_cache; -- -- /** -- * This option is an alternative to `kernel_cache`. Instead of -- * unconditionally keeping cached data, the cached data is -- * invalidated on open(2) if if the modification time or the -- * size of the file has changed since it was last opened. -- */ -- int auto_cache; -- -- /** -- * The timeout in seconds for which file attributes are cached -- * for the purpose of checking if auto_cache should flush the -- * file data on open. -- */ -- int ac_attr_timeout_set; -- double ac_attr_timeout; -- -- /** -- * If this option is given the file-system handlers for the -- * following operations will not receive path information: -- * read, write, flush, release, fsync, readdir, releasedir, -- * fsyncdir, lock, ioctl and poll. -- * -- * For the truncate, getattr, chmod, chown and utimens -- * operations the path will be provided only if the struct -- * fuse_file_info argument is NULL. -- */ -- int nullpath_ok; -- -- /** -- * The remaining options are used by libfuse internally and -- * should not be touched. -- */ -- int show_help; -- char *modules; -- int debug; -+ /** -+ * If `set_gid` is non-zero, the st_gid attribute of each file -+ * is overwritten with the value of `gid`. -+ */ -+ int set_gid; -+ unsigned int gid; -+ -+ /** -+ * If `set_uid` is non-zero, the st_uid attribute of each file -+ * is overwritten with the value of `uid`. -+ */ -+ int set_uid; -+ unsigned int uid; -+ -+ /** -+ * If `set_mode` is non-zero, the any permissions bits set in -+ * `umask` are unset in the st_mode attribute of each file. -+ */ -+ int set_mode; -+ unsigned int umask; -+ -+ /** -+ * The timeout in seconds for which name lookups will be -+ * cached. -+ */ -+ double entry_timeout; -+ -+ /** -+ * The timeout in seconds for which a negative lookup will be -+ * cached. This means, that if file did not exist (lookup -+ * retuned ENOENT), the lookup will only be redone after the -+ * timeout, and the file/directory will be assumed to not -+ * exist until then. A value of zero means that negative -+ * lookups are not cached. -+ */ -+ double negative_timeout; -+ -+ /** -+ * The timeout in seconds for which file/directory attributes -+ * (as returned by e.g. the `getattr` handler) are cached. -+ */ -+ double attr_timeout; -+ -+ /** -+ * Allow requests to be interrupted -+ */ -+ int intr; -+ -+ /** -+ * Specify which signal number to send to the filesystem when -+ * a request is interrupted. The default is hardcoded to -+ * USR1. -+ */ -+ int intr_signal; -+ -+ /** -+ * Normally, FUSE assigns inodes to paths only for as long as -+ * the kernel is aware of them. With this option inodes are -+ * instead remembered for at least this many seconds. This -+ * will require more memory, but may be necessary when using -+ * applications that make use of inode numbers. -+ * -+ * A number of -1 means that inodes will be remembered for the -+ * entire life-time of the file-system process. -+ */ -+ int remember; -+ -+ /** -+ * The default behavior is that if an open file is deleted, -+ * the file is renamed to a hidden file (.fuse_hiddenXXX), and -+ * only removed when the file is finally released. This -+ * relieves the filesystem implementation of having to deal -+ * with this problem. This option disables the hiding -+ * behavior, and files are removed immediately in an unlink -+ * operation (or in a rename operation which overwrites an -+ * existing file). -+ * -+ * It is recommended that you not use the hard_remove -+ * option. When hard_remove is set, the following libc -+ * functions fail on unlinked files (returning errno of -+ * ENOENT): read(2), write(2), fsync(2), close(2), f*xattr(2), -+ * ftruncate(2), fstat(2), fchmod(2), fchown(2) -+ */ -+ int hard_remove; -+ -+ /** -+ * Honor the st_ino field in the functions getattr() and -+ * fill_dir(). This value is used to fill in the st_ino field -+ * in the stat(2), lstat(2), fstat(2) functions and the d_ino -+ * field in the readdir(2) function. The filesystem does not -+ * have to guarantee uniqueness, however some applications -+ * rely on this value being unique for the whole filesystem. -+ * -+ * Note that this does *not* affect the inode that libfuse -+ * and the kernel use internally (also called the "nodeid"). -+ */ -+ int use_ino; -+ -+ /** -+ * If use_ino option is not given, still try to fill in the -+ * d_ino field in readdir(2). If the name was previously -+ * looked up, and is still in the cache, the inode number -+ * found there will be used. Otherwise it will be set to -1. -+ * If use_ino option is given, this option is ignored. -+ */ -+ int readdir_ino; -+ -+ /** -+ * This option disables the use of page cache (file content cache) -+ * in the kernel for this filesystem. This has several affects: -+ * -+ * 1. Each read(2) or write(2) system call will initiate one -+ * or more read or write operations, data will not be -+ * cached in the kernel. -+ * -+ * 2. The return value of the read() and write() system calls -+ * will correspond to the return values of the read and -+ * write operations. This is useful for example if the -+ * file size is not known in advance (before reading it). -+ * -+ * Internally, enabling this option causes fuse to set the -+ * `direct_io` field of `struct fuse_file_info` - overwriting -+ * any value that was put there by the file system. -+ */ -+ int direct_io; -+ -+ /** -+ * This option disables flushing the cache of the file -+ * contents on every open(2). This should only be enabled on -+ * filesystems where the file data is never changed -+ * externally (not through the mounted FUSE filesystem). Thus -+ * it is not suitable for network filesystems and other -+ * intermediate filesystems. -+ * -+ * NOTE: if this option is not specified (and neither -+ * direct_io) data is still cached after the open(2), so a -+ * read(2) system call will not always initiate a read -+ * operation. -+ * -+ * Internally, enabling this option causes fuse to set the -+ * `keep_cache` field of `struct fuse_file_info` - overwriting -+ * any value that was put there by the file system. -+ */ -+ int kernel_cache; -+ -+ /** -+ * This option is an alternative to `kernel_cache`. Instead of -+ * unconditionally keeping cached data, the cached data is -+ * invalidated on open(2) if if the modification time or the -+ * size of the file has changed since it was last opened. -+ */ -+ int auto_cache; -+ -+ /** -+ * The timeout in seconds for which file attributes are cached -+ * for the purpose of checking if auto_cache should flush the -+ * file data on open. -+ */ -+ int ac_attr_timeout_set; -+ double ac_attr_timeout; -+ -+ /** -+ * If this option is given the file-system handlers for the -+ * following operations will not receive path information: -+ * read, write, flush, release, fsync, readdir, releasedir, -+ * fsyncdir, lock, ioctl and poll. -+ * -+ * For the truncate, getattr, chmod, chown and utimens -+ * operations the path will be provided only if the struct -+ * fuse_file_info argument is NULL. -+ */ -+ int nullpath_ok; -+ -+ /** -+ * The remaining options are used by libfuse internally and -+ * should not be touched. -+ */ -+ int show_help; -+ char *modules; -+ int debug; - }; - - -@@ -293,515 +294,535 @@ struct fuse_config { - * Almost all operations take a path which can be of any length. - */ - struct fuse_operations { -- /** Get file attributes. -- * -- * Similar to stat(). The 'st_dev' and 'st_blksize' fields are -- * ignored. The 'st_ino' field is ignored except if the 'use_ino' -- * mount option is given. In that case it is passed to userspace, -- * but libfuse and the kernel will still assign a different -- * inode for internal use (called the "nodeid"). -- * -- * `fi` will always be NULL if the file is not currently open, but -- * may also be NULL if the file is open. -- */ -- int (*getattr) (const char *, struct stat *, struct fuse_file_info *fi); -- -- /** Read the target of a symbolic link -- * -- * The buffer should be filled with a null terminated string. The -- * buffer size argument includes the space for the terminating -- * null character. If the linkname is too long to fit in the -- * buffer, it should be truncated. The return value should be 0 -- * for success. -- */ -- int (*readlink) (const char *, char *, size_t); -- -- /** Create a file node -- * -- * This is called for creation of all non-directory, non-symlink -- * nodes. If the filesystem defines a create() method, then for -- * regular files that will be called instead. -- */ -- int (*mknod) (const char *, mode_t, dev_t); -- -- /** Create a directory -- * -- * Note that the mode argument may not have the type specification -- * bits set, i.e. S_ISDIR(mode) can be false. To obtain the -- * correct directory type bits use mode|S_IFDIR -- * */ -- int (*mkdir) (const char *, mode_t); -- -- /** Remove a file */ -- int (*unlink) (const char *); -- -- /** Remove a directory */ -- int (*rmdir) (const char *); -- -- /** Create a symbolic link */ -- int (*symlink) (const char *, const char *); -- -- /** Rename a file -- * -- * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If -- * RENAME_NOREPLACE is specified, the filesystem must not -- * overwrite *newname* if it exists and return an error -- * instead. If `RENAME_EXCHANGE` is specified, the filesystem -- * must atomically exchange the two files, i.e. both must -- * exist and neither may be deleted. -- */ -- int (*rename) (const char *, const char *, unsigned int flags); -- -- /** Create a hard link to a file */ -- int (*link) (const char *, const char *); -- -- /** Change the permission bits of a file -- * -- * `fi` will always be NULL if the file is not currenlty open, but -- * may also be NULL if the file is open. -- */ -- int (*chmod) (const char *, mode_t, struct fuse_file_info *fi); -- -- /** Change the owner and group of a file -- * -- * `fi` will always be NULL if the file is not currenlty open, but -- * may also be NULL if the file is open. -- * -- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -- * expected to reset the setuid and setgid bits. -- */ -- int (*chown) (const char *, uid_t, gid_t, struct fuse_file_info *fi); -- -- /** Change the size of a file -- * -- * `fi` will always be NULL if the file is not currenlty open, but -- * may also be NULL if the file is open. -- * -- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -- * expected to reset the setuid and setgid bits. -- */ -- int (*truncate) (const char *, off_t, struct fuse_file_info *fi); -- -- /** Open a file -- * -- * Open flags are available in fi->flags. The following rules -- * apply. -- * -- * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be -- * filtered out / handled by the kernel. -- * -- * - Access modes (O_RDONLY, O_WRONLY, O_RDWR, O_EXEC, O_SEARCH) -- * should be used by the filesystem to check if the operation is -- * permitted. If the ``-o default_permissions`` mount option is -- * given, this check is already done by the kernel before calling -- * open() and may thus be omitted by the filesystem. -- * -- * - When writeback caching is enabled, the kernel may send -- * read requests even for files opened with O_WRONLY. The -- * filesystem should be prepared to handle this. -- * -- * - When writeback caching is disabled, the filesystem is -- * expected to properly handle the O_APPEND flag and ensure -- * that each write is appending to the end of the file. -- * -- * - When writeback caching is enabled, the kernel will -- * handle O_APPEND. However, unless all changes to the file -- * come through the kernel this will not work reliably. The -- * filesystem should thus either ignore the O_APPEND flag -- * (and let the kernel handle it), or return an error -- * (indicating that reliably O_APPEND is not available). -- * -- * Filesystem may store an arbitrary file handle (pointer, -- * index, etc) in fi->fh, and use this in other all other file -- * operations (read, write, flush, release, fsync). -- * -- * Filesystem may also implement stateless file I/O and not store -- * anything in fi->fh. -- * -- * There are also some flags (direct_io, keep_cache) which the -- * filesystem may set in fi, to change the way the file is opened. -- * See fuse_file_info structure in for more details. -- * -- * If this request is answered with an error code of ENOSYS -- * and FUSE_CAP_NO_OPEN_SUPPORT is set in -- * `fuse_conn_info.capable`, this is treated as success and -- * future calls to open will also succeed without being send -- * to the filesystem process. -- * -- */ -- int (*open) (const char *, struct fuse_file_info *); -- -- /** Read data from an open file -- * -- * Read should return exactly the number of bytes requested except -- * on EOF or error, otherwise the rest of the data will be -- * substituted with zeroes. An exception to this is when the -- * 'direct_io' mount option is specified, in which case the return -- * value of the read system call will reflect the return value of -- * this operation. -- */ -- int (*read) (const char *, char *, size_t, off_t, -- struct fuse_file_info *); -- -- /** Write data to an open file -- * -- * Write should return exactly the number of bytes requested -- * except on error. An exception to this is when the 'direct_io' -- * mount option is specified (see read operation). -- * -- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -- * expected to reset the setuid and setgid bits. -- */ -- int (*write) (const char *, const char *, size_t, off_t, -- struct fuse_file_info *); -- -- /** Get file system statistics -- * -- * The 'f_favail', 'f_fsid' and 'f_flag' fields are ignored -- */ -- int (*statfs) (const char *, struct statvfs *); -- -- /** Possibly flush cached data -- * -- * BIG NOTE: This is not equivalent to fsync(). It's not a -- * request to sync dirty data. -- * -- * Flush is called on each close() of a file descriptor, as opposed to -- * release which is called on the close of the last file descriptor for -- * a file. Under Linux, errors returned by flush() will be passed to -- * userspace as errors from close(), so flush() is a good place to write -- * back any cached dirty data. However, many applications ignore errors -- * on close(), and on non-Linux systems, close() may succeed even if flush() -- * returns an error. For these reasons, filesystems should not assume -- * that errors returned by flush will ever be noticed or even -- * delivered. -- * -- * NOTE: The flush() method may be called more than once for each -- * open(). This happens if more than one file descriptor refers to an -- * open file handle, e.g. due to dup(), dup2() or fork() calls. It is -- * not possible to determine if a flush is final, so each flush should -- * be treated equally. Multiple write-flush sequences are relatively -- * rare, so this shouldn't be a problem. -- * -- * Filesystems shouldn't assume that flush will be called at any -- * particular point. It may be called more times than expected, or not -- * at all. -- * -- * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html -- */ -- int (*flush) (const char *, struct fuse_file_info *); -- -- /** Release an open file -- * -- * Release is called when there are no more references to an open -- * file: all file descriptors are closed and all memory mappings -- * are unmapped. -- * -- * For every open() call there will be exactly one release() call -- * with the same flags and file handle. It is possible to -- * have a file opened more than once, in which case only the last -- * release will mean, that no more reads/writes will happen on the -- * file. The return value of release is ignored. -- */ -- int (*release) (const char *, struct fuse_file_info *); -- -- /** Synchronize file contents -- * -- * If the datasync parameter is non-zero, then only the user data -- * should be flushed, not the meta data. -- */ -- int (*fsync) (const char *, int, struct fuse_file_info *); -- -- /** Set extended attributes */ -- int (*setxattr) (const char *, const char *, const char *, size_t, int); -- -- /** Get extended attributes */ -- int (*getxattr) (const char *, const char *, char *, size_t); -- -- /** List extended attributes */ -- int (*listxattr) (const char *, char *, size_t); -- -- /** Remove extended attributes */ -- int (*removexattr) (const char *, const char *); -- -- /** Open directory -- * -- * Unless the 'default_permissions' mount option is given, -- * this method should check if opendir is permitted for this -- * directory. Optionally opendir may also return an arbitrary -- * filehandle in the fuse_file_info structure, which will be -- * passed to readdir, releasedir and fsyncdir. -- */ -- int (*opendir) (const char *, struct fuse_file_info *); -- -- /** Read directory -- * -- * The filesystem may choose between two modes of operation: -- * -- * 1) The readdir implementation ignores the offset parameter, and -- * passes zero to the filler function's offset. The filler -- * function will not return '1' (unless an error happens), so the -- * whole directory is read in a single readdir operation. -- * -- * 2) The readdir implementation keeps track of the offsets of the -- * directory entries. It uses the offset parameter and always -- * passes non-zero offset to the filler function. When the buffer -- * is full (or an error happens) the filler function will return -- * '1'. -- */ -- int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t, -- struct fuse_file_info *, enum fuse_readdir_flags); -- -- /** Release directory -- */ -- int (*releasedir) (const char *, struct fuse_file_info *); -- -- /** Synchronize directory contents -- * -- * If the datasync parameter is non-zero, then only the user data -- * should be flushed, not the meta data -- */ -- int (*fsyncdir) (const char *, int, struct fuse_file_info *); -- -- /** -- * Initialize filesystem -- * -- * The return value will passed in the `private_data` field of -- * `struct fuse_context` to all file operations, and as a -- * parameter to the destroy() method. It overrides the initial -- * value provided to fuse_main() / fuse_new(). -- */ -- void *(*init) (struct fuse_conn_info *conn, -- struct fuse_config *cfg); -- -- /** -- * Clean up filesystem -- * -- * Called on filesystem exit. -- */ -- void (*destroy) (void *private_data); -- -- /** -- * Check file access permissions -- * -- * This will be called for the access() system call. If the -- * 'default_permissions' mount option is given, this method is not -- * called. -- * -- * This method is not called under Linux kernel versions 2.4.x -- */ -- int (*access) (const char *, int); -- -- /** -- * Create and open a file -- * -- * If the file does not exist, first create it with the specified -- * mode, and then open it. -- * -- * If this method is not implemented or under Linux kernel -- * versions earlier than 2.6.15, the mknod() and open() methods -- * will be called instead. -- */ -- int (*create) (const char *, mode_t, struct fuse_file_info *); -- -- /** -- * Perform POSIX file locking operation -- * -- * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW. -- * -- * For the meaning of fields in 'struct flock' see the man page -- * for fcntl(2). The l_whence field will always be set to -- * SEEK_SET. -- * -- * For checking lock ownership, the 'fuse_file_info->owner' -- * argument must be used. -- * -- * For F_GETLK operation, the library will first check currently -- * held locks, and if a conflicting lock is found it will return -- * information without calling this method. This ensures, that -- * for local locks the l_pid field is correctly filled in. The -- * results may not be accurate in case of race conditions and in -- * the presence of hard links, but it's unlikely that an -- * application would rely on accurate GETLK results in these -- * cases. If a conflicting lock is not found, this method will be -- * called, and the filesystem may fill out l_pid by a meaningful -- * value, or it may leave this field zero. -- * -- * For F_SETLK and F_SETLKW the l_pid field will be set to the pid -- * of the process performing the locking operation. -- * -- * Note: if this method is not implemented, the kernel will still -- * allow file locking to work locally. Hence it is only -- * interesting for network filesystems and similar. -- */ -- int (*lock) (const char *, struct fuse_file_info *, int cmd, -- struct flock *); -- -- /** -- * Change the access and modification times of a file with -- * nanosecond resolution -- * -- * This supersedes the old utime() interface. New applications -- * should use this. -- * -- * `fi` will always be NULL if the file is not currenlty open, but -- * may also be NULL if the file is open. -- * -- * See the utimensat(2) man page for details. -- */ -- int (*utimens) (const char *, const struct timespec tv[2], -- struct fuse_file_info *fi); -- -- /** -- * Map block index within file to block index within device -- * -- * Note: This makes sense only for block device backed filesystems -- * mounted with the 'blkdev' option -- */ -- int (*bmap) (const char *, size_t blocksize, uint64_t *idx); -- -- /** -- * Ioctl -- * -- * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in -- * 64bit environment. The size and direction of data is -- * determined by _IOC_*() decoding of cmd. For _IOC_NONE, -- * data will be NULL, for _IOC_WRITE data is out area, for -- * _IOC_READ in area and if both are set in/out area. In all -- * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes. -- * -- * If flags has FUSE_IOCTL_DIR then the fuse_file_info refers to a -- * directory file handle. -- * -- * Note : the unsigned long request submitted by the application -- * is truncated to 32 bits. -- */ -- int (*ioctl) (const char *, unsigned int cmd, void *arg, -- struct fuse_file_info *, unsigned int flags, void *data); -- -- /** -- * Poll for IO readiness events -- * -- * Note: If ph is non-NULL, the client should notify -- * when IO readiness events occur by calling -- * fuse_notify_poll() with the specified ph. -- * -- * Regardless of the number of times poll with a non-NULL ph -- * is received, single notification is enough to clear all. -- * Notifying more times incurs overhead but doesn't harm -- * correctness. -- * -- * The callee is responsible for destroying ph with -- * fuse_pollhandle_destroy() when no longer in use. -- */ -- int (*poll) (const char *, struct fuse_file_info *, -- struct fuse_pollhandle *ph, unsigned *reventsp); -- -- /** Write contents of buffer to an open file -- * -- * Similar to the write() method, but data is supplied in a -- * generic buffer. Use fuse_buf_copy() to transfer data to -- * the destination. -- * -- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -- * expected to reset the setuid and setgid bits. -- */ -- int (*write_buf) (const char *, struct fuse_bufvec *buf, off_t off, -- struct fuse_file_info *); -- -- /** Store data from an open file in a buffer -- * -- * Similar to the read() method, but data is stored and -- * returned in a generic buffer. -- * -- * No actual copying of data has to take place, the source -- * file descriptor may simply be stored in the buffer for -- * later data transfer. -- * -- * The buffer must be allocated dynamically and stored at the -- * location pointed to by bufp. If the buffer contains memory -- * regions, they too must be allocated using malloc(). The -- * allocated memory will be freed by the caller. -- */ -- int (*read_buf) (const char *, struct fuse_bufvec **bufp, -- size_t size, off_t off, struct fuse_file_info *); -- /** -- * Perform BSD file locking operation -- * -- * The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN -- * -- * Nonblocking requests will be indicated by ORing LOCK_NB to -- * the above operations -- * -- * For more information see the flock(2) manual page. -- * -- * Additionally fi->owner will be set to a value unique to -- * this open file. This same value will be supplied to -- * ->release() when the file is released. -- * -- * Note: if this method is not implemented, the kernel will still -- * allow file locking to work locally. Hence it is only -- * interesting for network filesystems and similar. -- */ -- int (*flock) (const char *, struct fuse_file_info *, int op); -- -- /** -- * Allocates space for an open file -- * -- * This function ensures that required space is allocated for specified -- * file. If this function returns success then any subsequent write -- * request to specified range is guaranteed not to fail because of lack -- * of space on the file system media. -- */ -- int (*fallocate) (const char *, int, off_t, off_t, -- struct fuse_file_info *); -- -- /** -- * Copy a range of data from one file to another -- * -- * Performs an optimized copy between two file descriptors without the -- * additional cost of transferring data through the FUSE kernel module -- * to user space (glibc) and then back into the FUSE filesystem again. -- * -- * In case this method is not implemented, glibc falls back to reading -- * data from the source and writing to the destination. Effectively -- * doing an inefficient copy of the data. -- */ -- ssize_t (*copy_file_range) (const char *path_in, -- struct fuse_file_info *fi_in, -- off_t offset_in, const char *path_out, -- struct fuse_file_info *fi_out, -- off_t offset_out, size_t size, int flags); -- -- /** -- * Find next data or hole after the specified offset -- */ -- off_t (*lseek) (const char *, off_t off, int whence, struct fuse_file_info *); -+ /** -+ * Get file attributes. -+ * -+ * Similar to stat(). The 'st_dev' and 'st_blksize' fields are -+ * ignored. The 'st_ino' field is ignored except if the 'use_ino' -+ * mount option is given. In that case it is passed to userspace, -+ * but libfuse and the kernel will still assign a different -+ * inode for internal use (called the "nodeid"). -+ * -+ * `fi` will always be NULL if the file is not currently open, but -+ * may also be NULL if the file is open. -+ */ -+ int (*getattr)(const char *, struct stat *, struct fuse_file_info *fi); -+ -+ /** -+ * Read the target of a symbolic link -+ * -+ * The buffer should be filled with a null terminated string. The -+ * buffer size argument includes the space for the terminating -+ * null character. If the linkname is too long to fit in the -+ * buffer, it should be truncated. The return value should be 0 -+ * for success. -+ */ -+ int (*readlink)(const char *, char *, size_t); -+ -+ /** -+ * Create a file node -+ * -+ * This is called for creation of all non-directory, non-symlink -+ * nodes. If the filesystem defines a create() method, then for -+ * regular files that will be called instead. -+ */ -+ int (*mknod)(const char *, mode_t, dev_t); -+ -+ /** -+ * Create a directory -+ * -+ * Note that the mode argument may not have the type specification -+ * bits set, i.e. S_ISDIR(mode) can be false. To obtain the -+ * correct directory type bits use mode|S_IFDIR -+ */ -+ int (*mkdir)(const char *, mode_t); -+ -+ /** Remove a file */ -+ int (*unlink)(const char *); -+ -+ /** Remove a directory */ -+ int (*rmdir)(const char *); -+ -+ /** Create a symbolic link */ -+ int (*symlink)(const char *, const char *); -+ -+ /** -+ * Rename a file -+ * -+ * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If -+ * RENAME_NOREPLACE is specified, the filesystem must not -+ * overwrite *newname* if it exists and return an error -+ * instead. If `RENAME_EXCHANGE` is specified, the filesystem -+ * must atomically exchange the two files, i.e. both must -+ * exist and neither may be deleted. -+ */ -+ int (*rename)(const char *, const char *, unsigned int flags); -+ -+ /** Create a hard link to a file */ -+ int (*link)(const char *, const char *); -+ -+ /** -+ * Change the permission bits of a file -+ * -+ * `fi` will always be NULL if the file is not currenlty open, but -+ * may also be NULL if the file is open. -+ */ -+ int (*chmod)(const char *, mode_t, struct fuse_file_info *fi); -+ -+ /** -+ * Change the owner and group of a file -+ * -+ * `fi` will always be NULL if the file is not currenlty open, but -+ * may also be NULL if the file is open. -+ * -+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -+ * expected to reset the setuid and setgid bits. -+ */ -+ int (*chown)(const char *, uid_t, gid_t, struct fuse_file_info *fi); -+ -+ /** -+ * Change the size of a file -+ * -+ * `fi` will always be NULL if the file is not currenlty open, but -+ * may also be NULL if the file is open. -+ * -+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -+ * expected to reset the setuid and setgid bits. -+ */ -+ int (*truncate)(const char *, off_t, struct fuse_file_info *fi); -+ -+ /** -+ * Open a file -+ * -+ * Open flags are available in fi->flags. The following rules -+ * apply. -+ * -+ * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be -+ * filtered out / handled by the kernel. -+ * -+ * - Access modes (O_RDONLY, O_WRONLY, O_RDWR, O_EXEC, O_SEARCH) -+ * should be used by the filesystem to check if the operation is -+ * permitted. If the ``-o default_permissions`` mount option is -+ * given, this check is already done by the kernel before calling -+ * open() and may thus be omitted by the filesystem. -+ * -+ * - When writeback caching is enabled, the kernel may send -+ * read requests even for files opened with O_WRONLY. The -+ * filesystem should be prepared to handle this. -+ * -+ * - When writeback caching is disabled, the filesystem is -+ * expected to properly handle the O_APPEND flag and ensure -+ * that each write is appending to the end of the file. -+ * -+ * - When writeback caching is enabled, the kernel will -+ * handle O_APPEND. However, unless all changes to the file -+ * come through the kernel this will not work reliably. The -+ * filesystem should thus either ignore the O_APPEND flag -+ * (and let the kernel handle it), or return an error -+ * (indicating that reliably O_APPEND is not available). -+ * -+ * Filesystem may store an arbitrary file handle (pointer, -+ * index, etc) in fi->fh, and use this in other all other file -+ * operations (read, write, flush, release, fsync). -+ * -+ * Filesystem may also implement stateless file I/O and not store -+ * anything in fi->fh. -+ * -+ * There are also some flags (direct_io, keep_cache) which the -+ * filesystem may set in fi, to change the way the file is opened. -+ * See fuse_file_info structure in for more details. -+ * -+ * If this request is answered with an error code of ENOSYS -+ * and FUSE_CAP_NO_OPEN_SUPPORT is set in -+ * `fuse_conn_info.capable`, this is treated as success and -+ * future calls to open will also succeed without being send -+ * to the filesystem process. -+ * -+ */ -+ int (*open)(const char *, struct fuse_file_info *); -+ -+ /** -+ * Read data from an open file -+ * -+ * Read should return exactly the number of bytes requested except -+ * on EOF or error, otherwise the rest of the data will be -+ * substituted with zeroes. An exception to this is when the -+ * 'direct_io' mount option is specified, in which case the return -+ * value of the read system call will reflect the return value of -+ * this operation. -+ */ -+ int (*read)(const char *, char *, size_t, off_t, struct fuse_file_info *); -+ -+ /** -+ * Write data to an open file -+ * -+ * Write should return exactly the number of bytes requested -+ * except on error. An exception to this is when the 'direct_io' -+ * mount option is specified (see read operation). -+ * -+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -+ * expected to reset the setuid and setgid bits. -+ */ -+ int (*write)(const char *, const char *, size_t, off_t, -+ struct fuse_file_info *); -+ -+ /** -+ * Get file system statistics -+ * -+ * The 'f_favail', 'f_fsid' and 'f_flag' fields are ignored -+ */ -+ int (*statfs)(const char *, struct statvfs *); -+ -+ /** -+ * Possibly flush cached data -+ * -+ * BIG NOTE: This is not equivalent to fsync(). It's not a -+ * request to sync dirty data. -+ * -+ * Flush is called on each close() of a file descriptor, as opposed to -+ * release which is called on the close of the last file descriptor for -+ * a file. Under Linux, errors returned by flush() will be passed to -+ * userspace as errors from close(), so flush() is a good place to write -+ * back any cached dirty data. However, many applications ignore errors -+ * on close(), and on non-Linux systems, close() may succeed even if flush() -+ * returns an error. For these reasons, filesystems should not assume -+ * that errors returned by flush will ever be noticed or even -+ * delivered. -+ * -+ * NOTE: The flush() method may be called more than once for each -+ * open(). This happens if more than one file descriptor refers to an -+ * open file handle, e.g. due to dup(), dup2() or fork() calls. It is -+ * not possible to determine if a flush is final, so each flush should -+ * be treated equally. Multiple write-flush sequences are relatively -+ * rare, so this shouldn't be a problem. -+ * -+ * Filesystems shouldn't assume that flush will be called at any -+ * particular point. It may be called more times than expected, or not -+ * at all. -+ * -+ * [close]: -+ * http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html -+ */ -+ int (*flush)(const char *, struct fuse_file_info *); -+ -+ /** -+ * Release an open file -+ * -+ * Release is called when there are no more references to an open -+ * file: all file descriptors are closed and all memory mappings -+ * are unmapped. -+ * -+ * For every open() call there will be exactly one release() call -+ * with the same flags and file handle. It is possible to -+ * have a file opened more than once, in which case only the last -+ * release will mean, that no more reads/writes will happen on the -+ * file. The return value of release is ignored. -+ */ -+ int (*release)(const char *, struct fuse_file_info *); -+ -+ /* -+ * Synchronize file contents -+ * -+ * If the datasync parameter is non-zero, then only the user data -+ * should be flushed, not the meta data. -+ */ -+ int (*fsync)(const char *, int, struct fuse_file_info *); -+ -+ /** Set extended attributes */ -+ int (*setxattr)(const char *, const char *, const char *, size_t, int); -+ -+ /** Get extended attributes */ -+ int (*getxattr)(const char *, const char *, char *, size_t); -+ -+ /** List extended attributes */ -+ int (*listxattr)(const char *, char *, size_t); -+ -+ /** Remove extended attributes */ -+ int (*removexattr)(const char *, const char *); -+ -+ /* -+ * Open directory -+ * -+ * Unless the 'default_permissions' mount option is given, -+ * this method should check if opendir is permitted for this -+ * directory. Optionally opendir may also return an arbitrary -+ * filehandle in the fuse_file_info structure, which will be -+ * passed to readdir, releasedir and fsyncdir. -+ */ -+ int (*opendir)(const char *, struct fuse_file_info *); -+ -+ /* -+ * Read directory -+ * -+ * The filesystem may choose between two modes of operation: -+ * -+ * 1) The readdir implementation ignores the offset parameter, and -+ * passes zero to the filler function's offset. The filler -+ * function will not return '1' (unless an error happens), so the -+ * whole directory is read in a single readdir operation. -+ * -+ * 2) The readdir implementation keeps track of the offsets of the -+ * directory entries. It uses the offset parameter and always -+ * passes non-zero offset to the filler function. When the buffer -+ * is full (or an error happens) the filler function will return -+ * '1'. -+ */ -+ int (*readdir)(const char *, void *, fuse_fill_dir_t, off_t, -+ struct fuse_file_info *, enum fuse_readdir_flags); -+ -+ /** -+ * Release directory -+ */ -+ int (*releasedir)(const char *, struct fuse_file_info *); -+ -+ /** -+ * Synchronize directory contents -+ * -+ * If the datasync parameter is non-zero, then only the user data -+ * should be flushed, not the meta data -+ */ -+ int (*fsyncdir)(const char *, int, struct fuse_file_info *); -+ -+ /** -+ * Initialize filesystem -+ * -+ * The return value will passed in the `private_data` field of -+ * `struct fuse_context` to all file operations, and as a -+ * parameter to the destroy() method. It overrides the initial -+ * value provided to fuse_main() / fuse_new(). -+ */ -+ void *(*init)(struct fuse_conn_info *conn, struct fuse_config *cfg); -+ -+ /** -+ * Clean up filesystem -+ * -+ * Called on filesystem exit. -+ */ -+ void (*destroy)(void *private_data); -+ -+ /** -+ * Check file access permissions -+ * -+ * This will be called for the access() system call. If the -+ * 'default_permissions' mount option is given, this method is not -+ * called. -+ * -+ * This method is not called under Linux kernel versions 2.4.x -+ */ -+ int (*access)(const char *, int); -+ -+ /** -+ * Create and open a file -+ * -+ * If the file does not exist, first create it with the specified -+ * mode, and then open it. -+ * -+ * If this method is not implemented or under Linux kernel -+ * versions earlier than 2.6.15, the mknod() and open() methods -+ * will be called instead. -+ */ -+ int (*create)(const char *, mode_t, struct fuse_file_info *); -+ -+ /** -+ * Perform POSIX file locking operation -+ * -+ * The cmd argument will be either F_GETLK, F_SETLK or F_SETLKW. -+ * -+ * For the meaning of fields in 'struct flock' see the man page -+ * for fcntl(2). The l_whence field will always be set to -+ * SEEK_SET. -+ * -+ * For checking lock ownership, the 'fuse_file_info->owner' -+ * argument must be used. -+ * -+ * For F_GETLK operation, the library will first check currently -+ * held locks, and if a conflicting lock is found it will return -+ * information without calling this method. This ensures, that -+ * for local locks the l_pid field is correctly filled in. The -+ * results may not be accurate in case of race conditions and in -+ * the presence of hard links, but it's unlikely that an -+ * application would rely on accurate GETLK results in these -+ * cases. If a conflicting lock is not found, this method will be -+ * called, and the filesystem may fill out l_pid by a meaningful -+ * value, or it may leave this field zero. -+ * -+ * For F_SETLK and F_SETLKW the l_pid field will be set to the pid -+ * of the process performing the locking operation. -+ * -+ * Note: if this method is not implemented, the kernel will still -+ * allow file locking to work locally. Hence it is only -+ * interesting for network filesystems and similar. -+ */ -+ int (*lock)(const char *, struct fuse_file_info *, int cmd, struct flock *); -+ -+ /** -+ * Change the access and modification times of a file with -+ * nanosecond resolution -+ * -+ * This supersedes the old utime() interface. New applications -+ * should use this. -+ * -+ * `fi` will always be NULL if the file is not currenlty open, but -+ * may also be NULL if the file is open. -+ * -+ * See the utimensat(2) man page for details. -+ */ -+ int (*utimens)(const char *, const struct timespec tv[2], -+ struct fuse_file_info *fi); -+ -+ /** -+ * Map block index within file to block index within device -+ * -+ * Note: This makes sense only for block device backed filesystems -+ * mounted with the 'blkdev' option -+ */ -+ int (*bmap)(const char *, size_t blocksize, uint64_t *idx); -+ -+ /** -+ * Ioctl -+ * -+ * flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in -+ * 64bit environment. The size and direction of data is -+ * determined by _IOC_*() decoding of cmd. For _IOC_NONE, -+ * data will be NULL, for _IOC_WRITE data is out area, for -+ * _IOC_READ in area and if both are set in/out area. In all -+ * non-NULL cases, the area is of _IOC_SIZE(cmd) bytes. -+ * -+ * If flags has FUSE_IOCTL_DIR then the fuse_file_info refers to a -+ * directory file handle. -+ * -+ * Note : the unsigned long request submitted by the application -+ * is truncated to 32 bits. -+ */ -+ int (*ioctl)(const char *, unsigned int cmd, void *arg, -+ struct fuse_file_info *, unsigned int flags, void *data); -+ -+ /** -+ * Poll for IO readiness events -+ * -+ * Note: If ph is non-NULL, the client should notify -+ * when IO readiness events occur by calling -+ * fuse_notify_poll() with the specified ph. -+ * -+ * Regardless of the number of times poll with a non-NULL ph -+ * is received, single notification is enough to clear all. -+ * Notifying more times incurs overhead but doesn't harm -+ * correctness. -+ * -+ * The callee is responsible for destroying ph with -+ * fuse_pollhandle_destroy() when no longer in use. -+ */ -+ int (*poll)(const char *, struct fuse_file_info *, -+ struct fuse_pollhandle *ph, unsigned *reventsp); -+ -+ /* -+ * Write contents of buffer to an open file -+ * -+ * Similar to the write() method, but data is supplied in a -+ * generic buffer. Use fuse_buf_copy() to transfer data to -+ * the destination. -+ * -+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -+ * expected to reset the setuid and setgid bits. -+ */ -+ int (*write_buf)(const char *, struct fuse_bufvec *buf, off_t off, -+ struct fuse_file_info *); -+ -+ /* -+ * Store data from an open file in a buffer -+ * -+ * Similar to the read() method, but data is stored and -+ * returned in a generic buffer. -+ * -+ * No actual copying of data has to take place, the source -+ * file descriptor may simply be stored in the buffer for -+ * later data transfer. -+ * -+ * The buffer must be allocated dynamically and stored at the -+ * location pointed to by bufp. If the buffer contains memory -+ * regions, they too must be allocated using malloc(). The -+ * allocated memory will be freed by the caller. -+ */ -+ int (*read_buf)(const char *, struct fuse_bufvec **bufp, size_t size, -+ off_t off, struct fuse_file_info *); -+ /** -+ * Perform BSD file locking operation -+ * -+ * The op argument will be either LOCK_SH, LOCK_EX or LOCK_UN -+ * -+ * Nonblocking requests will be indicated by ORing LOCK_NB to -+ * the above operations -+ * -+ * For more information see the flock(2) manual page. -+ * -+ * Additionally fi->owner will be set to a value unique to -+ * this open file. This same value will be supplied to -+ * ->release() when the file is released. -+ * -+ * Note: if this method is not implemented, the kernel will still -+ * allow file locking to work locally. Hence it is only -+ * interesting for network filesystems and similar. -+ */ -+ int (*flock)(const char *, struct fuse_file_info *, int op); -+ -+ /** -+ * Allocates space for an open file -+ * -+ * This function ensures that required space is allocated for specified -+ * file. If this function returns success then any subsequent write -+ * request to specified range is guaranteed not to fail because of lack -+ * of space on the file system media. -+ */ -+ int (*fallocate)(const char *, int, off_t, off_t, struct fuse_file_info *); -+ -+ /** -+ * Copy a range of data from one file to another -+ * -+ * Performs an optimized copy between two file descriptors without the -+ * additional cost of transferring data through the FUSE kernel module -+ * to user space (glibc) and then back into the FUSE filesystem again. -+ * -+ * In case this method is not implemented, glibc falls back to reading -+ * data from the source and writing to the destination. Effectively -+ * doing an inefficient copy of the data. -+ */ -+ ssize_t (*copy_file_range)(const char *path_in, -+ struct fuse_file_info *fi_in, off_t offset_in, -+ const char *path_out, -+ struct fuse_file_info *fi_out, off_t offset_out, -+ size_t size, int flags); -+ -+ /** -+ * Find next data or hole after the specified offset -+ */ -+ off_t (*lseek)(const char *, off_t off, int whence, -+ struct fuse_file_info *); - }; - --/** Extra context that may be needed by some filesystems -+/* -+ * Extra context that may be needed by some filesystems - * - * The uid, gid and pid fields are not filled in case of a writepage - * operation. - */ - struct fuse_context { -- /** Pointer to the fuse object */ -- struct fuse *fuse; -+ /** Pointer to the fuse object */ -+ struct fuse *fuse; - -- /** User ID of the calling process */ -- uid_t uid; -+ /** User ID of the calling process */ -+ uid_t uid; - -- /** Group ID of the calling process */ -- gid_t gid; -+ /** Group ID of the calling process */ -+ gid_t gid; - -- /** Process ID of the calling thread */ -- pid_t pid; -+ /** Process ID of the calling thread */ -+ pid_t pid; - -- /** Private filesystem data */ -- void *private_data; -+ /** Private filesystem data */ -+ void *private_data; - -- /** Umask of the calling process */ -- mode_t umask; -+ /** Umask of the calling process */ -+ mode_t umask; - }; - - /** -@@ -859,15 +880,15 @@ struct fuse_context { - * Example usage, see hello.c - */ - /* -- int fuse_main(int argc, char *argv[], const struct fuse_operations *op, -- void *private_data); --*/ --#define fuse_main(argc, argv, op, private_data) \ -- fuse_main_real(argc, argv, op, sizeof(*(op)), private_data) -+ * int fuse_main(int argc, char *argv[], const struct fuse_operations *op, -+ * void *private_data); -+ */ -+#define fuse_main(argc, argv, op, private_data) \ -+ fuse_main_real(argc, argv, op, sizeof(*(op)), private_data) - --/* ----------------------------------------------------------- * -- * More detailed API * -- * ----------------------------------------------------------- */ -+/* -+ * More detailed API -+ */ - - /** - * Print available options (high- and low-level) to stdout. This is -@@ -910,12 +931,13 @@ void fuse_lib_help(struct fuse_args *args); - * @return the created FUSE handle - */ - #if FUSE_USE_VERSION == 30 --struct fuse *fuse_new_30(struct fuse_args *args, const struct fuse_operations *op, -- size_t op_size, void *private_data); -+struct fuse *fuse_new_30(struct fuse_args *args, -+ const struct fuse_operations *op, size_t op_size, -+ void *private_data); - #define fuse_new(args, op, size, data) fuse_new_30(args, op, size, data) - #else - struct fuse *fuse_new(struct fuse_args *args, const struct fuse_operations *op, -- size_t op_size, void *private_data); -+ size_t op_size, void *private_data); - #endif - - /** -@@ -940,7 +962,7 @@ void fuse_unmount(struct fuse *f); - /** - * Destroy the FUSE handle. - * -- * NOTE: This function does not unmount the filesystem. If this is -+ * NOTE: This function does not unmount the filesystem. If this is - * needed, call fuse_unmount() before calling this function. - * - * @param f the FUSE handle -@@ -1030,7 +1052,7 @@ int fuse_invalidate_path(struct fuse *f, const char *path); - * Do not call this directly, use fuse_main() - */ - int fuse_main_real(int argc, char *argv[], const struct fuse_operations *op, -- size_t op_size, void *private_data); -+ size_t op_size, void *private_data); - - /** - * Start the cleanup thread when using option "remember". -@@ -1081,89 +1103,87 @@ struct fuse_fs; - */ - - int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf, -- struct fuse_file_info *fi); --int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, -- const char *newpath, unsigned int flags); -+ struct fuse_file_info *fi); -+int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath, const char *newpath, -+ unsigned int flags); - int fuse_fs_unlink(struct fuse_fs *fs, const char *path); - int fuse_fs_rmdir(struct fuse_fs *fs, const char *path); --int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, -- const char *path); -+int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname, const char *path); - int fuse_fs_link(struct fuse_fs *fs, const char *oldpath, const char *newpath); --int fuse_fs_release(struct fuse_fs *fs, const char *path, -- struct fuse_file_info *fi); -+int fuse_fs_release(struct fuse_fs *fs, const char *path, -+ struct fuse_file_info *fi); - int fuse_fs_open(struct fuse_fs *fs, const char *path, -- struct fuse_file_info *fi); -+ struct fuse_file_info *fi); - int fuse_fs_read(struct fuse_fs *fs, const char *path, char *buf, size_t size, -- off_t off, struct fuse_file_info *fi); -+ off_t off, struct fuse_file_info *fi); - int fuse_fs_read_buf(struct fuse_fs *fs, const char *path, -- struct fuse_bufvec **bufp, size_t size, off_t off, -- struct fuse_file_info *fi); -+ struct fuse_bufvec **bufp, size_t size, off_t off, -+ struct fuse_file_info *fi); - int fuse_fs_write(struct fuse_fs *fs, const char *path, const char *buf, -- size_t size, off_t off, struct fuse_file_info *fi); -+ size_t size, off_t off, struct fuse_file_info *fi); - int fuse_fs_write_buf(struct fuse_fs *fs, const char *path, -- struct fuse_bufvec *buf, off_t off, -- struct fuse_file_info *fi); -+ struct fuse_bufvec *buf, off_t off, -+ struct fuse_file_info *fi); - int fuse_fs_fsync(struct fuse_fs *fs, const char *path, int datasync, -- struct fuse_file_info *fi); -+ struct fuse_file_info *fi); - int fuse_fs_flush(struct fuse_fs *fs, const char *path, -- struct fuse_file_info *fi); -+ struct fuse_file_info *fi); - int fuse_fs_statfs(struct fuse_fs *fs, const char *path, struct statvfs *buf); - int fuse_fs_opendir(struct fuse_fs *fs, const char *path, -- struct fuse_file_info *fi); -+ struct fuse_file_info *fi); - int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf, -- fuse_fill_dir_t filler, off_t off, -- struct fuse_file_info *fi, enum fuse_readdir_flags flags); -+ fuse_fill_dir_t filler, off_t off, -+ struct fuse_file_info *fi, enum fuse_readdir_flags flags); - int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync, -- struct fuse_file_info *fi); -+ struct fuse_file_info *fi); - int fuse_fs_releasedir(struct fuse_fs *fs, const char *path, -- struct fuse_file_info *fi); -+ struct fuse_file_info *fi); - int fuse_fs_create(struct fuse_fs *fs, const char *path, mode_t mode, -- struct fuse_file_info *fi); -+ struct fuse_file_info *fi); - int fuse_fs_lock(struct fuse_fs *fs, const char *path, -- struct fuse_file_info *fi, int cmd, struct flock *lock); -+ struct fuse_file_info *fi, int cmd, struct flock *lock); - int fuse_fs_flock(struct fuse_fs *fs, const char *path, -- struct fuse_file_info *fi, int op); -+ struct fuse_file_info *fi, int op); - int fuse_fs_chmod(struct fuse_fs *fs, const char *path, mode_t mode, -- struct fuse_file_info *fi); -+ struct fuse_file_info *fi); - int fuse_fs_chown(struct fuse_fs *fs, const char *path, uid_t uid, gid_t gid, -- struct fuse_file_info *fi); -+ struct fuse_file_info *fi); - int fuse_fs_truncate(struct fuse_fs *fs, const char *path, off_t size, -- struct fuse_file_info *fi); -+ struct fuse_file_info *fi); - int fuse_fs_utimens(struct fuse_fs *fs, const char *path, -- const struct timespec tv[2], struct fuse_file_info *fi); -+ const struct timespec tv[2], struct fuse_file_info *fi); - int fuse_fs_access(struct fuse_fs *fs, const char *path, int mask); - int fuse_fs_readlink(struct fuse_fs *fs, const char *path, char *buf, -- size_t len); -+ size_t len); - int fuse_fs_mknod(struct fuse_fs *fs, const char *path, mode_t mode, -- dev_t rdev); -+ dev_t rdev); - int fuse_fs_mkdir(struct fuse_fs *fs, const char *path, mode_t mode); - int fuse_fs_setxattr(struct fuse_fs *fs, const char *path, const char *name, -- const char *value, size_t size, int flags); -+ const char *value, size_t size, int flags); - int fuse_fs_getxattr(struct fuse_fs *fs, const char *path, const char *name, -- char *value, size_t size); -+ char *value, size_t size); - int fuse_fs_listxattr(struct fuse_fs *fs, const char *path, char *list, -- size_t size); --int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, -- const char *name); -+ size_t size); -+int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name); - int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize, -- uint64_t *idx); -+ uint64_t *idx); - int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, unsigned int cmd, -- void *arg, struct fuse_file_info *fi, unsigned int flags, -- void *data); -+ void *arg, struct fuse_file_info *fi, unsigned int flags, -+ void *data); - int fuse_fs_poll(struct fuse_fs *fs, const char *path, -- struct fuse_file_info *fi, struct fuse_pollhandle *ph, -- unsigned *reventsp); -+ struct fuse_file_info *fi, struct fuse_pollhandle *ph, -+ unsigned *reventsp); - int fuse_fs_fallocate(struct fuse_fs *fs, const char *path, int mode, -- off_t offset, off_t length, struct fuse_file_info *fi); -+ off_t offset, off_t length, struct fuse_file_info *fi); - ssize_t fuse_fs_copy_file_range(struct fuse_fs *fs, const char *path_in, -- struct fuse_file_info *fi_in, off_t off_in, -- const char *path_out, -- struct fuse_file_info *fi_out, off_t off_out, -- size_t len, int flags); -+ struct fuse_file_info *fi_in, off_t off_in, -+ const char *path_out, -+ struct fuse_file_info *fi_out, off_t off_out, -+ size_t len, int flags); - off_t fuse_fs_lseek(struct fuse_fs *fs, const char *path, off_t off, int whence, -- struct fuse_file_info *fi); -+ struct fuse_file_info *fi); - void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn, -- struct fuse_config *cfg); -+ struct fuse_config *cfg); - void fuse_fs_destroy(struct fuse_fs *fs); - - int fuse_notify_poll(struct fuse_pollhandle *ph); -@@ -1182,7 +1202,7 @@ int fuse_notify_poll(struct fuse_pollhandle *ph); - * @return a new filesystem object - */ - struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, -- void *private_data); -+ void *private_data); - - /** - * Factory for creating filesystem objects -@@ -1199,7 +1219,7 @@ struct fuse_fs *fuse_fs_new(const struct fuse_operations *op, size_t op_size, - * @return the new filesystem object - */ - typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args, -- struct fuse_fs *fs[]); -+ struct fuse_fs *fs[]); - /** - * Register filesystem module - * -@@ -1211,7 +1231,7 @@ typedef struct fuse_fs *(*fuse_module_factory_t)(struct fuse_args *args, - * @param factory_ the factory function for this filesystem module - */ - #define FUSE_REGISTER_MODULE(name_, factory_) \ -- fuse_module_factory_t fuse_module_ ## name_ ## _factory = factory_ -+ fuse_module_factory_t fuse_module_##name_##_factory = factory_ - - /** Get session from fuse object */ - struct fuse_session *fuse_get_session(struct fuse *f); -diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h -index bf8f8cc865..bd9bf861f0 100644 ---- a/tools/virtiofsd/fuse_common.h -+++ b/tools/virtiofsd/fuse_common.h -@@ -1,21 +1,23 @@ --/* FUSE: Filesystem in Userspace -- Copyright (C) 2001-2007 Miklos Szeredi -- -- This program can be distributed under the terms of the GNU LGPLv2. -- See the file COPYING.LIB. --*/ -+/* -+ * FUSE: Filesystem in Userspace -+ * Copyright (C) 2001-2007 Miklos Szeredi -+ * -+ * This program can be distributed under the terms of the GNU LGPLv2. -+ * See the file COPYING.LIB. -+ */ - - /** @file */ - - #if !defined(FUSE_H_) && !defined(FUSE_LOWLEVEL_H_) --#error "Never include directly; use or instead." -+#error \ -+ "Never include directly; use or instead." - #endif - - #ifndef FUSE_COMMON_H_ - #define FUSE_COMMON_H_ - --#include "fuse_opt.h" - #include "fuse_log.h" -+#include "fuse_opt.h" - #include - #include - -@@ -25,7 +27,7 @@ - /** Minor version of FUSE library interface */ - #define FUSE_MINOR_VERSION 2 - --#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) -+#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min)) - #define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION) - - /** -@@ -38,67 +40,83 @@ - * descriptors can share a single file handle. - */ - struct fuse_file_info { -- /** Open flags. Available in open() and release() */ -- int flags; -- -- /** In case of a write operation indicates if this was caused -- by a delayed write from the page cache. If so, then the -- context's pid, uid, and gid fields will not be valid, and -- the *fh* value may not match the *fh* value that would -- have been sent with the corresponding individual write -- requests if write caching had been disabled. */ -- unsigned int writepage : 1; -- -- /** Can be filled in by open, to use direct I/O on this file. */ -- unsigned int direct_io : 1; -- -- /** Can be filled in by open. It signals the kernel that any -- currently cached file data (ie., data that the filesystem -- provided the last time the file was open) need not be -- invalidated. Has no effect when set in other contexts (in -- particular it does nothing when set by opendir()). */ -- unsigned int keep_cache : 1; -- -- /** Indicates a flush operation. Set in flush operation, also -- maybe set in highlevel lock operation and lowlevel release -- operation. */ -- unsigned int flush : 1; -- -- /** Can be filled in by open, to indicate that the file is not -- seekable. */ -- unsigned int nonseekable : 1; -- -- /* Indicates that flock locks for this file should be -- released. If set, lock_owner shall contain a valid value. -- May only be set in ->release(). */ -- unsigned int flock_release : 1; -- -- /** Can be filled in by opendir. It signals the kernel to -- enable caching of entries returned by readdir(). Has no -- effect when set in other contexts (in particular it does -- nothing when set by open()). */ -- unsigned int cache_readdir : 1; -- -- /** Padding. Reserved for future use*/ -- unsigned int padding : 25; -- unsigned int padding2 : 32; -- -- /** File handle id. May be filled in by filesystem in create, -- * open, and opendir(). Available in most other file operations on the -- * same file handle. */ -- uint64_t fh; -- -- /** Lock owner id. Available in locking operations and flush */ -- uint64_t lock_owner; -- -- /** Requested poll events. Available in ->poll. Only set on kernels -- which support it. If unsupported, this field is set to zero. */ -- uint32_t poll_events; -+ /** Open flags. Available in open() and release() */ -+ int flags; -+ -+ /* -+ * In case of a write operation indicates if this was caused -+ * by a delayed write from the page cache. If so, then the -+ * context's pid, uid, and gid fields will not be valid, and -+ * the *fh* value may not match the *fh* value that would -+ * have been sent with the corresponding individual write -+ * requests if write caching had been disabled. -+ */ -+ unsigned int writepage:1; -+ -+ /** Can be filled in by open, to use direct I/O on this file. */ -+ unsigned int direct_io:1; -+ -+ /* -+ * Can be filled in by open. It signals the kernel that any -+ * currently cached file data (ie., data that the filesystem -+ * provided the last time the file was open) need not be -+ * invalidated. Has no effect when set in other contexts (in -+ * particular it does nothing when set by opendir()). -+ */ -+ unsigned int keep_cache:1; -+ -+ /* -+ * Indicates a flush operation. Set in flush operation, also -+ * maybe set in highlevel lock operation and lowlevel release -+ * operation. -+ */ -+ unsigned int flush:1; -+ -+ /* -+ * Can be filled in by open, to indicate that the file is not -+ * seekable. -+ */ -+ unsigned int nonseekable:1; -+ -+ /* -+ * Indicates that flock locks for this file should be -+ * released. If set, lock_owner shall contain a valid value. -+ * May only be set in ->release(). -+ */ -+ unsigned int flock_release:1; -+ -+ /* -+ * Can be filled in by opendir. It signals the kernel to -+ * enable caching of entries returned by readdir(). Has no -+ * effect when set in other contexts (in particular it does -+ * nothing when set by open()). -+ */ -+ unsigned int cache_readdir:1; -+ -+ /** Padding. Reserved for future use*/ -+ unsigned int padding:25; -+ unsigned int padding2:32; -+ -+ /* -+ * File handle id. May be filled in by filesystem in create, -+ * open, and opendir(). Available in most other file operations on the -+ * same file handle. -+ */ -+ uint64_t fh; -+ -+ /** Lock owner id. Available in locking operations and flush */ -+ uint64_t lock_owner; -+ -+ /* -+ * Requested poll events. Available in ->poll. Only set on kernels -+ * which support it. If unsupported, this field is set to zero. -+ */ -+ uint32_t poll_events; - }; - --/************************************************************************** -- * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' * -- **************************************************************************/ -+/* -+ * Capability bits for 'fuse_conn_info.capable' and 'fuse_conn_info.want' -+ */ - - /** - * Indicates that the filesystem supports asynchronous read requests. -@@ -110,7 +128,7 @@ struct fuse_file_info { - * - * This feature is enabled by default when supported by the kernel. - */ --#define FUSE_CAP_ASYNC_READ (1 << 0) -+#define FUSE_CAP_ASYNC_READ (1 << 0) - - /** - * Indicates that the filesystem supports "remote" locking. -@@ -118,7 +136,7 @@ struct fuse_file_info { - * This feature is enabled by default when supported by the kernel, - * and if getlk() and setlk() handlers are implemented. - */ --#define FUSE_CAP_POSIX_LOCKS (1 << 1) -+#define FUSE_CAP_POSIX_LOCKS (1 << 1) - - /** - * Indicates that the filesystem supports the O_TRUNC open flag. If -@@ -127,14 +145,14 @@ struct fuse_file_info { - * - * This feature is enabled by default when supported by the kernel. - */ --#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3) -+#define FUSE_CAP_ATOMIC_O_TRUNC (1 << 3) - - /** - * Indicates that the filesystem supports lookups of "." and "..". - * - * This feature is disabled by default. - */ --#define FUSE_CAP_EXPORT_SUPPORT (1 << 4) -+#define FUSE_CAP_EXPORT_SUPPORT (1 << 4) - - /** - * Indicates that the kernel should not apply the umask to the -@@ -142,7 +160,7 @@ struct fuse_file_info { - * - * This feature is disabled by default. - */ --#define FUSE_CAP_DONT_MASK (1 << 6) -+#define FUSE_CAP_DONT_MASK (1 << 6) - - /** - * Indicates that libfuse should try to use splice() when writing to -@@ -150,7 +168,7 @@ struct fuse_file_info { - * - * This feature is disabled by default. - */ --#define FUSE_CAP_SPLICE_WRITE (1 << 7) -+#define FUSE_CAP_SPLICE_WRITE (1 << 7) - - /** - * Indicates that libfuse should try to move pages instead of copying when -@@ -158,7 +176,7 @@ struct fuse_file_info { - * - * This feature is disabled by default. - */ --#define FUSE_CAP_SPLICE_MOVE (1 << 8) -+#define FUSE_CAP_SPLICE_MOVE (1 << 8) - - /** - * Indicates that libfuse should try to use splice() when reading from -@@ -167,7 +185,7 @@ struct fuse_file_info { - * This feature is enabled by default when supported by the kernel and - * if the filesystem implements a write_buf() handler. - */ --#define FUSE_CAP_SPLICE_READ (1 << 9) -+#define FUSE_CAP_SPLICE_READ (1 << 9) - - /** - * If set, the calls to flock(2) will be emulated using POSIX locks and must -@@ -180,14 +198,14 @@ struct fuse_file_info { - * This feature is enabled by default when supported by the kernel and - * if the filesystem implements a flock() handler. - */ --#define FUSE_CAP_FLOCK_LOCKS (1 << 10) -+#define FUSE_CAP_FLOCK_LOCKS (1 << 10) - - /** - * Indicates that the filesystem supports ioctl's on directories. - * - * This feature is enabled by default when supported by the kernel. - */ --#define FUSE_CAP_IOCTL_DIR (1 << 11) -+#define FUSE_CAP_IOCTL_DIR (1 << 11) - - /** - * Traditionally, while a file is open the FUSE kernel module only -@@ -209,7 +227,7 @@ struct fuse_file_info { - * - * This feature is enabled by default when supported by the kernel. - */ --#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12) -+#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12) - - /** - * Indicates that the filesystem supports readdirplus. -@@ -217,7 +235,7 @@ struct fuse_file_info { - * This feature is enabled by default when supported by the kernel and if the - * filesystem implements a readdirplus() handler. - */ --#define FUSE_CAP_READDIRPLUS (1 << 13) -+#define FUSE_CAP_READDIRPLUS (1 << 13) - - /** - * Indicates that the filesystem supports adaptive readdirplus. -@@ -245,7 +263,7 @@ struct fuse_file_info { - * if the filesystem implements both a readdirplus() and a readdir() - * handler. - */ --#define FUSE_CAP_READDIRPLUS_AUTO (1 << 14) -+#define FUSE_CAP_READDIRPLUS_AUTO (1 << 14) - - /** - * Indicates that the filesystem supports asynchronous direct I/O submission. -@@ -256,7 +274,7 @@ struct fuse_file_info { - * - * This feature is enabled by default when supported by the kernel. - */ --#define FUSE_CAP_ASYNC_DIO (1 << 15) -+#define FUSE_CAP_ASYNC_DIO (1 << 15) - - /** - * Indicates that writeback caching should be enabled. This means that -@@ -265,7 +283,7 @@ struct fuse_file_info { - * - * This feature is disabled by default. - */ --#define FUSE_CAP_WRITEBACK_CACHE (1 << 16) -+#define FUSE_CAP_WRITEBACK_CACHE (1 << 16) - - /** - * Indicates support for zero-message opens. If this flag is set in -@@ -278,7 +296,7 @@ struct fuse_file_info { - * Setting (or unsetting) this flag in the `want` field has *no - * effect*. - */ --#define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17) -+#define FUSE_CAP_NO_OPEN_SUPPORT (1 << 17) - - /** - * Indicates support for parallel directory operations. If this flag -@@ -288,7 +306,7 @@ struct fuse_file_info { - * - * This feature is enabled by default when supported by the kernel. - */ --#define FUSE_CAP_PARALLEL_DIROPS (1 << 18) -+#define FUSE_CAP_PARALLEL_DIROPS (1 << 18) - - /** - * Indicates support for POSIX ACLs. -@@ -307,7 +325,7 @@ struct fuse_file_info { - * - * This feature is disabled by default. - */ --#define FUSE_CAP_POSIX_ACL (1 << 19) -+#define FUSE_CAP_POSIX_ACL (1 << 19) - - /** - * Indicates that the filesystem is responsible for unsetting -@@ -316,7 +334,7 @@ struct fuse_file_info { - * - * This feature is enabled by default when supported by the kernel. - */ --#define FUSE_CAP_HANDLE_KILLPRIV (1 << 20) -+#define FUSE_CAP_HANDLE_KILLPRIV (1 << 20) - - /** - * Indicates support for zero-message opendirs. If this flag is set in -@@ -328,7 +346,7 @@ struct fuse_file_info { - * - * Setting (or unsetting) this flag in the `want` field has *no effect*. - */ --#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24) -+#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24) - - /** - * Ioctl flags -@@ -340,12 +358,12 @@ struct fuse_file_info { - * - * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs - */ --#define FUSE_IOCTL_COMPAT (1 << 0) --#define FUSE_IOCTL_UNRESTRICTED (1 << 1) --#define FUSE_IOCTL_RETRY (1 << 2) --#define FUSE_IOCTL_DIR (1 << 4) -+#define FUSE_IOCTL_COMPAT (1 << 0) -+#define FUSE_IOCTL_UNRESTRICTED (1 << 1) -+#define FUSE_IOCTL_RETRY (1 << 2) -+#define FUSE_IOCTL_DIR (1 << 4) - --#define FUSE_IOCTL_MAX_IOV 256 -+#define FUSE_IOCTL_MAX_IOV 256 - - /** - * Connection information, passed to the ->init() method -@@ -355,114 +373,114 @@ struct fuse_file_info { - * value must usually be smaller than the indicated value. - */ - struct fuse_conn_info { -- /** -- * Major version of the protocol (read-only) -- */ -- unsigned proto_major; -- -- /** -- * Minor version of the protocol (read-only) -- */ -- unsigned proto_minor; -- -- /** -- * Maximum size of the write buffer -- */ -- unsigned max_write; -- -- /** -- * Maximum size of read requests. A value of zero indicates no -- * limit. However, even if the filesystem does not specify a -- * limit, the maximum size of read requests will still be -- * limited by the kernel. -- * -- * NOTE: For the time being, the maximum size of read requests -- * must be set both here *and* passed to fuse_session_new() -- * using the ``-o max_read=`` mount option. At some point -- * in the future, specifying the mount option will no longer -- * be necessary. -- */ -- unsigned max_read; -- -- /** -- * Maximum readahead -- */ -- unsigned max_readahead; -- -- /** -- * Capability flags that the kernel supports (read-only) -- */ -- unsigned capable; -- -- /** -- * Capability flags that the filesystem wants to enable. -- * -- * libfuse attempts to initialize this field with -- * reasonable default values before calling the init() handler. -- */ -- unsigned want; -- -- /** -- * Maximum number of pending "background" requests. A -- * background request is any type of request for which the -- * total number is not limited by other means. As of kernel -- * 4.8, only two types of requests fall into this category: -- * -- * 1. Read-ahead requests -- * 2. Asynchronous direct I/O requests -- * -- * Read-ahead requests are generated (if max_readahead is -- * non-zero) by the kernel to preemptively fill its caches -- * when it anticipates that userspace will soon read more -- * data. -- * -- * Asynchronous direct I/O requests are generated if -- * FUSE_CAP_ASYNC_DIO is enabled and userspace submits a large -- * direct I/O request. In this case the kernel will internally -- * split it up into multiple smaller requests and submit them -- * to the filesystem concurrently. -- * -- * Note that the following requests are *not* background -- * requests: writeback requests (limited by the kernel's -- * flusher algorithm), regular (i.e., synchronous and -- * buffered) userspace read/write requests (limited to one per -- * thread), asynchronous read requests (Linux's io_submit(2) -- * call actually blocks, so these are also limited to one per -- * thread). -- */ -- unsigned max_background; -- -- /** -- * Kernel congestion threshold parameter. If the number of pending -- * background requests exceeds this number, the FUSE kernel module will -- * mark the filesystem as "congested". This instructs the kernel to -- * expect that queued requests will take some time to complete, and to -- * adjust its algorithms accordingly (e.g. by putting a waiting thread -- * to sleep instead of using a busy-loop). -- */ -- unsigned congestion_threshold; -- -- /** -- * When FUSE_CAP_WRITEBACK_CACHE is enabled, the kernel is responsible -- * for updating mtime and ctime when write requests are received. The -- * updated values are passed to the filesystem with setattr() requests. -- * However, if the filesystem does not support the full resolution of -- * the kernel timestamps (nanoseconds), the mtime and ctime values used -- * by kernel and filesystem will differ (and result in an apparent -- * change of times after a cache flush). -- * -- * To prevent this problem, this variable can be used to inform the -- * kernel about the timestamp granularity supported by the file-system. -- * The value should be power of 10. The default is 1, i.e. full -- * nano-second resolution. Filesystems supporting only second resolution -- * should set this to 1000000000. -- */ -- unsigned time_gran; -- -- /** -- * For future use. -- */ -- unsigned reserved[22]; -+ /** -+ * Major version of the protocol (read-only) -+ */ -+ unsigned proto_major; -+ -+ /** -+ * Minor version of the protocol (read-only) -+ */ -+ unsigned proto_minor; -+ -+ /** -+ * Maximum size of the write buffer -+ */ -+ unsigned max_write; -+ -+ /** -+ * Maximum size of read requests. A value of zero indicates no -+ * limit. However, even if the filesystem does not specify a -+ * limit, the maximum size of read requests will still be -+ * limited by the kernel. -+ * -+ * NOTE: For the time being, the maximum size of read requests -+ * must be set both here *and* passed to fuse_session_new() -+ * using the ``-o max_read=`` mount option. At some point -+ * in the future, specifying the mount option will no longer -+ * be necessary. -+ */ -+ unsigned max_read; -+ -+ /** -+ * Maximum readahead -+ */ -+ unsigned max_readahead; -+ -+ /** -+ * Capability flags that the kernel supports (read-only) -+ */ -+ unsigned capable; -+ -+ /** -+ * Capability flags that the filesystem wants to enable. -+ * -+ * libfuse attempts to initialize this field with -+ * reasonable default values before calling the init() handler. -+ */ -+ unsigned want; -+ -+ /** -+ * Maximum number of pending "background" requests. A -+ * background request is any type of request for which the -+ * total number is not limited by other means. As of kernel -+ * 4.8, only two types of requests fall into this category: -+ * -+ * 1. Read-ahead requests -+ * 2. Asynchronous direct I/O requests -+ * -+ * Read-ahead requests are generated (if max_readahead is -+ * non-zero) by the kernel to preemptively fill its caches -+ * when it anticipates that userspace will soon read more -+ * data. -+ * -+ * Asynchronous direct I/O requests are generated if -+ * FUSE_CAP_ASYNC_DIO is enabled and userspace submits a large -+ * direct I/O request. In this case the kernel will internally -+ * split it up into multiple smaller requests and submit them -+ * to the filesystem concurrently. -+ * -+ * Note that the following requests are *not* background -+ * requests: writeback requests (limited by the kernel's -+ * flusher algorithm), regular (i.e., synchronous and -+ * buffered) userspace read/write requests (limited to one per -+ * thread), asynchronous read requests (Linux's io_submit(2) -+ * call actually blocks, so these are also limited to one per -+ * thread). -+ */ -+ unsigned max_background; -+ -+ /** -+ * Kernel congestion threshold parameter. If the number of pending -+ * background requests exceeds this number, the FUSE kernel module will -+ * mark the filesystem as "congested". This instructs the kernel to -+ * expect that queued requests will take some time to complete, and to -+ * adjust its algorithms accordingly (e.g. by putting a waiting thread -+ * to sleep instead of using a busy-loop). -+ */ -+ unsigned congestion_threshold; -+ -+ /** -+ * When FUSE_CAP_WRITEBACK_CACHE is enabled, the kernel is responsible -+ * for updating mtime and ctime when write requests are received. The -+ * updated values are passed to the filesystem with setattr() requests. -+ * However, if the filesystem does not support the full resolution of -+ * the kernel timestamps (nanoseconds), the mtime and ctime values used -+ * by kernel and filesystem will differ (and result in an apparent -+ * change of times after a cache flush). -+ * -+ * To prevent this problem, this variable can be used to inform the -+ * kernel about the timestamp granularity supported by the file-system. -+ * The value should be power of 10. The default is 1, i.e. full -+ * nano-second resolution. Filesystems supporting only second resolution -+ * should set this to 1000000000. -+ */ -+ unsigned time_gran; -+ -+ /** -+ * For future use. -+ */ -+ unsigned reserved[22]; - }; - - struct fuse_session; -@@ -489,21 +507,20 @@ struct fuse_conn_info_opts; - * -o async_read sets FUSE_CAP_ASYNC_READ in conn->want - * -o sync_read unsets FUSE_CAP_ASYNC_READ in conn->want - * -o atomic_o_trunc sets FUSE_CAP_ATOMIC_O_TRUNC in conn->want -- * -o no_remote_lock Equivalent to -o no_remote_flock,no_remote_posix_lock -- * -o no_remote_flock Unsets FUSE_CAP_FLOCK_LOCKS in conn->want -- * -o no_remote_posix_lock Unsets FUSE_CAP_POSIX_LOCKS in conn->want -- * -o [no_]splice_write (un-)sets FUSE_CAP_SPLICE_WRITE in conn->want -- * -o [no_]splice_move (un-)sets FUSE_CAP_SPLICE_MOVE in conn->want -- * -o [no_]splice_read (un-)sets FUSE_CAP_SPLICE_READ in conn->want -- * -o [no_]auto_inval_data (un-)sets FUSE_CAP_AUTO_INVAL_DATA in conn->want -- * -o readdirplus=no unsets FUSE_CAP_READDIRPLUS in conn->want -- * -o readdirplus=yes sets FUSE_CAP_READDIRPLUS and unsets -- * FUSE_CAP_READDIRPLUS_AUTO in conn->want -- * -o readdirplus=auto sets FUSE_CAP_READDIRPLUS and -- * FUSE_CAP_READDIRPLUS_AUTO in conn->want -- * -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in conn->want -- * -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in conn->want -- * -o time_gran=N sets conn->time_gran -+ * -o no_remote_lock Equivalent to -o -+ *no_remote_flock,no_remote_posix_lock -o no_remote_flock Unsets -+ *FUSE_CAP_FLOCK_LOCKS in conn->want -o no_remote_posix_lock Unsets -+ *FUSE_CAP_POSIX_LOCKS in conn->want -o [no_]splice_write (un-)sets -+ *FUSE_CAP_SPLICE_WRITE in conn->want -o [no_]splice_move (un-)sets -+ *FUSE_CAP_SPLICE_MOVE in conn->want -o [no_]splice_read (un-)sets -+ *FUSE_CAP_SPLICE_READ in conn->want -o [no_]auto_inval_data (un-)sets -+ *FUSE_CAP_AUTO_INVAL_DATA in conn->want -o readdirplus=no unsets -+ *FUSE_CAP_READDIRPLUS in conn->want -o readdirplus=yes sets -+ *FUSE_CAP_READDIRPLUS and unsets FUSE_CAP_READDIRPLUS_AUTO in conn->want -o -+ *readdirplus=auto sets FUSE_CAP_READDIRPLUS and FUSE_CAP_READDIRPLUS_AUTO -+ *in conn->want -o [no_]async_dio (un-)sets FUSE_CAP_ASYNC_DIO in -+ *conn->want -o [no_]writeback_cache (un-)sets FUSE_CAP_WRITEBACK_CACHE in -+ *conn->want -o time_gran=N sets conn->time_gran - * - * Known options will be removed from *args*, unknown options will be - * passed through unchanged. -@@ -511,7 +528,7 @@ struct fuse_conn_info_opts; - * @param args argument vector (input+output) - * @return parsed options - **/ --struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args); -+struct fuse_conn_info_opts *fuse_parse_conn_info_opts(struct fuse_args *args); - - /** - * This function applies the (parsed) parameters in *opts* to the -@@ -521,7 +538,7 @@ struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args); - * option has been explicitly set. - */ - void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, -- struct fuse_conn_info *conn); -+ struct fuse_conn_info *conn); - - /** - * Go into the background -@@ -552,81 +569,81 @@ const char *fuse_pkgversion(void); - */ - void fuse_pollhandle_destroy(struct fuse_pollhandle *ph); - --/* ----------------------------------------------------------- * -- * Data buffer * -- * ----------------------------------------------------------- */ -+/* -+ * Data buffer -+ */ - - /** - * Buffer flags - */ - enum fuse_buf_flags { -- /** -- * Buffer contains a file descriptor -- * -- * If this flag is set, the .fd field is valid, otherwise the -- * .mem fields is valid. -- */ -- FUSE_BUF_IS_FD = (1 << 1), -- -- /** -- * Seek on the file descriptor -- * -- * If this flag is set then the .pos field is valid and is -- * used to seek to the given offset before performing -- * operation on file descriptor. -- */ -- FUSE_BUF_FD_SEEK = (1 << 2), -- -- /** -- * Retry operation on file descriptor -- * -- * If this flag is set then retry operation on file descriptor -- * until .size bytes have been copied or an error or EOF is -- * detected. -- */ -- FUSE_BUF_FD_RETRY = (1 << 3), -+ /** -+ * Buffer contains a file descriptor -+ * -+ * If this flag is set, the .fd field is valid, otherwise the -+ * .mem fields is valid. -+ */ -+ FUSE_BUF_IS_FD = (1 << 1), -+ -+ /** -+ * Seek on the file descriptor -+ * -+ * If this flag is set then the .pos field is valid and is -+ * used to seek to the given offset before performing -+ * operation on file descriptor. -+ */ -+ FUSE_BUF_FD_SEEK = (1 << 2), -+ -+ /** -+ * Retry operation on file descriptor -+ * -+ * If this flag is set then retry operation on file descriptor -+ * until .size bytes have been copied or an error or EOF is -+ * detected. -+ */ -+ FUSE_BUF_FD_RETRY = (1 << 3), - }; - - /** - * Buffer copy flags - */ - enum fuse_buf_copy_flags { -- /** -- * Don't use splice(2) -- * -- * Always fall back to using read and write instead of -- * splice(2) to copy data from one file descriptor to another. -- * -- * If this flag is not set, then only fall back if splice is -- * unavailable. -- */ -- FUSE_BUF_NO_SPLICE = (1 << 1), -- -- /** -- * Force splice -- * -- * Always use splice(2) to copy data from one file descriptor -- * to another. If splice is not available, return -EINVAL. -- */ -- FUSE_BUF_FORCE_SPLICE = (1 << 2), -- -- /** -- * Try to move data with splice. -- * -- * If splice is used, try to move pages from the source to the -- * destination instead of copying. See documentation of -- * SPLICE_F_MOVE in splice(2) man page. -- */ -- FUSE_BUF_SPLICE_MOVE = (1 << 3), -- -- /** -- * Don't block on the pipe when copying data with splice -- * -- * Makes the operations on the pipe non-blocking (if the pipe -- * is full or empty). See SPLICE_F_NONBLOCK in the splice(2) -- * man page. -- */ -- FUSE_BUF_SPLICE_NONBLOCK= (1 << 4), -+ /** -+ * Don't use splice(2) -+ * -+ * Always fall back to using read and write instead of -+ * splice(2) to copy data from one file descriptor to another. -+ * -+ * If this flag is not set, then only fall back if splice is -+ * unavailable. -+ */ -+ FUSE_BUF_NO_SPLICE = (1 << 1), -+ -+ /** -+ * Force splice -+ * -+ * Always use splice(2) to copy data from one file descriptor -+ * to another. If splice is not available, return -EINVAL. -+ */ -+ FUSE_BUF_FORCE_SPLICE = (1 << 2), -+ -+ /** -+ * Try to move data with splice. -+ * -+ * If splice is used, try to move pages from the source to the -+ * destination instead of copying. See documentation of -+ * SPLICE_F_MOVE in splice(2) man page. -+ */ -+ FUSE_BUF_SPLICE_MOVE = (1 << 3), -+ -+ /** -+ * Don't block on the pipe when copying data with splice -+ * -+ * Makes the operations on the pipe non-blocking (if the pipe -+ * is full or empty). See SPLICE_F_NONBLOCK in the splice(2) -+ * man page. -+ */ -+ FUSE_BUF_SPLICE_NONBLOCK = (1 << 4), - }; - - /** -@@ -636,36 +653,36 @@ enum fuse_buf_copy_flags { - * be supplied as a memory pointer or as a file descriptor - */ - struct fuse_buf { -- /** -- * Size of data in bytes -- */ -- size_t size; -- -- /** -- * Buffer flags -- */ -- enum fuse_buf_flags flags; -- -- /** -- * Memory pointer -- * -- * Used unless FUSE_BUF_IS_FD flag is set. -- */ -- void *mem; -- -- /** -- * File descriptor -- * -- * Used if FUSE_BUF_IS_FD flag is set. -- */ -- int fd; -- -- /** -- * File position -- * -- * Used if FUSE_BUF_FD_SEEK flag is set. -- */ -- off_t pos; -+ /** -+ * Size of data in bytes -+ */ -+ size_t size; -+ -+ /** -+ * Buffer flags -+ */ -+ enum fuse_buf_flags flags; -+ -+ /** -+ * Memory pointer -+ * -+ * Used unless FUSE_BUF_IS_FD flag is set. -+ */ -+ void *mem; -+ -+ /** -+ * File descriptor -+ * -+ * Used if FUSE_BUF_IS_FD flag is set. -+ */ -+ int fd; -+ -+ /** -+ * File position -+ * -+ * Used if FUSE_BUF_FD_SEEK flag is set. -+ */ -+ off_t pos; - }; - - /** -@@ -677,41 +694,39 @@ struct fuse_buf { - * Allocate dynamically to add more than one buffer. - */ - struct fuse_bufvec { -- /** -- * Number of buffers in the array -- */ -- size_t count; -- -- /** -- * Index of current buffer within the array -- */ -- size_t idx; -- -- /** -- * Current offset within the current buffer -- */ -- size_t off; -- -- /** -- * Array of buffers -- */ -- struct fuse_buf buf[1]; -+ /** -+ * Number of buffers in the array -+ */ -+ size_t count; -+ -+ /** -+ * Index of current buffer within the array -+ */ -+ size_t idx; -+ -+ /** -+ * Current offset within the current buffer -+ */ -+ size_t off; -+ -+ /** -+ * Array of buffers -+ */ -+ struct fuse_buf buf[1]; - }; - - /* Initialize bufvec with a single buffer of given size */ --#define FUSE_BUFVEC_INIT(size__) \ -- ((struct fuse_bufvec) { \ -- /* .count= */ 1, \ -- /* .idx = */ 0, \ -- /* .off = */ 0, \ -- /* .buf = */ { /* [0] = */ { \ -- /* .size = */ (size__), \ -- /* .flags = */ (enum fuse_buf_flags) 0, \ -- /* .mem = */ NULL, \ -- /* .fd = */ -1, \ -- /* .pos = */ 0, \ -- } } \ -- } ) -+#define FUSE_BUFVEC_INIT(size__) \ -+ ((struct fuse_bufvec){ /* .count= */ 1, \ -+ /* .idx = */ 0, \ -+ /* .off = */ 0, /* .buf = */ \ -+ { /* [0] = */ { \ -+ /* .size = */ (size__), \ -+ /* .flags = */ (enum fuse_buf_flags)0, \ -+ /* .mem = */ NULL, \ -+ /* .fd = */ -1, \ -+ /* .pos = */ 0, \ -+ } } }) - - /** - * Get total size of data in a fuse buffer vector -@@ -730,16 +745,16 @@ size_t fuse_buf_size(const struct fuse_bufvec *bufv); - * @return actual number of bytes copied or -errno on error - */ - ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, -- enum fuse_buf_copy_flags flags); -+ enum fuse_buf_copy_flags flags); - --/* ----------------------------------------------------------- * -- * Signal handling * -- * ----------------------------------------------------------- */ -+/* -+ * Signal handling -+ */ - - /** - * Exit session on HUP, TERM and INT signals and ignore PIPE signal - * -- * Stores session in a global variable. May only be called once per -+ * Stores session in a global variable. May only be called once per - * process until fuse_remove_signal_handlers() is called. - * - * Once either of the POSIX signals arrives, the signal handler calls -@@ -766,12 +781,12 @@ int fuse_set_signal_handlers(struct fuse_session *se); - */ - void fuse_remove_signal_handlers(struct fuse_session *se); - --/* ----------------------------------------------------------- * -- * Compatibility stuff * -- * ----------------------------------------------------------- */ -+/* -+ * Compatibility stuff -+ */ - - #if !defined(FUSE_USE_VERSION) || FUSE_USE_VERSION < 30 --# error only API version 30 or greater is supported -+#error only API version 30 or greater is supported - #endif - - -@@ -781,11 +796,14 @@ void fuse_remove_signal_handlers(struct fuse_session *se); - * On 32bit systems please add -D_FILE_OFFSET_BITS=64 to your compile flags! - */ - --#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus -+#if defined(__GNUC__) && \ -+ (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && \ -+ !defined __cplusplus - _Static_assert(sizeof(off_t) == 8, "fuse: off_t must be 64bit"); - #else --struct _fuse_off_t_must_be_64bit_dummy_struct \ -- { unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); }; -+struct _fuse_off_t_must_be_64bit_dummy_struct { -+ unsigned _fuse_off_t_must_be_64bit:((sizeof(off_t) == 8) ? 1 : -1); -+}; - #endif - - #endif /* FUSE_COMMON_H_ */ -diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h -index b39522e3ca..e63cb58388 100644 ---- a/tools/virtiofsd/fuse_i.h -+++ b/tools/virtiofsd/fuse_i.h -@@ -1,71 +1,71 @@ - /* -- FUSE: Filesystem in Userspace -- Copyright (C) 2001-2007 Miklos Szeredi -- -- This program can be distributed under the terms of the GNU LGPLv2. -- See the file COPYING.LIB --*/ -+ * FUSE: Filesystem in Userspace -+ * Copyright (C) 2001-2007 Miklos Szeredi -+ * -+ * This program can be distributed under the terms of the GNU LGPLv2. -+ * See the file COPYING.LIB -+ */ - - #include "fuse.h" - #include "fuse_lowlevel.h" - - struct fuse_req { -- struct fuse_session *se; -- uint64_t unique; -- int ctr; -- pthread_mutex_t lock; -- struct fuse_ctx ctx; -- struct fuse_chan *ch; -- int interrupted; -- unsigned int ioctl_64bit : 1; -- union { -- struct { -- uint64_t unique; -- } i; -- struct { -- fuse_interrupt_func_t func; -- void *data; -- } ni; -- } u; -- struct fuse_req *next; -- struct fuse_req *prev; -+ struct fuse_session *se; -+ uint64_t unique; -+ int ctr; -+ pthread_mutex_t lock; -+ struct fuse_ctx ctx; -+ struct fuse_chan *ch; -+ int interrupted; -+ unsigned int ioctl_64bit:1; -+ union { -+ struct { -+ uint64_t unique; -+ } i; -+ struct { -+ fuse_interrupt_func_t func; -+ void *data; -+ } ni; -+ } u; -+ struct fuse_req *next; -+ struct fuse_req *prev; - }; - - struct fuse_notify_req { -- uint64_t unique; -- void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t, -- const void *, const struct fuse_buf *); -- struct fuse_notify_req *next; -- struct fuse_notify_req *prev; -+ uint64_t unique; -+ void (*reply)(struct fuse_notify_req *, fuse_req_t, fuse_ino_t, -+ const void *, const struct fuse_buf *); -+ struct fuse_notify_req *next; -+ struct fuse_notify_req *prev; - }; - - struct fuse_session { -- char *mountpoint; -- volatile int exited; -- int fd; -- int debug; -- int deny_others; -- struct fuse_lowlevel_ops op; -- int got_init; -- struct cuse_data *cuse_data; -- void *userdata; -- uid_t owner; -- struct fuse_conn_info conn; -- struct fuse_req list; -- struct fuse_req interrupts; -- pthread_mutex_t lock; -- int got_destroy; -- int broken_splice_nonblock; -- uint64_t notify_ctr; -- struct fuse_notify_req notify_list; -- size_t bufsize; -- int error; -+ char *mountpoint; -+ volatile int exited; -+ int fd; -+ int debug; -+ int deny_others; -+ struct fuse_lowlevel_ops op; -+ int got_init; -+ struct cuse_data *cuse_data; -+ void *userdata; -+ uid_t owner; -+ struct fuse_conn_info conn; -+ struct fuse_req list; -+ struct fuse_req interrupts; -+ pthread_mutex_t lock; -+ int got_destroy; -+ int broken_splice_nonblock; -+ uint64_t notify_ctr; -+ struct fuse_notify_req notify_list; -+ size_t bufsize; -+ int error; - }; - - struct fuse_chan { -- pthread_mutex_t lock; -- int ctr; -- int fd; -+ pthread_mutex_t lock; -+ int ctr; -+ int fd; - }; - - /** -@@ -76,19 +76,20 @@ struct fuse_chan { - * - */ - struct fuse_module { -- char *name; -- fuse_module_factory_t factory; -- struct fuse_module *next; -- struct fusemod_so *so; -- int ctr; -+ char *name; -+ fuse_module_factory_t factory; -+ struct fuse_module *next; -+ struct fusemod_so *so; -+ int ctr; - }; - - int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, -- int count); -+ int count); - void fuse_free_req(fuse_req_t req); - - void fuse_session_process_buf_int(struct fuse_session *se, -- const struct fuse_buf *buf, struct fuse_chan *ch); -+ const struct fuse_buf *buf, -+ struct fuse_chan *ch); - - - #define FUSE_MAX_MAX_PAGES 256 -diff --git a/tools/virtiofsd/fuse_log.c b/tools/virtiofsd/fuse_log.c -index 0d268ab014..11345f9ec8 100644 ---- a/tools/virtiofsd/fuse_log.c -+++ b/tools/virtiofsd/fuse_log.c -@@ -1,40 +1,40 @@ - /* -- FUSE: Filesystem in Userspace -- Copyright (C) 2019 Red Hat, Inc. -- -- Logging API. -- -- This program can be distributed under the terms of the GNU LGPLv2. -- See the file COPYING.LIB --*/ -+ * FUSE: Filesystem in Userspace -+ * Copyright (C) 2019 Red Hat, Inc. -+ * -+ * Logging API. -+ * -+ * This program can be distributed under the terms of the GNU LGPLv2. -+ * See the file COPYING.LIB -+ */ - - #include "fuse_log.h" - - #include - #include - --static void default_log_func( -- __attribute__(( unused )) enum fuse_log_level level, -- const char *fmt, va_list ap) -+static void default_log_func(__attribute__((unused)) enum fuse_log_level level, -+ const char *fmt, va_list ap) - { -- vfprintf(stderr, fmt, ap); -+ vfprintf(stderr, fmt, ap); - } - - static fuse_log_func_t log_func = default_log_func; - - void fuse_set_log_func(fuse_log_func_t func) - { -- if (!func) -- func = default_log_func; -+ if (!func) { -+ func = default_log_func; -+ } - -- log_func = func; -+ log_func = func; - } - - void fuse_log(enum fuse_log_level level, const char *fmt, ...) - { -- va_list ap; -+ va_list ap; - -- va_start(ap, fmt); -- log_func(level, fmt, ap); -- va_end(ap); -+ va_start(ap, fmt); -+ log_func(level, fmt, ap); -+ va_end(ap); - } -diff --git a/tools/virtiofsd/fuse_log.h b/tools/virtiofsd/fuse_log.h -index 0af700da6b..bf6c11ff11 100644 ---- a/tools/virtiofsd/fuse_log.h -+++ b/tools/virtiofsd/fuse_log.h -@@ -1,10 +1,10 @@ - /* -- FUSE: Filesystem in Userspace -- Copyright (C) 2019 Red Hat, Inc. -- -- This program can be distributed under the terms of the GNU LGPLv2. -- See the file COPYING.LIB. --*/ -+ * FUSE: Filesystem in Userspace -+ * Copyright (C) 2019 Red Hat, Inc. -+ * -+ * This program can be distributed under the terms of the GNU LGPLv2. -+ * See the file COPYING.LIB. -+ */ - - #ifndef FUSE_LOG_H_ - #define FUSE_LOG_H_ -@@ -22,14 +22,14 @@ - * These levels correspond to syslog(2) log levels since they are widely used. - */ - enum fuse_log_level { -- FUSE_LOG_EMERG, -- FUSE_LOG_ALERT, -- FUSE_LOG_CRIT, -- FUSE_LOG_ERR, -- FUSE_LOG_WARNING, -- FUSE_LOG_NOTICE, -- FUSE_LOG_INFO, -- FUSE_LOG_DEBUG -+ FUSE_LOG_EMERG, -+ FUSE_LOG_ALERT, -+ FUSE_LOG_CRIT, -+ FUSE_LOG_ERR, -+ FUSE_LOG_WARNING, -+ FUSE_LOG_NOTICE, -+ FUSE_LOG_INFO, -+ FUSE_LOG_DEBUG - }; - - /** -@@ -45,8 +45,8 @@ enum fuse_log_level { - * @param fmt sprintf-style format string including newline - * @param ap format string arguments - */ --typedef void (*fuse_log_func_t)(enum fuse_log_level level, -- const char *fmt, va_list ap); -+typedef void (*fuse_log_func_t)(enum fuse_log_level level, const char *fmt, -+ va_list ap); - - /** - * Install a custom log handler function. -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index e6fa247924..5c9cb52f2a 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -1,2380 +1,2515 @@ - /* -- FUSE: Filesystem in Userspace -- Copyright (C) 2001-2007 Miklos Szeredi -- -- Implementation of (most of) the low-level FUSE API. The session loop -- functions are implemented in separate files. -- -- This program can be distributed under the terms of the GNU LGPLv2. -- See the file COPYING.LIB --*/ -+ * FUSE: Filesystem in Userspace -+ * Copyright (C) 2001-2007 Miklos Szeredi -+ * -+ * Implementation of (most of) the low-level FUSE API. The session loop -+ * functions are implemented in separate files. -+ * -+ * This program can be distributed under the terms of the GNU LGPLv2. -+ * See the file COPYING.LIB -+ */ - - #define _GNU_SOURCE - - #include "config.h" - #include "fuse_i.h" - #include "fuse_kernel.h" --#include "fuse_opt.h" - #include "fuse_misc.h" -+#include "fuse_opt.h" - -+#include -+#include -+#include -+#include - #include - #include --#include - #include --#include --#include --#include --#include - #include -- -+#include - - - #define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) - #define OFFSET_MAX 0x7fffffffffffffffLL - --#define container_of(ptr, type, member) ({ \ -- const typeof( ((type *)0)->member ) *__mptr = (ptr); \ -- (type *)( (char *)__mptr - offsetof(type,member) );}) -+#define container_of(ptr, type, member) \ -+ ({ \ -+ const typeof(((type *)0)->member) *__mptr = (ptr); \ -+ (type *)((char *)__mptr - offsetof(type, member)); \ -+ }) - - struct fuse_pollhandle { -- uint64_t kh; -- struct fuse_session *se; -+ uint64_t kh; -+ struct fuse_session *se; - }; - - static size_t pagesize; - - static __attribute__((constructor)) void fuse_ll_init_pagesize(void) - { -- pagesize = getpagesize(); -+ pagesize = getpagesize(); - } - - static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr) - { -- attr->ino = stbuf->st_ino; -- attr->mode = stbuf->st_mode; -- attr->nlink = stbuf->st_nlink; -- attr->uid = stbuf->st_uid; -- attr->gid = stbuf->st_gid; -- attr->rdev = stbuf->st_rdev; -- attr->size = stbuf->st_size; -- attr->blksize = stbuf->st_blksize; -- attr->blocks = stbuf->st_blocks; -- attr->atime = stbuf->st_atime; -- attr->mtime = stbuf->st_mtime; -- attr->ctime = stbuf->st_ctime; -- attr->atimensec = ST_ATIM_NSEC(stbuf); -- attr->mtimensec = ST_MTIM_NSEC(stbuf); -- attr->ctimensec = ST_CTIM_NSEC(stbuf); -+ attr->ino = stbuf->st_ino; -+ attr->mode = stbuf->st_mode; -+ attr->nlink = stbuf->st_nlink; -+ attr->uid = stbuf->st_uid; -+ attr->gid = stbuf->st_gid; -+ attr->rdev = stbuf->st_rdev; -+ attr->size = stbuf->st_size; -+ attr->blksize = stbuf->st_blksize; -+ attr->blocks = stbuf->st_blocks; -+ attr->atime = stbuf->st_atime; -+ attr->mtime = stbuf->st_mtime; -+ attr->ctime = stbuf->st_ctime; -+ attr->atimensec = ST_ATIM_NSEC(stbuf); -+ attr->mtimensec = ST_MTIM_NSEC(stbuf); -+ attr->ctimensec = ST_CTIM_NSEC(stbuf); - } - - static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf) - { -- stbuf->st_mode = attr->mode; -- stbuf->st_uid = attr->uid; -- stbuf->st_gid = attr->gid; -- stbuf->st_size = attr->size; -- stbuf->st_atime = attr->atime; -- stbuf->st_mtime = attr->mtime; -- stbuf->st_ctime = attr->ctime; -- ST_ATIM_NSEC_SET(stbuf, attr->atimensec); -- ST_MTIM_NSEC_SET(stbuf, attr->mtimensec); -- ST_CTIM_NSEC_SET(stbuf, attr->ctimensec); -+ stbuf->st_mode = attr->mode; -+ stbuf->st_uid = attr->uid; -+ stbuf->st_gid = attr->gid; -+ stbuf->st_size = attr->size; -+ stbuf->st_atime = attr->atime; -+ stbuf->st_mtime = attr->mtime; -+ stbuf->st_ctime = attr->ctime; -+ ST_ATIM_NSEC_SET(stbuf, attr->atimensec); -+ ST_MTIM_NSEC_SET(stbuf, attr->mtimensec); -+ ST_CTIM_NSEC_SET(stbuf, attr->ctimensec); - } - --static size_t iov_length(const struct iovec *iov, size_t count) -+static size_t iov_length(const struct iovec *iov, size_t count) - { -- size_t seg; -- size_t ret = 0; -+ size_t seg; -+ size_t ret = 0; - -- for (seg = 0; seg < count; seg++) -- ret += iov[seg].iov_len; -- return ret; -+ for (seg = 0; seg < count; seg++) { -+ ret += iov[seg].iov_len; -+ } -+ return ret; - } - - static void list_init_req(struct fuse_req *req) - { -- req->next = req; -- req->prev = req; -+ req->next = req; -+ req->prev = req; - } - - static void list_del_req(struct fuse_req *req) - { -- struct fuse_req *prev = req->prev; -- struct fuse_req *next = req->next; -- prev->next = next; -- next->prev = prev; -+ struct fuse_req *prev = req->prev; -+ struct fuse_req *next = req->next; -+ prev->next = next; -+ next->prev = prev; - } - - static void list_add_req(struct fuse_req *req, struct fuse_req *next) - { -- struct fuse_req *prev = next->prev; -- req->next = next; -- req->prev = prev; -- prev->next = req; -- next->prev = req; -+ struct fuse_req *prev = next->prev; -+ req->next = next; -+ req->prev = prev; -+ prev->next = req; -+ next->prev = req; - } - - static void destroy_req(fuse_req_t req) - { -- pthread_mutex_destroy(&req->lock); -- free(req); -+ pthread_mutex_destroy(&req->lock); -+ free(req); - } - - void fuse_free_req(fuse_req_t req) - { -- int ctr; -- struct fuse_session *se = req->se; -+ int ctr; -+ struct fuse_session *se = req->se; - -- pthread_mutex_lock(&se->lock); -- req->u.ni.func = NULL; -- req->u.ni.data = NULL; -- list_del_req(req); -- ctr = --req->ctr; -- req->ch = NULL; -- pthread_mutex_unlock(&se->lock); -- if (!ctr) -- destroy_req(req); -+ pthread_mutex_lock(&se->lock); -+ req->u.ni.func = NULL; -+ req->u.ni.data = NULL; -+ list_del_req(req); -+ ctr = --req->ctr; -+ req->ch = NULL; -+ pthread_mutex_unlock(&se->lock); -+ if (!ctr) { -+ destroy_req(req); -+ } - } - - static struct fuse_req *fuse_ll_alloc_req(struct fuse_session *se) - { -- struct fuse_req *req; -+ struct fuse_req *req; - -- req = (struct fuse_req *) calloc(1, sizeof(struct fuse_req)); -- if (req == NULL) { -- fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n"); -- } else { -- req->se = se; -- req->ctr = 1; -- list_init_req(req); -- fuse_mutex_init(&req->lock); -- } -+ req = (struct fuse_req *)calloc(1, sizeof(struct fuse_req)); -+ if (req == NULL) { -+ fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate request\n"); -+ } else { -+ req->se = se; -+ req->ctr = 1; -+ list_init_req(req); -+ fuse_mutex_init(&req->lock); -+ } - -- return req; -+ return req; - } - - /* Send data. If *ch* is NULL, send via session master fd */ - static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, -- struct iovec *iov, int count) -+ struct iovec *iov, int count) - { -- struct fuse_out_header *out = iov[0].iov_base; -+ struct fuse_out_header *out = iov[0].iov_base; - -- out->len = iov_length(iov, count); -- if (se->debug) { -- if (out->unique == 0) { -- fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n", -- out->error, out->len); -- } else if (out->error) { -- fuse_log(FUSE_LOG_DEBUG, -- " unique: %llu, error: %i (%s), outsize: %i\n", -- (unsigned long long) out->unique, out->error, -- strerror(-out->error), out->len); -- } else { -- fuse_log(FUSE_LOG_DEBUG, -- " unique: %llu, success, outsize: %i\n", -- (unsigned long long) out->unique, out->len); -- } -- } -+ out->len = iov_length(iov, count); -+ if (se->debug) { -+ if (out->unique == 0) { -+ fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n", out->error, -+ out->len); -+ } else if (out->error) { -+ fuse_log(FUSE_LOG_DEBUG, -+ " unique: %llu, error: %i (%s), outsize: %i\n", -+ (unsigned long long)out->unique, out->error, -+ strerror(-out->error), out->len); -+ } else { -+ fuse_log(FUSE_LOG_DEBUG, " unique: %llu, success, outsize: %i\n", -+ (unsigned long long)out->unique, out->len); -+ } -+ } - -- abort(); /* virtio should have taken it before here */ -- return 0; -+ abort(); /* virtio should have taken it before here */ -+ return 0; - } - - - int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, -- int count) -+ int count) - { -- struct fuse_out_header out; -+ struct fuse_out_header out; - -- if (error <= -1000 || error > 0) { -- fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error); -- error = -ERANGE; -- } -+ if (error <= -1000 || error > 0) { -+ fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error); -+ error = -ERANGE; -+ } - -- out.unique = req->unique; -- out.error = error; -+ out.unique = req->unique; -+ out.error = error; - -- iov[0].iov_base = &out; -- iov[0].iov_len = sizeof(struct fuse_out_header); -+ iov[0].iov_base = &out; -+ iov[0].iov_len = sizeof(struct fuse_out_header); - -- return fuse_send_msg(req->se, req->ch, iov, count); -+ return fuse_send_msg(req->se, req->ch, iov, count); - } - - static int send_reply_iov(fuse_req_t req, int error, struct iovec *iov, -- int count) -+ int count) - { -- int res; -+ int res; - -- res = fuse_send_reply_iov_nofree(req, error, iov, count); -- fuse_free_req(req); -- return res; -+ res = fuse_send_reply_iov_nofree(req, error, iov, count); -+ fuse_free_req(req); -+ return res; - } - - static int send_reply(fuse_req_t req, int error, const void *arg, -- size_t argsize) -+ size_t argsize) - { -- struct iovec iov[2]; -- int count = 1; -- if (argsize) { -- iov[1].iov_base = (void *) arg; -- iov[1].iov_len = argsize; -- count++; -- } -- return send_reply_iov(req, error, iov, count); -+ struct iovec iov[2]; -+ int count = 1; -+ if (argsize) { -+ iov[1].iov_base = (void *)arg; -+ iov[1].iov_len = argsize; -+ count++; -+ } -+ return send_reply_iov(req, error, iov, count); - } - - int fuse_reply_iov(fuse_req_t req, const struct iovec *iov, int count) - { -- int res; -- struct iovec *padded_iov; -+ int res; -+ struct iovec *padded_iov; - -- padded_iov = malloc((count + 1) * sizeof(struct iovec)); -- if (padded_iov == NULL) -- return fuse_reply_err(req, ENOMEM); -+ padded_iov = malloc((count + 1) * sizeof(struct iovec)); -+ if (padded_iov == NULL) { -+ return fuse_reply_err(req, ENOMEM); -+ } - -- memcpy(padded_iov + 1, iov, count * sizeof(struct iovec)); -- count++; -+ memcpy(padded_iov + 1, iov, count * sizeof(struct iovec)); -+ count++; - -- res = send_reply_iov(req, 0, padded_iov, count); -- free(padded_iov); -+ res = send_reply_iov(req, 0, padded_iov, count); -+ free(padded_iov); - -- return res; -+ return res; - } - - --/* `buf` is allowed to be empty so that the proper size may be -- allocated by the caller */ -+/* -+ * 'buf` is allowed to be empty so that the proper size may be -+ * allocated by the caller -+ */ - size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, -- const char *name, const struct stat *stbuf, off_t off) -+ const char *name, const struct stat *stbuf, off_t off) - { -- (void)req; -- size_t namelen; -- size_t entlen; -- size_t entlen_padded; -- struct fuse_dirent *dirent; -+ (void)req; -+ size_t namelen; -+ size_t entlen; -+ size_t entlen_padded; -+ struct fuse_dirent *dirent; - -- namelen = strlen(name); -- entlen = FUSE_NAME_OFFSET + namelen; -- entlen_padded = FUSE_DIRENT_ALIGN(entlen); -+ namelen = strlen(name); -+ entlen = FUSE_NAME_OFFSET + namelen; -+ entlen_padded = FUSE_DIRENT_ALIGN(entlen); - -- if ((buf == NULL) || (entlen_padded > bufsize)) -- return entlen_padded; -+ if ((buf == NULL) || (entlen_padded > bufsize)) { -+ return entlen_padded; -+ } - -- dirent = (struct fuse_dirent*) buf; -- dirent->ino = stbuf->st_ino; -- dirent->off = off; -- dirent->namelen = namelen; -- dirent->type = (stbuf->st_mode & S_IFMT) >> 12; -- memcpy(dirent->name, name, namelen); -- memset(dirent->name + namelen, 0, entlen_padded - entlen); -+ dirent = (struct fuse_dirent *)buf; -+ dirent->ino = stbuf->st_ino; -+ dirent->off = off; -+ dirent->namelen = namelen; -+ dirent->type = (stbuf->st_mode & S_IFMT) >> 12; -+ memcpy(dirent->name, name, namelen); -+ memset(dirent->name + namelen, 0, entlen_padded - entlen); - -- return entlen_padded; -+ return entlen_padded; - } - - static void convert_statfs(const struct statvfs *stbuf, -- struct fuse_kstatfs *kstatfs) -+ struct fuse_kstatfs *kstatfs) - { -- kstatfs->bsize = stbuf->f_bsize; -- kstatfs->frsize = stbuf->f_frsize; -- kstatfs->blocks = stbuf->f_blocks; -- kstatfs->bfree = stbuf->f_bfree; -- kstatfs->bavail = stbuf->f_bavail; -- kstatfs->files = stbuf->f_files; -- kstatfs->ffree = stbuf->f_ffree; -- kstatfs->namelen = stbuf->f_namemax; -+ kstatfs->bsize = stbuf->f_bsize; -+ kstatfs->frsize = stbuf->f_frsize; -+ kstatfs->blocks = stbuf->f_blocks; -+ kstatfs->bfree = stbuf->f_bfree; -+ kstatfs->bavail = stbuf->f_bavail; -+ kstatfs->files = stbuf->f_files; -+ kstatfs->ffree = stbuf->f_ffree; -+ kstatfs->namelen = stbuf->f_namemax; - } - - static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize) - { -- return send_reply(req, 0, arg, argsize); -+ return send_reply(req, 0, arg, argsize); - } - - int fuse_reply_err(fuse_req_t req, int err) - { -- return send_reply(req, -err, NULL, 0); -+ return send_reply(req, -err, NULL, 0); - } - - void fuse_reply_none(fuse_req_t req) - { -- fuse_free_req(req); -+ fuse_free_req(req); - } - - static unsigned long calc_timeout_sec(double t) - { -- if (t > (double) ULONG_MAX) -- return ULONG_MAX; -- else if (t < 0.0) -- return 0; -- else -- return (unsigned long) t; -+ if (t > (double)ULONG_MAX) { -+ return ULONG_MAX; -+ } else if (t < 0.0) { -+ return 0; -+ } else { -+ return (unsigned long)t; -+ } - } - - static unsigned int calc_timeout_nsec(double t) - { -- double f = t - (double) calc_timeout_sec(t); -- if (f < 0.0) -- return 0; -- else if (f >= 0.999999999) -- return 999999999; -- else -- return (unsigned int) (f * 1.0e9); -+ double f = t - (double)calc_timeout_sec(t); -+ if (f < 0.0) { -+ return 0; -+ } else if (f >= 0.999999999) { -+ return 999999999; -+ } else { -+ return (unsigned int)(f * 1.0e9); -+ } - } - - static void fill_entry(struct fuse_entry_out *arg, -- const struct fuse_entry_param *e) -+ const struct fuse_entry_param *e) - { -- arg->nodeid = e->ino; -- arg->generation = e->generation; -- arg->entry_valid = calc_timeout_sec(e->entry_timeout); -- arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout); -- arg->attr_valid = calc_timeout_sec(e->attr_timeout); -- arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout); -- convert_stat(&e->attr, &arg->attr); -+ arg->nodeid = e->ino; -+ arg->generation = e->generation; -+ arg->entry_valid = calc_timeout_sec(e->entry_timeout); -+ arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout); -+ arg->attr_valid = calc_timeout_sec(e->attr_timeout); -+ arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout); -+ convert_stat(&e->attr, &arg->attr); - } - --/* `buf` is allowed to be empty so that the proper size may be -- allocated by the caller */ -+/* -+ * `buf` is allowed to be empty so that the proper size may be -+ * allocated by the caller -+ */ - size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, -- const char *name, -- const struct fuse_entry_param *e, off_t off) --{ -- (void)req; -- size_t namelen; -- size_t entlen; -- size_t entlen_padded; -- -- namelen = strlen(name); -- entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen; -- entlen_padded = FUSE_DIRENT_ALIGN(entlen); -- if ((buf == NULL) || (entlen_padded > bufsize)) -- return entlen_padded; -- -- struct fuse_direntplus *dp = (struct fuse_direntplus *) buf; -- memset(&dp->entry_out, 0, sizeof(dp->entry_out)); -- fill_entry(&dp->entry_out, e); -- -- struct fuse_dirent *dirent = &dp->dirent; -- dirent->ino = e->attr.st_ino; -- dirent->off = off; -- dirent->namelen = namelen; -- dirent->type = (e->attr.st_mode & S_IFMT) >> 12; -- memcpy(dirent->name, name, namelen); -- memset(dirent->name + namelen, 0, entlen_padded - entlen); -- -- return entlen_padded; --} -- --static void fill_open(struct fuse_open_out *arg, -- const struct fuse_file_info *f) --{ -- arg->fh = f->fh; -- if (f->direct_io) -- arg->open_flags |= FOPEN_DIRECT_IO; -- if (f->keep_cache) -- arg->open_flags |= FOPEN_KEEP_CACHE; -- if (f->cache_readdir) -- arg->open_flags |= FOPEN_CACHE_DIR; -- if (f->nonseekable) -- arg->open_flags |= FOPEN_NONSEEKABLE; -+ const char *name, -+ const struct fuse_entry_param *e, off_t off) -+{ -+ (void)req; -+ size_t namelen; -+ size_t entlen; -+ size_t entlen_padded; -+ -+ namelen = strlen(name); -+ entlen = FUSE_NAME_OFFSET_DIRENTPLUS + namelen; -+ entlen_padded = FUSE_DIRENT_ALIGN(entlen); -+ if ((buf == NULL) || (entlen_padded > bufsize)) { -+ return entlen_padded; -+ } -+ -+ struct fuse_direntplus *dp = (struct fuse_direntplus *)buf; -+ memset(&dp->entry_out, 0, sizeof(dp->entry_out)); -+ fill_entry(&dp->entry_out, e); -+ -+ struct fuse_dirent *dirent = &dp->dirent; -+ dirent->ino = e->attr.st_ino; -+ dirent->off = off; -+ dirent->namelen = namelen; -+ dirent->type = (e->attr.st_mode & S_IFMT) >> 12; -+ memcpy(dirent->name, name, namelen); -+ memset(dirent->name + namelen, 0, entlen_padded - entlen); -+ -+ return entlen_padded; -+} -+ -+static void fill_open(struct fuse_open_out *arg, const struct fuse_file_info *f) -+{ -+ arg->fh = f->fh; -+ if (f->direct_io) { -+ arg->open_flags |= FOPEN_DIRECT_IO; -+ } -+ if (f->keep_cache) { -+ arg->open_flags |= FOPEN_KEEP_CACHE; -+ } -+ if (f->cache_readdir) { -+ arg->open_flags |= FOPEN_CACHE_DIR; -+ } -+ if (f->nonseekable) { -+ arg->open_flags |= FOPEN_NONSEEKABLE; -+ } - } - - int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e) - { -- struct fuse_entry_out arg; -- size_t size = req->se->conn.proto_minor < 9 ? -- FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(arg); -+ struct fuse_entry_out arg; -+ size_t size = req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ENTRY_OUT_SIZE : -+ sizeof(arg); - -- /* before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant -- negative entry */ -- if (!e->ino && req->se->conn.proto_minor < 4) -- return fuse_reply_err(req, ENOENT); -+ /* -+ * before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant -+ * negative entry -+ */ -+ if (!e->ino && req->se->conn.proto_minor < 4) { -+ return fuse_reply_err(req, ENOENT); -+ } - -- memset(&arg, 0, sizeof(arg)); -- fill_entry(&arg, e); -- return send_reply_ok(req, &arg, size); -+ memset(&arg, 0, sizeof(arg)); -+ fill_entry(&arg, e); -+ return send_reply_ok(req, &arg, size); - } - - int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, -- const struct fuse_file_info *f) -+ const struct fuse_file_info *f) - { -- char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)]; -- size_t entrysize = req->se->conn.proto_minor < 9 ? -- FUSE_COMPAT_ENTRY_OUT_SIZE : sizeof(struct fuse_entry_out); -- struct fuse_entry_out *earg = (struct fuse_entry_out *) buf; -- struct fuse_open_out *oarg = (struct fuse_open_out *) (buf + entrysize); -+ char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)]; -+ size_t entrysize = req->se->conn.proto_minor < 9 ? -+ FUSE_COMPAT_ENTRY_OUT_SIZE : -+ sizeof(struct fuse_entry_out); -+ struct fuse_entry_out *earg = (struct fuse_entry_out *)buf; -+ struct fuse_open_out *oarg = (struct fuse_open_out *)(buf + entrysize); - -- memset(buf, 0, sizeof(buf)); -- fill_entry(earg, e); -- fill_open(oarg, f); -- return send_reply_ok(req, buf, -- entrysize + sizeof(struct fuse_open_out)); -+ memset(buf, 0, sizeof(buf)); -+ fill_entry(earg, e); -+ fill_open(oarg, f); -+ return send_reply_ok(req, buf, entrysize + sizeof(struct fuse_open_out)); - } - - int fuse_reply_attr(fuse_req_t req, const struct stat *attr, -- double attr_timeout) -+ double attr_timeout) - { -- struct fuse_attr_out arg; -- size_t size = req->se->conn.proto_minor < 9 ? -- FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg); -+ struct fuse_attr_out arg; -+ size_t size = -+ req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg); - -- memset(&arg, 0, sizeof(arg)); -- arg.attr_valid = calc_timeout_sec(attr_timeout); -- arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout); -- convert_stat(attr, &arg.attr); -+ memset(&arg, 0, sizeof(arg)); -+ arg.attr_valid = calc_timeout_sec(attr_timeout); -+ arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout); -+ convert_stat(attr, &arg.attr); - -- return send_reply_ok(req, &arg, size); -+ return send_reply_ok(req, &arg, size); - } - - int fuse_reply_readlink(fuse_req_t req, const char *linkname) - { -- return send_reply_ok(req, linkname, strlen(linkname)); -+ return send_reply_ok(req, linkname, strlen(linkname)); - } - - int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f) - { -- struct fuse_open_out arg; -+ struct fuse_open_out arg; - -- memset(&arg, 0, sizeof(arg)); -- fill_open(&arg, f); -- return send_reply_ok(req, &arg, sizeof(arg)); -+ memset(&arg, 0, sizeof(arg)); -+ fill_open(&arg, f); -+ return send_reply_ok(req, &arg, sizeof(arg)); - } - - int fuse_reply_write(fuse_req_t req, size_t count) - { -- struct fuse_write_out arg; -+ struct fuse_write_out arg; - -- memset(&arg, 0, sizeof(arg)); -- arg.size = count; -+ memset(&arg, 0, sizeof(arg)); -+ arg.size = count; - -- return send_reply_ok(req, &arg, sizeof(arg)); -+ return send_reply_ok(req, &arg, sizeof(arg)); - } - - int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size) - { -- return send_reply_ok(req, buf, size); -+ return send_reply_ok(req, buf, size); - } - - static int fuse_send_data_iov_fallback(struct fuse_session *se, -- struct fuse_chan *ch, -- struct iovec *iov, int iov_count, -- struct fuse_bufvec *buf, -- size_t len) -+ struct fuse_chan *ch, struct iovec *iov, -+ int iov_count, struct fuse_bufvec *buf, -+ size_t len) - { -- /* Optimize common case */ -- if (buf->count == 1 && buf->idx == 0 && buf->off == 0 && -- !(buf->buf[0].flags & FUSE_BUF_IS_FD)) { -- /* FIXME: also avoid memory copy if there are multiple buffers -- but none of them contain an fd */ -+ /* Optimize common case */ -+ if (buf->count == 1 && buf->idx == 0 && buf->off == 0 && -+ !(buf->buf[0].flags & FUSE_BUF_IS_FD)) { -+ /* -+ * FIXME: also avoid memory copy if there are multiple buffers -+ * but none of them contain an fd -+ */ - -- iov[iov_count].iov_base = buf->buf[0].mem; -- iov[iov_count].iov_len = len; -- iov_count++; -- return fuse_send_msg(se, ch, iov, iov_count); -- } -+ iov[iov_count].iov_base = buf->buf[0].mem; -+ iov[iov_count].iov_len = len; -+ iov_count++; -+ return fuse_send_msg(se, ch, iov, iov_count); -+ } - -- abort(); /* Will have taken vhost path */ -- return 0; -+ abort(); /* Will have taken vhost path */ -+ return 0; - } - - static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, -- struct iovec *iov, int iov_count, -- struct fuse_bufvec *buf, unsigned int flags) -+ struct iovec *iov, int iov_count, -+ struct fuse_bufvec *buf, unsigned int flags) - { -- size_t len = fuse_buf_size(buf); -- (void) flags; -+ size_t len = fuse_buf_size(buf); -+ (void)flags; - -- return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); -+ return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); - } - - int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, -- enum fuse_buf_copy_flags flags) -+ enum fuse_buf_copy_flags flags) - { -- struct iovec iov[2]; -- struct fuse_out_header out; -- int res; -+ struct iovec iov[2]; -+ struct fuse_out_header out; -+ int res; - -- iov[0].iov_base = &out; -- iov[0].iov_len = sizeof(struct fuse_out_header); -+ iov[0].iov_base = &out; -+ iov[0].iov_len = sizeof(struct fuse_out_header); - -- out.unique = req->unique; -- out.error = 0; -+ out.unique = req->unique; -+ out.error = 0; - -- res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags); -- if (res <= 0) { -- fuse_free_req(req); -- return res; -- } else { -- return fuse_reply_err(req, res); -- } -+ res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags); -+ if (res <= 0) { -+ fuse_free_req(req); -+ return res; -+ } else { -+ return fuse_reply_err(req, res); -+ } - } - - int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf) - { -- struct fuse_statfs_out arg; -- size_t size = req->se->conn.proto_minor < 4 ? -- FUSE_COMPAT_STATFS_SIZE : sizeof(arg); -+ struct fuse_statfs_out arg; -+ size_t size = -+ req->se->conn.proto_minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(arg); - -- memset(&arg, 0, sizeof(arg)); -- convert_statfs(stbuf, &arg.st); -+ memset(&arg, 0, sizeof(arg)); -+ convert_statfs(stbuf, &arg.st); - -- return send_reply_ok(req, &arg, size); -+ return send_reply_ok(req, &arg, size); - } - - int fuse_reply_xattr(fuse_req_t req, size_t count) - { -- struct fuse_getxattr_out arg; -+ struct fuse_getxattr_out arg; - -- memset(&arg, 0, sizeof(arg)); -- arg.size = count; -+ memset(&arg, 0, sizeof(arg)); -+ arg.size = count; - -- return send_reply_ok(req, &arg, sizeof(arg)); -+ return send_reply_ok(req, &arg, sizeof(arg)); - } - - int fuse_reply_lock(fuse_req_t req, const struct flock *lock) - { -- struct fuse_lk_out arg; -+ struct fuse_lk_out arg; - -- memset(&arg, 0, sizeof(arg)); -- arg.lk.type = lock->l_type; -- if (lock->l_type != F_UNLCK) { -- arg.lk.start = lock->l_start; -- if (lock->l_len == 0) -- arg.lk.end = OFFSET_MAX; -- else -- arg.lk.end = lock->l_start + lock->l_len - 1; -- } -- arg.lk.pid = lock->l_pid; -- return send_reply_ok(req, &arg, sizeof(arg)); -+ memset(&arg, 0, sizeof(arg)); -+ arg.lk.type = lock->l_type; -+ if (lock->l_type != F_UNLCK) { -+ arg.lk.start = lock->l_start; -+ if (lock->l_len == 0) { -+ arg.lk.end = OFFSET_MAX; -+ } else { -+ arg.lk.end = lock->l_start + lock->l_len - 1; -+ } -+ } -+ arg.lk.pid = lock->l_pid; -+ return send_reply_ok(req, &arg, sizeof(arg)); - } - - int fuse_reply_bmap(fuse_req_t req, uint64_t idx) - { -- struct fuse_bmap_out arg; -+ struct fuse_bmap_out arg; - -- memset(&arg, 0, sizeof(arg)); -- arg.block = idx; -+ memset(&arg, 0, sizeof(arg)); -+ arg.block = idx; - -- return send_reply_ok(req, &arg, sizeof(arg)); -+ return send_reply_ok(req, &arg, sizeof(arg)); - } - - static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov, -- size_t count) --{ -- struct fuse_ioctl_iovec *fiov; -- size_t i; -- -- fiov = malloc(sizeof(fiov[0]) * count); -- if (!fiov) -- return NULL; -- -- for (i = 0; i < count; i++) { -- fiov[i].base = (uintptr_t) iov[i].iov_base; -- fiov[i].len = iov[i].iov_len; -- } -- -- return fiov; --} -- --int fuse_reply_ioctl_retry(fuse_req_t req, -- const struct iovec *in_iov, size_t in_count, -- const struct iovec *out_iov, size_t out_count) --{ -- struct fuse_ioctl_out arg; -- struct fuse_ioctl_iovec *in_fiov = NULL; -- struct fuse_ioctl_iovec *out_fiov = NULL; -- struct iovec iov[4]; -- size_t count = 1; -- int res; -- -- memset(&arg, 0, sizeof(arg)); -- arg.flags |= FUSE_IOCTL_RETRY; -- arg.in_iovs = in_count; -- arg.out_iovs = out_count; -- iov[count].iov_base = &arg; -- iov[count].iov_len = sizeof(arg); -- count++; -- -- if (req->se->conn.proto_minor < 16) { -- if (in_count) { -- iov[count].iov_base = (void *)in_iov; -- iov[count].iov_len = sizeof(in_iov[0]) * in_count; -- count++; -- } -- -- if (out_count) { -- iov[count].iov_base = (void *)out_iov; -- iov[count].iov_len = sizeof(out_iov[0]) * out_count; -- count++; -- } -- } else { -- /* Can't handle non-compat 64bit ioctls on 32bit */ -- if (sizeof(void *) == 4 && req->ioctl_64bit) { -- res = fuse_reply_err(req, EINVAL); -- goto out; -- } -- -- if (in_count) { -- in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count); -- if (!in_fiov) -- goto enomem; -- -- iov[count].iov_base = (void *)in_fiov; -- iov[count].iov_len = sizeof(in_fiov[0]) * in_count; -- count++; -- } -- if (out_count) { -- out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count); -- if (!out_fiov) -- goto enomem; -- -- iov[count].iov_base = (void *)out_fiov; -- iov[count].iov_len = sizeof(out_fiov[0]) * out_count; -- count++; -- } -- } -- -- res = send_reply_iov(req, 0, iov, count); -+ size_t count) -+{ -+ struct fuse_ioctl_iovec *fiov; -+ size_t i; -+ -+ fiov = malloc(sizeof(fiov[0]) * count); -+ if (!fiov) { -+ return NULL; -+ } -+ -+ for (i = 0; i < count; i++) { -+ fiov[i].base = (uintptr_t)iov[i].iov_base; -+ fiov[i].len = iov[i].iov_len; -+ } -+ -+ return fiov; -+} -+ -+int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, -+ size_t in_count, const struct iovec *out_iov, -+ size_t out_count) -+{ -+ struct fuse_ioctl_out arg; -+ struct fuse_ioctl_iovec *in_fiov = NULL; -+ struct fuse_ioctl_iovec *out_fiov = NULL; -+ struct iovec iov[4]; -+ size_t count = 1; -+ int res; -+ -+ memset(&arg, 0, sizeof(arg)); -+ arg.flags |= FUSE_IOCTL_RETRY; -+ arg.in_iovs = in_count; -+ arg.out_iovs = out_count; -+ iov[count].iov_base = &arg; -+ iov[count].iov_len = sizeof(arg); -+ count++; -+ -+ if (req->se->conn.proto_minor < 16) { -+ if (in_count) { -+ iov[count].iov_base = (void *)in_iov; -+ iov[count].iov_len = sizeof(in_iov[0]) * in_count; -+ count++; -+ } -+ -+ if (out_count) { -+ iov[count].iov_base = (void *)out_iov; -+ iov[count].iov_len = sizeof(out_iov[0]) * out_count; -+ count++; -+ } -+ } else { -+ /* Can't handle non-compat 64bit ioctls on 32bit */ -+ if (sizeof(void *) == 4 && req->ioctl_64bit) { -+ res = fuse_reply_err(req, EINVAL); -+ goto out; -+ } -+ -+ if (in_count) { -+ in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count); -+ if (!in_fiov) { -+ goto enomem; -+ } -+ -+ iov[count].iov_base = (void *)in_fiov; -+ iov[count].iov_len = sizeof(in_fiov[0]) * in_count; -+ count++; -+ } -+ if (out_count) { -+ out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count); -+ if (!out_fiov) { -+ goto enomem; -+ } -+ -+ iov[count].iov_base = (void *)out_fiov; -+ iov[count].iov_len = sizeof(out_fiov[0]) * out_count; -+ count++; -+ } -+ } -+ -+ res = send_reply_iov(req, 0, iov, count); - out: -- free(in_fiov); -- free(out_fiov); -+ free(in_fiov); -+ free(out_fiov); - -- return res; -+ return res; - - enomem: -- res = fuse_reply_err(req, ENOMEM); -- goto out; -+ res = fuse_reply_err(req, ENOMEM); -+ goto out; - } - - int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size) - { -- struct fuse_ioctl_out arg; -- struct iovec iov[3]; -- size_t count = 1; -+ struct fuse_ioctl_out arg; -+ struct iovec iov[3]; -+ size_t count = 1; - -- memset(&arg, 0, sizeof(arg)); -- arg.result = result; -- iov[count].iov_base = &arg; -- iov[count].iov_len = sizeof(arg); -- count++; -+ memset(&arg, 0, sizeof(arg)); -+ arg.result = result; -+ iov[count].iov_base = &arg; -+ iov[count].iov_len = sizeof(arg); -+ count++; - -- if (size) { -- iov[count].iov_base = (char *) buf; -- iov[count].iov_len = size; -- count++; -- } -+ if (size) { -+ iov[count].iov_base = (char *)buf; -+ iov[count].iov_len = size; -+ count++; -+ } - -- return send_reply_iov(req, 0, iov, count); -+ return send_reply_iov(req, 0, iov, count); - } - - int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, -- int count) -+ int count) - { -- struct iovec *padded_iov; -- struct fuse_ioctl_out arg; -- int res; -+ struct iovec *padded_iov; -+ struct fuse_ioctl_out arg; -+ int res; - -- padded_iov = malloc((count + 2) * sizeof(struct iovec)); -- if (padded_iov == NULL) -- return fuse_reply_err(req, ENOMEM); -+ padded_iov = malloc((count + 2) * sizeof(struct iovec)); -+ if (padded_iov == NULL) { -+ return fuse_reply_err(req, ENOMEM); -+ } - -- memset(&arg, 0, sizeof(arg)); -- arg.result = result; -- padded_iov[1].iov_base = &arg; -- padded_iov[1].iov_len = sizeof(arg); -+ memset(&arg, 0, sizeof(arg)); -+ arg.result = result; -+ padded_iov[1].iov_base = &arg; -+ padded_iov[1].iov_len = sizeof(arg); - -- memcpy(&padded_iov[2], iov, count * sizeof(struct iovec)); -+ memcpy(&padded_iov[2], iov, count * sizeof(struct iovec)); - -- res = send_reply_iov(req, 0, padded_iov, count + 2); -- free(padded_iov); -+ res = send_reply_iov(req, 0, padded_iov, count + 2); -+ free(padded_iov); - -- return res; -+ return res; - } - - int fuse_reply_poll(fuse_req_t req, unsigned revents) - { -- struct fuse_poll_out arg; -+ struct fuse_poll_out arg; - -- memset(&arg, 0, sizeof(arg)); -- arg.revents = revents; -+ memset(&arg, 0, sizeof(arg)); -+ arg.revents = revents; - -- return send_reply_ok(req, &arg, sizeof(arg)); -+ return send_reply_ok(req, &arg, sizeof(arg)); - } - - int fuse_reply_lseek(fuse_req_t req, off_t off) - { -- struct fuse_lseek_out arg; -+ struct fuse_lseek_out arg; - -- memset(&arg, 0, sizeof(arg)); -- arg.offset = off; -+ memset(&arg, 0, sizeof(arg)); -+ arg.offset = off; - -- return send_reply_ok(req, &arg, sizeof(arg)); -+ return send_reply_ok(req, &arg, sizeof(arg)); - } - - static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- char *name = (char *) inarg; -+ char *name = (char *)inarg; - -- if (req->se->op.lookup) -- req->se->op.lookup(req, nodeid, name); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.lookup) { -+ req->se->op.lookup(req, nodeid, name); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_forget_in *arg = (struct fuse_forget_in *) inarg; -+ struct fuse_forget_in *arg = (struct fuse_forget_in *)inarg; - -- if (req->se->op.forget) -- req->se->op.forget(req, nodeid, arg->nlookup); -- else -- fuse_reply_none(req); -+ if (req->se->op.forget) { -+ req->se->op.forget(req, nodeid, arg->nlookup); -+ } else { -+ fuse_reply_none(req); -+ } - } - - static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid, -- const void *inarg) -+ const void *inarg) - { -- struct fuse_batch_forget_in *arg = (void *) inarg; -- struct fuse_forget_one *param = (void *) PARAM(arg); -- unsigned int i; -+ struct fuse_batch_forget_in *arg = (void *)inarg; -+ struct fuse_forget_one *param = (void *)PARAM(arg); -+ unsigned int i; - -- (void) nodeid; -+ (void)nodeid; - -- if (req->se->op.forget_multi) { -- req->se->op.forget_multi(req, arg->count, -- (struct fuse_forget_data *) param); -- } else if (req->se->op.forget) { -- for (i = 0; i < arg->count; i++) { -- struct fuse_forget_one *forget = ¶m[i]; -- struct fuse_req *dummy_req; -+ if (req->se->op.forget_multi) { -+ req->se->op.forget_multi(req, arg->count, -+ (struct fuse_forget_data *)param); -+ } else if (req->se->op.forget) { -+ for (i = 0; i < arg->count; i++) { -+ struct fuse_forget_one *forget = ¶m[i]; -+ struct fuse_req *dummy_req; - -- dummy_req = fuse_ll_alloc_req(req->se); -- if (dummy_req == NULL) -- break; -+ dummy_req = fuse_ll_alloc_req(req->se); -+ if (dummy_req == NULL) { -+ break; -+ } - -- dummy_req->unique = req->unique; -- dummy_req->ctx = req->ctx; -- dummy_req->ch = NULL; -+ dummy_req->unique = req->unique; -+ dummy_req->ctx = req->ctx; -+ dummy_req->ch = NULL; - -- req->se->op.forget(dummy_req, forget->nodeid, -- forget->nlookup); -- } -- fuse_reply_none(req); -- } else { -- fuse_reply_none(req); -- } -+ req->se->op.forget(dummy_req, forget->nodeid, forget->nlookup); -+ } -+ fuse_reply_none(req); -+ } else { -+ fuse_reply_none(req); -+ } - } - - static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_file_info *fip = NULL; -- struct fuse_file_info fi; -+ struct fuse_file_info *fip = NULL; -+ struct fuse_file_info fi; - -- if (req->se->conn.proto_minor >= 9) { -- struct fuse_getattr_in *arg = (struct fuse_getattr_in *) inarg; -+ if (req->se->conn.proto_minor >= 9) { -+ struct fuse_getattr_in *arg = (struct fuse_getattr_in *)inarg; - -- if (arg->getattr_flags & FUSE_GETATTR_FH) { -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -- fip = &fi; -- } -- } -+ if (arg->getattr_flags & FUSE_GETATTR_FH) { -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ fip = &fi; -+ } -+ } - -- if (req->se->op.getattr) -- req->se->op.getattr(req, nodeid, fip); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.getattr) { -+ req->se->op.getattr(req, nodeid, fip); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_setattr_in *arg = (struct fuse_setattr_in *) inarg; -- -- if (req->se->op.setattr) { -- struct fuse_file_info *fi = NULL; -- struct fuse_file_info fi_store; -- struct stat stbuf; -- memset(&stbuf, 0, sizeof(stbuf)); -- convert_attr(arg, &stbuf); -- if (arg->valid & FATTR_FH) { -- arg->valid &= ~FATTR_FH; -- memset(&fi_store, 0, sizeof(fi_store)); -- fi = &fi_store; -- fi->fh = arg->fh; -- } -- arg->valid &= -- FUSE_SET_ATTR_MODE | -- FUSE_SET_ATTR_UID | -- FUSE_SET_ATTR_GID | -- FUSE_SET_ATTR_SIZE | -- FUSE_SET_ATTR_ATIME | -- FUSE_SET_ATTR_MTIME | -- FUSE_SET_ATTR_ATIME_NOW | -- FUSE_SET_ATTR_MTIME_NOW | -- FUSE_SET_ATTR_CTIME; -- -- req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi); -- } else -- fuse_reply_err(req, ENOSYS); -+ struct fuse_setattr_in *arg = (struct fuse_setattr_in *)inarg; -+ -+ if (req->se->op.setattr) { -+ struct fuse_file_info *fi = NULL; -+ struct fuse_file_info fi_store; -+ struct stat stbuf; -+ memset(&stbuf, 0, sizeof(stbuf)); -+ convert_attr(arg, &stbuf); -+ if (arg->valid & FATTR_FH) { -+ arg->valid &= ~FATTR_FH; -+ memset(&fi_store, 0, sizeof(fi_store)); -+ fi = &fi_store; -+ fi->fh = arg->fh; -+ } -+ arg->valid &= FUSE_SET_ATTR_MODE | FUSE_SET_ATTR_UID | -+ FUSE_SET_ATTR_GID | FUSE_SET_ATTR_SIZE | -+ FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME | -+ FUSE_SET_ATTR_ATIME_NOW | FUSE_SET_ATTR_MTIME_NOW | -+ FUSE_SET_ATTR_CTIME; -+ -+ req->se->op.setattr(req, nodeid, &stbuf, arg->valid, fi); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_access_in *arg = (struct fuse_access_in *) inarg; -+ struct fuse_access_in *arg = (struct fuse_access_in *)inarg; - -- if (req->se->op.access) -- req->se->op.access(req, nodeid, arg->mask); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.access) { -+ req->se->op.access(req, nodeid, arg->mask); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- (void) inarg; -+ (void)inarg; - -- if (req->se->op.readlink) -- req->se->op.readlink(req, nodeid); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.readlink) { -+ req->se->op.readlink(req, nodeid); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_mknod_in *arg = (struct fuse_mknod_in *) inarg; -- char *name = PARAM(arg); -+ struct fuse_mknod_in *arg = (struct fuse_mknod_in *)inarg; -+ char *name = PARAM(arg); - -- if (req->se->conn.proto_minor >= 12) -- req->ctx.umask = arg->umask; -- else -- name = (char *) inarg + FUSE_COMPAT_MKNOD_IN_SIZE; -+ if (req->se->conn.proto_minor >= 12) { -+ req->ctx.umask = arg->umask; -+ } else { -+ name = (char *)inarg + FUSE_COMPAT_MKNOD_IN_SIZE; -+ } - -- if (req->se->op.mknod) -- req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.mknod) { -+ req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *) inarg; -+ struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *)inarg; - -- if (req->se->conn.proto_minor >= 12) -- req->ctx.umask = arg->umask; -+ if (req->se->conn.proto_minor >= 12) { -+ req->ctx.umask = arg->umask; -+ } - -- if (req->se->op.mkdir) -- req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.mkdir) { -+ req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- char *name = (char *) inarg; -+ char *name = (char *)inarg; - -- if (req->se->op.unlink) -- req->se->op.unlink(req, nodeid, name); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.unlink) { -+ req->se->op.unlink(req, nodeid, name); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- char *name = (char *) inarg; -+ char *name = (char *)inarg; - -- if (req->se->op.rmdir) -- req->se->op.rmdir(req, nodeid, name); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.rmdir) { -+ req->se->op.rmdir(req, nodeid, name); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- char *name = (char *) inarg; -- char *linkname = ((char *) inarg) + strlen((char *) inarg) + 1; -+ char *name = (char *)inarg; -+ char *linkname = ((char *)inarg) + strlen((char *)inarg) + 1; - -- if (req->se->op.symlink) -- req->se->op.symlink(req, linkname, nodeid, name); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.symlink) { -+ req->se->op.symlink(req, linkname, nodeid, name); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_rename_in *arg = (struct fuse_rename_in *) inarg; -- char *oldname = PARAM(arg); -- char *newname = oldname + strlen(oldname) + 1; -+ struct fuse_rename_in *arg = (struct fuse_rename_in *)inarg; -+ char *oldname = PARAM(arg); -+ char *newname = oldname + strlen(oldname) + 1; - -- if (req->se->op.rename) -- req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, -- 0); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.rename) { -+ req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, 0); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_rename2_in *arg = (struct fuse_rename2_in *) inarg; -- char *oldname = PARAM(arg); -- char *newname = oldname + strlen(oldname) + 1; -+ struct fuse_rename2_in *arg = (struct fuse_rename2_in *)inarg; -+ char *oldname = PARAM(arg); -+ char *newname = oldname + strlen(oldname) + 1; - -- if (req->se->op.rename) -- req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, -- arg->flags); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.rename) { -+ req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, -+ arg->flags); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_link_in *arg = (struct fuse_link_in *) inarg; -+ struct fuse_link_in *arg = (struct fuse_link_in *)inarg; - -- if (req->se->op.link) -- req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg)); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.link) { -+ req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg)); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_create_in *arg = (struct fuse_create_in *) inarg; -+ struct fuse_create_in *arg = (struct fuse_create_in *)inarg; - -- if (req->se->op.create) { -- struct fuse_file_info fi; -- char *name = PARAM(arg); -+ if (req->se->op.create) { -+ struct fuse_file_info fi; -+ char *name = PARAM(arg); - -- memset(&fi, 0, sizeof(fi)); -- fi.flags = arg->flags; -+ memset(&fi, 0, sizeof(fi)); -+ fi.flags = arg->flags; - -- if (req->se->conn.proto_minor >= 12) -- req->ctx.umask = arg->umask; -- else -- name = (char *) inarg + sizeof(struct fuse_open_in); -+ if (req->se->conn.proto_minor >= 12) { -+ req->ctx.umask = arg->umask; -+ } else { -+ name = (char *)inarg + sizeof(struct fuse_open_in); -+ } - -- req->se->op.create(req, nodeid, name, arg->mode, &fi); -- } else -- fuse_reply_err(req, ENOSYS); -+ req->se->op.create(req, nodeid, name, arg->mode, &fi); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_open_in *arg = (struct fuse_open_in *) inarg; -- struct fuse_file_info fi; -+ struct fuse_open_in *arg = (struct fuse_open_in *)inarg; -+ struct fuse_file_info fi; - -- memset(&fi, 0, sizeof(fi)); -- fi.flags = arg->flags; -+ memset(&fi, 0, sizeof(fi)); -+ fi.flags = arg->flags; - -- if (req->se->op.open) -- req->se->op.open(req, nodeid, &fi); -- else -- fuse_reply_open(req, &fi); -+ if (req->se->op.open) { -+ req->se->op.open(req, nodeid, &fi); -+ } else { -+ fuse_reply_open(req, &fi); -+ } - } - - static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_read_in *arg = (struct fuse_read_in *) inarg; -+ struct fuse_read_in *arg = (struct fuse_read_in *)inarg; - -- if (req->se->op.read) { -- struct fuse_file_info fi; -+ if (req->se->op.read) { -+ struct fuse_file_info fi; - -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -- if (req->se->conn.proto_minor >= 9) { -- fi.lock_owner = arg->lock_owner; -- fi.flags = arg->flags; -- } -- req->se->op.read(req, nodeid, arg->size, arg->offset, &fi); -- } else -- fuse_reply_err(req, ENOSYS); -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ if (req->se->conn.proto_minor >= 9) { -+ fi.lock_owner = arg->lock_owner; -+ fi.flags = arg->flags; -+ } -+ req->se->op.read(req, nodeid, arg->size, arg->offset, &fi); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_write_in *arg = (struct fuse_write_in *) inarg; -- struct fuse_file_info fi; -- char *param; -+ struct fuse_write_in *arg = (struct fuse_write_in *)inarg; -+ struct fuse_file_info fi; -+ char *param; - -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -- fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0; -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0; - -- if (req->se->conn.proto_minor < 9) { -- param = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE; -- } else { -- fi.lock_owner = arg->lock_owner; -- fi.flags = arg->flags; -- param = PARAM(arg); -- } -+ if (req->se->conn.proto_minor < 9) { -+ param = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE; -+ } else { -+ fi.lock_owner = arg->lock_owner; -+ fi.flags = arg->flags; -+ param = PARAM(arg); -+ } - -- if (req->se->op.write) -- req->se->op.write(req, nodeid, param, arg->size, -- arg->offset, &fi); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.write) { -+ req->se->op.write(req, nodeid, param, arg->size, arg->offset, &fi); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, -- const struct fuse_buf *ibuf) --{ -- struct fuse_session *se = req->se; -- struct fuse_bufvec bufv = { -- .buf[0] = *ibuf, -- .count = 1, -- }; -- struct fuse_write_in *arg = (struct fuse_write_in *) inarg; -- struct fuse_file_info fi; -- -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -- fi.writepage = arg->write_flags & FUSE_WRITE_CACHE; -- -- if (se->conn.proto_minor < 9) { -- bufv.buf[0].mem = ((char *) arg) + FUSE_COMPAT_WRITE_IN_SIZE; -- bufv.buf[0].size -= sizeof(struct fuse_in_header) + -- FUSE_COMPAT_WRITE_IN_SIZE; -- assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD)); -- } else { -- fi.lock_owner = arg->lock_owner; -- fi.flags = arg->flags; -- if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) -- bufv.buf[0].mem = PARAM(arg); -- -- bufv.buf[0].size -= sizeof(struct fuse_in_header) + -- sizeof(struct fuse_write_in); -- } -- if (bufv.buf[0].size < arg->size) { -- fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n"); -- fuse_reply_err(req, EIO); -- return; -- } -- bufv.buf[0].size = arg->size; -- -- se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi); -+ const struct fuse_buf *ibuf) -+{ -+ struct fuse_session *se = req->se; -+ struct fuse_bufvec bufv = { -+ .buf[0] = *ibuf, -+ .count = 1, -+ }; -+ struct fuse_write_in *arg = (struct fuse_write_in *)inarg; -+ struct fuse_file_info fi; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ fi.writepage = arg->write_flags & FUSE_WRITE_CACHE; -+ -+ if (se->conn.proto_minor < 9) { -+ bufv.buf[0].mem = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE; -+ bufv.buf[0].size -= -+ sizeof(struct fuse_in_header) + FUSE_COMPAT_WRITE_IN_SIZE; -+ assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD)); -+ } else { -+ fi.lock_owner = arg->lock_owner; -+ fi.flags = arg->flags; -+ if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) { -+ bufv.buf[0].mem = PARAM(arg); -+ } -+ -+ bufv.buf[0].size -= -+ sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); -+ } -+ if (bufv.buf[0].size < arg->size) { -+ fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n"); -+ fuse_reply_err(req, EIO); -+ return; -+ } -+ bufv.buf[0].size = arg->size; -+ -+ se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi); - } - - static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_flush_in *arg = (struct fuse_flush_in *) inarg; -- struct fuse_file_info fi; -+ struct fuse_flush_in *arg = (struct fuse_flush_in *)inarg; -+ struct fuse_file_info fi; - -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -- fi.flush = 1; -- if (req->se->conn.proto_minor >= 7) -- fi.lock_owner = arg->lock_owner; -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ fi.flush = 1; -+ if (req->se->conn.proto_minor >= 7) { -+ fi.lock_owner = arg->lock_owner; -+ } - -- if (req->se->op.flush) -- req->se->op.flush(req, nodeid, &fi); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.flush) { -+ req->se->op.flush(req, nodeid, &fi); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_release_in *arg = (struct fuse_release_in *) inarg; -- struct fuse_file_info fi; -+ struct fuse_release_in *arg = (struct fuse_release_in *)inarg; -+ struct fuse_file_info fi; - -- memset(&fi, 0, sizeof(fi)); -- fi.flags = arg->flags; -- fi.fh = arg->fh; -- if (req->se->conn.proto_minor >= 8) { -- fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; -- fi.lock_owner = arg->lock_owner; -- } -- if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) { -- fi.flock_release = 1; -- fi.lock_owner = arg->lock_owner; -- } -+ memset(&fi, 0, sizeof(fi)); -+ fi.flags = arg->flags; -+ fi.fh = arg->fh; -+ if (req->se->conn.proto_minor >= 8) { -+ fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; -+ fi.lock_owner = arg->lock_owner; -+ } -+ if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) { -+ fi.flock_release = 1; -+ fi.lock_owner = arg->lock_owner; -+ } - -- if (req->se->op.release) -- req->se->op.release(req, nodeid, &fi); -- else -- fuse_reply_err(req, 0); -+ if (req->se->op.release) { -+ req->se->op.release(req, nodeid, &fi); -+ } else { -+ fuse_reply_err(req, 0); -+ } - } - - static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; -- struct fuse_file_info fi; -- int datasync = arg->fsync_flags & 1; -+ struct fuse_fsync_in *arg = (struct fuse_fsync_in *)inarg; -+ struct fuse_file_info fi; -+ int datasync = arg->fsync_flags & 1; - -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; - -- if (req->se->op.fsync) -- req->se->op.fsync(req, nodeid, datasync, &fi); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.fsync) { -+ req->se->op.fsync(req, nodeid, datasync, &fi); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_open_in *arg = (struct fuse_open_in *) inarg; -- struct fuse_file_info fi; -+ struct fuse_open_in *arg = (struct fuse_open_in *)inarg; -+ struct fuse_file_info fi; - -- memset(&fi, 0, sizeof(fi)); -- fi.flags = arg->flags; -+ memset(&fi, 0, sizeof(fi)); -+ fi.flags = arg->flags; - -- if (req->se->op.opendir) -- req->se->op.opendir(req, nodeid, &fi); -- else -- fuse_reply_open(req, &fi); -+ if (req->se->op.opendir) { -+ req->se->op.opendir(req, nodeid, &fi); -+ } else { -+ fuse_reply_open(req, &fi); -+ } - } - - static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_read_in *arg = (struct fuse_read_in *) inarg; -- struct fuse_file_info fi; -+ struct fuse_read_in *arg = (struct fuse_read_in *)inarg; -+ struct fuse_file_info fi; - -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; - -- if (req->se->op.readdir) -- req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.readdir) { -+ req->se->op.readdir(req, nodeid, arg->size, arg->offset, &fi); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_read_in *arg = (struct fuse_read_in *) inarg; -- struct fuse_file_info fi; -+ struct fuse_read_in *arg = (struct fuse_read_in *)inarg; -+ struct fuse_file_info fi; - -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; - -- if (req->se->op.readdirplus) -- req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.readdirplus) { -+ req->se->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_release_in *arg = (struct fuse_release_in *) inarg; -- struct fuse_file_info fi; -+ struct fuse_release_in *arg = (struct fuse_release_in *)inarg; -+ struct fuse_file_info fi; - -- memset(&fi, 0, sizeof(fi)); -- fi.flags = arg->flags; -- fi.fh = arg->fh; -+ memset(&fi, 0, sizeof(fi)); -+ fi.flags = arg->flags; -+ fi.fh = arg->fh; - -- if (req->se->op.releasedir) -- req->se->op.releasedir(req, nodeid, &fi); -- else -- fuse_reply_err(req, 0); -+ if (req->se->op.releasedir) { -+ req->se->op.releasedir(req, nodeid, &fi); -+ } else { -+ fuse_reply_err(req, 0); -+ } - } - - static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_fsync_in *arg = (struct fuse_fsync_in *) inarg; -- struct fuse_file_info fi; -- int datasync = arg->fsync_flags & 1; -+ struct fuse_fsync_in *arg = (struct fuse_fsync_in *)inarg; -+ struct fuse_file_info fi; -+ int datasync = arg->fsync_flags & 1; - -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; - -- if (req->se->op.fsyncdir) -- req->se->op.fsyncdir(req, nodeid, datasync, &fi); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.fsyncdir) { -+ req->se->op.fsyncdir(req, nodeid, datasync, &fi); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- (void) nodeid; -- (void) inarg; -+ (void)nodeid; -+ (void)inarg; - -- if (req->se->op.statfs) -- req->se->op.statfs(req, nodeid); -- else { -- struct statvfs buf = { -- .f_namemax = 255, -- .f_bsize = 512, -- }; -- fuse_reply_statfs(req, &buf); -- } -+ if (req->se->op.statfs) { -+ req->se->op.statfs(req, nodeid); -+ } else { -+ struct statvfs buf = { -+ .f_namemax = 255, -+ .f_bsize = 512, -+ }; -+ fuse_reply_statfs(req, &buf); -+ } - } - - static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *) inarg; -- char *name = PARAM(arg); -- char *value = name + strlen(name) + 1; -+ struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *)inarg; -+ char *name = PARAM(arg); -+ char *value = name + strlen(name) + 1; - -- if (req->se->op.setxattr) -- req->se->op.setxattr(req, nodeid, name, value, arg->size, -- arg->flags); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.setxattr) { -+ req->se->op.setxattr(req, nodeid, name, value, arg->size, arg->flags); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; -+ struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *)inarg; - -- if (req->se->op.getxattr) -- req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.getxattr) { -+ req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *) inarg; -+ struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *)inarg; - -- if (req->se->op.listxattr) -- req->se->op.listxattr(req, nodeid, arg->size); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.listxattr) { -+ req->se->op.listxattr(req, nodeid, arg->size); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- char *name = (char *) inarg; -+ char *name = (char *)inarg; - -- if (req->se->op.removexattr) -- req->se->op.removexattr(req, nodeid, name); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.removexattr) { -+ req->se->op.removexattr(req, nodeid, name); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void convert_fuse_file_lock(struct fuse_file_lock *fl, -- struct flock *flock) -+ struct flock *flock) - { -- memset(flock, 0, sizeof(struct flock)); -- flock->l_type = fl->type; -- flock->l_whence = SEEK_SET; -- flock->l_start = fl->start; -- if (fl->end == OFFSET_MAX) -- flock->l_len = 0; -- else -- flock->l_len = fl->end - fl->start + 1; -- flock->l_pid = fl->pid; -+ memset(flock, 0, sizeof(struct flock)); -+ flock->l_type = fl->type; -+ flock->l_whence = SEEK_SET; -+ flock->l_start = fl->start; -+ if (fl->end == OFFSET_MAX) { -+ flock->l_len = 0; -+ } else { -+ flock->l_len = fl->end - fl->start + 1; -+ } -+ flock->l_pid = fl->pid; - } - - static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg; -- struct fuse_file_info fi; -- struct flock flock; -+ struct fuse_lk_in *arg = (struct fuse_lk_in *)inarg; -+ struct fuse_file_info fi; -+ struct flock flock; - -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -- fi.lock_owner = arg->owner; -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ fi.lock_owner = arg->owner; - -- convert_fuse_file_lock(&arg->lk, &flock); -- if (req->se->op.getlk) -- req->se->op.getlk(req, nodeid, &fi, &flock); -- else -- fuse_reply_err(req, ENOSYS); -+ convert_fuse_file_lock(&arg->lk, &flock); -+ if (req->se->op.getlk) { -+ req->se->op.getlk(req, nodeid, &fi, &flock); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, -- const void *inarg, int sleep) --{ -- struct fuse_lk_in *arg = (struct fuse_lk_in *) inarg; -- struct fuse_file_info fi; -- struct flock flock; -- -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -- fi.lock_owner = arg->owner; -- -- if (arg->lk_flags & FUSE_LK_FLOCK) { -- int op = 0; -- -- switch (arg->lk.type) { -- case F_RDLCK: -- op = LOCK_SH; -- break; -- case F_WRLCK: -- op = LOCK_EX; -- break; -- case F_UNLCK: -- op = LOCK_UN; -- break; -- } -- if (!sleep) -- op |= LOCK_NB; -- -- if (req->se->op.flock) -- req->se->op.flock(req, nodeid, &fi, op); -- else -- fuse_reply_err(req, ENOSYS); -- } else { -- convert_fuse_file_lock(&arg->lk, &flock); -- if (req->se->op.setlk) -- req->se->op.setlk(req, nodeid, &fi, &flock, sleep); -- else -- fuse_reply_err(req, ENOSYS); -- } -+ const void *inarg, int sleep) -+{ -+ struct fuse_lk_in *arg = (struct fuse_lk_in *)inarg; -+ struct fuse_file_info fi; -+ struct flock flock; -+ -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ fi.lock_owner = arg->owner; -+ -+ if (arg->lk_flags & FUSE_LK_FLOCK) { -+ int op = 0; -+ -+ switch (arg->lk.type) { -+ case F_RDLCK: -+ op = LOCK_SH; -+ break; -+ case F_WRLCK: -+ op = LOCK_EX; -+ break; -+ case F_UNLCK: -+ op = LOCK_UN; -+ break; -+ } -+ if (!sleep) { -+ op |= LOCK_NB; -+ } -+ -+ if (req->se->op.flock) { -+ req->se->op.flock(req, nodeid, &fi, op); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } -+ } else { -+ convert_fuse_file_lock(&arg->lk, &flock); -+ if (req->se->op.setlk) { -+ req->se->op.setlk(req, nodeid, &fi, &flock, sleep); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } -+ } - } - - static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- do_setlk_common(req, nodeid, inarg, 0); -+ do_setlk_common(req, nodeid, inarg, 0); - } - - static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- do_setlk_common(req, nodeid, inarg, 1); -+ do_setlk_common(req, nodeid, inarg, 1); - } - - static int find_interrupted(struct fuse_session *se, struct fuse_req *req) - { -- struct fuse_req *curr; -- -- for (curr = se->list.next; curr != &se->list; curr = curr->next) { -- if (curr->unique == req->u.i.unique) { -- fuse_interrupt_func_t func; -- void *data; -- -- curr->ctr++; -- pthread_mutex_unlock(&se->lock); -- -- /* Ugh, ugly locking */ -- pthread_mutex_lock(&curr->lock); -- pthread_mutex_lock(&se->lock); -- curr->interrupted = 1; -- func = curr->u.ni.func; -- data = curr->u.ni.data; -- pthread_mutex_unlock(&se->lock); -- if (func) -- func(curr, data); -- pthread_mutex_unlock(&curr->lock); -- -- pthread_mutex_lock(&se->lock); -- curr->ctr--; -- if (!curr->ctr) -- destroy_req(curr); -- -- return 1; -- } -- } -- for (curr = se->interrupts.next; curr != &se->interrupts; -- curr = curr->next) { -- if (curr->u.i.unique == req->u.i.unique) -- return 1; -- } -- return 0; -+ struct fuse_req *curr; -+ -+ for (curr = se->list.next; curr != &se->list; curr = curr->next) { -+ if (curr->unique == req->u.i.unique) { -+ fuse_interrupt_func_t func; -+ void *data; -+ -+ curr->ctr++; -+ pthread_mutex_unlock(&se->lock); -+ -+ /* Ugh, ugly locking */ -+ pthread_mutex_lock(&curr->lock); -+ pthread_mutex_lock(&se->lock); -+ curr->interrupted = 1; -+ func = curr->u.ni.func; -+ data = curr->u.ni.data; -+ pthread_mutex_unlock(&se->lock); -+ if (func) { -+ func(curr, data); -+ } -+ pthread_mutex_unlock(&curr->lock); -+ -+ pthread_mutex_lock(&se->lock); -+ curr->ctr--; -+ if (!curr->ctr) { -+ destroy_req(curr); -+ } -+ -+ return 1; -+ } -+ } -+ for (curr = se->interrupts.next; curr != &se->interrupts; -+ curr = curr->next) { -+ if (curr->u.i.unique == req->u.i.unique) { -+ return 1; -+ } -+ } -+ return 0; - } - - static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *) inarg; -- struct fuse_session *se = req->se; -+ struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *)inarg; -+ struct fuse_session *se = req->se; - -- (void) nodeid; -- if (se->debug) -- fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n", -- (unsigned long long) arg->unique); -+ (void)nodeid; -+ if (se->debug) { -+ fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n", -+ (unsigned long long)arg->unique); -+ } - -- req->u.i.unique = arg->unique; -+ req->u.i.unique = arg->unique; - -- pthread_mutex_lock(&se->lock); -- if (find_interrupted(se, req)) -- destroy_req(req); -- else -- list_add_req(req, &se->interrupts); -- pthread_mutex_unlock(&se->lock); -+ pthread_mutex_lock(&se->lock); -+ if (find_interrupted(se, req)) { -+ destroy_req(req); -+ } else { -+ list_add_req(req, &se->interrupts); -+ } -+ pthread_mutex_unlock(&se->lock); - } - - static struct fuse_req *check_interrupt(struct fuse_session *se, -- struct fuse_req *req) --{ -- struct fuse_req *curr; -- -- for (curr = se->interrupts.next; curr != &se->interrupts; -- curr = curr->next) { -- if (curr->u.i.unique == req->unique) { -- req->interrupted = 1; -- list_del_req(curr); -- free(curr); -- return NULL; -- } -- } -- curr = se->interrupts.next; -- if (curr != &se->interrupts) { -- list_del_req(curr); -- list_init_req(curr); -- return curr; -- } else -- return NULL; -+ struct fuse_req *req) -+{ -+ struct fuse_req *curr; -+ -+ for (curr = se->interrupts.next; curr != &se->interrupts; -+ curr = curr->next) { -+ if (curr->u.i.unique == req->unique) { -+ req->interrupted = 1; -+ list_del_req(curr); -+ free(curr); -+ return NULL; -+ } -+ } -+ curr = se->interrupts.next; -+ if (curr != &se->interrupts) { -+ list_del_req(curr); -+ list_init_req(curr); -+ return curr; -+ } else { -+ return NULL; -+ } - } - - static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_bmap_in *arg = (struct fuse_bmap_in *) inarg; -+ struct fuse_bmap_in *arg = (struct fuse_bmap_in *)inarg; - -- if (req->se->op.bmap) -- req->se->op.bmap(req, nodeid, arg->blocksize, arg->block); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.bmap) { -+ req->se->op.bmap(req, nodeid, arg->blocksize, arg->block); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg; -- unsigned int flags = arg->flags; -- void *in_buf = arg->in_size ? PARAM(arg) : NULL; -- struct fuse_file_info fi; -+ struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *)inarg; -+ unsigned int flags = arg->flags; -+ void *in_buf = arg->in_size ? PARAM(arg) : NULL; -+ struct fuse_file_info fi; - -- if (flags & FUSE_IOCTL_DIR && -- !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) { -- fuse_reply_err(req, ENOTTY); -- return; -- } -+ if (flags & FUSE_IOCTL_DIR && !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) { -+ fuse_reply_err(req, ENOTTY); -+ return; -+ } - -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; - -- if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 && -- !(flags & FUSE_IOCTL_32BIT)) { -- req->ioctl_64bit = 1; -- } -+ if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 && -+ !(flags & FUSE_IOCTL_32BIT)) { -+ req->ioctl_64bit = 1; -+ } - -- if (req->se->op.ioctl) -- req->se->op.ioctl(req, nodeid, arg->cmd, -- (void *)(uintptr_t)arg->arg, &fi, flags, -- in_buf, arg->in_size, arg->out_size); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.ioctl) { -+ req->se->op.ioctl(req, nodeid, arg->cmd, (void *)(uintptr_t)arg->arg, -+ &fi, flags, in_buf, arg->in_size, arg->out_size); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - void fuse_pollhandle_destroy(struct fuse_pollhandle *ph) - { -- free(ph); -+ free(ph); - } - - static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_poll_in *arg = (struct fuse_poll_in *) inarg; -- struct fuse_file_info fi; -+ struct fuse_poll_in *arg = (struct fuse_poll_in *)inarg; -+ struct fuse_file_info fi; - -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -- fi.poll_events = arg->events; -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ fi.poll_events = arg->events; - -- if (req->se->op.poll) { -- struct fuse_pollhandle *ph = NULL; -+ if (req->se->op.poll) { -+ struct fuse_pollhandle *ph = NULL; - -- if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) { -- ph = malloc(sizeof(struct fuse_pollhandle)); -- if (ph == NULL) { -- fuse_reply_err(req, ENOMEM); -- return; -- } -- ph->kh = arg->kh; -- ph->se = req->se; -- } -+ if (arg->flags & FUSE_POLL_SCHEDULE_NOTIFY) { -+ ph = malloc(sizeof(struct fuse_pollhandle)); -+ if (ph == NULL) { -+ fuse_reply_err(req, ENOMEM); -+ return; -+ } -+ ph->kh = arg->kh; -+ ph->se = req->se; -+ } - -- req->se->op.poll(req, nodeid, &fi, ph); -- } else { -- fuse_reply_err(req, ENOSYS); -- } -+ req->se->op.poll(req, nodeid, &fi, ph); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *) inarg; -- struct fuse_file_info fi; -+ struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *)inarg; -+ struct fuse_file_info fi; - -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; - -- if (req->se->op.fallocate) -- req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, &fi); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.fallocate) { -+ req->se->op.fallocate(req, nodeid, arg->mode, arg->offset, arg->length, -+ &fi); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - --static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, const void *inarg) -+static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, -+ const void *inarg) - { -- struct fuse_copy_file_range_in *arg = (struct fuse_copy_file_range_in *) inarg; -- struct fuse_file_info fi_in, fi_out; -+ struct fuse_copy_file_range_in *arg = -+ (struct fuse_copy_file_range_in *)inarg; -+ struct fuse_file_info fi_in, fi_out; - -- memset(&fi_in, 0, sizeof(fi_in)); -- fi_in.fh = arg->fh_in; -+ memset(&fi_in, 0, sizeof(fi_in)); -+ fi_in.fh = arg->fh_in; - -- memset(&fi_out, 0, sizeof(fi_out)); -- fi_out.fh = arg->fh_out; -+ memset(&fi_out, 0, sizeof(fi_out)); -+ fi_out.fh = arg->fh_out; - - -- if (req->se->op.copy_file_range) -- req->se->op.copy_file_range(req, nodeid_in, arg->off_in, -- &fi_in, arg->nodeid_out, -- arg->off_out, &fi_out, arg->len, -- arg->flags); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.copy_file_range) { -+ req->se->op.copy_file_range(req, nodeid_in, arg->off_in, &fi_in, -+ arg->nodeid_out, arg->off_out, &fi_out, -+ arg->len, arg->flags); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_lseek_in *arg = (struct fuse_lseek_in *) inarg; -- struct fuse_file_info fi; -+ struct fuse_lseek_in *arg = (struct fuse_lseek_in *)inarg; -+ struct fuse_file_info fi; - -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; - -- if (req->se->op.lseek) -- req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi); -- else -- fuse_reply_err(req, ENOSYS); -+ if (req->se->op.lseek) { -+ req->se->op.lseek(req, nodeid, arg->offset, arg->whence, &fi); -+ } else { -+ fuse_reply_err(req, ENOSYS); -+ } - } - - static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_init_in *arg = (struct fuse_init_in *) inarg; -- struct fuse_init_out outarg; -- struct fuse_session *se = req->se; -- size_t bufsize = se->bufsize; -- size_t outargsize = sizeof(outarg); -- -- (void) nodeid; -- if (se->debug) { -- fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor); -- if (arg->major == 7 && arg->minor >= 6) { -- fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags); -- fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n", -- arg->max_readahead); -- } -- } -- se->conn.proto_major = arg->major; -- se->conn.proto_minor = arg->minor; -- se->conn.capable = 0; -- se->conn.want = 0; -- -- memset(&outarg, 0, sizeof(outarg)); -- outarg.major = FUSE_KERNEL_VERSION; -- outarg.minor = FUSE_KERNEL_MINOR_VERSION; -- -- if (arg->major < 7) { -- fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n", -- arg->major, arg->minor); -- fuse_reply_err(req, EPROTO); -- return; -- } -- -- if (arg->major > 7) { -- /* Wait for a second INIT request with a 7.X version */ -- send_reply_ok(req, &outarg, sizeof(outarg)); -- return; -- } -- -- if (arg->minor >= 6) { -- if (arg->max_readahead < se->conn.max_readahead) -- se->conn.max_readahead = arg->max_readahead; -- if (arg->flags & FUSE_ASYNC_READ) -- se->conn.capable |= FUSE_CAP_ASYNC_READ; -- if (arg->flags & FUSE_POSIX_LOCKS) -- se->conn.capable |= FUSE_CAP_POSIX_LOCKS; -- if (arg->flags & FUSE_ATOMIC_O_TRUNC) -- se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC; -- if (arg->flags & FUSE_EXPORT_SUPPORT) -- se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT; -- if (arg->flags & FUSE_DONT_MASK) -- se->conn.capable |= FUSE_CAP_DONT_MASK; -- if (arg->flags & FUSE_FLOCK_LOCKS) -- se->conn.capable |= FUSE_CAP_FLOCK_LOCKS; -- if (arg->flags & FUSE_AUTO_INVAL_DATA) -- se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA; -- if (arg->flags & FUSE_DO_READDIRPLUS) -- se->conn.capable |= FUSE_CAP_READDIRPLUS; -- if (arg->flags & FUSE_READDIRPLUS_AUTO) -- se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO; -- if (arg->flags & FUSE_ASYNC_DIO) -- se->conn.capable |= FUSE_CAP_ASYNC_DIO; -- if (arg->flags & FUSE_WRITEBACK_CACHE) -- se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE; -- if (arg->flags & FUSE_NO_OPEN_SUPPORT) -- se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT; -- if (arg->flags & FUSE_PARALLEL_DIROPS) -- se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS; -- if (arg->flags & FUSE_POSIX_ACL) -- se->conn.capable |= FUSE_CAP_POSIX_ACL; -- if (arg->flags & FUSE_HANDLE_KILLPRIV) -- se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV; -- if (arg->flags & FUSE_NO_OPENDIR_SUPPORT) -- se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT; -- if (!(arg->flags & FUSE_MAX_PAGES)) { -- size_t max_bufsize = -- FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() -- + FUSE_BUFFER_HEADER_SIZE; -- if (bufsize > max_bufsize) { -- bufsize = max_bufsize; -- } -- } -- } else { -- se->conn.max_readahead = 0; -- } -- -- if (se->conn.proto_minor >= 14) { -+ struct fuse_init_in *arg = (struct fuse_init_in *)inarg; -+ struct fuse_init_out outarg; -+ struct fuse_session *se = req->se; -+ size_t bufsize = se->bufsize; -+ size_t outargsize = sizeof(outarg); -+ -+ (void)nodeid; -+ if (se->debug) { -+ fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor); -+ if (arg->major == 7 && arg->minor >= 6) { -+ fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags); -+ fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n", -+ arg->max_readahead); -+ } -+ } -+ se->conn.proto_major = arg->major; -+ se->conn.proto_minor = arg->minor; -+ se->conn.capable = 0; -+ se->conn.want = 0; -+ -+ memset(&outarg, 0, sizeof(outarg)); -+ outarg.major = FUSE_KERNEL_VERSION; -+ outarg.minor = FUSE_KERNEL_MINOR_VERSION; -+ -+ if (arg->major < 7) { -+ fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n", -+ arg->major, arg->minor); -+ fuse_reply_err(req, EPROTO); -+ return; -+ } -+ -+ if (arg->major > 7) { -+ /* Wait for a second INIT request with a 7.X version */ -+ send_reply_ok(req, &outarg, sizeof(outarg)); -+ return; -+ } -+ -+ if (arg->minor >= 6) { -+ if (arg->max_readahead < se->conn.max_readahead) { -+ se->conn.max_readahead = arg->max_readahead; -+ } -+ if (arg->flags & FUSE_ASYNC_READ) { -+ se->conn.capable |= FUSE_CAP_ASYNC_READ; -+ } -+ if (arg->flags & FUSE_POSIX_LOCKS) { -+ se->conn.capable |= FUSE_CAP_POSIX_LOCKS; -+ } -+ if (arg->flags & FUSE_ATOMIC_O_TRUNC) { -+ se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC; -+ } -+ if (arg->flags & FUSE_EXPORT_SUPPORT) { -+ se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT; -+ } -+ if (arg->flags & FUSE_DONT_MASK) { -+ se->conn.capable |= FUSE_CAP_DONT_MASK; -+ } -+ if (arg->flags & FUSE_FLOCK_LOCKS) { -+ se->conn.capable |= FUSE_CAP_FLOCK_LOCKS; -+ } -+ if (arg->flags & FUSE_AUTO_INVAL_DATA) { -+ se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA; -+ } -+ if (arg->flags & FUSE_DO_READDIRPLUS) { -+ se->conn.capable |= FUSE_CAP_READDIRPLUS; -+ } -+ if (arg->flags & FUSE_READDIRPLUS_AUTO) { -+ se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO; -+ } -+ if (arg->flags & FUSE_ASYNC_DIO) { -+ se->conn.capable |= FUSE_CAP_ASYNC_DIO; -+ } -+ if (arg->flags & FUSE_WRITEBACK_CACHE) { -+ se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE; -+ } -+ if (arg->flags & FUSE_NO_OPEN_SUPPORT) { -+ se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT; -+ } -+ if (arg->flags & FUSE_PARALLEL_DIROPS) { -+ se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS; -+ } -+ if (arg->flags & FUSE_POSIX_ACL) { -+ se->conn.capable |= FUSE_CAP_POSIX_ACL; -+ } -+ if (arg->flags & FUSE_HANDLE_KILLPRIV) { -+ se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV; -+ } -+ if (arg->flags & FUSE_NO_OPENDIR_SUPPORT) { -+ se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT; -+ } -+ if (!(arg->flags & FUSE_MAX_PAGES)) { -+ size_t max_bufsize = -+ FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() + -+ FUSE_BUFFER_HEADER_SIZE; -+ if (bufsize > max_bufsize) { -+ bufsize = max_bufsize; -+ } -+ } -+ } else { -+ se->conn.max_readahead = 0; -+ } -+ -+ if (se->conn.proto_minor >= 14) { - #ifdef HAVE_SPLICE - #ifdef HAVE_VMSPLICE -- se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; -+ se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; - #endif -- se->conn.capable |= FUSE_CAP_SPLICE_READ; -+ se->conn.capable |= FUSE_CAP_SPLICE_READ; - #endif -- } -- if (se->conn.proto_minor >= 18) -- se->conn.capable |= FUSE_CAP_IOCTL_DIR; -- -- /* Default settings for modern filesystems. -- * -- * Most of these capabilities were disabled by default in -- * libfuse2 for backwards compatibility reasons. In libfuse3, -- * we can finally enable them by default (as long as they're -- * supported by the kernel). -- */ --#define LL_SET_DEFAULT(cond, cap) \ -- if ((cond) && (se->conn.capable & (cap))) \ -- se->conn.want |= (cap) -- LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ); -- LL_SET_DEFAULT(1, FUSE_CAP_PARALLEL_DIROPS); -- LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA); -- LL_SET_DEFAULT(1, FUSE_CAP_HANDLE_KILLPRIV); -- LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO); -- LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR); -- LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC); -- LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ); -- LL_SET_DEFAULT(se->op.getlk && se->op.setlk, -- FUSE_CAP_POSIX_LOCKS); -- LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS); -- LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS); -- LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir, -- FUSE_CAP_READDIRPLUS_AUTO); -- se->conn.time_gran = 1; -- -- if (bufsize < FUSE_MIN_READ_BUFFER) { -- fuse_log(FUSE_LOG_ERR, "fuse: warning: buffer size too small: %zu\n", -- bufsize); -- bufsize = FUSE_MIN_READ_BUFFER; -- } -- se->bufsize = bufsize; -- -- if (se->conn.max_write > bufsize - FUSE_BUFFER_HEADER_SIZE) -- se->conn.max_write = bufsize - FUSE_BUFFER_HEADER_SIZE; -- -- se->got_init = 1; -- if (se->op.init) -- se->op.init(se->userdata, &se->conn); -- -- if (se->conn.want & (~se->conn.capable)) { -- fuse_log(FUSE_LOG_ERR, "fuse: error: filesystem requested capabilities " -- "0x%x that are not supported by kernel, aborting.\n", -- se->conn.want & (~se->conn.capable)); -- fuse_reply_err(req, EPROTO); -- se->error = -EPROTO; -- fuse_session_exit(se); -- return; -- } -- -- if (se->conn.max_write < bufsize - FUSE_BUFFER_HEADER_SIZE) { -- se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE; -- } -- if (arg->flags & FUSE_MAX_PAGES) { -- outarg.flags |= FUSE_MAX_PAGES; -- outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1; -- } -- -- /* Always enable big writes, this is superseded -- by the max_write option */ -- outarg.flags |= FUSE_BIG_WRITES; -- -- if (se->conn.want & FUSE_CAP_ASYNC_READ) -- outarg.flags |= FUSE_ASYNC_READ; -- if (se->conn.want & FUSE_CAP_POSIX_LOCKS) -- outarg.flags |= FUSE_POSIX_LOCKS; -- if (se->conn.want & FUSE_CAP_ATOMIC_O_TRUNC) -- outarg.flags |= FUSE_ATOMIC_O_TRUNC; -- if (se->conn.want & FUSE_CAP_EXPORT_SUPPORT) -- outarg.flags |= FUSE_EXPORT_SUPPORT; -- if (se->conn.want & FUSE_CAP_DONT_MASK) -- outarg.flags |= FUSE_DONT_MASK; -- if (se->conn.want & FUSE_CAP_FLOCK_LOCKS) -- outarg.flags |= FUSE_FLOCK_LOCKS; -- if (se->conn.want & FUSE_CAP_AUTO_INVAL_DATA) -- outarg.flags |= FUSE_AUTO_INVAL_DATA; -- if (se->conn.want & FUSE_CAP_READDIRPLUS) -- outarg.flags |= FUSE_DO_READDIRPLUS; -- if (se->conn.want & FUSE_CAP_READDIRPLUS_AUTO) -- outarg.flags |= FUSE_READDIRPLUS_AUTO; -- if (se->conn.want & FUSE_CAP_ASYNC_DIO) -- outarg.flags |= FUSE_ASYNC_DIO; -- if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE) -- outarg.flags |= FUSE_WRITEBACK_CACHE; -- if (se->conn.want & FUSE_CAP_POSIX_ACL) -- outarg.flags |= FUSE_POSIX_ACL; -- outarg.max_readahead = se->conn.max_readahead; -- outarg.max_write = se->conn.max_write; -- if (se->conn.proto_minor >= 13) { -- if (se->conn.max_background >= (1 << 16)) -- se->conn.max_background = (1 << 16) - 1; -- if (se->conn.congestion_threshold > se->conn.max_background) -- se->conn.congestion_threshold = se->conn.max_background; -- if (!se->conn.congestion_threshold) { -- se->conn.congestion_threshold = -- se->conn.max_background * 3 / 4; -- } -- -- outarg.max_background = se->conn.max_background; -- outarg.congestion_threshold = se->conn.congestion_threshold; -- } -- if (se->conn.proto_minor >= 23) -- outarg.time_gran = se->conn.time_gran; -- -- if (se->debug) { -- fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor); -- fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); -- fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n", -- outarg.max_readahead); -- fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write); -- fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n", -- outarg.max_background); -- fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n", -- outarg.congestion_threshold); -- fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", -- outarg.time_gran); -- } -- if (arg->minor < 5) -- outargsize = FUSE_COMPAT_INIT_OUT_SIZE; -- else if (arg->minor < 23) -- outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE; -- -- send_reply_ok(req, &outarg, outargsize); -+ } -+ if (se->conn.proto_minor >= 18) { -+ se->conn.capable |= FUSE_CAP_IOCTL_DIR; -+ } -+ -+ /* -+ * Default settings for modern filesystems. -+ * -+ * Most of these capabilities were disabled by default in -+ * libfuse2 for backwards compatibility reasons. In libfuse3, -+ * we can finally enable them by default (as long as they're -+ * supported by the kernel). -+ */ -+#define LL_SET_DEFAULT(cond, cap) \ -+ if ((cond) && (se->conn.capable & (cap))) \ -+ se->conn.want |= (cap) -+ LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_READ); -+ LL_SET_DEFAULT(1, FUSE_CAP_PARALLEL_DIROPS); -+ LL_SET_DEFAULT(1, FUSE_CAP_AUTO_INVAL_DATA); -+ LL_SET_DEFAULT(1, FUSE_CAP_HANDLE_KILLPRIV); -+ LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO); -+ LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR); -+ LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC); -+ LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ); -+ LL_SET_DEFAULT(se->op.getlk && se->op.setlk, FUSE_CAP_POSIX_LOCKS); -+ LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS); -+ LL_SET_DEFAULT(se->op.readdirplus, FUSE_CAP_READDIRPLUS); -+ LL_SET_DEFAULT(se->op.readdirplus && se->op.readdir, -+ FUSE_CAP_READDIRPLUS_AUTO); -+ se->conn.time_gran = 1; -+ -+ if (bufsize < FUSE_MIN_READ_BUFFER) { -+ fuse_log(FUSE_LOG_ERR, "fuse: warning: buffer size too small: %zu\n", -+ bufsize); -+ bufsize = FUSE_MIN_READ_BUFFER; -+ } -+ se->bufsize = bufsize; -+ -+ if (se->conn.max_write > bufsize - FUSE_BUFFER_HEADER_SIZE) { -+ se->conn.max_write = bufsize - FUSE_BUFFER_HEADER_SIZE; -+ } -+ -+ se->got_init = 1; -+ if (se->op.init) { -+ se->op.init(se->userdata, &se->conn); -+ } -+ -+ if (se->conn.want & (~se->conn.capable)) { -+ fuse_log(FUSE_LOG_ERR, -+ "fuse: error: filesystem requested capabilities " -+ "0x%x that are not supported by kernel, aborting.\n", -+ se->conn.want & (~se->conn.capable)); -+ fuse_reply_err(req, EPROTO); -+ se->error = -EPROTO; -+ fuse_session_exit(se); -+ return; -+ } -+ -+ if (se->conn.max_write < bufsize - FUSE_BUFFER_HEADER_SIZE) { -+ se->bufsize = se->conn.max_write + FUSE_BUFFER_HEADER_SIZE; -+ } -+ if (arg->flags & FUSE_MAX_PAGES) { -+ outarg.flags |= FUSE_MAX_PAGES; -+ outarg.max_pages = (se->conn.max_write - 1) / getpagesize() + 1; -+ } -+ -+ /* -+ * Always enable big writes, this is superseded -+ * by the max_write option -+ */ -+ outarg.flags |= FUSE_BIG_WRITES; -+ -+ if (se->conn.want & FUSE_CAP_ASYNC_READ) { -+ outarg.flags |= FUSE_ASYNC_READ; -+ } -+ if (se->conn.want & FUSE_CAP_POSIX_LOCKS) { -+ outarg.flags |= FUSE_POSIX_LOCKS; -+ } -+ if (se->conn.want & FUSE_CAP_ATOMIC_O_TRUNC) { -+ outarg.flags |= FUSE_ATOMIC_O_TRUNC; -+ } -+ if (se->conn.want & FUSE_CAP_EXPORT_SUPPORT) { -+ outarg.flags |= FUSE_EXPORT_SUPPORT; -+ } -+ if (se->conn.want & FUSE_CAP_DONT_MASK) { -+ outarg.flags |= FUSE_DONT_MASK; -+ } -+ if (se->conn.want & FUSE_CAP_FLOCK_LOCKS) { -+ outarg.flags |= FUSE_FLOCK_LOCKS; -+ } -+ if (se->conn.want & FUSE_CAP_AUTO_INVAL_DATA) { -+ outarg.flags |= FUSE_AUTO_INVAL_DATA; -+ } -+ if (se->conn.want & FUSE_CAP_READDIRPLUS) { -+ outarg.flags |= FUSE_DO_READDIRPLUS; -+ } -+ if (se->conn.want & FUSE_CAP_READDIRPLUS_AUTO) { -+ outarg.flags |= FUSE_READDIRPLUS_AUTO; -+ } -+ if (se->conn.want & FUSE_CAP_ASYNC_DIO) { -+ outarg.flags |= FUSE_ASYNC_DIO; -+ } -+ if (se->conn.want & FUSE_CAP_WRITEBACK_CACHE) { -+ outarg.flags |= FUSE_WRITEBACK_CACHE; -+ } -+ if (se->conn.want & FUSE_CAP_POSIX_ACL) { -+ outarg.flags |= FUSE_POSIX_ACL; -+ } -+ outarg.max_readahead = se->conn.max_readahead; -+ outarg.max_write = se->conn.max_write; -+ if (se->conn.proto_minor >= 13) { -+ if (se->conn.max_background >= (1 << 16)) { -+ se->conn.max_background = (1 << 16) - 1; -+ } -+ if (se->conn.congestion_threshold > se->conn.max_background) { -+ se->conn.congestion_threshold = se->conn.max_background; -+ } -+ if (!se->conn.congestion_threshold) { -+ se->conn.congestion_threshold = se->conn.max_background * 3 / 4; -+ } -+ -+ outarg.max_background = se->conn.max_background; -+ outarg.congestion_threshold = se->conn.congestion_threshold; -+ } -+ if (se->conn.proto_minor >= 23) { -+ outarg.time_gran = se->conn.time_gran; -+ } -+ -+ if (se->debug) { -+ fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, -+ outarg.minor); -+ fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); -+ fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n", -+ outarg.max_readahead); -+ fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write); -+ fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n", -+ outarg.max_background); -+ fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n", -+ outarg.congestion_threshold); -+ fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", outarg.time_gran); -+ } -+ if (arg->minor < 5) { -+ outargsize = FUSE_COMPAT_INIT_OUT_SIZE; -+ } else if (arg->minor < 23) { -+ outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE; -+ } -+ -+ send_reply_ok(req, &outarg, outargsize); - } - - static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { -- struct fuse_session *se = req->se; -+ struct fuse_session *se = req->se; - -- (void) nodeid; -- (void) inarg; -+ (void)nodeid; -+ (void)inarg; - -- se->got_destroy = 1; -- if (se->op.destroy) -- se->op.destroy(se->userdata); -+ se->got_destroy = 1; -+ if (se->op.destroy) { -+ se->op.destroy(se->userdata); -+ } - -- send_reply_ok(req, NULL, 0); -+ send_reply_ok(req, NULL, 0); - } - - static void list_del_nreq(struct fuse_notify_req *nreq) - { -- struct fuse_notify_req *prev = nreq->prev; -- struct fuse_notify_req *next = nreq->next; -- prev->next = next; -- next->prev = prev; -+ struct fuse_notify_req *prev = nreq->prev; -+ struct fuse_notify_req *next = nreq->next; -+ prev->next = next; -+ next->prev = prev; - } - - static void list_add_nreq(struct fuse_notify_req *nreq, -- struct fuse_notify_req *next) -+ struct fuse_notify_req *next) - { -- struct fuse_notify_req *prev = next->prev; -- nreq->next = next; -- nreq->prev = prev; -- prev->next = nreq; -- next->prev = nreq; -+ struct fuse_notify_req *prev = next->prev; -+ nreq->next = next; -+ nreq->prev = prev; -+ prev->next = nreq; -+ next->prev = nreq; - } - - static void list_init_nreq(struct fuse_notify_req *nreq) - { -- nreq->next = nreq; -- nreq->prev = nreq; -+ nreq->next = nreq; -+ nreq->prev = nreq; - } - - static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid, -- const void *inarg, const struct fuse_buf *buf) -+ const void *inarg, const struct fuse_buf *buf) - { -- struct fuse_session *se = req->se; -- struct fuse_notify_req *nreq; -- struct fuse_notify_req *head; -+ struct fuse_session *se = req->se; -+ struct fuse_notify_req *nreq; -+ struct fuse_notify_req *head; - -- pthread_mutex_lock(&se->lock); -- head = &se->notify_list; -- for (nreq = head->next; nreq != head; nreq = nreq->next) { -- if (nreq->unique == req->unique) { -- list_del_nreq(nreq); -- break; -- } -- } -- pthread_mutex_unlock(&se->lock); -+ pthread_mutex_lock(&se->lock); -+ head = &se->notify_list; -+ for (nreq = head->next; nreq != head; nreq = nreq->next) { -+ if (nreq->unique == req->unique) { -+ list_del_nreq(nreq); -+ break; -+ } -+ } -+ pthread_mutex_unlock(&se->lock); - -- if (nreq != head) -- nreq->reply(nreq, req, nodeid, inarg, buf); -+ if (nreq != head) { -+ nreq->reply(nreq, req, nodeid, inarg, buf); -+ } - } - - static int send_notify_iov(struct fuse_session *se, int notify_code, -- struct iovec *iov, int count) -+ struct iovec *iov, int count) - { -- struct fuse_out_header out; -+ struct fuse_out_header out; - -- if (!se->got_init) -- return -ENOTCONN; -+ if (!se->got_init) { -+ return -ENOTCONN; -+ } - -- out.unique = 0; -- out.error = notify_code; -- iov[0].iov_base = &out; -- iov[0].iov_len = sizeof(struct fuse_out_header); -+ out.unique = 0; -+ out.error = notify_code; -+ iov[0].iov_base = &out; -+ iov[0].iov_len = sizeof(struct fuse_out_header); - -- return fuse_send_msg(se, NULL, iov, count); -+ return fuse_send_msg(se, NULL, iov, count); - } - - int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph) - { -- if (ph != NULL) { -- struct fuse_notify_poll_wakeup_out outarg; -- struct iovec iov[2]; -+ if (ph != NULL) { -+ struct fuse_notify_poll_wakeup_out outarg; -+ struct iovec iov[2]; - -- outarg.kh = ph->kh; -+ outarg.kh = ph->kh; - -- iov[1].iov_base = &outarg; -- iov[1].iov_len = sizeof(outarg); -+ iov[1].iov_base = &outarg; -+ iov[1].iov_len = sizeof(outarg); - -- return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2); -- } else { -- return 0; -- } -+ return send_notify_iov(ph->se, FUSE_NOTIFY_POLL, iov, 2); -+ } else { -+ return 0; -+ } - } - - int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, -- off_t off, off_t len) -+ off_t off, off_t len) - { -- struct fuse_notify_inval_inode_out outarg; -- struct iovec iov[2]; -+ struct fuse_notify_inval_inode_out outarg; -+ struct iovec iov[2]; -+ -+ if (!se) { -+ return -EINVAL; -+ } - -- if (!se) -- return -EINVAL; -+ if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) { -+ return -ENOSYS; -+ } - -- if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) -- return -ENOSYS; -- -- outarg.ino = ino; -- outarg.off = off; -- outarg.len = len; -+ outarg.ino = ino; -+ outarg.off = off; -+ outarg.len = len; - -- iov[1].iov_base = &outarg; -- iov[1].iov_len = sizeof(outarg); -+ iov[1].iov_base = &outarg; -+ iov[1].iov_len = sizeof(outarg); - -- return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2); -+ return send_notify_iov(se, FUSE_NOTIFY_INVAL_INODE, iov, 2); - } - - int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, -- const char *name, size_t namelen) -+ const char *name, size_t namelen) - { -- struct fuse_notify_inval_entry_out outarg; -- struct iovec iov[3]; -+ struct fuse_notify_inval_entry_out outarg; -+ struct iovec iov[3]; -+ -+ if (!se) { -+ return -EINVAL; -+ } - -- if (!se) -- return -EINVAL; -- -- if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) -- return -ENOSYS; -+ if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) { -+ return -ENOSYS; -+ } - -- outarg.parent = parent; -- outarg.namelen = namelen; -- outarg.padding = 0; -+ outarg.parent = parent; -+ outarg.namelen = namelen; -+ outarg.padding = 0; - -- iov[1].iov_base = &outarg; -- iov[1].iov_len = sizeof(outarg); -- iov[2].iov_base = (void *)name; -- iov[2].iov_len = namelen + 1; -+ iov[1].iov_base = &outarg; -+ iov[1].iov_len = sizeof(outarg); -+ iov[2].iov_base = (void *)name; -+ iov[2].iov_len = namelen + 1; - -- return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3); -+ return send_notify_iov(se, FUSE_NOTIFY_INVAL_ENTRY, iov, 3); - } - --int fuse_lowlevel_notify_delete(struct fuse_session *se, -- fuse_ino_t parent, fuse_ino_t child, -- const char *name, size_t namelen) -+int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, -+ fuse_ino_t child, const char *name, -+ size_t namelen) - { -- struct fuse_notify_delete_out outarg; -- struct iovec iov[3]; -+ struct fuse_notify_delete_out outarg; -+ struct iovec iov[3]; - -- if (!se) -- return -EINVAL; -+ if (!se) { -+ return -EINVAL; -+ } - -- if (se->conn.proto_major < 6 || se->conn.proto_minor < 18) -- return -ENOSYS; -+ if (se->conn.proto_major < 6 || se->conn.proto_minor < 18) { -+ return -ENOSYS; -+ } - -- outarg.parent = parent; -- outarg.child = child; -- outarg.namelen = namelen; -- outarg.padding = 0; -+ outarg.parent = parent; -+ outarg.child = child; -+ outarg.namelen = namelen; -+ outarg.padding = 0; - -- iov[1].iov_base = &outarg; -- iov[1].iov_len = sizeof(outarg); -- iov[2].iov_base = (void *)name; -- iov[2].iov_len = namelen + 1; -+ iov[1].iov_base = &outarg; -+ iov[1].iov_len = sizeof(outarg); -+ iov[2].iov_base = (void *)name; -+ iov[2].iov_len = namelen + 1; - -- return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3); -+ return send_notify_iov(se, FUSE_NOTIFY_DELETE, iov, 3); - } - - int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, -- off_t offset, struct fuse_bufvec *bufv, -- enum fuse_buf_copy_flags flags) -+ off_t offset, struct fuse_bufvec *bufv, -+ enum fuse_buf_copy_flags flags) - { -- struct fuse_out_header out; -- struct fuse_notify_store_out outarg; -- struct iovec iov[3]; -- size_t size = fuse_buf_size(bufv); -- int res; -+ struct fuse_out_header out; -+ struct fuse_notify_store_out outarg; -+ struct iovec iov[3]; -+ size_t size = fuse_buf_size(bufv); -+ int res; - -- if (!se) -- return -EINVAL; -+ if (!se) { -+ return -EINVAL; -+ } - -- if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) -- return -ENOSYS; -+ if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) { -+ return -ENOSYS; -+ } - -- out.unique = 0; -- out.error = FUSE_NOTIFY_STORE; -+ out.unique = 0; -+ out.error = FUSE_NOTIFY_STORE; - -- outarg.nodeid = ino; -- outarg.offset = offset; -- outarg.size = size; -- outarg.padding = 0; -+ outarg.nodeid = ino; -+ outarg.offset = offset; -+ outarg.size = size; -+ outarg.padding = 0; - -- iov[0].iov_base = &out; -- iov[0].iov_len = sizeof(out); -- iov[1].iov_base = &outarg; -- iov[1].iov_len = sizeof(outarg); -+ iov[0].iov_base = &out; -+ iov[0].iov_len = sizeof(out); -+ iov[1].iov_base = &outarg; -+ iov[1].iov_len = sizeof(outarg); - -- res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags); -- if (res > 0) -- res = -res; -+ res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags); -+ if (res > 0) { -+ res = -res; -+ } - -- return res; -+ return res; - } - - struct fuse_retrieve_req { -- struct fuse_notify_req nreq; -- void *cookie; -+ struct fuse_notify_req nreq; -+ void *cookie; - }; - --static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq, -- fuse_req_t req, fuse_ino_t ino, -- const void *inarg, -- const struct fuse_buf *ibuf) --{ -- struct fuse_session *se = req->se; -- struct fuse_retrieve_req *rreq = -- container_of(nreq, struct fuse_retrieve_req, nreq); -- const struct fuse_notify_retrieve_in *arg = inarg; -- struct fuse_bufvec bufv = { -- .buf[0] = *ibuf, -- .count = 1, -- }; -- -- if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) -- bufv.buf[0].mem = PARAM(arg); -- -- bufv.buf[0].size -= sizeof(struct fuse_in_header) + -- sizeof(struct fuse_notify_retrieve_in); -- -- if (bufv.buf[0].size < arg->size) { -- fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n"); -- fuse_reply_none(req); -- goto out; -- } -- bufv.buf[0].size = arg->size; -- -- if (se->op.retrieve_reply) { -- se->op.retrieve_reply(req, rreq->cookie, ino, -- arg->offset, &bufv); -- } else { -- fuse_reply_none(req); -- } -+static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq, fuse_req_t req, -+ fuse_ino_t ino, const void *inarg, -+ const struct fuse_buf *ibuf) -+{ -+ struct fuse_session *se = req->se; -+ struct fuse_retrieve_req *rreq = -+ container_of(nreq, struct fuse_retrieve_req, nreq); -+ const struct fuse_notify_retrieve_in *arg = inarg; -+ struct fuse_bufvec bufv = { -+ .buf[0] = *ibuf, -+ .count = 1, -+ }; -+ -+ if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) { -+ bufv.buf[0].mem = PARAM(arg); -+ } -+ -+ bufv.buf[0].size -= -+ sizeof(struct fuse_in_header) + sizeof(struct fuse_notify_retrieve_in); -+ -+ if (bufv.buf[0].size < arg->size) { -+ fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n"); -+ fuse_reply_none(req); -+ goto out; -+ } -+ bufv.buf[0].size = arg->size; -+ -+ if (se->op.retrieve_reply) { -+ se->op.retrieve_reply(req, rreq->cookie, ino, arg->offset, &bufv); -+ } else { -+ fuse_reply_none(req); -+ } - out: -- free(rreq); -+ free(rreq); - } - - int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, -- size_t size, off_t offset, void *cookie) -+ size_t size, off_t offset, void *cookie) - { -- struct fuse_notify_retrieve_out outarg; -- struct iovec iov[2]; -- struct fuse_retrieve_req *rreq; -- int err; -+ struct fuse_notify_retrieve_out outarg; -+ struct iovec iov[2]; -+ struct fuse_retrieve_req *rreq; -+ int err; - -- if (!se) -- return -EINVAL; -+ if (!se) { -+ return -EINVAL; -+ } - -- if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) -- return -ENOSYS; -+ if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) { -+ return -ENOSYS; -+ } - -- rreq = malloc(sizeof(*rreq)); -- if (rreq == NULL) -- return -ENOMEM; -+ rreq = malloc(sizeof(*rreq)); -+ if (rreq == NULL) { -+ return -ENOMEM; -+ } - -- pthread_mutex_lock(&se->lock); -- rreq->cookie = cookie; -- rreq->nreq.unique = se->notify_ctr++; -- rreq->nreq.reply = fuse_ll_retrieve_reply; -- list_add_nreq(&rreq->nreq, &se->notify_list); -- pthread_mutex_unlock(&se->lock); -+ pthread_mutex_lock(&se->lock); -+ rreq->cookie = cookie; -+ rreq->nreq.unique = se->notify_ctr++; -+ rreq->nreq.reply = fuse_ll_retrieve_reply; -+ list_add_nreq(&rreq->nreq, &se->notify_list); -+ pthread_mutex_unlock(&se->lock); - -- outarg.notify_unique = rreq->nreq.unique; -- outarg.nodeid = ino; -- outarg.offset = offset; -- outarg.size = size; -- outarg.padding = 0; -+ outarg.notify_unique = rreq->nreq.unique; -+ outarg.nodeid = ino; -+ outarg.offset = offset; -+ outarg.size = size; -+ outarg.padding = 0; - -- iov[1].iov_base = &outarg; -- iov[1].iov_len = sizeof(outarg); -+ iov[1].iov_base = &outarg; -+ iov[1].iov_len = sizeof(outarg); - -- err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2); -- if (err) { -- pthread_mutex_lock(&se->lock); -- list_del_nreq(&rreq->nreq); -- pthread_mutex_unlock(&se->lock); -- free(rreq); -- } -+ err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2); -+ if (err) { -+ pthread_mutex_lock(&se->lock); -+ list_del_nreq(&rreq->nreq); -+ pthread_mutex_unlock(&se->lock); -+ free(rreq); -+ } - -- return err; -+ return err; - } - - void *fuse_req_userdata(fuse_req_t req) - { -- return req->se->userdata; -+ return req->se->userdata; - } - - const struct fuse_ctx *fuse_req_ctx(fuse_req_t req) - { -- return &req->ctx; -+ return &req->ctx; - } - - void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, -- void *data) -+ void *data) - { -- pthread_mutex_lock(&req->lock); -- pthread_mutex_lock(&req->se->lock); -- req->u.ni.func = func; -- req->u.ni.data = data; -- pthread_mutex_unlock(&req->se->lock); -- if (req->interrupted && func) -- func(req, data); -- pthread_mutex_unlock(&req->lock); -+ pthread_mutex_lock(&req->lock); -+ pthread_mutex_lock(&req->se->lock); -+ req->u.ni.func = func; -+ req->u.ni.data = data; -+ pthread_mutex_unlock(&req->se->lock); -+ if (req->interrupted && func) { -+ func(req, data); -+ } -+ pthread_mutex_unlock(&req->lock); - } - - int fuse_req_interrupted(fuse_req_t req) - { -- int interrupted; -+ int interrupted; - -- pthread_mutex_lock(&req->se->lock); -- interrupted = req->interrupted; -- pthread_mutex_unlock(&req->se->lock); -+ pthread_mutex_lock(&req->se->lock); -+ interrupted = req->interrupted; -+ pthread_mutex_unlock(&req->se->lock); - -- return interrupted; -+ return interrupted; - } - - static struct { -- void (*func)(fuse_req_t, fuse_ino_t, const void *); -- const char *name; -+ void (*func)(fuse_req_t, fuse_ino_t, const void *); -+ const char *name; - } fuse_ll_ops[] = { -- [FUSE_LOOKUP] = { do_lookup, "LOOKUP" }, -- [FUSE_FORGET] = { do_forget, "FORGET" }, -- [FUSE_GETATTR] = { do_getattr, "GETATTR" }, -- [FUSE_SETATTR] = { do_setattr, "SETATTR" }, -- [FUSE_READLINK] = { do_readlink, "READLINK" }, -- [FUSE_SYMLINK] = { do_symlink, "SYMLINK" }, -- [FUSE_MKNOD] = { do_mknod, "MKNOD" }, -- [FUSE_MKDIR] = { do_mkdir, "MKDIR" }, -- [FUSE_UNLINK] = { do_unlink, "UNLINK" }, -- [FUSE_RMDIR] = { do_rmdir, "RMDIR" }, -- [FUSE_RENAME] = { do_rename, "RENAME" }, -- [FUSE_LINK] = { do_link, "LINK" }, -- [FUSE_OPEN] = { do_open, "OPEN" }, -- [FUSE_READ] = { do_read, "READ" }, -- [FUSE_WRITE] = { do_write, "WRITE" }, -- [FUSE_STATFS] = { do_statfs, "STATFS" }, -- [FUSE_RELEASE] = { do_release, "RELEASE" }, -- [FUSE_FSYNC] = { do_fsync, "FSYNC" }, -- [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" }, -- [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" }, -- [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" }, -- [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" }, -- [FUSE_FLUSH] = { do_flush, "FLUSH" }, -- [FUSE_INIT] = { do_init, "INIT" }, -- [FUSE_OPENDIR] = { do_opendir, "OPENDIR" }, -- [FUSE_READDIR] = { do_readdir, "READDIR" }, -- [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" }, -- [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" }, -- [FUSE_GETLK] = { do_getlk, "GETLK" }, -- [FUSE_SETLK] = { do_setlk, "SETLK" }, -- [FUSE_SETLKW] = { do_setlkw, "SETLKW" }, -- [FUSE_ACCESS] = { do_access, "ACCESS" }, -- [FUSE_CREATE] = { do_create, "CREATE" }, -- [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" }, -- [FUSE_BMAP] = { do_bmap, "BMAP" }, -- [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, -- [FUSE_POLL] = { do_poll, "POLL" }, -- [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" }, -- [FUSE_DESTROY] = { do_destroy, "DESTROY" }, -- [FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" }, -- [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" }, -- [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"}, -- [FUSE_RENAME2] = { do_rename2, "RENAME2" }, -- [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" }, -- [FUSE_LSEEK] = { do_lseek, "LSEEK" }, -+ [FUSE_LOOKUP] = { do_lookup, "LOOKUP" }, -+ [FUSE_FORGET] = { do_forget, "FORGET" }, -+ [FUSE_GETATTR] = { do_getattr, "GETATTR" }, -+ [FUSE_SETATTR] = { do_setattr, "SETATTR" }, -+ [FUSE_READLINK] = { do_readlink, "READLINK" }, -+ [FUSE_SYMLINK] = { do_symlink, "SYMLINK" }, -+ [FUSE_MKNOD] = { do_mknod, "MKNOD" }, -+ [FUSE_MKDIR] = { do_mkdir, "MKDIR" }, -+ [FUSE_UNLINK] = { do_unlink, "UNLINK" }, -+ [FUSE_RMDIR] = { do_rmdir, "RMDIR" }, -+ [FUSE_RENAME] = { do_rename, "RENAME" }, -+ [FUSE_LINK] = { do_link, "LINK" }, -+ [FUSE_OPEN] = { do_open, "OPEN" }, -+ [FUSE_READ] = { do_read, "READ" }, -+ [FUSE_WRITE] = { do_write, "WRITE" }, -+ [FUSE_STATFS] = { do_statfs, "STATFS" }, -+ [FUSE_RELEASE] = { do_release, "RELEASE" }, -+ [FUSE_FSYNC] = { do_fsync, "FSYNC" }, -+ [FUSE_SETXATTR] = { do_setxattr, "SETXATTR" }, -+ [FUSE_GETXATTR] = { do_getxattr, "GETXATTR" }, -+ [FUSE_LISTXATTR] = { do_listxattr, "LISTXATTR" }, -+ [FUSE_REMOVEXATTR] = { do_removexattr, "REMOVEXATTR" }, -+ [FUSE_FLUSH] = { do_flush, "FLUSH" }, -+ [FUSE_INIT] = { do_init, "INIT" }, -+ [FUSE_OPENDIR] = { do_opendir, "OPENDIR" }, -+ [FUSE_READDIR] = { do_readdir, "READDIR" }, -+ [FUSE_RELEASEDIR] = { do_releasedir, "RELEASEDIR" }, -+ [FUSE_FSYNCDIR] = { do_fsyncdir, "FSYNCDIR" }, -+ [FUSE_GETLK] = { do_getlk, "GETLK" }, -+ [FUSE_SETLK] = { do_setlk, "SETLK" }, -+ [FUSE_SETLKW] = { do_setlkw, "SETLKW" }, -+ [FUSE_ACCESS] = { do_access, "ACCESS" }, -+ [FUSE_CREATE] = { do_create, "CREATE" }, -+ [FUSE_INTERRUPT] = { do_interrupt, "INTERRUPT" }, -+ [FUSE_BMAP] = { do_bmap, "BMAP" }, -+ [FUSE_IOCTL] = { do_ioctl, "IOCTL" }, -+ [FUSE_POLL] = { do_poll, "POLL" }, -+ [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" }, -+ [FUSE_DESTROY] = { do_destroy, "DESTROY" }, -+ [FUSE_NOTIFY_REPLY] = { (void *)1, "NOTIFY_REPLY" }, -+ [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" }, -+ [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS" }, -+ [FUSE_RENAME2] = { do_rename2, "RENAME2" }, -+ [FUSE_COPY_FILE_RANGE] = { do_copy_file_range, "COPY_FILE_RANGE" }, -+ [FUSE_LSEEK] = { do_lseek, "LSEEK" }, - }; - - #define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0])) - - static const char *opname(enum fuse_opcode opcode) - { -- if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name) -- return "???"; -- else -- return fuse_ll_ops[opcode].name; -+ if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name) { -+ return "???"; -+ } else { -+ return fuse_ll_ops[opcode].name; -+ } - } - - void fuse_session_process_buf(struct fuse_session *se, -- const struct fuse_buf *buf) -+ const struct fuse_buf *buf) - { -- fuse_session_process_buf_int(se, buf, NULL); -+ fuse_session_process_buf_int(se, buf, NULL); - } - - void fuse_session_process_buf_int(struct fuse_session *se, -- const struct fuse_buf *buf, struct fuse_chan *ch) --{ -- struct fuse_in_header *in; -- const void *inarg; -- struct fuse_req *req; -- int err; -- -- in = buf->mem; -- -- if (se->debug) { -- fuse_log(FUSE_LOG_DEBUG, -- "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n", -- (unsigned long long) in->unique, -- opname((enum fuse_opcode) in->opcode), in->opcode, -- (unsigned long long) in->nodeid, buf->size, in->pid); -- } -- -- req = fuse_ll_alloc_req(se); -- if (req == NULL) { -- struct fuse_out_header out = { -- .unique = in->unique, -- .error = -ENOMEM, -- }; -- struct iovec iov = { -- .iov_base = &out, -- .iov_len = sizeof(struct fuse_out_header), -- }; -- -- fuse_send_msg(se, ch, &iov, 1); -- return; -- } -- -- req->unique = in->unique; -- req->ctx.uid = in->uid; -- req->ctx.gid = in->gid; -- req->ctx.pid = in->pid; -- req->ch = ch; -- -- err = EIO; -- if (!se->got_init) { -- enum fuse_opcode expected; -- -- expected = se->cuse_data ? CUSE_INIT : FUSE_INIT; -- if (in->opcode != expected) -- goto reply_err; -- } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT) -- goto reply_err; -- -- err = EACCES; -- /* Implement -o allow_root */ -- if (se->deny_others && in->uid != se->owner && in->uid != 0 && -- in->opcode != FUSE_INIT && in->opcode != FUSE_READ && -- in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC && -- in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR && -- in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR && -- in->opcode != FUSE_NOTIFY_REPLY && -- in->opcode != FUSE_READDIRPLUS) -- goto reply_err; -- -- err = ENOSYS; -- if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) -- goto reply_err; -- if (in->opcode != FUSE_INTERRUPT) { -- struct fuse_req *intr; -- pthread_mutex_lock(&se->lock); -- intr = check_interrupt(se, req); -- list_add_req(req, &se->list); -- pthread_mutex_unlock(&se->lock); -- if (intr) -- fuse_reply_err(intr, EAGAIN); -- } -- -- inarg = (void *) &in[1]; -- if (in->opcode == FUSE_WRITE && se->op.write_buf) -- do_write_buf(req, in->nodeid, inarg, buf); -- else if (in->opcode == FUSE_NOTIFY_REPLY) -- do_notify_reply(req, in->nodeid, inarg, buf); -- else -- fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); -- -- return; -+ const struct fuse_buf *buf, -+ struct fuse_chan *ch) -+{ -+ struct fuse_in_header *in; -+ const void *inarg; -+ struct fuse_req *req; -+ int err; -+ -+ in = buf->mem; -+ -+ if (se->debug) { -+ fuse_log(FUSE_LOG_DEBUG, -+ "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, " -+ "pid: %u\n", -+ (unsigned long long)in->unique, -+ opname((enum fuse_opcode)in->opcode), in->opcode, -+ (unsigned long long)in->nodeid, buf->size, in->pid); -+ } -+ -+ req = fuse_ll_alloc_req(se); -+ if (req == NULL) { -+ struct fuse_out_header out = { -+ .unique = in->unique, -+ .error = -ENOMEM, -+ }; -+ struct iovec iov = { -+ .iov_base = &out, -+ .iov_len = sizeof(struct fuse_out_header), -+ }; -+ -+ fuse_send_msg(se, ch, &iov, 1); -+ return; -+ } -+ -+ req->unique = in->unique; -+ req->ctx.uid = in->uid; -+ req->ctx.gid = in->gid; -+ req->ctx.pid = in->pid; -+ req->ch = ch; -+ -+ err = EIO; -+ if (!se->got_init) { -+ enum fuse_opcode expected; -+ -+ expected = se->cuse_data ? CUSE_INIT : FUSE_INIT; -+ if (in->opcode != expected) { -+ goto reply_err; -+ } -+ } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT) { -+ goto reply_err; -+ } -+ -+ err = EACCES; -+ /* Implement -o allow_root */ -+ if (se->deny_others && in->uid != se->owner && in->uid != 0 && -+ in->opcode != FUSE_INIT && in->opcode != FUSE_READ && -+ in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC && -+ in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR && -+ in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR && -+ in->opcode != FUSE_NOTIFY_REPLY && in->opcode != FUSE_READDIRPLUS) { -+ goto reply_err; -+ } -+ -+ err = ENOSYS; -+ if (in->opcode >= FUSE_MAXOP || !fuse_ll_ops[in->opcode].func) { -+ goto reply_err; -+ } -+ if (in->opcode != FUSE_INTERRUPT) { -+ struct fuse_req *intr; -+ pthread_mutex_lock(&se->lock); -+ intr = check_interrupt(se, req); -+ list_add_req(req, &se->list); -+ pthread_mutex_unlock(&se->lock); -+ if (intr) { -+ fuse_reply_err(intr, EAGAIN); -+ } -+ } -+ -+ inarg = (void *)&in[1]; -+ if (in->opcode == FUSE_WRITE && se->op.write_buf) { -+ do_write_buf(req, in->nodeid, inarg, buf); -+ } else if (in->opcode == FUSE_NOTIFY_REPLY) { -+ do_notify_reply(req, in->nodeid, inarg, buf); -+ } else { -+ fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); -+ } -+ -+ return; - - reply_err: -- fuse_reply_err(req, err); -+ fuse_reply_err(req, err); - } - --#define LL_OPTION(n,o,v) \ -- { n, offsetof(struct fuse_session, o), v } -+#define LL_OPTION(n, o, v) \ -+ { \ -+ n, offsetof(struct fuse_session, o), v \ -+ } - - static const struct fuse_opt fuse_ll_opts[] = { -- LL_OPTION("debug", debug, 1), -- LL_OPTION("-d", debug, 1), -- LL_OPTION("--debug", debug, 1), -- LL_OPTION("allow_root", deny_others, 1), -- FUSE_OPT_END -+ LL_OPTION("debug", debug, 1), LL_OPTION("-d", debug, 1), -+ LL_OPTION("--debug", debug, 1), LL_OPTION("allow_root", deny_others, 1), -+ FUSE_OPT_END - }; - - void fuse_lowlevel_version(void) - { -- printf("using FUSE kernel interface version %i.%i\n", -- FUSE_KERNEL_VERSION, FUSE_KERNEL_MINOR_VERSION); -+ printf("using FUSE kernel interface version %i.%i\n", FUSE_KERNEL_VERSION, -+ FUSE_KERNEL_MINOR_VERSION); - } - - void fuse_lowlevel_help(void) - { -- /* These are not all options, but the ones that are -- potentially of interest to an end-user */ -- printf( --" -o allow_root allow access by root\n" --); -+ /* -+ * These are not all options, but the ones that are -+ * potentially of interest to an end-user -+ */ -+ printf(" -o allow_root allow access by root\n"); - } - - void fuse_session_destroy(struct fuse_session *se) - { -- if (se->got_init && !se->got_destroy) { -- if (se->op.destroy) -- se->op.destroy(se->userdata); -- } -- pthread_mutex_destroy(&se->lock); -- free(se->cuse_data); -- if (se->fd != -1) -- close(se->fd); -- free(se); -+ if (se->got_init && !se->got_destroy) { -+ if (se->op.destroy) { -+ se->op.destroy(se->userdata); -+ } -+ } -+ pthread_mutex_destroy(&se->lock); -+ free(se->cuse_data); -+ if (se->fd != -1) { -+ close(se->fd); -+ } -+ free(se); - } - - - struct fuse_session *fuse_session_new(struct fuse_args *args, -- const struct fuse_lowlevel_ops *op, -- size_t op_size, void *userdata) --{ -- struct fuse_session *se; -- -- if (sizeof(struct fuse_lowlevel_ops) < op_size) { -- fuse_log(FUSE_LOG_ERR, "fuse: warning: library too old, some operations may not work\n"); -- op_size = sizeof(struct fuse_lowlevel_ops); -- } -- -- if (args->argc == 0) { -- fuse_log(FUSE_LOG_ERR, "fuse: empty argv passed to fuse_session_new().\n"); -- return NULL; -- } -- -- se = (struct fuse_session *) calloc(1, sizeof(struct fuse_session)); -- if (se == NULL) { -- fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n"); -- goto out1; -- } -- se->fd = -1; -- se->conn.max_write = UINT_MAX; -- se->conn.max_readahead = UINT_MAX; -- -- /* Parse options */ -- if(fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1) -- goto out2; -- if(args->argc == 1 && -- args->argv[0][0] == '-') { -- fuse_log(FUSE_LOG_ERR, "fuse: warning: argv[0] looks like an option, but " -- "will be ignored\n"); -- } else if (args->argc != 1) { -- int i; -- fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `"); -- for(i = 1; i < args->argc-1; i++) -- fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]); -- fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]); -- goto out4; -- } -- -- se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() + -- FUSE_BUFFER_HEADER_SIZE; -- -- list_init_req(&se->list); -- list_init_req(&se->interrupts); -- list_init_nreq(&se->notify_list); -- se->notify_ctr = 1; -- fuse_mutex_init(&se->lock); -- -- memcpy(&se->op, op, op_size); -- se->owner = getuid(); -- se->userdata = userdata; -- -- return se; -+ const struct fuse_lowlevel_ops *op, -+ size_t op_size, void *userdata) -+{ -+ struct fuse_session *se; -+ -+ if (sizeof(struct fuse_lowlevel_ops) < op_size) { -+ fuse_log( -+ FUSE_LOG_ERR, -+ "fuse: warning: library too old, some operations may not work\n"); -+ op_size = sizeof(struct fuse_lowlevel_ops); -+ } -+ -+ if (args->argc == 0) { -+ fuse_log(FUSE_LOG_ERR, -+ "fuse: empty argv passed to fuse_session_new().\n"); -+ return NULL; -+ } -+ -+ se = (struct fuse_session *)calloc(1, sizeof(struct fuse_session)); -+ if (se == NULL) { -+ fuse_log(FUSE_LOG_ERR, "fuse: failed to allocate fuse object\n"); -+ goto out1; -+ } -+ se->fd = -1; -+ se->conn.max_write = UINT_MAX; -+ se->conn.max_readahead = UINT_MAX; -+ -+ /* Parse options */ -+ if (fuse_opt_parse(args, se, fuse_ll_opts, NULL) == -1) { -+ goto out2; -+ } -+ if (args->argc == 1 && args->argv[0][0] == '-') { -+ fuse_log(FUSE_LOG_ERR, -+ "fuse: warning: argv[0] looks like an option, but " -+ "will be ignored\n"); -+ } else if (args->argc != 1) { -+ int i; -+ fuse_log(FUSE_LOG_ERR, "fuse: unknown option(s): `"); -+ for (i = 1; i < args->argc - 1; i++) { -+ fuse_log(FUSE_LOG_ERR, "%s ", args->argv[i]); -+ } -+ fuse_log(FUSE_LOG_ERR, "%s'\n", args->argv[i]); -+ goto out4; -+ } -+ -+ se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() + FUSE_BUFFER_HEADER_SIZE; -+ -+ list_init_req(&se->list); -+ list_init_req(&se->interrupts); -+ list_init_nreq(&se->notify_list); -+ se->notify_ctr = 1; -+ fuse_mutex_init(&se->lock); -+ -+ memcpy(&se->op, op, op_size); -+ se->owner = getuid(); -+ se->userdata = userdata; -+ -+ return se; - - out4: -- fuse_opt_free_args(args); -+ fuse_opt_free_args(args); - out2: -- free(se); -+ free(se); - out1: -- return NULL; -+ return NULL; - } - - int fuse_session_mount(struct fuse_session *se, const char *mountpoint) - { -- int fd; -- -- /* -- * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos -- * would ensue. -- */ -- do { -- fd = open("/dev/null", O_RDWR); -- if (fd > 2) -- close(fd); -- } while (fd >= 0 && fd <= 2); -- -- /* -- * To allow FUSE daemons to run without privileges, the caller may open -- * /dev/fuse before launching the file system and pass on the file -- * descriptor by specifying /dev/fd/N as the mount point. Note that the -- * parent process takes care of performing the mount in this case. -- */ -- fd = fuse_mnt_parse_fuse_fd(mountpoint); -- if (fd != -1) { -- if (fcntl(fd, F_GETFD) == -1) { -- fuse_log(FUSE_LOG_ERR, -- "fuse: Invalid file descriptor /dev/fd/%u\n", -- fd); -- return -1; -- } -- se->fd = fd; -- return 0; -- } -- -- /* Open channel */ -- fd = fuse_kern_mount(mountpoint, se->mo); -- if (fd == -1) -- return -1; -- se->fd = fd; -- -- /* Save mountpoint */ -- se->mountpoint = strdup(mountpoint); -- if (se->mountpoint == NULL) -- goto error_out; -- -- return 0; -+ int fd; -+ -+ /* -+ * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos -+ * would ensue. -+ */ -+ do { -+ fd = open("/dev/null", O_RDWR); -+ if (fd > 2) { -+ close(fd); -+ } -+ } while (fd >= 0 && fd <= 2); -+ -+ /* -+ * To allow FUSE daemons to run without privileges, the caller may open -+ * /dev/fuse before launching the file system and pass on the file -+ * descriptor by specifying /dev/fd/N as the mount point. Note that the -+ * parent process takes care of performing the mount in this case. -+ */ -+ fd = fuse_mnt_parse_fuse_fd(mountpoint); -+ if (fd != -1) { -+ if (fcntl(fd, F_GETFD) == -1) { -+ fuse_log(FUSE_LOG_ERR, "fuse: Invalid file descriptor /dev/fd/%u\n", -+ fd); -+ return -1; -+ } -+ se->fd = fd; -+ return 0; -+ } -+ -+ /* Open channel */ -+ fd = fuse_kern_mount(mountpoint, se->mo); -+ if (fd == -1) { -+ return -1; -+ } -+ se->fd = fd; -+ -+ /* Save mountpoint */ -+ se->mountpoint = strdup(mountpoint); -+ if (se->mountpoint == NULL) { -+ goto error_out; -+ } -+ -+ return 0; - - error_out: -- fuse_kern_unmount(mountpoint, fd); -- return -1; -+ fuse_kern_unmount(mountpoint, fd); -+ return -1; - } - - int fuse_session_fd(struct fuse_session *se) - { -- return se->fd; -+ return se->fd; - } - - void fuse_session_unmount(struct fuse_session *se) -@@ -2384,61 +2519,66 @@ void fuse_session_unmount(struct fuse_session *se) - #ifdef linux - int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) - { -- char *buf; -- size_t bufsize = 1024; -- char path[128]; -- int ret; -- int fd; -- unsigned long pid = req->ctx.pid; -- char *s; -+ char *buf; -+ size_t bufsize = 1024; -+ char path[128]; -+ int ret; -+ int fd; -+ unsigned long pid = req->ctx.pid; -+ char *s; - -- sprintf(path, "/proc/%lu/task/%lu/status", pid, pid); -+ sprintf(path, "/proc/%lu/task/%lu/status", pid, pid); - - retry: -- buf = malloc(bufsize); -- if (buf == NULL) -- return -ENOMEM; -- -- ret = -EIO; -- fd = open(path, O_RDONLY); -- if (fd == -1) -- goto out_free; -- -- ret = read(fd, buf, bufsize); -- close(fd); -- if (ret < 0) { -- ret = -EIO; -- goto out_free; -- } -- -- if ((size_t)ret == bufsize) { -- free(buf); -- bufsize *= 4; -- goto retry; -- } -- -- ret = -EIO; -- s = strstr(buf, "\nGroups:"); -- if (s == NULL) -- goto out_free; -- -- s += 8; -- ret = 0; -- while (1) { -- char *end; -- unsigned long val = strtoul(s, &end, 0); -- if (end == s) -- break; -- -- s = end; -- if (ret < size) -- list[ret] = val; -- ret++; -- } -+ buf = malloc(bufsize); -+ if (buf == NULL) { -+ return -ENOMEM; -+ } -+ -+ ret = -EIO; -+ fd = open(path, O_RDONLY); -+ if (fd == -1) { -+ goto out_free; -+ } -+ -+ ret = read(fd, buf, bufsize); -+ close(fd); -+ if (ret < 0) { -+ ret = -EIO; -+ goto out_free; -+ } -+ -+ if ((size_t)ret == bufsize) { -+ free(buf); -+ bufsize *= 4; -+ goto retry; -+ } -+ -+ ret = -EIO; -+ s = strstr(buf, "\nGroups:"); -+ if (s == NULL) { -+ goto out_free; -+ } -+ -+ s += 8; -+ ret = 0; -+ while (1) { -+ char *end; -+ unsigned long val = strtoul(s, &end, 0); -+ if (end == s) { -+ break; -+ } -+ -+ s = end; -+ if (ret < size) { -+ list[ret] = val; -+ } -+ ret++; -+ } - - out_free: -- free(buf); -- return ret; -+ free(buf); -+ return ret; - } - #else /* linux */ - /* -@@ -2446,23 +2586,25 @@ out_free: - */ - int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) - { -- (void) req; (void) size; (void) list; -- return -ENOSYS; -+ (void)req; -+ (void)size; -+ (void)list; -+ return -ENOSYS; - } - #endif - - void fuse_session_exit(struct fuse_session *se) - { -- se->exited = 1; -+ se->exited = 1; - } - - void fuse_session_reset(struct fuse_session *se) - { -- se->exited = 0; -- se->error = 0; -+ se->exited = 0; -+ se->error = 0; - } - - int fuse_session_exited(struct fuse_session *se) - { -- return se->exited; -+ return se->exited; - } -diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h -index 6b1adfcfd1..adb9054bb1 100644 ---- a/tools/virtiofsd/fuse_lowlevel.h -+++ b/tools/virtiofsd/fuse_lowlevel.h -@@ -1,15 +1,16 @@ - /* -- FUSE: Filesystem in Userspace -- Copyright (C) 2001-2007 Miklos Szeredi -- -- This program can be distributed under the terms of the GNU LGPLv2. -- See the file COPYING.LIB. --*/ -+ * FUSE: Filesystem in Userspace -+ * Copyright (C) 2001-2007 Miklos Szeredi -+ * -+ * This program can be distributed under the terms of the GNU LGPLv2. -+ * See the file COPYING.LIB. -+ */ - - #ifndef FUSE_LOWLEVEL_H_ - #define FUSE_LOWLEVEL_H_ - --/** @file -+/** -+ * @file - * - * Low level API - * -@@ -24,16 +25,16 @@ - - #include "fuse_common.h" - --#include - #include --#include - #include - #include -+#include - #include -+#include - --/* ----------------------------------------------------------- * -- * Miscellaneous definitions * -- * ----------------------------------------------------------- */ -+/* -+ * Miscellaneous definitions -+ */ - - /** The node ID of the root inode */ - #define FUSE_ROOT_ID 1 -@@ -53,47 +54,54 @@ struct fuse_session; - - /** Directory entry parameters supplied to fuse_reply_entry() */ - struct fuse_entry_param { -- /** Unique inode number -- * -- * In lookup, zero means negative entry (from version 2.5) -- * Returning ENOENT also means negative entry, but by setting zero -- * ino the kernel may cache negative entries for entry_timeout -- * seconds. -- */ -- fuse_ino_t ino; -- -- /** Generation number for this entry. -- * -- * If the file system will be exported over NFS, the -- * ino/generation pairs need to be unique over the file -- * system's lifetime (rather than just the mount time). So if -- * the file system reuses an inode after it has been deleted, -- * it must assign a new, previously unused generation number -- * to the inode at the same time. -- * -- */ -- uint64_t generation; -- -- /** Inode attributes. -- * -- * Even if attr_timeout == 0, attr must be correct. For example, -- * for open(), FUSE uses attr.st_size from lookup() to determine -- * how many bytes to request. If this value is not correct, -- * incorrect data will be returned. -- */ -- struct stat attr; -- -- /** Validity timeout (in seconds) for inode attributes. If -- attributes only change as a result of requests that come -- through the kernel, this should be set to a very large -- value. */ -- double attr_timeout; -- -- /** Validity timeout (in seconds) for the name. If directory -- entries are changed/deleted only as a result of requests -- that come through the kernel, this should be set to a very -- large value. */ -- double entry_timeout; -+ /** -+ * Unique inode number -+ * -+ * In lookup, zero means negative entry (from version 2.5) -+ * Returning ENOENT also means negative entry, but by setting zero -+ * ino the kernel may cache negative entries for entry_timeout -+ * seconds. -+ */ -+ fuse_ino_t ino; -+ -+ /** -+ * Generation number for this entry. -+ * -+ * If the file system will be exported over NFS, the -+ * ino/generation pairs need to be unique over the file -+ * system's lifetime (rather than just the mount time). So if -+ * the file system reuses an inode after it has been deleted, -+ * it must assign a new, previously unused generation number -+ * to the inode at the same time. -+ * -+ */ -+ uint64_t generation; -+ -+ /** -+ * Inode attributes. -+ * -+ * Even if attr_timeout == 0, attr must be correct. For example, -+ * for open(), FUSE uses attr.st_size from lookup() to determine -+ * how many bytes to request. If this value is not correct, -+ * incorrect data will be returned. -+ */ -+ struct stat attr; -+ -+ /** -+ * Validity timeout (in seconds) for inode attributes. If -+ * attributes only change as a result of requests that come -+ * through the kernel, this should be set to a very large -+ * value. -+ */ -+ double attr_timeout; -+ -+ /** -+ * Validity timeout (in seconds) for the name. If directory -+ * entries are changed/deleted only as a result of requests -+ * that come through the kernel, this should be set to a very -+ * large value. -+ */ -+ double entry_timeout; - }; - - /** -@@ -105,38 +113,38 @@ struct fuse_entry_param { - * there is no valid uid/pid/gid that could be reported. - */ - struct fuse_ctx { -- /** User ID of the calling process */ -- uid_t uid; -+ /** User ID of the calling process */ -+ uid_t uid; - -- /** Group ID of the calling process */ -- gid_t gid; -+ /** Group ID of the calling process */ -+ gid_t gid; - -- /** Thread ID of the calling process */ -- pid_t pid; -+ /** Thread ID of the calling process */ -+ pid_t pid; - -- /** Umask of the calling process */ -- mode_t umask; -+ /** Umask of the calling process */ -+ mode_t umask; - }; - - struct fuse_forget_data { -- fuse_ino_t ino; -- uint64_t nlookup; -+ fuse_ino_t ino; -+ uint64_t nlookup; - }; - - /* 'to_set' flags in setattr */ --#define FUSE_SET_ATTR_MODE (1 << 0) --#define FUSE_SET_ATTR_UID (1 << 1) --#define FUSE_SET_ATTR_GID (1 << 2) --#define FUSE_SET_ATTR_SIZE (1 << 3) --#define FUSE_SET_ATTR_ATIME (1 << 4) --#define FUSE_SET_ATTR_MTIME (1 << 5) --#define FUSE_SET_ATTR_ATIME_NOW (1 << 7) --#define FUSE_SET_ATTR_MTIME_NOW (1 << 8) --#define FUSE_SET_ATTR_CTIME (1 << 10) -- --/* ----------------------------------------------------------- * -- * Request methods and replies * -- * ----------------------------------------------------------- */ -+#define FUSE_SET_ATTR_MODE (1 << 0) -+#define FUSE_SET_ATTR_UID (1 << 1) -+#define FUSE_SET_ATTR_GID (1 << 2) -+#define FUSE_SET_ATTR_SIZE (1 << 3) -+#define FUSE_SET_ATTR_ATIME (1 << 4) -+#define FUSE_SET_ATTR_MTIME (1 << 5) -+#define FUSE_SET_ATTR_ATIME_NOW (1 << 7) -+#define FUSE_SET_ATTR_MTIME_NOW (1 << 8) -+#define FUSE_SET_ATTR_CTIME (1 << 10) -+ -+/* -+ * Request methods and replies -+ */ - - /** - * Low level filesystem operations -@@ -166,1075 +174,1069 @@ struct fuse_forget_data { - * this file will not be called. - */ - struct fuse_lowlevel_ops { -- /** -- * Initialize filesystem -- * -- * This function is called when libfuse establishes -- * communication with the FUSE kernel module. The file system -- * should use this module to inspect and/or modify the -- * connection parameters provided in the `conn` structure. -- * -- * Note that some parameters may be overwritten by options -- * passed to fuse_session_new() which take precedence over the -- * values set in this handler. -- * -- * There's no reply to this function -- * -- * @param userdata the user data passed to fuse_session_new() -- */ -- void (*init) (void *userdata, struct fuse_conn_info *conn); -- -- /** -- * Clean up filesystem. -- * -- * Called on filesystem exit. When this method is called, the -- * connection to the kernel may be gone already, so that eg. calls -- * to fuse_lowlevel_notify_* will fail. -- * -- * There's no reply to this function -- * -- * @param userdata the user data passed to fuse_session_new() -- */ -- void (*destroy) (void *userdata); -- -- /** -- * Look up a directory entry by name and get its attributes. -- * -- * Valid replies: -- * fuse_reply_entry -- * fuse_reply_err -- * -- * @param req request handle -- * @param parent inode number of the parent directory -- * @param name the name to look up -- */ -- void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name); -- -- /** -- * Forget about an inode -- * -- * This function is called when the kernel removes an inode -- * from its internal caches. -- * -- * The inode's lookup count increases by one for every call to -- * fuse_reply_entry and fuse_reply_create. The nlookup parameter -- * indicates by how much the lookup count should be decreased. -- * -- * Inodes with a non-zero lookup count may receive request from -- * the kernel even after calls to unlink, rmdir or (when -- * overwriting an existing file) rename. Filesystems must handle -- * such requests properly and it is recommended to defer removal -- * of the inode until the lookup count reaches zero. Calls to -- * unlink, rmdir or rename will be followed closely by forget -- * unless the file or directory is open, in which case the -- * kernel issues forget only after the release or releasedir -- * calls. -- * -- * Note that if a file system will be exported over NFS the -- * inodes lifetime must extend even beyond forget. See the -- * generation field in struct fuse_entry_param above. -- * -- * On unmount the lookup count for all inodes implicitly drops -- * to zero. It is not guaranteed that the file system will -- * receive corresponding forget messages for the affected -- * inodes. -- * -- * Valid replies: -- * fuse_reply_none -- * -- * @param req request handle -- * @param ino the inode number -- * @param nlookup the number of lookups to forget -- */ -- void (*forget) (fuse_req_t req, fuse_ino_t ino, uint64_t nlookup); -- -- /** -- * Get file attributes. -- * -- * If writeback caching is enabled, the kernel may have a -- * better idea of a file's length than the FUSE file system -- * (eg if there has been a write that extended the file size, -- * but that has not yet been passed to the filesystem.n -- * -- * In this case, the st_size value provided by the file system -- * will be ignored. -- * -- * Valid replies: -- * fuse_reply_attr -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param fi for future use, currently always NULL -- */ -- void (*getattr) (fuse_req_t req, fuse_ino_t ino, -- struct fuse_file_info *fi); -- -- /** -- * Set file attributes -- * -- * In the 'attr' argument only members indicated by the 'to_set' -- * bitmask contain valid values. Other members contain undefined -- * values. -- * -- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -- * expected to reset the setuid and setgid bits if the file -- * size or owner is being changed. -- * -- * If the setattr was invoked from the ftruncate() system call -- * under Linux kernel versions 2.6.15 or later, the fi->fh will -- * contain the value set by the open method or will be undefined -- * if the open method didn't set any value. Otherwise (not -- * ftruncate call, or kernel version earlier than 2.6.15) the fi -- * parameter will be NULL. -- * -- * Valid replies: -- * fuse_reply_attr -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param attr the attributes -- * @param to_set bit mask of attributes which should be set -- * @param fi file information, or NULL -- */ -- void (*setattr) (fuse_req_t req, fuse_ino_t ino, struct stat *attr, -- int to_set, struct fuse_file_info *fi); -- -- /** -- * Read symbolic link -- * -- * Valid replies: -- * fuse_reply_readlink -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- */ -- void (*readlink) (fuse_req_t req, fuse_ino_t ino); -- -- /** -- * Create file node -- * -- * Create a regular file, character device, block device, fifo or -- * socket node. -- * -- * Valid replies: -- * fuse_reply_entry -- * fuse_reply_err -- * -- * @param req request handle -- * @param parent inode number of the parent directory -- * @param name to create -- * @param mode file type and mode with which to create the new file -- * @param rdev the device number (only valid if created file is a device) -- */ -- void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name, -- mode_t mode, dev_t rdev); -- -- /** -- * Create a directory -- * -- * Valid replies: -- * fuse_reply_entry -- * fuse_reply_err -- * -- * @param req request handle -- * @param parent inode number of the parent directory -- * @param name to create -- * @param mode with which to create the new file -- */ -- void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name, -- mode_t mode); -- -- /** -- * Remove a file -- * -- * If the file's inode's lookup count is non-zero, the file -- * system is expected to postpone any removal of the inode -- * until the lookup count reaches zero (see description of the -- * forget function). -- * -- * Valid replies: -- * fuse_reply_err -- * -- * @param req request handle -- * @param parent inode number of the parent directory -- * @param name to remove -- */ -- void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name); -- -- /** -- * Remove a directory -- * -- * If the directory's inode's lookup count is non-zero, the -- * file system is expected to postpone any removal of the -- * inode until the lookup count reaches zero (see description -- * of the forget function). -- * -- * Valid replies: -- * fuse_reply_err -- * -- * @param req request handle -- * @param parent inode number of the parent directory -- * @param name to remove -- */ -- void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name); -- -- /** -- * Create a symbolic link -- * -- * Valid replies: -- * fuse_reply_entry -- * fuse_reply_err -- * -- * @param req request handle -- * @param link the contents of the symbolic link -- * @param parent inode number of the parent directory -- * @param name to create -- */ -- void (*symlink) (fuse_req_t req, const char *link, fuse_ino_t parent, -- const char *name); -- -- /** Rename a file -- * -- * If the target exists it should be atomically replaced. If -- * the target's inode's lookup count is non-zero, the file -- * system is expected to postpone any removal of the inode -- * until the lookup count reaches zero (see description of the -- * forget function). -- * -- * If this request is answered with an error code of ENOSYS, this is -- * treated as a permanent failure with error code EINVAL, i.e. all -- * future bmap requests will fail with EINVAL without being -- * send to the filesystem process. -- * -- * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If -- * RENAME_NOREPLACE is specified, the filesystem must not -- * overwrite *newname* if it exists and return an error -- * instead. If `RENAME_EXCHANGE` is specified, the filesystem -- * must atomically exchange the two files, i.e. both must -- * exist and neither may be deleted. -- * -- * Valid replies: -- * fuse_reply_err -- * -- * @param req request handle -- * @param parent inode number of the old parent directory -- * @param name old name -- * @param newparent inode number of the new parent directory -- * @param newname new name -- */ -- void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name, -- fuse_ino_t newparent, const char *newname, -- unsigned int flags); -- -- /** -- * Create a hard link -- * -- * Valid replies: -- * fuse_reply_entry -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the old inode number -- * @param newparent inode number of the new parent directory -- * @param newname new name to create -- */ -- void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, -- const char *newname); -- -- /** -- * Open a file -- * -- * Open flags are available in fi->flags. The following rules -- * apply. -- * -- * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be -- * filtered out / handled by the kernel. -- * -- * - Access modes (O_RDONLY, O_WRONLY, O_RDWR) should be used -- * by the filesystem to check if the operation is -- * permitted. If the ``-o default_permissions`` mount -- * option is given, this check is already done by the -- * kernel before calling open() and may thus be omitted by -- * the filesystem. -- * -- * - When writeback caching is enabled, the kernel may send -- * read requests even for files opened with O_WRONLY. The -- * filesystem should be prepared to handle this. -- * -- * - When writeback caching is disabled, the filesystem is -- * expected to properly handle the O_APPEND flag and ensure -- * that each write is appending to the end of the file. -- * -- * - When writeback caching is enabled, the kernel will -- * handle O_APPEND. However, unless all changes to the file -- * come through the kernel this will not work reliably. The -- * filesystem should thus either ignore the O_APPEND flag -- * (and let the kernel handle it), or return an error -- * (indicating that reliably O_APPEND is not available). -- * -- * Filesystem may store an arbitrary file handle (pointer, -- * index, etc) in fi->fh, and use this in other all other file -- * operations (read, write, flush, release, fsync). -- * -- * Filesystem may also implement stateless file I/O and not store -- * anything in fi->fh. -- * -- * There are also some flags (direct_io, keep_cache) which the -- * filesystem may set in fi, to change the way the file is opened. -- * See fuse_file_info structure in for more details. -- * -- * If this request is answered with an error code of ENOSYS -- * and FUSE_CAP_NO_OPEN_SUPPORT is set in -- * `fuse_conn_info.capable`, this is treated as success and -- * future calls to open and release will also succeed without being -- * sent to the filesystem process. -- * -- * Valid replies: -- * fuse_reply_open -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param fi file information -- */ -- void (*open) (fuse_req_t req, fuse_ino_t ino, -- struct fuse_file_info *fi); -- -- /** -- * Read data -- * -- * Read should send exactly the number of bytes requested except -- * on EOF or error, otherwise the rest of the data will be -- * substituted with zeroes. An exception to this is when the file -- * has been opened in 'direct_io' mode, in which case the return -- * value of the read system call will reflect the return value of -- * this operation. -- * -- * fi->fh will contain the value set by the open method, or will -- * be undefined if the open method didn't set any value. -- * -- * Valid replies: -- * fuse_reply_buf -- * fuse_reply_iov -- * fuse_reply_data -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param size number of bytes to read -- * @param off offset to read from -- * @param fi file information -- */ -- void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, -- struct fuse_file_info *fi); -- -- /** -- * Write data -- * -- * Write should return exactly the number of bytes requested -- * except on error. An exception to this is when the file has -- * been opened in 'direct_io' mode, in which case the return value -- * of the write system call will reflect the return value of this -- * operation. -- * -- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -- * expected to reset the setuid and setgid bits. -- * -- * fi->fh will contain the value set by the open method, or will -- * be undefined if the open method didn't set any value. -- * -- * Valid replies: -- * fuse_reply_write -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param buf data to write -- * @param size number of bytes to write -- * @param off offset to write to -- * @param fi file information -- */ -- void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf, -- size_t size, off_t off, struct fuse_file_info *fi); -- -- /** -- * Flush method -- * -- * This is called on each close() of the opened file. -- * -- * Since file descriptors can be duplicated (dup, dup2, fork), for -- * one open call there may be many flush calls. -- * -- * Filesystems shouldn't assume that flush will always be called -- * after some writes, or that if will be called at all. -- * -- * fi->fh will contain the value set by the open method, or will -- * be undefined if the open method didn't set any value. -- * -- * NOTE: the name of the method is misleading, since (unlike -- * fsync) the filesystem is not forced to flush pending writes. -- * One reason to flush data is if the filesystem wants to return -- * write errors during close. However, such use is non-portable -- * because POSIX does not require [close] to wait for delayed I/O to -- * complete. -- * -- * If the filesystem supports file locking operations (setlk, -- * getlk) it should remove all locks belonging to 'fi->owner'. -- * -- * If this request is answered with an error code of ENOSYS, -- * this is treated as success and future calls to flush() will -- * succeed automatically without being send to the filesystem -- * process. -- * -- * Valid replies: -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param fi file information -- * -- * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html -- */ -- void (*flush) (fuse_req_t req, fuse_ino_t ino, -- struct fuse_file_info *fi); -- -- /** -- * Release an open file -- * -- * Release is called when there are no more references to an open -- * file: all file descriptors are closed and all memory mappings -- * are unmapped. -- * -- * For every open call there will be exactly one release call (unless -- * the filesystem is force-unmounted). -- * -- * The filesystem may reply with an error, but error values are -- * not returned to close() or munmap() which triggered the -- * release. -- * -- * fi->fh will contain the value set by the open method, or will -- * be undefined if the open method didn't set any value. -- * fi->flags will contain the same flags as for open. -- * -- * Valid replies: -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param fi file information -- */ -- void (*release) (fuse_req_t req, fuse_ino_t ino, -- struct fuse_file_info *fi); -- -- /** -- * Synchronize file contents -- * -- * If the datasync parameter is non-zero, then only the user data -- * should be flushed, not the meta data. -- * -- * If this request is answered with an error code of ENOSYS, -- * this is treated as success and future calls to fsync() will -- * succeed automatically without being send to the filesystem -- * process. -- * -- * Valid replies: -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param datasync flag indicating if only data should be flushed -- * @param fi file information -- */ -- void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync, -- struct fuse_file_info *fi); -- -- /** -- * Open a directory -- * -- * Filesystem may store an arbitrary file handle (pointer, index, -- * etc) in fi->fh, and use this in other all other directory -- * stream operations (readdir, releasedir, fsyncdir). -- * -- * If this request is answered with an error code of ENOSYS and -- * FUSE_CAP_NO_OPENDIR_SUPPORT is set in `fuse_conn_info.capable`, -- * this is treated as success and future calls to opendir and -- * releasedir will also succeed without being sent to the filesystem -- * process. In addition, the kernel will cache readdir results -- * as if opendir returned FOPEN_KEEP_CACHE | FOPEN_CACHE_DIR. -- * -- * Valid replies: -- * fuse_reply_open -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param fi file information -- */ -- void (*opendir) (fuse_req_t req, fuse_ino_t ino, -- struct fuse_file_info *fi); -- -- /** -- * Read directory -- * -- * Send a buffer filled using fuse_add_direntry(), with size not -- * exceeding the requested size. Send an empty buffer on end of -- * stream. -- * -- * fi->fh will contain the value set by the opendir method, or -- * will be undefined if the opendir method didn't set any value. -- * -- * Returning a directory entry from readdir() does not affect -- * its lookup count. -- * -- * If off_t is non-zero, then it will correspond to one of the off_t -- * values that was previously returned by readdir() for the same -- * directory handle. In this case, readdir() should skip over entries -- * coming before the position defined by the off_t value. If entries -- * are added or removed while the directory handle is open, they filesystem -- * may still include the entries that have been removed, and may not -- * report the entries that have been created. However, addition or -- * removal of entries must never cause readdir() to skip over unrelated -- * entries or to report them more than once. This means -- * that off_t can not be a simple index that enumerates the entries -- * that have been returned but must contain sufficient information to -- * uniquely determine the next directory entry to return even when the -- * set of entries is changing. -- * -- * The function does not have to report the '.' and '..' -- * entries, but is allowed to do so. Note that, if readdir does -- * not return '.' or '..', they will not be implicitly returned, -- * and this behavior is observable by the caller. -- * -- * Valid replies: -- * fuse_reply_buf -- * fuse_reply_data -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param size maximum number of bytes to send -- * @param off offset to continue reading the directory stream -- * @param fi file information -- */ -- void (*readdir) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, -- struct fuse_file_info *fi); -- -- /** -- * Release an open directory -- * -- * For every opendir call there will be exactly one releasedir -- * call (unless the filesystem is force-unmounted). -- * -- * fi->fh will contain the value set by the opendir method, or -- * will be undefined if the opendir method didn't set any value. -- * -- * Valid replies: -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param fi file information -- */ -- void (*releasedir) (fuse_req_t req, fuse_ino_t ino, -- struct fuse_file_info *fi); -- -- /** -- * Synchronize directory contents -- * -- * If the datasync parameter is non-zero, then only the directory -- * contents should be flushed, not the meta data. -- * -- * fi->fh will contain the value set by the opendir method, or -- * will be undefined if the opendir method didn't set any value. -- * -- * If this request is answered with an error code of ENOSYS, -- * this is treated as success and future calls to fsyncdir() will -- * succeed automatically without being send to the filesystem -- * process. -- * -- * Valid replies: -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param datasync flag indicating if only data should be flushed -- * @param fi file information -- */ -- void (*fsyncdir) (fuse_req_t req, fuse_ino_t ino, int datasync, -- struct fuse_file_info *fi); -- -- /** -- * Get file system statistics -- * -- * Valid replies: -- * fuse_reply_statfs -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number, zero means "undefined" -- */ -- void (*statfs) (fuse_req_t req, fuse_ino_t ino); -- -- /** -- * Set an extended attribute -- * -- * If this request is answered with an error code of ENOSYS, this is -- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -- * future setxattr() requests will fail with EOPNOTSUPP without being -- * send to the filesystem process. -- * -- * Valid replies: -- * fuse_reply_err -- */ -- void (*setxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, -- const char *value, size_t size, int flags); -- -- /** -- * Get an extended attribute -- * -- * If size is zero, the size of the value should be sent with -- * fuse_reply_xattr. -- * -- * If the size is non-zero, and the value fits in the buffer, the -- * value should be sent with fuse_reply_buf. -- * -- * If the size is too small for the value, the ERANGE error should -- * be sent. -- * -- * If this request is answered with an error code of ENOSYS, this is -- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -- * future getxattr() requests will fail with EOPNOTSUPP without being -- * send to the filesystem process. -- * -- * Valid replies: -- * fuse_reply_buf -- * fuse_reply_data -- * fuse_reply_xattr -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param name of the extended attribute -- * @param size maximum size of the value to send -- */ -- void (*getxattr) (fuse_req_t req, fuse_ino_t ino, const char *name, -- size_t size); -- -- /** -- * List extended attribute names -- * -- * If size is zero, the total size of the attribute list should be -- * sent with fuse_reply_xattr. -- * -- * If the size is non-zero, and the null character separated -- * attribute list fits in the buffer, the list should be sent with -- * fuse_reply_buf. -- * -- * If the size is too small for the list, the ERANGE error should -- * be sent. -- * -- * If this request is answered with an error code of ENOSYS, this is -- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -- * future listxattr() requests will fail with EOPNOTSUPP without being -- * send to the filesystem process. -- * -- * Valid replies: -- * fuse_reply_buf -- * fuse_reply_data -- * fuse_reply_xattr -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param size maximum size of the list to send -- */ -- void (*listxattr) (fuse_req_t req, fuse_ino_t ino, size_t size); -- -- /** -- * Remove an extended attribute -- * -- * If this request is answered with an error code of ENOSYS, this is -- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -- * future removexattr() requests will fail with EOPNOTSUPP without being -- * send to the filesystem process. -- * -- * Valid replies: -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param name of the extended attribute -- */ -- void (*removexattr) (fuse_req_t req, fuse_ino_t ino, const char *name); -- -- /** -- * Check file access permissions -- * -- * This will be called for the access() and chdir() system -- * calls. If the 'default_permissions' mount option is given, -- * this method is not called. -- * -- * This method is not called under Linux kernel versions 2.4.x -- * -- * If this request is answered with an error code of ENOSYS, this is -- * treated as a permanent success, i.e. this and all future access() -- * requests will succeed without being send to the filesystem process. -- * -- * Valid replies: -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param mask requested access mode -- */ -- void (*access) (fuse_req_t req, fuse_ino_t ino, int mask); -- -- /** -- * Create and open a file -- * -- * If the file does not exist, first create it with the specified -- * mode, and then open it. -- * -- * See the description of the open handler for more -- * information. -- * -- * If this method is not implemented or under Linux kernel -- * versions earlier than 2.6.15, the mknod() and open() methods -- * will be called instead. -- * -- * If this request is answered with an error code of ENOSYS, the handler -- * is treated as not implemented (i.e., for this and future requests the -- * mknod() and open() handlers will be called instead). -- * -- * Valid replies: -- * fuse_reply_create -- * fuse_reply_err -- * -- * @param req request handle -- * @param parent inode number of the parent directory -- * @param name to create -- * @param mode file type and mode with which to create the new file -- * @param fi file information -- */ -- void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name, -- mode_t mode, struct fuse_file_info *fi); -- -- /** -- * Test for a POSIX file lock -- * -- * Valid replies: -- * fuse_reply_lock -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param fi file information -- * @param lock the region/type to test -- */ -- void (*getlk) (fuse_req_t req, fuse_ino_t ino, -- struct fuse_file_info *fi, struct flock *lock); -- -- /** -- * Acquire, modify or release a POSIX file lock -- * -- * For POSIX threads (NPTL) there's a 1-1 relation between pid and -- * owner, but otherwise this is not always the case. For checking -- * lock ownership, 'fi->owner' must be used. The l_pid field in -- * 'struct flock' should only be used to fill in this field in -- * getlk(). -- * -- * Note: if the locking methods are not implemented, the kernel -- * will still allow file locking to work locally. Hence these are -- * only interesting for network filesystems and similar. -- * -- * Valid replies: -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param fi file information -- * @param lock the region/type to set -- * @param sleep locking operation may sleep -- */ -- void (*setlk) (fuse_req_t req, fuse_ino_t ino, -- struct fuse_file_info *fi, -- struct flock *lock, int sleep); -- -- /** -- * Map block index within file to block index within device -- * -- * Note: This makes sense only for block device backed filesystems -- * mounted with the 'blkdev' option -- * -- * If this request is answered with an error code of ENOSYS, this is -- * treated as a permanent failure, i.e. all future bmap() requests will -- * fail with the same error code without being send to the filesystem -- * process. -- * -- * Valid replies: -- * fuse_reply_bmap -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param blocksize unit of block index -- * @param idx block index within file -- */ -- void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize, -- uint64_t idx); -- -- /** -- * Ioctl -- * -- * Note: For unrestricted ioctls (not allowed for FUSE -- * servers), data in and out areas can be discovered by giving -- * iovs and setting FUSE_IOCTL_RETRY in *flags*. For -- * restricted ioctls, kernel prepares in/out data area -- * according to the information encoded in cmd. -- * -- * Valid replies: -- * fuse_reply_ioctl_retry -- * fuse_reply_ioctl -- * fuse_reply_ioctl_iov -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param cmd ioctl command -- * @param arg ioctl argument -- * @param fi file information -- * @param flags for FUSE_IOCTL_* flags -- * @param in_buf data fetched from the caller -- * @param in_bufsz number of fetched bytes -- * @param out_bufsz maximum size of output data -- * -- * Note : the unsigned long request submitted by the application -- * is truncated to 32 bits. -- */ -- void (*ioctl) (fuse_req_t req, fuse_ino_t ino, unsigned int cmd, -- void *arg, struct fuse_file_info *fi, unsigned flags, -- const void *in_buf, size_t in_bufsz, size_t out_bufsz); -- -- /** -- * Poll for IO readiness -- * -- * Note: If ph is non-NULL, the client should notify -- * when IO readiness events occur by calling -- * fuse_lowlevel_notify_poll() with the specified ph. -- * -- * Regardless of the number of times poll with a non-NULL ph -- * is received, single notification is enough to clear all. -- * Notifying more times incurs overhead but doesn't harm -- * correctness. -- * -- * The callee is responsible for destroying ph with -- * fuse_pollhandle_destroy() when no longer in use. -- * -- * If this request is answered with an error code of ENOSYS, this is -- * treated as success (with a kernel-defined default poll-mask) and -- * future calls to pull() will succeed the same way without being send -- * to the filesystem process. -- * -- * Valid replies: -- * fuse_reply_poll -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param fi file information -- * @param ph poll handle to be used for notification -- */ -- void (*poll) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, -- struct fuse_pollhandle *ph); -- -- /** -- * Write data made available in a buffer -- * -- * This is a more generic version of the ->write() method. If -- * FUSE_CAP_SPLICE_READ is set in fuse_conn_info.want and the -- * kernel supports splicing from the fuse device, then the -- * data will be made available in pipe for supporting zero -- * copy data transfer. -- * -- * buf->count is guaranteed to be one (and thus buf->idx is -- * always zero). The write_buf handler must ensure that -- * bufv->off is correctly updated (reflecting the number of -- * bytes read from bufv->buf[0]). -- * -- * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -- * expected to reset the setuid and setgid bits. -- * -- * Valid replies: -- * fuse_reply_write -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param bufv buffer containing the data -- * @param off offset to write to -- * @param fi file information -- */ -- void (*write_buf) (fuse_req_t req, fuse_ino_t ino, -- struct fuse_bufvec *bufv, off_t off, -- struct fuse_file_info *fi); -- -- /** -- * Callback function for the retrieve request -- * -- * Valid replies: -- * fuse_reply_none -- * -- * @param req request handle -- * @param cookie user data supplied to fuse_lowlevel_notify_retrieve() -- * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve() -- * @param offset the offset supplied to fuse_lowlevel_notify_retrieve() -- * @param bufv the buffer containing the returned data -- */ -- void (*retrieve_reply) (fuse_req_t req, void *cookie, fuse_ino_t ino, -- off_t offset, struct fuse_bufvec *bufv); -- -- /** -- * Forget about multiple inodes -- * -- * See description of the forget function for more -- * information. -- * -- * Valid replies: -- * fuse_reply_none -- * -- * @param req request handle -- */ -- void (*forget_multi) (fuse_req_t req, size_t count, -- struct fuse_forget_data *forgets); -- -- /** -- * Acquire, modify or release a BSD file lock -- * -- * Note: if the locking methods are not implemented, the kernel -- * will still allow file locking to work locally. Hence these are -- * only interesting for network filesystems and similar. -- * -- * Valid replies: -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param fi file information -- * @param op the locking operation, see flock(2) -- */ -- void (*flock) (fuse_req_t req, fuse_ino_t ino, -- struct fuse_file_info *fi, int op); -- -- /** -- * Allocate requested space. If this function returns success then -- * subsequent writes to the specified range shall not fail due to the lack -- * of free space on the file system storage media. -- * -- * If this request is answered with an error code of ENOSYS, this is -- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -- * future fallocate() requests will fail with EOPNOTSUPP without being -- * send to the filesystem process. -- * -- * Valid replies: -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param offset starting point for allocated region -- * @param length size of allocated region -- * @param mode determines the operation to be performed on the given range, -- * see fallocate(2) -- */ -- void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode, -- off_t offset, off_t length, struct fuse_file_info *fi); -- -- /** -- * Read directory with attributes -- * -- * Send a buffer filled using fuse_add_direntry_plus(), with size not -- * exceeding the requested size. Send an empty buffer on end of -- * stream. -- * -- * fi->fh will contain the value set by the opendir method, or -- * will be undefined if the opendir method didn't set any value. -- * -- * In contrast to readdir() (which does not affect the lookup counts), -- * the lookup count of every entry returned by readdirplus(), except "." -- * and "..", is incremented by one. -- * -- * Valid replies: -- * fuse_reply_buf -- * fuse_reply_data -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param size maximum number of bytes to send -- * @param off offset to continue reading the directory stream -- * @param fi file information -- */ -- void (*readdirplus) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, -- struct fuse_file_info *fi); -- -- /** -- * Copy a range of data from one file to another -- * -- * Performs an optimized copy between two file descriptors without the -- * additional cost of transferring data through the FUSE kernel module -- * to user space (glibc) and then back into the FUSE filesystem again. -- * -- * In case this method is not implemented, glibc falls back to reading -- * data from the source and writing to the destination. Effectively -- * doing an inefficient copy of the data. -- * -- * If this request is answered with an error code of ENOSYS, this is -- * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -- * future copy_file_range() requests will fail with EOPNOTSUPP without -- * being send to the filesystem process. -- * -- * Valid replies: -- * fuse_reply_write -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino_in the inode number or the source file -- * @param off_in starting point from were the data should be read -- * @param fi_in file information of the source file -- * @param ino_out the inode number or the destination file -- * @param off_out starting point where the data should be written -- * @param fi_out file information of the destination file -- * @param len maximum size of the data to copy -- * @param flags passed along with the copy_file_range() syscall -- */ -- void (*copy_file_range) (fuse_req_t req, fuse_ino_t ino_in, -- off_t off_in, struct fuse_file_info *fi_in, -- fuse_ino_t ino_out, off_t off_out, -- struct fuse_file_info *fi_out, size_t len, -- int flags); -- -- /** -- * Find next data or hole after the specified offset -- * -- * If this request is answered with an error code of ENOSYS, this is -- * treated as a permanent failure, i.e. all future lseek() requests will -- * fail with the same error code without being send to the filesystem -- * process. -- * -- * Valid replies: -- * fuse_reply_lseek -- * fuse_reply_err -- * -- * @param req request handle -- * @param ino the inode number -- * @param off offset to start search from -- * @param whence either SEEK_DATA or SEEK_HOLE -- * @param fi file information -- */ -- void (*lseek) (fuse_req_t req, fuse_ino_t ino, off_t off, int whence, -- struct fuse_file_info *fi); -+ /** -+ * Initialize filesystem -+ * -+ * This function is called when libfuse establishes -+ * communication with the FUSE kernel module. The file system -+ * should use this module to inspect and/or modify the -+ * connection parameters provided in the `conn` structure. -+ * -+ * Note that some parameters may be overwritten by options -+ * passed to fuse_session_new() which take precedence over the -+ * values set in this handler. -+ * -+ * There's no reply to this function -+ * -+ * @param userdata the user data passed to fuse_session_new() -+ */ -+ void (*init)(void *userdata, struct fuse_conn_info *conn); -+ -+ /** -+ * Clean up filesystem. -+ * -+ * Called on filesystem exit. When this method is called, the -+ * connection to the kernel may be gone already, so that eg. calls -+ * to fuse_lowlevel_notify_* will fail. -+ * -+ * There's no reply to this function -+ * -+ * @param userdata the user data passed to fuse_session_new() -+ */ -+ void (*destroy)(void *userdata); -+ -+ /** -+ * Look up a directory entry by name and get its attributes. -+ * -+ * Valid replies: -+ * fuse_reply_entry -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param parent inode number of the parent directory -+ * @param name the name to look up -+ */ -+ void (*lookup)(fuse_req_t req, fuse_ino_t parent, const char *name); -+ -+ /** -+ * Forget about an inode -+ * -+ * This function is called when the kernel removes an inode -+ * from its internal caches. -+ * -+ * The inode's lookup count increases by one for every call to -+ * fuse_reply_entry and fuse_reply_create. The nlookup parameter -+ * indicates by how much the lookup count should be decreased. -+ * -+ * Inodes with a non-zero lookup count may receive request from -+ * the kernel even after calls to unlink, rmdir or (when -+ * overwriting an existing file) rename. Filesystems must handle -+ * such requests properly and it is recommended to defer removal -+ * of the inode until the lookup count reaches zero. Calls to -+ * unlink, rmdir or rename will be followed closely by forget -+ * unless the file or directory is open, in which case the -+ * kernel issues forget only after the release or releasedir -+ * calls. -+ * -+ * Note that if a file system will be exported over NFS the -+ * inodes lifetime must extend even beyond forget. See the -+ * generation field in struct fuse_entry_param above. -+ * -+ * On unmount the lookup count for all inodes implicitly drops -+ * to zero. It is not guaranteed that the file system will -+ * receive corresponding forget messages for the affected -+ * inodes. -+ * -+ * Valid replies: -+ * fuse_reply_none -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param nlookup the number of lookups to forget -+ */ -+ void (*forget)(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup); -+ -+ /** -+ * Get file attributes. -+ * -+ * If writeback caching is enabled, the kernel may have a -+ * better idea of a file's length than the FUSE file system -+ * (eg if there has been a write that extended the file size, -+ * but that has not yet been passed to the filesystem.n -+ * -+ * In this case, the st_size value provided by the file system -+ * will be ignored. -+ * -+ * Valid replies: -+ * fuse_reply_attr -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi for future use, currently always NULL -+ */ -+ void (*getattr)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); -+ -+ /** -+ * Set file attributes -+ * -+ * In the 'attr' argument only members indicated by the 'to_set' -+ * bitmask contain valid values. Other members contain undefined -+ * values. -+ * -+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -+ * expected to reset the setuid and setgid bits if the file -+ * size or owner is being changed. -+ * -+ * If the setattr was invoked from the ftruncate() system call -+ * under Linux kernel versions 2.6.15 or later, the fi->fh will -+ * contain the value set by the open method or will be undefined -+ * if the open method didn't set any value. Otherwise (not -+ * ftruncate call, or kernel version earlier than 2.6.15) the fi -+ * parameter will be NULL. -+ * -+ * Valid replies: -+ * fuse_reply_attr -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param attr the attributes -+ * @param to_set bit mask of attributes which should be set -+ * @param fi file information, or NULL -+ */ -+ void (*setattr)(fuse_req_t req, fuse_ino_t ino, struct stat *attr, -+ int to_set, struct fuse_file_info *fi); -+ -+ /** -+ * Read symbolic link -+ * -+ * Valid replies: -+ * fuse_reply_readlink -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ */ -+ void (*readlink)(fuse_req_t req, fuse_ino_t ino); -+ -+ /** -+ * Create file node -+ * -+ * Create a regular file, character device, block device, fifo or -+ * socket node. -+ * -+ * Valid replies: -+ * fuse_reply_entry -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param parent inode number of the parent directory -+ * @param name to create -+ * @param mode file type and mode with which to create the new file -+ * @param rdev the device number (only valid if created file is a device) -+ */ -+ void (*mknod)(fuse_req_t req, fuse_ino_t parent, const char *name, -+ mode_t mode, dev_t rdev); -+ -+ /** -+ * Create a directory -+ * -+ * Valid replies: -+ * fuse_reply_entry -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param parent inode number of the parent directory -+ * @param name to create -+ * @param mode with which to create the new file -+ */ -+ void (*mkdir)(fuse_req_t req, fuse_ino_t parent, const char *name, -+ mode_t mode); -+ -+ /** -+ * Remove a file -+ * -+ * If the file's inode's lookup count is non-zero, the file -+ * system is expected to postpone any removal of the inode -+ * until the lookup count reaches zero (see description of the -+ * forget function). -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param parent inode number of the parent directory -+ * @param name to remove -+ */ -+ void (*unlink)(fuse_req_t req, fuse_ino_t parent, const char *name); -+ -+ /** -+ * Remove a directory -+ * -+ * If the directory's inode's lookup count is non-zero, the -+ * file system is expected to postpone any removal of the -+ * inode until the lookup count reaches zero (see description -+ * of the forget function). -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param parent inode number of the parent directory -+ * @param name to remove -+ */ -+ void (*rmdir)(fuse_req_t req, fuse_ino_t parent, const char *name); -+ -+ /** -+ * Create a symbolic link -+ * -+ * Valid replies: -+ * fuse_reply_entry -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param link the contents of the symbolic link -+ * @param parent inode number of the parent directory -+ * @param name to create -+ */ -+ void (*symlink)(fuse_req_t req, const char *link, fuse_ino_t parent, -+ const char *name); -+ -+ /** -+ * Rename a file -+ * -+ * If the target exists it should be atomically replaced. If -+ * the target's inode's lookup count is non-zero, the file -+ * system is expected to postpone any removal of the inode -+ * until the lookup count reaches zero (see description of the -+ * forget function). -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure with error code EINVAL, i.e. all -+ * future bmap requests will fail with EINVAL without being -+ * send to the filesystem process. -+ * -+ * *flags* may be `RENAME_EXCHANGE` or `RENAME_NOREPLACE`. If -+ * RENAME_NOREPLACE is specified, the filesystem must not -+ * overwrite *newname* if it exists and return an error -+ * instead. If `RENAME_EXCHANGE` is specified, the filesystem -+ * must atomically exchange the two files, i.e. both must -+ * exist and neither may be deleted. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param parent inode number of the old parent directory -+ * @param name old name -+ * @param newparent inode number of the new parent directory -+ * @param newname new name -+ */ -+ void (*rename)(fuse_req_t req, fuse_ino_t parent, const char *name, -+ fuse_ino_t newparent, const char *newname, -+ unsigned int flags); -+ -+ /** -+ * Create a hard link -+ * -+ * Valid replies: -+ * fuse_reply_entry -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the old inode number -+ * @param newparent inode number of the new parent directory -+ * @param newname new name to create -+ */ -+ void (*link)(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, -+ const char *newname); -+ -+ /** -+ * Open a file -+ * -+ * Open flags are available in fi->flags. The following rules -+ * apply. -+ * -+ * - Creation (O_CREAT, O_EXCL, O_NOCTTY) flags will be -+ * filtered out / handled by the kernel. -+ * -+ * - Access modes (O_RDONLY, O_WRONLY, O_RDWR) should be used -+ * by the filesystem to check if the operation is -+ * permitted. If the ``-o default_permissions`` mount -+ * option is given, this check is already done by the -+ * kernel before calling open() and may thus be omitted by -+ * the filesystem. -+ * -+ * - When writeback caching is enabled, the kernel may send -+ * read requests even for files opened with O_WRONLY. The -+ * filesystem should be prepared to handle this. -+ * -+ * - When writeback caching is disabled, the filesystem is -+ * expected to properly handle the O_APPEND flag and ensure -+ * that each write is appending to the end of the file. -+ * -+ * - When writeback caching is enabled, the kernel will -+ * handle O_APPEND. However, unless all changes to the file -+ * come through the kernel this will not work reliably. The -+ * filesystem should thus either ignore the O_APPEND flag -+ * (and let the kernel handle it), or return an error -+ * (indicating that reliably O_APPEND is not available). -+ * -+ * Filesystem may store an arbitrary file handle (pointer, -+ * index, etc) in fi->fh, and use this in other all other file -+ * operations (read, write, flush, release, fsync). -+ * -+ * Filesystem may also implement stateless file I/O and not store -+ * anything in fi->fh. -+ * -+ * There are also some flags (direct_io, keep_cache) which the -+ * filesystem may set in fi, to change the way the file is opened. -+ * See fuse_file_info structure in for more details. -+ * -+ * If this request is answered with an error code of ENOSYS -+ * and FUSE_CAP_NO_OPEN_SUPPORT is set in -+ * `fuse_conn_info.capable`, this is treated as success and -+ * future calls to open and release will also succeed without being -+ * sent to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_open -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ */ -+ void (*open)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); -+ -+ /** -+ * Read data -+ * -+ * Read should send exactly the number of bytes requested except -+ * on EOF or error, otherwise the rest of the data will be -+ * substituted with zeroes. An exception to this is when the file -+ * has been opened in 'direct_io' mode, in which case the return -+ * value of the read system call will reflect the return value of -+ * this operation. -+ * -+ * fi->fh will contain the value set by the open method, or will -+ * be undefined if the open method didn't set any value. -+ * -+ * Valid replies: -+ * fuse_reply_buf -+ * fuse_reply_iov -+ * fuse_reply_data -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param size number of bytes to read -+ * @param off offset to read from -+ * @param fi file information -+ */ -+ void (*read)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Write data -+ * -+ * Write should return exactly the number of bytes requested -+ * except on error. An exception to this is when the file has -+ * been opened in 'direct_io' mode, in which case the return value -+ * of the write system call will reflect the return value of this -+ * operation. -+ * -+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -+ * expected to reset the setuid and setgid bits. -+ * -+ * fi->fh will contain the value set by the open method, or will -+ * be undefined if the open method didn't set any value. -+ * -+ * Valid replies: -+ * fuse_reply_write -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param buf data to write -+ * @param size number of bytes to write -+ * @param off offset to write to -+ * @param fi file information -+ */ -+ void (*write)(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, -+ off_t off, struct fuse_file_info *fi); -+ -+ /** -+ * Flush method -+ * -+ * This is called on each close() of the opened file. -+ * -+ * Since file descriptors can be duplicated (dup, dup2, fork), for -+ * one open call there may be many flush calls. -+ * -+ * Filesystems shouldn't assume that flush will always be called -+ * after some writes, or that if will be called at all. -+ * -+ * fi->fh will contain the value set by the open method, or will -+ * be undefined if the open method didn't set any value. -+ * -+ * NOTE: the name of the method is misleading, since (unlike -+ * fsync) the filesystem is not forced to flush pending writes. -+ * One reason to flush data is if the filesystem wants to return -+ * write errors during close. However, such use is non-portable -+ * because POSIX does not require [close] to wait for delayed I/O to -+ * complete. -+ * -+ * If the filesystem supports file locking operations (setlk, -+ * getlk) it should remove all locks belonging to 'fi->owner'. -+ * -+ * If this request is answered with an error code of ENOSYS, -+ * this is treated as success and future calls to flush() will -+ * succeed automatically without being send to the filesystem -+ * process. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ * -+ * [close]: -+ * http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html -+ */ -+ void (*flush)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); -+ -+ /** -+ * Release an open file -+ * -+ * Release is called when there are no more references to an open -+ * file: all file descriptors are closed and all memory mappings -+ * are unmapped. -+ * -+ * For every open call there will be exactly one release call (unless -+ * the filesystem is force-unmounted). -+ * -+ * The filesystem may reply with an error, but error values are -+ * not returned to close() or munmap() which triggered the -+ * release. -+ * -+ * fi->fh will contain the value set by the open method, or will -+ * be undefined if the open method didn't set any value. -+ * fi->flags will contain the same flags as for open. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ */ -+ void (*release)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); -+ -+ /** -+ * Synchronize file contents -+ * -+ * If the datasync parameter is non-zero, then only the user data -+ * should be flushed, not the meta data. -+ * -+ * If this request is answered with an error code of ENOSYS, -+ * this is treated as success and future calls to fsync() will -+ * succeed automatically without being send to the filesystem -+ * process. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param datasync flag indicating if only data should be flushed -+ * @param fi file information -+ */ -+ void (*fsync)(fuse_req_t req, fuse_ino_t ino, int datasync, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Open a directory -+ * -+ * Filesystem may store an arbitrary file handle (pointer, index, -+ * etc) in fi->fh, and use this in other all other directory -+ * stream operations (readdir, releasedir, fsyncdir). -+ * -+ * If this request is answered with an error code of ENOSYS and -+ * FUSE_CAP_NO_OPENDIR_SUPPORT is set in `fuse_conn_info.capable`, -+ * this is treated as success and future calls to opendir and -+ * releasedir will also succeed without being sent to the filesystem -+ * process. In addition, the kernel will cache readdir results -+ * as if opendir returned FOPEN_KEEP_CACHE | FOPEN_CACHE_DIR. -+ * -+ * Valid replies: -+ * fuse_reply_open -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ */ -+ void (*opendir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); -+ -+ /** -+ * Read directory -+ * -+ * Send a buffer filled using fuse_add_direntry(), with size not -+ * exceeding the requested size. Send an empty buffer on end of -+ * stream. -+ * -+ * fi->fh will contain the value set by the opendir method, or -+ * will be undefined if the opendir method didn't set any value. -+ * -+ * Returning a directory entry from readdir() does not affect -+ * its lookup count. -+ * -+ * If off_t is non-zero, then it will correspond to one of the off_t -+ * values that was previously returned by readdir() for the same -+ * directory handle. In this case, readdir() should skip over entries -+ * coming before the position defined by the off_t value. If entries -+ * are added or removed while the directory handle is open, they filesystem -+ * may still include the entries that have been removed, and may not -+ * report the entries that have been created. However, addition or -+ * removal of entries must never cause readdir() to skip over unrelated -+ * entries or to report them more than once. This means -+ * that off_t can not be a simple index that enumerates the entries -+ * that have been returned but must contain sufficient information to -+ * uniquely determine the next directory entry to return even when the -+ * set of entries is changing. -+ * -+ * The function does not have to report the '.' and '..' -+ * entries, but is allowed to do so. Note that, if readdir does -+ * not return '.' or '..', they will not be implicitly returned, -+ * and this behavior is observable by the caller. -+ * -+ * Valid replies: -+ * fuse_reply_buf -+ * fuse_reply_data -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param size maximum number of bytes to send -+ * @param off offset to continue reading the directory stream -+ * @param fi file information -+ */ -+ void (*readdir)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Release an open directory -+ * -+ * For every opendir call there will be exactly one releasedir -+ * call (unless the filesystem is force-unmounted). -+ * -+ * fi->fh will contain the value set by the opendir method, or -+ * will be undefined if the opendir method didn't set any value. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ */ -+ void (*releasedir)(fuse_req_t req, fuse_ino_t ino, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Synchronize directory contents -+ * -+ * If the datasync parameter is non-zero, then only the directory -+ * contents should be flushed, not the meta data. -+ * -+ * fi->fh will contain the value set by the opendir method, or -+ * will be undefined if the opendir method didn't set any value. -+ * -+ * If this request is answered with an error code of ENOSYS, -+ * this is treated as success and future calls to fsyncdir() will -+ * succeed automatically without being send to the filesystem -+ * process. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param datasync flag indicating if only data should be flushed -+ * @param fi file information -+ */ -+ void (*fsyncdir)(fuse_req_t req, fuse_ino_t ino, int datasync, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Get file system statistics -+ * -+ * Valid replies: -+ * fuse_reply_statfs -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number, zero means "undefined" -+ */ -+ void (*statfs)(fuse_req_t req, fuse_ino_t ino); -+ -+ /** -+ * Set an extended attribute -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -+ * future setxattr() requests will fail with EOPNOTSUPP without being -+ * send to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ */ -+ void (*setxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, -+ const char *value, size_t size, int flags); -+ -+ /** -+ * Get an extended attribute -+ * -+ * If size is zero, the size of the value should be sent with -+ * fuse_reply_xattr. -+ * -+ * If the size is non-zero, and the value fits in the buffer, the -+ * value should be sent with fuse_reply_buf. -+ * -+ * If the size is too small for the value, the ERANGE error should -+ * be sent. -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -+ * future getxattr() requests will fail with EOPNOTSUPP without being -+ * send to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_buf -+ * fuse_reply_data -+ * fuse_reply_xattr -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param name of the extended attribute -+ * @param size maximum size of the value to send -+ */ -+ void (*getxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, -+ size_t size); -+ -+ /** -+ * List extended attribute names -+ * -+ * If size is zero, the total size of the attribute list should be -+ * sent with fuse_reply_xattr. -+ * -+ * If the size is non-zero, and the null character separated -+ * attribute list fits in the buffer, the list should be sent with -+ * fuse_reply_buf. -+ * -+ * If the size is too small for the list, the ERANGE error should -+ * be sent. -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -+ * future listxattr() requests will fail with EOPNOTSUPP without being -+ * send to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_buf -+ * fuse_reply_data -+ * fuse_reply_xattr -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param size maximum size of the list to send -+ */ -+ void (*listxattr)(fuse_req_t req, fuse_ino_t ino, size_t size); -+ -+ /** -+ * Remove an extended attribute -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -+ * future removexattr() requests will fail with EOPNOTSUPP without being -+ * send to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param name of the extended attribute -+ */ -+ void (*removexattr)(fuse_req_t req, fuse_ino_t ino, const char *name); -+ -+ /** -+ * Check file access permissions -+ * -+ * This will be called for the access() and chdir() system -+ * calls. If the 'default_permissions' mount option is given, -+ * this method is not called. -+ * -+ * This method is not called under Linux kernel versions 2.4.x -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent success, i.e. this and all future access() -+ * requests will succeed without being send to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param mask requested access mode -+ */ -+ void (*access)(fuse_req_t req, fuse_ino_t ino, int mask); -+ -+ /** -+ * Create and open a file -+ * -+ * If the file does not exist, first create it with the specified -+ * mode, and then open it. -+ * -+ * See the description of the open handler for more -+ * information. -+ * -+ * If this method is not implemented or under Linux kernel -+ * versions earlier than 2.6.15, the mknod() and open() methods -+ * will be called instead. -+ * -+ * If this request is answered with an error code of ENOSYS, the handler -+ * is treated as not implemented (i.e., for this and future requests the -+ * mknod() and open() handlers will be called instead). -+ * -+ * Valid replies: -+ * fuse_reply_create -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param parent inode number of the parent directory -+ * @param name to create -+ * @param mode file type and mode with which to create the new file -+ * @param fi file information -+ */ -+ void (*create)(fuse_req_t req, fuse_ino_t parent, const char *name, -+ mode_t mode, struct fuse_file_info *fi); -+ -+ /** -+ * Test for a POSIX file lock -+ * -+ * Valid replies: -+ * fuse_reply_lock -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ * @param lock the region/type to test -+ */ -+ void (*getlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, -+ struct flock *lock); -+ -+ /** -+ * Acquire, modify or release a POSIX file lock -+ * -+ * For POSIX threads (NPTL) there's a 1-1 relation between pid and -+ * owner, but otherwise this is not always the case. For checking -+ * lock ownership, 'fi->owner' must be used. The l_pid field in -+ * 'struct flock' should only be used to fill in this field in -+ * getlk(). -+ * -+ * Note: if the locking methods are not implemented, the kernel -+ * will still allow file locking to work locally. Hence these are -+ * only interesting for network filesystems and similar. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ * @param lock the region/type to set -+ * @param sleep locking operation may sleep -+ */ -+ void (*setlk)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, -+ struct flock *lock, int sleep); -+ -+ /** -+ * Map block index within file to block index within device -+ * -+ * Note: This makes sense only for block device backed filesystems -+ * mounted with the 'blkdev' option -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure, i.e. all future bmap() requests will -+ * fail with the same error code without being send to the filesystem -+ * process. -+ * -+ * Valid replies: -+ * fuse_reply_bmap -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param blocksize unit of block index -+ * @param idx block index within file -+ */ -+ void (*bmap)(fuse_req_t req, fuse_ino_t ino, size_t blocksize, -+ uint64_t idx); -+ -+ /** -+ * Ioctl -+ * -+ * Note: For unrestricted ioctls (not allowed for FUSE -+ * servers), data in and out areas can be discovered by giving -+ * iovs and setting FUSE_IOCTL_RETRY in *flags*. For -+ * restricted ioctls, kernel prepares in/out data area -+ * according to the information encoded in cmd. -+ * -+ * Valid replies: -+ * fuse_reply_ioctl_retry -+ * fuse_reply_ioctl -+ * fuse_reply_ioctl_iov -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param cmd ioctl command -+ * @param arg ioctl argument -+ * @param fi file information -+ * @param flags for FUSE_IOCTL_* flags -+ * @param in_buf data fetched from the caller -+ * @param in_bufsz number of fetched bytes -+ * @param out_bufsz maximum size of output data -+ * -+ * Note : the unsigned long request submitted by the application -+ * is truncated to 32 bits. -+ */ -+ void (*ioctl)(fuse_req_t req, fuse_ino_t ino, unsigned int cmd, void *arg, -+ struct fuse_file_info *fi, unsigned flags, const void *in_buf, -+ size_t in_bufsz, size_t out_bufsz); -+ -+ /** -+ * Poll for IO readiness -+ * -+ * Note: If ph is non-NULL, the client should notify -+ * when IO readiness events occur by calling -+ * fuse_lowlevel_notify_poll() with the specified ph. -+ * -+ * Regardless of the number of times poll with a non-NULL ph -+ * is received, single notification is enough to clear all. -+ * Notifying more times incurs overhead but doesn't harm -+ * correctness. -+ * -+ * The callee is responsible for destroying ph with -+ * fuse_pollhandle_destroy() when no longer in use. -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as success (with a kernel-defined default poll-mask) and -+ * future calls to pull() will succeed the same way without being send -+ * to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_poll -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ * @param ph poll handle to be used for notification -+ */ -+ void (*poll)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, -+ struct fuse_pollhandle *ph); -+ -+ /** -+ * Write data made available in a buffer -+ * -+ * This is a more generic version of the ->write() method. If -+ * FUSE_CAP_SPLICE_READ is set in fuse_conn_info.want and the -+ * kernel supports splicing from the fuse device, then the -+ * data will be made available in pipe for supporting zero -+ * copy data transfer. -+ * -+ * buf->count is guaranteed to be one (and thus buf->idx is -+ * always zero). The write_buf handler must ensure that -+ * bufv->off is correctly updated (reflecting the number of -+ * bytes read from bufv->buf[0]). -+ * -+ * Unless FUSE_CAP_HANDLE_KILLPRIV is disabled, this method is -+ * expected to reset the setuid and setgid bits. -+ * -+ * Valid replies: -+ * fuse_reply_write -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param bufv buffer containing the data -+ * @param off offset to write to -+ * @param fi file information -+ */ -+ void (*write_buf)(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv, -+ off_t off, struct fuse_file_info *fi); -+ -+ /** -+ * Callback function for the retrieve request -+ * -+ * Valid replies: -+ * fuse_reply_none -+ * -+ * @param req request handle -+ * @param cookie user data supplied to fuse_lowlevel_notify_retrieve() -+ * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve() -+ * @param offset the offset supplied to fuse_lowlevel_notify_retrieve() -+ * @param bufv the buffer containing the returned data -+ */ -+ void (*retrieve_reply)(fuse_req_t req, void *cookie, fuse_ino_t ino, -+ off_t offset, struct fuse_bufvec *bufv); -+ -+ /** -+ * Forget about multiple inodes -+ * -+ * See description of the forget function for more -+ * information. -+ * -+ * Valid replies: -+ * fuse_reply_none -+ * -+ * @param req request handle -+ */ -+ void (*forget_multi)(fuse_req_t req, size_t count, -+ struct fuse_forget_data *forgets); -+ -+ /** -+ * Acquire, modify or release a BSD file lock -+ * -+ * Note: if the locking methods are not implemented, the kernel -+ * will still allow file locking to work locally. Hence these are -+ * only interesting for network filesystems and similar. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param fi file information -+ * @param op the locking operation, see flock(2) -+ */ -+ void (*flock)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, -+ int op); -+ -+ /** -+ * Allocate requested space. If this function returns success then -+ * subsequent writes to the specified range shall not fail due to the lack -+ * of free space on the file system storage media. -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -+ * future fallocate() requests will fail with EOPNOTSUPP without being -+ * send to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param offset starting point for allocated region -+ * @param length size of allocated region -+ * @param mode determines the operation to be performed on the given range, -+ * see fallocate(2) -+ */ -+ void (*fallocate)(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, -+ off_t length, struct fuse_file_info *fi); -+ -+ /** -+ * Read directory with attributes -+ * -+ * Send a buffer filled using fuse_add_direntry_plus(), with size not -+ * exceeding the requested size. Send an empty buffer on end of -+ * stream. -+ * -+ * fi->fh will contain the value set by the opendir method, or -+ * will be undefined if the opendir method didn't set any value. -+ * -+ * In contrast to readdir() (which does not affect the lookup counts), -+ * the lookup count of every entry returned by readdirplus(), except "." -+ * and "..", is incremented by one. -+ * -+ * Valid replies: -+ * fuse_reply_buf -+ * fuse_reply_data -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param size maximum number of bytes to send -+ * @param off offset to continue reading the directory stream -+ * @param fi file information -+ */ -+ void (*readdirplus)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, -+ struct fuse_file_info *fi); -+ -+ /** -+ * Copy a range of data from one file to another -+ * -+ * Performs an optimized copy between two file descriptors without the -+ * additional cost of transferring data through the FUSE kernel module -+ * to user space (glibc) and then back into the FUSE filesystem again. -+ * -+ * In case this method is not implemented, glibc falls back to reading -+ * data from the source and writing to the destination. Effectively -+ * doing an inefficient copy of the data. -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure with error code EOPNOTSUPP, i.e. all -+ * future copy_file_range() requests will fail with EOPNOTSUPP without -+ * being send to the filesystem process. -+ * -+ * Valid replies: -+ * fuse_reply_write -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino_in the inode number or the source file -+ * @param off_in starting point from were the data should be read -+ * @param fi_in file information of the source file -+ * @param ino_out the inode number or the destination file -+ * @param off_out starting point where the data should be written -+ * @param fi_out file information of the destination file -+ * @param len maximum size of the data to copy -+ * @param flags passed along with the copy_file_range() syscall -+ */ -+ void (*copy_file_range)(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, -+ struct fuse_file_info *fi_in, fuse_ino_t ino_out, -+ off_t off_out, struct fuse_file_info *fi_out, -+ size_t len, int flags); -+ -+ /** -+ * Find next data or hole after the specified offset -+ * -+ * If this request is answered with an error code of ENOSYS, this is -+ * treated as a permanent failure, i.e. all future lseek() requests will -+ * fail with the same error code without being send to the filesystem -+ * process. -+ * -+ * Valid replies: -+ * fuse_reply_lseek -+ * fuse_reply_err -+ * -+ * @param req request handle -+ * @param ino the inode number -+ * @param off offset to start search from -+ * @param whence either SEEK_DATA or SEEK_HOLE -+ * @param fi file information -+ */ -+ void (*lseek)(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, -+ struct fuse_file_info *fi); - }; - - /** -@@ -1305,7 +1307,7 @@ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e); - * @return zero for success, -errno for failure to send reply - */ - int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, -- const struct fuse_file_info *fi); -+ const struct fuse_file_info *fi); - - /** - * Reply with attributes -@@ -1315,11 +1317,11 @@ int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, - * - * @param req request handle - * @param attr the attributes -- * @param attr_timeout validity timeout (in seconds) for the attributes -+ * @param attr_timeout validity timeout (in seconds) for the attributes - * @return zero for success, -errno for failure to send reply - */ - int fuse_reply_attr(fuse_req_t req, const struct stat *attr, -- double attr_timeout); -+ double attr_timeout); - - /** - * Reply with the contents of a symbolic link -@@ -1417,7 +1419,7 @@ int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size); - * @return zero for success, -errno for failure to send reply - */ - int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, -- enum fuse_buf_copy_flags flags); -+ enum fuse_buf_copy_flags flags); - - /** - * Reply with data vector -@@ -1480,9 +1482,9 @@ int fuse_reply_lock(fuse_req_t req, const struct flock *lock); - */ - int fuse_reply_bmap(fuse_req_t req, uint64_t idx); - --/* ----------------------------------------------------------- * -- * Filling a buffer in readdir * -- * ----------------------------------------------------------- */ -+/* -+ * Filling a buffer in readdir -+ */ - - /** - * Add a directory entry to the buffer -@@ -1512,8 +1514,7 @@ int fuse_reply_bmap(fuse_req_t req, uint64_t idx); - * @return the space needed for the entry - */ - size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, -- const char *name, const struct stat *stbuf, -- off_t off); -+ const char *name, const struct stat *stbuf, off_t off); - - /** - * Add a directory entry to the buffer with the attributes -@@ -1529,8 +1530,8 @@ size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, - * @return the space needed for the entry - */ - size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, -- const char *name, -- const struct fuse_entry_param *e, off_t off); -+ const char *name, -+ const struct fuse_entry_param *e, off_t off); - - /** - * Reply to ask for data fetch and output buffer preparation. ioctl -@@ -1547,9 +1548,9 @@ size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, - * @param out_count number of entries in out_iov - * @return zero for success, -errno for failure to send reply - */ --int fuse_reply_ioctl_retry(fuse_req_t req, -- const struct iovec *in_iov, size_t in_count, -- const struct iovec *out_iov, size_t out_count); -+int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, -+ size_t in_count, const struct iovec *out_iov, -+ size_t out_count); - - /** - * Reply to finish ioctl -@@ -1576,7 +1577,7 @@ int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size); - * @param count the size of vector - */ - int fuse_reply_ioctl_iov(fuse_req_t req, int result, const struct iovec *iov, -- int count); -+ int count); - - /** - * Reply with poll result event mask -@@ -1598,9 +1599,9 @@ int fuse_reply_poll(fuse_req_t req, unsigned revents); - */ - int fuse_reply_lseek(fuse_req_t req, off_t off); - --/* ----------------------------------------------------------- * -- * Notification * -- * ----------------------------------------------------------- */ -+/* -+ * Notification -+ */ - - /** - * Notify IO readiness event -@@ -1635,7 +1636,7 @@ int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph); - * @return zero for success, -errno for failure - */ - int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, -- off_t off, off_t len); -+ off_t off, off_t len); - - /** - * Notify to invalidate parent attributes and the dentry matching -@@ -1663,7 +1664,7 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, - * @return zero for success, -errno for failure - */ - int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, -- const char *name, size_t namelen); -+ const char *name, size_t namelen); - - /** - * This function behaves like fuse_lowlevel_notify_inval_entry() with -@@ -1693,9 +1694,9 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, - * @param namelen strlen() of file name - * @return zero for success, -errno for failure - */ --int fuse_lowlevel_notify_delete(struct fuse_session *se, -- fuse_ino_t parent, fuse_ino_t child, -- const char *name, size_t namelen); -+int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, -+ fuse_ino_t child, const char *name, -+ size_t namelen); - - /** - * Store data to the kernel buffers -@@ -1723,8 +1724,8 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, - * @return zero for success, -errno for failure - */ - int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, -- off_t offset, struct fuse_bufvec *bufv, -- enum fuse_buf_copy_flags flags); -+ off_t offset, struct fuse_bufvec *bufv, -+ enum fuse_buf_copy_flags flags); - /** - * Retrieve data from the kernel buffers - * -@@ -1755,12 +1756,12 @@ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, - * @return zero for success, -errno for failure - */ - int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, -- size_t size, off_t offset, void *cookie); -+ size_t size, off_t offset, void *cookie); - - --/* ----------------------------------------------------------- * -- * Utility functions * -- * ----------------------------------------------------------- */ -+/* -+ * Utility functions -+ */ - - /** - * Get the userdata from the request -@@ -1822,7 +1823,7 @@ typedef void (*fuse_interrupt_func_t)(fuse_req_t req, void *data); - * @param data user data passed to the callback function - */ - void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, -- void *data); -+ void *data); - - /** - * Check if a request has already been interrupted -@@ -1833,9 +1834,9 @@ void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, - int fuse_req_interrupted(fuse_req_t req); - - --/* ----------------------------------------------------------- * -- * Inquiry functions * -- * ----------------------------------------------------------- */ -+/* -+ * Inquiry functions -+ */ - - /** - * Print low-level version information to stdout. -@@ -1854,18 +1855,18 @@ void fuse_lowlevel_help(void); - */ - void fuse_cmdline_help(void); - --/* ----------------------------------------------------------- * -- * Filesystem setup & teardown * -- * ----------------------------------------------------------- */ -+/* -+ * Filesystem setup & teardown -+ */ - - struct fuse_cmdline_opts { -- int foreground; -- int debug; -- int nodefault_subtype; -- char *mountpoint; -- int show_version; -- int show_help; -- unsigned int max_idle_threads; -+ int foreground; -+ int debug; -+ int nodefault_subtype; -+ char *mountpoint; -+ int show_version; -+ int show_help; -+ unsigned int max_idle_threads; - }; - - /** -@@ -1886,8 +1887,7 @@ struct fuse_cmdline_opts { - * @param opts output argument for parsed options - * @return 0 on success, -1 on failure - */ --int fuse_parse_cmdline(struct fuse_args *args, -- struct fuse_cmdline_opts *opts); -+int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts); - - /** - * Create a low level session. -@@ -1918,8 +1918,8 @@ int fuse_parse_cmdline(struct fuse_args *args, - * @return the fuse session on success, NULL on failure - **/ - struct fuse_session *fuse_session_new(struct fuse_args *args, -- const struct fuse_lowlevel_ops *op, -- size_t op_size, void *userdata); -+ const struct fuse_lowlevel_ops *op, -+ size_t op_size, void *userdata); - - /** - * Mount a FUSE file system. -@@ -2014,9 +2014,9 @@ void fuse_session_unmount(struct fuse_session *se); - */ - void fuse_session_destroy(struct fuse_session *se); - --/* ----------------------------------------------------------- * -- * Custom event loop support * -- * ----------------------------------------------------------- */ -+/* -+ * Custom event loop support -+ */ - - /** - * Return file descriptor for communication with kernel. -@@ -2043,7 +2043,7 @@ int fuse_session_fd(struct fuse_session *se); - * @param buf the fuse_buf containing the request - */ - void fuse_session_process_buf(struct fuse_session *se, -- const struct fuse_buf *buf); -+ const struct fuse_buf *buf); - - /** - * Read a raw request from the kernel into the supplied buffer. -diff --git a/tools/virtiofsd/fuse_misc.h b/tools/virtiofsd/fuse_misc.h -index 2f6663ed7d..f252baa752 100644 ---- a/tools/virtiofsd/fuse_misc.h -+++ b/tools/virtiofsd/fuse_misc.h -@@ -1,18 +1,18 @@ - /* -- FUSE: Filesystem in Userspace -- Copyright (C) 2001-2007 Miklos Szeredi -- -- This program can be distributed under the terms of the GNU LGPLv2. -- See the file COPYING.LIB --*/ -+ * FUSE: Filesystem in Userspace -+ * Copyright (C) 2001-2007 Miklos Szeredi -+ * -+ * This program can be distributed under the terms of the GNU LGPLv2. -+ * See the file COPYING.LIB -+ */ - - #include - - /* -- Versioned symbols cannot be used in some cases because it -- - confuse the dynamic linker in uClibc -- - not supported on MacOSX (in MachO binary format) --*/ -+ * Versioned symbols cannot be used in some cases because it -+ * - confuse the dynamic linker in uClibc -+ * - not supported on MacOSX (in MachO binary format) -+ */ - #if (!defined(__UCLIBC__) && !defined(__APPLE__)) - #define FUSE_SYMVER(x) __asm__(x) - #else -@@ -25,11 +25,11 @@ - /* Is this hack still needed? */ - static inline void fuse_mutex_init(pthread_mutex_t *mut) - { -- pthread_mutexattr_t attr; -- pthread_mutexattr_init(&attr); -- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); -- pthread_mutex_init(mut, &attr); -- pthread_mutexattr_destroy(&attr); -+ pthread_mutexattr_t attr; -+ pthread_mutexattr_init(&attr); -+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP); -+ pthread_mutex_init(mut, &attr); -+ pthread_mutexattr_destroy(&attr); - } - #endif - -diff --git a/tools/virtiofsd/fuse_opt.c b/tools/virtiofsd/fuse_opt.c -index 93066b926e..edd36f4a3b 100644 ---- a/tools/virtiofsd/fuse_opt.c -+++ b/tools/virtiofsd/fuse_opt.c -@@ -1,423 +1,450 @@ - /* -- FUSE: Filesystem in Userspace -- Copyright (C) 2001-2007 Miklos Szeredi -- -- Implementation of option parsing routines (dealing with `struct -- fuse_args`). -- -- This program can be distributed under the terms of the GNU LGPLv2. -- See the file COPYING.LIB --*/ -+ * FUSE: Filesystem in Userspace -+ * Copyright (C) 2001-2007 Miklos Szeredi -+ * -+ * Implementation of option parsing routines (dealing with `struct -+ * fuse_args`). -+ * -+ * This program can be distributed under the terms of the GNU LGPLv2. -+ * See the file COPYING.LIB -+ */ - -+#include "fuse_opt.h" - #include "config.h" - #include "fuse_i.h" --#include "fuse_opt.h" - #include "fuse_misc.h" - -+#include - #include - #include - #include --#include - - struct fuse_opt_context { -- void *data; -- const struct fuse_opt *opt; -- fuse_opt_proc_t proc; -- int argctr; -- int argc; -- char **argv; -- struct fuse_args outargs; -- char *opts; -- int nonopt; -+ void *data; -+ const struct fuse_opt *opt; -+ fuse_opt_proc_t proc; -+ int argctr; -+ int argc; -+ char **argv; -+ struct fuse_args outargs; -+ char *opts; -+ int nonopt; - }; - - void fuse_opt_free_args(struct fuse_args *args) - { -- if (args) { -- if (args->argv && args->allocated) { -- int i; -- for (i = 0; i < args->argc; i++) -- free(args->argv[i]); -- free(args->argv); -- } -- args->argc = 0; -- args->argv = NULL; -- args->allocated = 0; -- } -+ if (args) { -+ if (args->argv && args->allocated) { -+ int i; -+ for (i = 0; i < args->argc; i++) { -+ free(args->argv[i]); -+ } -+ free(args->argv); -+ } -+ args->argc = 0; -+ args->argv = NULL; -+ args->allocated = 0; -+ } - } - - static int alloc_failed(void) - { -- fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); -- return -1; -+ fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); -+ return -1; - } - - int fuse_opt_add_arg(struct fuse_args *args, const char *arg) - { -- char **newargv; -- char *newarg; -- -- assert(!args->argv || args->allocated); -- -- newarg = strdup(arg); -- if (!newarg) -- return alloc_failed(); -- -- newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); -- if (!newargv) { -- free(newarg); -- return alloc_failed(); -- } -- -- args->argv = newargv; -- args->allocated = 1; -- args->argv[args->argc++] = newarg; -- args->argv[args->argc] = NULL; -- return 0; -+ char **newargv; -+ char *newarg; -+ -+ assert(!args->argv || args->allocated); -+ -+ newarg = strdup(arg); -+ if (!newarg) { -+ return alloc_failed(); -+ } -+ -+ newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *)); -+ if (!newargv) { -+ free(newarg); -+ return alloc_failed(); -+ } -+ -+ args->argv = newargv; -+ args->allocated = 1; -+ args->argv[args->argc++] = newarg; -+ args->argv[args->argc] = NULL; -+ return 0; - } - - static int fuse_opt_insert_arg_common(struct fuse_args *args, int pos, -- const char *arg) -+ const char *arg) - { -- assert(pos <= args->argc); -- if (fuse_opt_add_arg(args, arg) == -1) -- return -1; -- -- if (pos != args->argc - 1) { -- char *newarg = args->argv[args->argc - 1]; -- memmove(&args->argv[pos + 1], &args->argv[pos], -- sizeof(char *) * (args->argc - pos - 1)); -- args->argv[pos] = newarg; -- } -- return 0; -+ assert(pos <= args->argc); -+ if (fuse_opt_add_arg(args, arg) == -1) { -+ return -1; -+ } -+ -+ if (pos != args->argc - 1) { -+ char *newarg = args->argv[args->argc - 1]; -+ memmove(&args->argv[pos + 1], &args->argv[pos], -+ sizeof(char *) * (args->argc - pos - 1)); -+ args->argv[pos] = newarg; -+ } -+ return 0; - } - - int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg) - { -- return fuse_opt_insert_arg_common(args, pos, arg); -+ return fuse_opt_insert_arg_common(args, pos, arg); - } - - static int next_arg(struct fuse_opt_context *ctx, const char *opt) - { -- if (ctx->argctr + 1 >= ctx->argc) { -- fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt); -- return -1; -- } -- ctx->argctr++; -- return 0; -+ if (ctx->argctr + 1 >= ctx->argc) { -+ fuse_log(FUSE_LOG_ERR, "fuse: missing argument after `%s'\n", opt); -+ return -1; -+ } -+ ctx->argctr++; -+ return 0; - } - - static int add_arg(struct fuse_opt_context *ctx, const char *arg) - { -- return fuse_opt_add_arg(&ctx->outargs, arg); -+ return fuse_opt_add_arg(&ctx->outargs, arg); - } - - static int add_opt_common(char **opts, const char *opt, int esc) - { -- unsigned oldlen = *opts ? strlen(*opts) : 0; -- char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1); -- -- if (!d) -- return alloc_failed(); -- -- *opts = d; -- if (oldlen) { -- d += oldlen; -- *d++ = ','; -- } -- -- for (; *opt; opt++) { -- if (esc && (*opt == ',' || *opt == '\\')) -- *d++ = '\\'; -- *d++ = *opt; -- } -- *d = '\0'; -- -- return 0; -+ unsigned oldlen = *opts ? strlen(*opts) : 0; -+ char *d = realloc(*opts, oldlen + 1 + strlen(opt) * 2 + 1); -+ -+ if (!d) { -+ return alloc_failed(); -+ } -+ -+ *opts = d; -+ if (oldlen) { -+ d += oldlen; -+ *d++ = ','; -+ } -+ -+ for (; *opt; opt++) { -+ if (esc && (*opt == ',' || *opt == '\\')) { -+ *d++ = '\\'; -+ } -+ *d++ = *opt; -+ } -+ *d = '\0'; -+ -+ return 0; - } - - int fuse_opt_add_opt(char **opts, const char *opt) - { -- return add_opt_common(opts, opt, 0); -+ return add_opt_common(opts, opt, 0); - } - - int fuse_opt_add_opt_escaped(char **opts, const char *opt) - { -- return add_opt_common(opts, opt, 1); -+ return add_opt_common(opts, opt, 1); - } - - static int add_opt(struct fuse_opt_context *ctx, const char *opt) - { -- return add_opt_common(&ctx->opts, opt, 1); -+ return add_opt_common(&ctx->opts, opt, 1); - } - - static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key, -- int iso) -+ int iso) - { -- if (key == FUSE_OPT_KEY_DISCARD) -- return 0; -- -- if (key != FUSE_OPT_KEY_KEEP && ctx->proc) { -- int res = ctx->proc(ctx->data, arg, key, &ctx->outargs); -- if (res == -1 || !res) -- return res; -- } -- if (iso) -- return add_opt(ctx, arg); -- else -- return add_arg(ctx, arg); -+ if (key == FUSE_OPT_KEY_DISCARD) { -+ return 0; -+ } -+ -+ if (key != FUSE_OPT_KEY_KEEP && ctx->proc) { -+ int res = ctx->proc(ctx->data, arg, key, &ctx->outargs); -+ if (res == -1 || !res) { -+ return res; -+ } -+ } -+ if (iso) { -+ return add_opt(ctx, arg); -+ } else { -+ return add_arg(ctx, arg); -+ } - } - - static int match_template(const char *t, const char *arg, unsigned *sepp) - { -- int arglen = strlen(arg); -- const char *sep = strchr(t, '='); -- sep = sep ? sep : strchr(t, ' '); -- if (sep && (!sep[1] || sep[1] == '%')) { -- int tlen = sep - t; -- if (sep[0] == '=') -- tlen ++; -- if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { -- *sepp = sep - t; -- return 1; -- } -- } -- if (strcmp(t, arg) == 0) { -- *sepp = 0; -- return 1; -- } -- return 0; -+ int arglen = strlen(arg); -+ const char *sep = strchr(t, '='); -+ sep = sep ? sep : strchr(t, ' '); -+ if (sep && (!sep[1] || sep[1] == '%')) { -+ int tlen = sep - t; -+ if (sep[0] == '=') { -+ tlen++; -+ } -+ if (arglen >= tlen && strncmp(arg, t, tlen) == 0) { -+ *sepp = sep - t; -+ return 1; -+ } -+ } -+ if (strcmp(t, arg) == 0) { -+ *sepp = 0; -+ return 1; -+ } -+ return 0; - } - - static const struct fuse_opt *find_opt(const struct fuse_opt *opt, -- const char *arg, unsigned *sepp) -+ const char *arg, unsigned *sepp) - { -- for (; opt && opt->templ; opt++) -- if (match_template(opt->templ, arg, sepp)) -- return opt; -- return NULL; -+ for (; opt && opt->templ; opt++) { -+ if (match_template(opt->templ, arg, sepp)) { -+ return opt; -+ } -+ } -+ return NULL; - } - - int fuse_opt_match(const struct fuse_opt *opts, const char *opt) - { -- unsigned dummy; -- return find_opt(opts, opt, &dummy) ? 1 : 0; -+ unsigned dummy; -+ return find_opt(opts, opt, &dummy) ? 1 : 0; - } - - static int process_opt_param(void *var, const char *format, const char *param, -- const char *arg) -+ const char *arg) - { -- assert(format[0] == '%'); -- if (format[1] == 's') { -- char **s = var; -- char *copy = strdup(param); -- if (!copy) -- return alloc_failed(); -- -- free(*s); -- *s = copy; -- } else { -- if (sscanf(param, format, var) != 1) { -- fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n", arg); -- return -1; -- } -- } -- return 0; -+ assert(format[0] == '%'); -+ if (format[1] == 's') { -+ char **s = var; -+ char *copy = strdup(param); -+ if (!copy) { -+ return alloc_failed(); -+ } -+ -+ free(*s); -+ *s = copy; -+ } else { -+ if (sscanf(param, format, var) != 1) { -+ fuse_log(FUSE_LOG_ERR, "fuse: invalid parameter in option `%s'\n", -+ arg); -+ return -1; -+ } -+ } -+ return 0; - } - --static int process_opt(struct fuse_opt_context *ctx, -- const struct fuse_opt *opt, unsigned sep, -- const char *arg, int iso) -+static int process_opt(struct fuse_opt_context *ctx, const struct fuse_opt *opt, -+ unsigned sep, const char *arg, int iso) - { -- if (opt->offset == -1U) { -- if (call_proc(ctx, arg, opt->value, iso) == -1) -- return -1; -- } else { -- void *var = (char *)ctx->data + opt->offset; -- if (sep && opt->templ[sep + 1]) { -- const char *param = arg + sep; -- if (opt->templ[sep] == '=') -- param ++; -- if (process_opt_param(var, opt->templ + sep + 1, -- param, arg) == -1) -- return -1; -- } else -- *(int *)var = opt->value; -- } -- return 0; -+ if (opt->offset == -1U) { -+ if (call_proc(ctx, arg, opt->value, iso) == -1) { -+ return -1; -+ } -+ } else { -+ void *var = (char *)ctx->data + opt->offset; -+ if (sep && opt->templ[sep + 1]) { -+ const char *param = arg + sep; -+ if (opt->templ[sep] == '=') { -+ param++; -+ } -+ if (process_opt_param(var, opt->templ + sep + 1, param, arg) == -+ -1) { -+ return -1; -+ } -+ } else { -+ *(int *)var = opt->value; -+ } -+ } -+ return 0; - } - - static int process_opt_sep_arg(struct fuse_opt_context *ctx, -- const struct fuse_opt *opt, unsigned sep, -- const char *arg, int iso) -+ const struct fuse_opt *opt, unsigned sep, -+ const char *arg, int iso) - { -- int res; -- char *newarg; -- char *param; -- -- if (next_arg(ctx, arg) == -1) -- return -1; -- -- param = ctx->argv[ctx->argctr]; -- newarg = malloc(sep + strlen(param) + 1); -- if (!newarg) -- return alloc_failed(); -- -- memcpy(newarg, arg, sep); -- strcpy(newarg + sep, param); -- res = process_opt(ctx, opt, sep, newarg, iso); -- free(newarg); -- -- return res; -+ int res; -+ char *newarg; -+ char *param; -+ -+ if (next_arg(ctx, arg) == -1) { -+ return -1; -+ } -+ -+ param = ctx->argv[ctx->argctr]; -+ newarg = malloc(sep + strlen(param) + 1); -+ if (!newarg) { -+ return alloc_failed(); -+ } -+ -+ memcpy(newarg, arg, sep); -+ strcpy(newarg + sep, param); -+ res = process_opt(ctx, opt, sep, newarg, iso); -+ free(newarg); -+ -+ return res; - } - - static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso) - { -- unsigned sep; -- const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); -- if (opt) { -- for (; opt; opt = find_opt(opt + 1, arg, &sep)) { -- int res; -- if (sep && opt->templ[sep] == ' ' && !arg[sep]) -- res = process_opt_sep_arg(ctx, opt, sep, arg, -- iso); -- else -- res = process_opt(ctx, opt, sep, arg, iso); -- if (res == -1) -- return -1; -- } -- return 0; -- } else -- return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); -+ unsigned sep; -+ const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep); -+ if (opt) { -+ for (; opt; opt = find_opt(opt + 1, arg, &sep)) { -+ int res; -+ if (sep && opt->templ[sep] == ' ' && !arg[sep]) { -+ res = process_opt_sep_arg(ctx, opt, sep, arg, iso); -+ } else { -+ res = process_opt(ctx, opt, sep, arg, iso); -+ } -+ if (res == -1) { -+ return -1; -+ } -+ } -+ return 0; -+ } else { -+ return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso); -+ } - } - - static int process_real_option_group(struct fuse_opt_context *ctx, char *opts) - { -- char *s = opts; -- char *d = s; -- int end = 0; -- -- while (!end) { -- if (*s == '\0') -- end = 1; -- if (*s == ',' || end) { -- int res; -- -- *d = '\0'; -- res = process_gopt(ctx, opts, 1); -- if (res == -1) -- return -1; -- d = opts; -- } else { -- if (s[0] == '\\' && s[1] != '\0') { -- s++; -- if (s[0] >= '0' && s[0] <= '3' && -- s[1] >= '0' && s[1] <= '7' && -- s[2] >= '0' && s[2] <= '7') { -- *d++ = (s[0] - '0') * 0100 + -- (s[1] - '0') * 0010 + -- (s[2] - '0'); -- s += 2; -- } else { -- *d++ = *s; -- } -- } else { -- *d++ = *s; -- } -- } -- s++; -- } -- -- return 0; -+ char *s = opts; -+ char *d = s; -+ int end = 0; -+ -+ while (!end) { -+ if (*s == '\0') { -+ end = 1; -+ } -+ if (*s == ',' || end) { -+ int res; -+ -+ *d = '\0'; -+ res = process_gopt(ctx, opts, 1); -+ if (res == -1) { -+ return -1; -+ } -+ d = opts; -+ } else { -+ if (s[0] == '\\' && s[1] != '\0') { -+ s++; -+ if (s[0] >= '0' && s[0] <= '3' && s[1] >= '0' && s[1] <= '7' && -+ s[2] >= '0' && s[2] <= '7') { -+ *d++ = (s[0] - '0') * 0100 + (s[1] - '0') * 0010 + -+ (s[2] - '0'); -+ s += 2; -+ } else { -+ *d++ = *s; -+ } -+ } else { -+ *d++ = *s; -+ } -+ } -+ s++; -+ } -+ -+ return 0; - } - - static int process_option_group(struct fuse_opt_context *ctx, const char *opts) - { -- int res; -- char *copy = strdup(opts); -- -- if (!copy) { -- fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); -- return -1; -- } -- res = process_real_option_group(ctx, copy); -- free(copy); -- return res; -+ int res; -+ char *copy = strdup(opts); -+ -+ if (!copy) { -+ fuse_log(FUSE_LOG_ERR, "fuse: memory allocation failed\n"); -+ return -1; -+ } -+ res = process_real_option_group(ctx, copy); -+ free(copy); -+ return res; - } - - static int process_one(struct fuse_opt_context *ctx, const char *arg) - { -- if (ctx->nonopt || arg[0] != '-') -- return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); -- else if (arg[1] == 'o') { -- if (arg[2]) -- return process_option_group(ctx, arg + 2); -- else { -- if (next_arg(ctx, arg) == -1) -- return -1; -- -- return process_option_group(ctx, -- ctx->argv[ctx->argctr]); -- } -- } else if (arg[1] == '-' && !arg[2]) { -- if (add_arg(ctx, arg) == -1) -- return -1; -- ctx->nonopt = ctx->outargs.argc; -- return 0; -- } else -- return process_gopt(ctx, arg, 0); -+ if (ctx->nonopt || arg[0] != '-') { -+ return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0); -+ } else if (arg[1] == 'o') { -+ if (arg[2]) { -+ return process_option_group(ctx, arg + 2); -+ } else { -+ if (next_arg(ctx, arg) == -1) { -+ return -1; -+ } -+ -+ return process_option_group(ctx, ctx->argv[ctx->argctr]); -+ } -+ } else if (arg[1] == '-' && !arg[2]) { -+ if (add_arg(ctx, arg) == -1) { -+ return -1; -+ } -+ ctx->nonopt = ctx->outargs.argc; -+ return 0; -+ } else { -+ return process_gopt(ctx, arg, 0); -+ } - } - - static int opt_parse(struct fuse_opt_context *ctx) - { -- if (ctx->argc) { -- if (add_arg(ctx, ctx->argv[0]) == -1) -- return -1; -- } -- -- for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) -- if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) -- return -1; -- -- if (ctx->opts) { -- if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 || -- fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) -- return -1; -- } -- -- /* If option separator ("--") is the last argument, remove it */ -- if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc && -- strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) { -- free(ctx->outargs.argv[ctx->outargs.argc - 1]); -- ctx->outargs.argv[--ctx->outargs.argc] = NULL; -- } -- -- return 0; -+ if (ctx->argc) { -+ if (add_arg(ctx, ctx->argv[0]) == -1) { -+ return -1; -+ } -+ } -+ -+ for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++) { -+ if (process_one(ctx, ctx->argv[ctx->argctr]) == -1) { -+ return -1; -+ } -+ } -+ -+ if (ctx->opts) { -+ if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 || -+ fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1) { -+ return -1; -+ } -+ } -+ -+ /* If option separator ("--") is the last argument, remove it */ -+ if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc && -+ strcmp(ctx->outargs.argv[ctx->outargs.argc - 1], "--") == 0) { -+ free(ctx->outargs.argv[ctx->outargs.argc - 1]); -+ ctx->outargs.argv[--ctx->outargs.argc] = NULL; -+ } -+ -+ return 0; - } - - int fuse_opt_parse(struct fuse_args *args, void *data, -- const struct fuse_opt opts[], fuse_opt_proc_t proc) -+ const struct fuse_opt opts[], fuse_opt_proc_t proc) - { -- int res; -- struct fuse_opt_context ctx = { -- .data = data, -- .opt = opts, -- .proc = proc, -- }; -- -- if (!args || !args->argv || !args->argc) -- return 0; -- -- ctx.argc = args->argc; -- ctx.argv = args->argv; -- -- res = opt_parse(&ctx); -- if (res != -1) { -- struct fuse_args tmp = *args; -- *args = ctx.outargs; -- ctx.outargs = tmp; -- } -- free(ctx.opts); -- fuse_opt_free_args(&ctx.outargs); -- return res; -+ int res; -+ struct fuse_opt_context ctx = { -+ .data = data, -+ .opt = opts, -+ .proc = proc, -+ }; -+ -+ if (!args || !args->argv || !args->argc) { -+ return 0; -+ } -+ -+ ctx.argc = args->argc; -+ ctx.argv = args->argv; -+ -+ res = opt_parse(&ctx); -+ if (res != -1) { -+ struct fuse_args tmp = *args; -+ *args = ctx.outargs; -+ ctx.outargs = tmp; -+ } -+ free(ctx.opts); -+ fuse_opt_free_args(&ctx.outargs); -+ return res; - } -diff --git a/tools/virtiofsd/fuse_opt.h b/tools/virtiofsd/fuse_opt.h -index 69102555be..8f59b4d301 100644 ---- a/tools/virtiofsd/fuse_opt.h -+++ b/tools/virtiofsd/fuse_opt.h -@@ -1,10 +1,10 @@ - /* -- FUSE: Filesystem in Userspace -- Copyright (C) 2001-2007 Miklos Szeredi -- -- This program can be distributed under the terms of the GNU LGPLv2. -- See the file COPYING.LIB. --*/ -+ * FUSE: Filesystem in Userspace -+ * Copyright (C) 2001-2007 Miklos Szeredi -+ * -+ * This program can be distributed under the terms of the GNU LGPLv2. -+ * See the file COPYING.LIB. -+ */ - - #ifndef FUSE_OPT_H_ - #define FUSE_OPT_H_ -@@ -37,7 +37,7 @@ - * - * - 'offsetof(struct foo, member)' actions i) and iii) - * -- * - -1 action ii) -+ * - -1 action ii) - * - * The 'offsetof()' macro is defined in the header. - * -@@ -48,7 +48,7 @@ - * - * The types of templates are: - * -- * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only -+ * 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only - * themselves. Invalid values are "--" and anything beginning - * with "-o" - * -@@ -71,58 +71,67 @@ - * freed. - */ - struct fuse_opt { -- /** Matching template and optional parameter formatting */ -- const char *templ; -+ /** Matching template and optional parameter formatting */ -+ const char *templ; - -- /** -- * Offset of variable within 'data' parameter of fuse_opt_parse() -- * or -1 -- */ -- unsigned long offset; -+ /** -+ * Offset of variable within 'data' parameter of fuse_opt_parse() -+ * or -1 -+ */ -+ unsigned long offset; - -- /** -- * Value to set the variable to, or to be passed as 'key' to the -- * processing function. Ignored if template has a format -- */ -- int value; -+ /** -+ * Value to set the variable to, or to be passed as 'key' to the -+ * processing function. Ignored if template has a format -+ */ -+ int value; - }; - - /** -- * Key option. In case of a match, the processing function will be -+ * Key option. In case of a match, the processing function will be - * called with the specified key. - */ --#define FUSE_OPT_KEY(templ, key) { templ, -1U, key } -+#define FUSE_OPT_KEY(templ, key) \ -+ { \ -+ templ, -1U, key \ -+ } - - /** -- * Last option. An array of 'struct fuse_opt' must end with a NULL -+ * Last option. An array of 'struct fuse_opt' must end with a NULL - * template value - */ --#define FUSE_OPT_END { NULL, 0, 0 } -+#define FUSE_OPT_END \ -+ { \ -+ NULL, 0, 0 \ -+ } - - /** - * Argument list - */ - struct fuse_args { -- /** Argument count */ -- int argc; -+ /** Argument count */ -+ int argc; - -- /** Argument vector. NULL terminated */ -- char **argv; -+ /** Argument vector. NULL terminated */ -+ char **argv; - -- /** Is 'argv' allocated? */ -- int allocated; -+ /** Is 'argv' allocated? */ -+ int allocated; - }; - - /** - * Initializer for 'struct fuse_args' - */ --#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 } -+#define FUSE_ARGS_INIT(argc, argv) \ -+ { \ -+ argc, argv, 0 \ -+ } - - /** - * Key value passed to the processing function if an option did not - * match any template - */ --#define FUSE_OPT_KEY_OPT -1 -+#define FUSE_OPT_KEY_OPT -1 - - /** - * Key value passed to the processing function for all non-options -@@ -130,7 +139,7 @@ struct fuse_args { - * Non-options are the arguments beginning with a character other than - * '-' or all arguments after the special '--' option - */ --#define FUSE_OPT_KEY_NONOPT -2 -+#define FUSE_OPT_KEY_NONOPT -2 - - /** - * Special key value for options to keep -@@ -174,7 +183,7 @@ struct fuse_args { - * @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept - */ - typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, -- struct fuse_args *outargs); -+ struct fuse_args *outargs); - - /** - * Option parsing function -@@ -197,7 +206,7 @@ typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key, - * @return -1 on error, 0 on success - */ - int fuse_opt_parse(struct fuse_args *args, void *data, -- const struct fuse_opt opts[], fuse_opt_proc_t proc); -+ const struct fuse_opt opts[], fuse_opt_proc_t proc); - - /** - * Add an option to a comma separated option list -diff --git a/tools/virtiofsd/fuse_signals.c b/tools/virtiofsd/fuse_signals.c -index 4271947bd4..19d6791cb9 100644 ---- a/tools/virtiofsd/fuse_signals.c -+++ b/tools/virtiofsd/fuse_signals.c -@@ -1,91 +1,95 @@ - /* -- FUSE: Filesystem in Userspace -- Copyright (C) 2001-2007 Miklos Szeredi -- -- Utility functions for setting signal handlers. -- -- This program can be distributed under the terms of the GNU LGPLv2. -- See the file COPYING.LIB --*/ -+ * FUSE: Filesystem in Userspace -+ * Copyright (C) 2001-2007 Miklos Szeredi -+ * -+ * Utility functions for setting signal handlers. -+ * -+ * This program can be distributed under the terms of the GNU LGPLv2. -+ * See the file COPYING.LIB -+ */ - - #include "config.h" --#include "fuse_lowlevel.h" - #include "fuse_i.h" -+#include "fuse_lowlevel.h" - --#include --#include - #include -+#include - #include -+#include - - static struct fuse_session *fuse_instance; - - static void exit_handler(int sig) - { -- if (fuse_instance) { -- fuse_session_exit(fuse_instance); -- if(sig <= 0) { -- fuse_log(FUSE_LOG_ERR, "assertion error: signal value <= 0\n"); -- abort(); -- } -- fuse_instance->error = sig; -- } -+ if (fuse_instance) { -+ fuse_session_exit(fuse_instance); -+ if (sig <= 0) { -+ fuse_log(FUSE_LOG_ERR, "assertion error: signal value <= 0\n"); -+ abort(); -+ } -+ fuse_instance->error = sig; -+ } - } - - static void do_nothing(int sig) - { -- (void) sig; -+ (void)sig; - } - - static int set_one_signal_handler(int sig, void (*handler)(int), int remove) - { -- struct sigaction sa; -- struct sigaction old_sa; -+ struct sigaction sa; -+ struct sigaction old_sa; - -- memset(&sa, 0, sizeof(struct sigaction)); -- sa.sa_handler = remove ? SIG_DFL : handler; -- sigemptyset(&(sa.sa_mask)); -- sa.sa_flags = 0; -+ memset(&sa, 0, sizeof(struct sigaction)); -+ sa.sa_handler = remove ? SIG_DFL : handler; -+ sigemptyset(&(sa.sa_mask)); -+ sa.sa_flags = 0; - -- if (sigaction(sig, NULL, &old_sa) == -1) { -- perror("fuse: cannot get old signal handler"); -- return -1; -- } -+ if (sigaction(sig, NULL, &old_sa) == -1) { -+ perror("fuse: cannot get old signal handler"); -+ return -1; -+ } - -- if (old_sa.sa_handler == (remove ? handler : SIG_DFL) && -- sigaction(sig, &sa, NULL) == -1) { -- perror("fuse: cannot set signal handler"); -- return -1; -- } -- return 0; -+ if (old_sa.sa_handler == (remove ? handler : SIG_DFL) && -+ sigaction(sig, &sa, NULL) == -1) { -+ perror("fuse: cannot set signal handler"); -+ return -1; -+ } -+ return 0; - } - - int fuse_set_signal_handlers(struct fuse_session *se) - { -- /* If we used SIG_IGN instead of the do_nothing function, -- then we would be unable to tell if we set SIG_IGN (and -- thus should reset to SIG_DFL in fuse_remove_signal_handlers) -- or if it was already set to SIG_IGN (and should be left -- untouched. */ -- if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 || -- set_one_signal_handler(SIGINT, exit_handler, 0) == -1 || -- set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 || -- set_one_signal_handler(SIGPIPE, do_nothing, 0) == -1) -- return -1; -+ /* -+ * If we used SIG_IGN instead of the do_nothing function, -+ * then we would be unable to tell if we set SIG_IGN (and -+ * thus should reset to SIG_DFL in fuse_remove_signal_handlers) -+ * or if it was already set to SIG_IGN (and should be left -+ * untouched. -+ */ -+ if (set_one_signal_handler(SIGHUP, exit_handler, 0) == -1 || -+ set_one_signal_handler(SIGINT, exit_handler, 0) == -1 || -+ set_one_signal_handler(SIGTERM, exit_handler, 0) == -1 || -+ set_one_signal_handler(SIGPIPE, do_nothing, 0) == -1) { -+ return -1; -+ } - -- fuse_instance = se; -- return 0; -+ fuse_instance = se; -+ return 0; - } - - void fuse_remove_signal_handlers(struct fuse_session *se) - { -- if (fuse_instance != se) -- fuse_log(FUSE_LOG_ERR, -- "fuse: fuse_remove_signal_handlers: unknown session\n"); -- else -- fuse_instance = NULL; -+ if (fuse_instance != se) { -+ fuse_log(FUSE_LOG_ERR, -+ "fuse: fuse_remove_signal_handlers: unknown session\n"); -+ } else { -+ fuse_instance = NULL; -+ } - -- set_one_signal_handler(SIGHUP, exit_handler, 1); -- set_one_signal_handler(SIGINT, exit_handler, 1); -- set_one_signal_handler(SIGTERM, exit_handler, 1); -- set_one_signal_handler(SIGPIPE, do_nothing, 1); -+ set_one_signal_handler(SIGHUP, exit_handler, 1); -+ set_one_signal_handler(SIGINT, exit_handler, 1); -+ set_one_signal_handler(SIGTERM, exit_handler, 1); -+ set_one_signal_handler(SIGPIPE, do_nothing, 1); - } -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index 5a2e64c6d0..5711dd2660 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -1,297 +1,309 @@ - /* -- FUSE: Filesystem in Userspace -- Copyright (C) 2001-2007 Miklos Szeredi -+ * FUSE: Filesystem in Userspace -+ * Copyright (C) 2001-2007 Miklos Szeredi -+ * -+ * Helper functions to create (simple) standalone programs. With the -+ * aid of these functions it should be possible to create full FUSE -+ * file system by implementing nothing but the request handlers. - -- Helper functions to create (simple) standalone programs. With the -- aid of these functions it should be possible to create full FUSE -- file system by implementing nothing but the request handlers. -- -- This program can be distributed under the terms of the GNU LGPLv2. -- See the file COPYING.LIB. --*/ -+ * This program can be distributed under the terms of the GNU LGPLv2. -+ * See the file COPYING.LIB. -+ */ - - #include "config.h" - #include "fuse_i.h" -+#include "fuse_lowlevel.h" - #include "fuse_misc.h" - #include "fuse_opt.h" --#include "fuse_lowlevel.h" - #include "mount_util.h" - -+#include -+#include -+#include - #include - #include --#include --#include - #include --#include --#include - #include -+#include - --#define FUSE_HELPER_OPT(t, p) \ -- { t, offsetof(struct fuse_cmdline_opts, p), 1 } -+#define FUSE_HELPER_OPT(t, p) \ -+ { \ -+ t, offsetof(struct fuse_cmdline_opts, p), 1 \ -+ } - - static const struct fuse_opt fuse_helper_opts[] = { -- FUSE_HELPER_OPT("-h", show_help), -- FUSE_HELPER_OPT("--help", show_help), -- FUSE_HELPER_OPT("-V", show_version), -- FUSE_HELPER_OPT("--version", show_version), -- FUSE_HELPER_OPT("-d", debug), -- FUSE_HELPER_OPT("debug", debug), -- FUSE_HELPER_OPT("-d", foreground), -- FUSE_HELPER_OPT("debug", foreground), -- FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), -- FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), -- FUSE_HELPER_OPT("-f", foreground), -- FUSE_HELPER_OPT("fsname=", nodefault_subtype), -- FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), -- FUSE_HELPER_OPT("subtype=", nodefault_subtype), -- FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), -- FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads), -- FUSE_OPT_END -+ FUSE_HELPER_OPT("-h", show_help), -+ FUSE_HELPER_OPT("--help", show_help), -+ FUSE_HELPER_OPT("-V", show_version), -+ FUSE_HELPER_OPT("--version", show_version), -+ FUSE_HELPER_OPT("-d", debug), -+ FUSE_HELPER_OPT("debug", debug), -+ FUSE_HELPER_OPT("-d", foreground), -+ FUSE_HELPER_OPT("debug", foreground), -+ FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), -+ FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), -+ FUSE_HELPER_OPT("-f", foreground), -+ FUSE_HELPER_OPT("fsname=", nodefault_subtype), -+ FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), -+ FUSE_HELPER_OPT("subtype=", nodefault_subtype), -+ FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), -+ FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads), -+ FUSE_OPT_END - }; - - struct fuse_conn_info_opts { -- int atomic_o_trunc; -- int no_remote_posix_lock; -- int no_remote_flock; -- int splice_write; -- int splice_move; -- int splice_read; -- int no_splice_write; -- int no_splice_move; -- int no_splice_read; -- int auto_inval_data; -- int no_auto_inval_data; -- int no_readdirplus; -- int no_readdirplus_auto; -- int async_dio; -- int no_async_dio; -- int writeback_cache; -- int no_writeback_cache; -- int async_read; -- int sync_read; -- unsigned max_write; -- unsigned max_readahead; -- unsigned max_background; -- unsigned congestion_threshold; -- unsigned time_gran; -- int set_max_write; -- int set_max_readahead; -- int set_max_background; -- int set_congestion_threshold; -- int set_time_gran; -+ int atomic_o_trunc; -+ int no_remote_posix_lock; -+ int no_remote_flock; -+ int splice_write; -+ int splice_move; -+ int splice_read; -+ int no_splice_write; -+ int no_splice_move; -+ int no_splice_read; -+ int auto_inval_data; -+ int no_auto_inval_data; -+ int no_readdirplus; -+ int no_readdirplus_auto; -+ int async_dio; -+ int no_async_dio; -+ int writeback_cache; -+ int no_writeback_cache; -+ int async_read; -+ int sync_read; -+ unsigned max_write; -+ unsigned max_readahead; -+ unsigned max_background; -+ unsigned congestion_threshold; -+ unsigned time_gran; -+ int set_max_write; -+ int set_max_readahead; -+ int set_max_background; -+ int set_congestion_threshold; -+ int set_time_gran; - }; - --#define CONN_OPTION(t, p, v) \ -- { t, offsetof(struct fuse_conn_info_opts, p), v } -+#define CONN_OPTION(t, p, v) \ -+ { \ -+ t, offsetof(struct fuse_conn_info_opts, p), v \ -+ } - static const struct fuse_opt conn_info_opt_spec[] = { -- CONN_OPTION("max_write=%u", max_write, 0), -- CONN_OPTION("max_write=", set_max_write, 1), -- CONN_OPTION("max_readahead=%u", max_readahead, 0), -- CONN_OPTION("max_readahead=", set_max_readahead, 1), -- CONN_OPTION("max_background=%u", max_background, 0), -- CONN_OPTION("max_background=", set_max_background, 1), -- CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0), -- CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1), -- CONN_OPTION("sync_read", sync_read, 1), -- CONN_OPTION("async_read", async_read, 1), -- CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1), -- CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1), -- CONN_OPTION("no_remote_lock", no_remote_flock, 1), -- CONN_OPTION("no_remote_flock", no_remote_flock, 1), -- CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1), -- CONN_OPTION("splice_write", splice_write, 1), -- CONN_OPTION("no_splice_write", no_splice_write, 1), -- CONN_OPTION("splice_move", splice_move, 1), -- CONN_OPTION("no_splice_move", no_splice_move, 1), -- CONN_OPTION("splice_read", splice_read, 1), -- CONN_OPTION("no_splice_read", no_splice_read, 1), -- CONN_OPTION("auto_inval_data", auto_inval_data, 1), -- CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1), -- CONN_OPTION("readdirplus=no", no_readdirplus, 1), -- CONN_OPTION("readdirplus=yes", no_readdirplus, 0), -- CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1), -- CONN_OPTION("readdirplus=auto", no_readdirplus, 0), -- CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0), -- CONN_OPTION("async_dio", async_dio, 1), -- CONN_OPTION("no_async_dio", no_async_dio, 1), -- CONN_OPTION("writeback_cache", writeback_cache, 1), -- CONN_OPTION("no_writeback_cache", no_writeback_cache, 1), -- CONN_OPTION("time_gran=%u", time_gran, 0), -- CONN_OPTION("time_gran=", set_time_gran, 1), -- FUSE_OPT_END -+ CONN_OPTION("max_write=%u", max_write, 0), -+ CONN_OPTION("max_write=", set_max_write, 1), -+ CONN_OPTION("max_readahead=%u", max_readahead, 0), -+ CONN_OPTION("max_readahead=", set_max_readahead, 1), -+ CONN_OPTION("max_background=%u", max_background, 0), -+ CONN_OPTION("max_background=", set_max_background, 1), -+ CONN_OPTION("congestion_threshold=%u", congestion_threshold, 0), -+ CONN_OPTION("congestion_threshold=", set_congestion_threshold, 1), -+ CONN_OPTION("sync_read", sync_read, 1), -+ CONN_OPTION("async_read", async_read, 1), -+ CONN_OPTION("atomic_o_trunc", atomic_o_trunc, 1), -+ CONN_OPTION("no_remote_lock", no_remote_posix_lock, 1), -+ CONN_OPTION("no_remote_lock", no_remote_flock, 1), -+ CONN_OPTION("no_remote_flock", no_remote_flock, 1), -+ CONN_OPTION("no_remote_posix_lock", no_remote_posix_lock, 1), -+ CONN_OPTION("splice_write", splice_write, 1), -+ CONN_OPTION("no_splice_write", no_splice_write, 1), -+ CONN_OPTION("splice_move", splice_move, 1), -+ CONN_OPTION("no_splice_move", no_splice_move, 1), -+ CONN_OPTION("splice_read", splice_read, 1), -+ CONN_OPTION("no_splice_read", no_splice_read, 1), -+ CONN_OPTION("auto_inval_data", auto_inval_data, 1), -+ CONN_OPTION("no_auto_inval_data", no_auto_inval_data, 1), -+ CONN_OPTION("readdirplus=no", no_readdirplus, 1), -+ CONN_OPTION("readdirplus=yes", no_readdirplus, 0), -+ CONN_OPTION("readdirplus=yes", no_readdirplus_auto, 1), -+ CONN_OPTION("readdirplus=auto", no_readdirplus, 0), -+ CONN_OPTION("readdirplus=auto", no_readdirplus_auto, 0), -+ CONN_OPTION("async_dio", async_dio, 1), -+ CONN_OPTION("no_async_dio", no_async_dio, 1), -+ CONN_OPTION("writeback_cache", writeback_cache, 1), -+ CONN_OPTION("no_writeback_cache", no_writeback_cache, 1), -+ CONN_OPTION("time_gran=%u", time_gran, 0), -+ CONN_OPTION("time_gran=", set_time_gran, 1), -+ FUSE_OPT_END - }; - - - void fuse_cmdline_help(void) - { -- printf(" -h --help print help\n" -- " -V --version print version\n" -- " -d -o debug enable debug output (implies -f)\n" -- " -f foreground operation\n" -- " -o max_idle_threads the maximum number of idle worker threads\n" -- " allowed (default: 10)\n"); -+ printf( -+ " -h --help print help\n" -+ " -V --version print version\n" -+ " -d -o debug enable debug output (implies -f)\n" -+ " -f foreground operation\n" -+ " -o max_idle_threads the maximum number of idle worker threads\n" -+ " allowed (default: 10)\n"); - } - - static int fuse_helper_opt_proc(void *data, const char *arg, int key, -- struct fuse_args *outargs) -+ struct fuse_args *outargs) - { -- (void) outargs; -- struct fuse_cmdline_opts *opts = data; -- -- switch (key) { -- case FUSE_OPT_KEY_NONOPT: -- if (!opts->mountpoint) { -- if (fuse_mnt_parse_fuse_fd(arg) != -1) { -- return fuse_opt_add_opt(&opts->mountpoint, arg); -- } -- -- char mountpoint[PATH_MAX] = ""; -- if (realpath(arg, mountpoint) == NULL) { -- fuse_log(FUSE_LOG_ERR, -- "fuse: bad mount point `%s': %s\n", -- arg, strerror(errno)); -- return -1; -- } -- return fuse_opt_add_opt(&opts->mountpoint, mountpoint); -- } else { -- fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg); -- return -1; -- } -- -- default: -- /* Pass through unknown options */ -- return 1; -- } -+ (void)outargs; -+ struct fuse_cmdline_opts *opts = data; -+ -+ switch (key) { -+ case FUSE_OPT_KEY_NONOPT: -+ if (!opts->mountpoint) { -+ if (fuse_mnt_parse_fuse_fd(arg) != -1) { -+ return fuse_opt_add_opt(&opts->mountpoint, arg); -+ } -+ -+ char mountpoint[PATH_MAX] = ""; -+ if (realpath(arg, mountpoint) == NULL) { -+ fuse_log(FUSE_LOG_ERR, "fuse: bad mount point `%s': %s\n", arg, -+ strerror(errno)); -+ return -1; -+ } -+ return fuse_opt_add_opt(&opts->mountpoint, mountpoint); -+ } else { -+ fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg); -+ return -1; -+ } -+ -+ default: -+ /* Pass through unknown options */ -+ return 1; -+ } - } - --int fuse_parse_cmdline(struct fuse_args *args, -- struct fuse_cmdline_opts *opts) -+int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts) - { -- memset(opts, 0, sizeof(struct fuse_cmdline_opts)); -+ memset(opts, 0, sizeof(struct fuse_cmdline_opts)); - -- opts->max_idle_threads = 10; -+ opts->max_idle_threads = 10; - -- if (fuse_opt_parse(args, opts, fuse_helper_opts, -- fuse_helper_opt_proc) == -1) -- return -1; -+ if (fuse_opt_parse(args, opts, fuse_helper_opts, fuse_helper_opt_proc) == -+ -1) { -+ return -1; -+ } - -- return 0; -+ return 0; - } - - - int fuse_daemonize(int foreground) - { -- if (!foreground) { -- int nullfd; -- int waiter[2]; -- char completed; -- -- if (pipe(waiter)) { -- perror("fuse_daemonize: pipe"); -- return -1; -- } -- -- /* -- * demonize current process by forking it and killing the -- * parent. This makes current process as a child of 'init'. -- */ -- switch(fork()) { -- case -1: -- perror("fuse_daemonize: fork"); -- return -1; -- case 0: -- break; -- default: -- (void) read(waiter[0], &completed, sizeof(completed)); -- _exit(0); -- } -- -- if (setsid() == -1) { -- perror("fuse_daemonize: setsid"); -- return -1; -- } -- -- (void) chdir("/"); -- -- nullfd = open("/dev/null", O_RDWR, 0); -- if (nullfd != -1) { -- (void) dup2(nullfd, 0); -- (void) dup2(nullfd, 1); -- (void) dup2(nullfd, 2); -- if (nullfd > 2) -- close(nullfd); -- } -- -- /* Propagate completion of daemon initialization */ -- completed = 1; -- (void) write(waiter[1], &completed, sizeof(completed)); -- close(waiter[0]); -- close(waiter[1]); -- } else { -- (void) chdir("/"); -- } -- return 0; -+ if (!foreground) { -+ int nullfd; -+ int waiter[2]; -+ char completed; -+ -+ if (pipe(waiter)) { -+ perror("fuse_daemonize: pipe"); -+ return -1; -+ } -+ -+ /* -+ * demonize current process by forking it and killing the -+ * parent. This makes current process as a child of 'init'. -+ */ -+ switch (fork()) { -+ case -1: -+ perror("fuse_daemonize: fork"); -+ return -1; -+ case 0: -+ break; -+ default: -+ (void)read(waiter[0], &completed, sizeof(completed)); -+ _exit(0); -+ } -+ -+ if (setsid() == -1) { -+ perror("fuse_daemonize: setsid"); -+ return -1; -+ } -+ -+ (void)chdir("/"); -+ -+ nullfd = open("/dev/null", O_RDWR, 0); -+ if (nullfd != -1) { -+ (void)dup2(nullfd, 0); -+ (void)dup2(nullfd, 1); -+ (void)dup2(nullfd, 2); -+ if (nullfd > 2) { -+ close(nullfd); -+ } -+ } -+ -+ /* Propagate completion of daemon initialization */ -+ completed = 1; -+ (void)write(waiter[1], &completed, sizeof(completed)); -+ close(waiter[0]); -+ close(waiter[1]); -+ } else { -+ (void)chdir("/"); -+ } -+ return 0; - } - - void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, -- struct fuse_conn_info *conn) -+ struct fuse_conn_info *conn) - { -- if(opts->set_max_write) -- conn->max_write = opts->max_write; -- if(opts->set_max_background) -- conn->max_background = opts->max_background; -- if(opts->set_congestion_threshold) -- conn->congestion_threshold = opts->congestion_threshold; -- if(opts->set_time_gran) -- conn->time_gran = opts->time_gran; -- if(opts->set_max_readahead) -- conn->max_readahead = opts->max_readahead; -- --#define LL_ENABLE(cond,cap) \ -- if (cond) conn->want |= (cap) --#define LL_DISABLE(cond,cap) \ -- if (cond) conn->want &= ~(cap) -- -- LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ); -- LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ); -- -- LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE); -- LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE); -- -- LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE); -- LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE); -- -- LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); -- LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); -- -- LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS); -- LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO); -- -- LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO); -- LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO); -- -- LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE); -- LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE); -- -- LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ); -- LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ); -- -- LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS); -- LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS); -+ if (opts->set_max_write) { -+ conn->max_write = opts->max_write; -+ } -+ if (opts->set_max_background) { -+ conn->max_background = opts->max_background; -+ } -+ if (opts->set_congestion_threshold) { -+ conn->congestion_threshold = opts->congestion_threshold; -+ } -+ if (opts->set_time_gran) { -+ conn->time_gran = opts->time_gran; -+ } -+ if (opts->set_max_readahead) { -+ conn->max_readahead = opts->max_readahead; -+ } -+ -+#define LL_ENABLE(cond, cap) \ -+ if (cond) \ -+ conn->want |= (cap) -+#define LL_DISABLE(cond, cap) \ -+ if (cond) \ -+ conn->want &= ~(cap) -+ -+ LL_ENABLE(opts->splice_read, FUSE_CAP_SPLICE_READ); -+ LL_DISABLE(opts->no_splice_read, FUSE_CAP_SPLICE_READ); -+ -+ LL_ENABLE(opts->splice_write, FUSE_CAP_SPLICE_WRITE); -+ LL_DISABLE(opts->no_splice_write, FUSE_CAP_SPLICE_WRITE); -+ -+ LL_ENABLE(opts->splice_move, FUSE_CAP_SPLICE_MOVE); -+ LL_DISABLE(opts->no_splice_move, FUSE_CAP_SPLICE_MOVE); -+ -+ LL_ENABLE(opts->auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); -+ LL_DISABLE(opts->no_auto_inval_data, FUSE_CAP_AUTO_INVAL_DATA); -+ -+ LL_DISABLE(opts->no_readdirplus, FUSE_CAP_READDIRPLUS); -+ LL_DISABLE(opts->no_readdirplus_auto, FUSE_CAP_READDIRPLUS_AUTO); -+ -+ LL_ENABLE(opts->async_dio, FUSE_CAP_ASYNC_DIO); -+ LL_DISABLE(opts->no_async_dio, FUSE_CAP_ASYNC_DIO); -+ -+ LL_ENABLE(opts->writeback_cache, FUSE_CAP_WRITEBACK_CACHE); -+ LL_DISABLE(opts->no_writeback_cache, FUSE_CAP_WRITEBACK_CACHE); -+ -+ LL_ENABLE(opts->async_read, FUSE_CAP_ASYNC_READ); -+ LL_DISABLE(opts->sync_read, FUSE_CAP_ASYNC_READ); -+ -+ LL_DISABLE(opts->no_remote_posix_lock, FUSE_CAP_POSIX_LOCKS); -+ LL_DISABLE(opts->no_remote_flock, FUSE_CAP_FLOCK_LOCKS); - } - --struct fuse_conn_info_opts* fuse_parse_conn_info_opts(struct fuse_args *args) -+struct fuse_conn_info_opts *fuse_parse_conn_info_opts(struct fuse_args *args) - { -- struct fuse_conn_info_opts *opts; -- -- opts = calloc(1, sizeof(struct fuse_conn_info_opts)); -- if(opts == NULL) { -- fuse_log(FUSE_LOG_ERR, "calloc failed\n"); -- return NULL; -- } -- if(fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) { -- free(opts); -- return NULL; -- } -- return opts; -+ struct fuse_conn_info_opts *opts; -+ -+ opts = calloc(1, sizeof(struct fuse_conn_info_opts)); -+ if (opts == NULL) { -+ fuse_log(FUSE_LOG_ERR, "calloc failed\n"); -+ return NULL; -+ } -+ if (fuse_opt_parse(args, opts, conn_info_opt_spec, NULL) == -1) { -+ free(opts); -+ return NULL; -+ } -+ return opts; - } -diff --git a/tools/virtiofsd/passthrough_helpers.h b/tools/virtiofsd/passthrough_helpers.h -index 7c5f561fbc..0b98275ed5 100644 ---- a/tools/virtiofsd/passthrough_helpers.h -+++ b/tools/virtiofsd/passthrough_helpers.h -@@ -28,23 +28,24 @@ - * operation - */ - static int mknod_wrapper(int dirfd, const char *path, const char *link, -- int mode, dev_t rdev) -+ int mode, dev_t rdev) - { -- int res; -+ int res; - -- if (S_ISREG(mode)) { -- res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode); -- if (res >= 0) -- res = close(res); -- } else if (S_ISDIR(mode)) { -- res = mkdirat(dirfd, path, mode); -- } else if (S_ISLNK(mode) && link != NULL) { -- res = symlinkat(link, dirfd, path); -- } else if (S_ISFIFO(mode)) { -- res = mkfifoat(dirfd, path, mode); -- } else { -- res = mknodat(dirfd, path, mode, rdev); -- } -+ if (S_ISREG(mode)) { -+ res = openat(dirfd, path, O_CREAT | O_EXCL | O_WRONLY, mode); -+ if (res >= 0) { -+ res = close(res); -+ } -+ } else if (S_ISDIR(mode)) { -+ res = mkdirat(dirfd, path, mode); -+ } else if (S_ISLNK(mode) && link != NULL) { -+ res = symlinkat(link, dirfd, path); -+ } else if (S_ISFIFO(mode)) { -+ res = mkfifoat(dirfd, path, mode); -+ } else { -+ res = mknodat(dirfd, path, mode, rdev); -+ } - -- return res; -+ return res; - } -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index e5f7115bc1..c5850ef803 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1,12 +1,12 @@ - /* -- FUSE: Filesystem in Userspace -- Copyright (C) 2001-2007 Miklos Szeredi -- -- This program can be distributed under the terms of the GNU GPLv2. -- See the file COPYING. --*/ -+ * FUSE: Filesystem in Userspace -+ * Copyright (C) 2001-2007 Miklos Szeredi -+ * -+ * This program can be distributed under the terms of the GNU GPLv2. -+ * See the file COPYING. -+ */ - --/** @file -+/* - * - * This file system mirrors the existing file system hierarchy of the - * system, starting at the root file system. This is implemented by -@@ -28,7 +28,8 @@ - * - * Compile with: - * -- * gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o passthrough_ll -+ * gcc -Wall passthrough_ll.c `pkg-config fuse3 --cflags --libs` -o -+ * passthrough_ll - * - * ## Source code ## - * \include passthrough_ll.c -@@ -39,1299 +40,1365 @@ - - #include "config.h" - --#include --#include --#include --#include --#include --#include --#include --#include --#include - #include -+#include - #include -+#include - #include -+#include - #include -+#include -+#include -+#include -+#include -+#include - #include - #include -+#include - - #include "passthrough_helpers.h" - --/* We are re-using pointers to our `struct lo_inode` and `struct -- lo_dirp` elements as inodes. This means that we must be able to -- store uintptr_t values in a fuse_ino_t variable. The following -- incantation checks this condition at compile time. */ --#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && !defined __cplusplus -+/* -+ * We are re-using pointers to our `struct lo_inode` and `struct -+ * lo_dirp` elements as inodes. This means that we must be able to -+ * store uintptr_t values in a fuse_ino_t variable. The following -+ * incantation checks this condition at compile time. -+ */ -+#if defined(__GNUC__) && \ -+ (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && \ -+ !defined __cplusplus - _Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t), -- "fuse_ino_t too small to hold uintptr_t values!"); -+ "fuse_ino_t too small to hold uintptr_t values!"); - #else --struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct \ -- { unsigned _uintptr_to_must_hold_fuse_ino_t: -- ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); }; -+struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { -+ unsigned _uintptr_to_must_hold_fuse_ino_t -+ : ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); -+}; - #endif - - struct lo_inode { -- struct lo_inode *next; /* protected by lo->mutex */ -- struct lo_inode *prev; /* protected by lo->mutex */ -- int fd; -- bool is_symlink; -- ino_t ino; -- dev_t dev; -- uint64_t refcount; /* protected by lo->mutex */ -+ struct lo_inode *next; /* protected by lo->mutex */ -+ struct lo_inode *prev; /* protected by lo->mutex */ -+ int fd; -+ bool is_symlink; -+ ino_t ino; -+ dev_t dev; -+ uint64_t refcount; /* protected by lo->mutex */ - }; - - enum { -- CACHE_NEVER, -- CACHE_NORMAL, -- CACHE_ALWAYS, -+ CACHE_NEVER, -+ CACHE_NORMAL, -+ CACHE_ALWAYS, - }; - - struct lo_data { -- pthread_mutex_t mutex; -- int debug; -- int writeback; -- int flock; -- int xattr; -- const char *source; -- double timeout; -- int cache; -- int timeout_set; -- struct lo_inode root; /* protected by lo->mutex */ -+ pthread_mutex_t mutex; -+ int debug; -+ int writeback; -+ int flock; -+ int xattr; -+ const char *source; -+ double timeout; -+ int cache; -+ int timeout_set; -+ struct lo_inode root; /* protected by lo->mutex */ - }; - - static const struct fuse_opt lo_opts[] = { -- { "writeback", -- offsetof(struct lo_data, writeback), 1 }, -- { "no_writeback", -- offsetof(struct lo_data, writeback), 0 }, -- { "source=%s", -- offsetof(struct lo_data, source), 0 }, -- { "flock", -- offsetof(struct lo_data, flock), 1 }, -- { "no_flock", -- offsetof(struct lo_data, flock), 0 }, -- { "xattr", -- offsetof(struct lo_data, xattr), 1 }, -- { "no_xattr", -- offsetof(struct lo_data, xattr), 0 }, -- { "timeout=%lf", -- offsetof(struct lo_data, timeout), 0 }, -- { "timeout=", -- offsetof(struct lo_data, timeout_set), 1 }, -- { "cache=never", -- offsetof(struct lo_data, cache), CACHE_NEVER }, -- { "cache=auto", -- offsetof(struct lo_data, cache), CACHE_NORMAL }, -- { "cache=always", -- offsetof(struct lo_data, cache), CACHE_ALWAYS }, -- -- FUSE_OPT_END -+ { "writeback", offsetof(struct lo_data, writeback), 1 }, -+ { "no_writeback", offsetof(struct lo_data, writeback), 0 }, -+ { "source=%s", offsetof(struct lo_data, source), 0 }, -+ { "flock", offsetof(struct lo_data, flock), 1 }, -+ { "no_flock", offsetof(struct lo_data, flock), 0 }, -+ { "xattr", offsetof(struct lo_data, xattr), 1 }, -+ { "no_xattr", offsetof(struct lo_data, xattr), 0 }, -+ { "timeout=%lf", offsetof(struct lo_data, timeout), 0 }, -+ { "timeout=", offsetof(struct lo_data, timeout_set), 1 }, -+ { "cache=never", offsetof(struct lo_data, cache), CACHE_NEVER }, -+ { "cache=auto", offsetof(struct lo_data, cache), CACHE_NORMAL }, -+ { "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS }, -+ -+ FUSE_OPT_END - }; - - static struct lo_data *lo_data(fuse_req_t req) - { -- return (struct lo_data *) fuse_req_userdata(req); -+ return (struct lo_data *)fuse_req_userdata(req); - } - - static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) - { -- if (ino == FUSE_ROOT_ID) -- return &lo_data(req)->root; -- else -- return (struct lo_inode *) (uintptr_t) ino; -+ if (ino == FUSE_ROOT_ID) { -+ return &lo_data(req)->root; -+ } else { -+ return (struct lo_inode *)(uintptr_t)ino; -+ } - } - - static int lo_fd(fuse_req_t req, fuse_ino_t ino) - { -- return lo_inode(req, ino)->fd; -+ return lo_inode(req, ino)->fd; - } - - static bool lo_debug(fuse_req_t req) - { -- return lo_data(req)->debug != 0; -+ return lo_data(req)->debug != 0; - } - --static void lo_init(void *userdata, -- struct fuse_conn_info *conn) -+static void lo_init(void *userdata, struct fuse_conn_info *conn) - { -- struct lo_data *lo = (struct lo_data*) userdata; -- -- if(conn->capable & FUSE_CAP_EXPORT_SUPPORT) -- conn->want |= FUSE_CAP_EXPORT_SUPPORT; -- -- if (lo->writeback && -- conn->capable & FUSE_CAP_WRITEBACK_CACHE) { -- if (lo->debug) -- fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); -- conn->want |= FUSE_CAP_WRITEBACK_CACHE; -- } -- if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { -- if (lo->debug) -- fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); -- conn->want |= FUSE_CAP_FLOCK_LOCKS; -- } -+ struct lo_data *lo = (struct lo_data *)userdata; -+ -+ if (conn->capable & FUSE_CAP_EXPORT_SUPPORT) { -+ conn->want |= FUSE_CAP_EXPORT_SUPPORT; -+ } -+ -+ if (lo->writeback && conn->capable & FUSE_CAP_WRITEBACK_CACHE) { -+ if (lo->debug) { -+ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); -+ } -+ conn->want |= FUSE_CAP_WRITEBACK_CACHE; -+ } -+ if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { -+ if (lo->debug) { -+ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); -+ } -+ conn->want |= FUSE_CAP_FLOCK_LOCKS; -+ } - } - - static void lo_getattr(fuse_req_t req, fuse_ino_t ino, -- struct fuse_file_info *fi) -+ struct fuse_file_info *fi) - { -- int res; -- struct stat buf; -- struct lo_data *lo = lo_data(req); -+ int res; -+ struct stat buf; -+ struct lo_data *lo = lo_data(req); - -- (void) fi; -+ (void)fi; - -- res = fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -- if (res == -1) -- return (void) fuse_reply_err(req, errno); -+ res = -+ fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -+ if (res == -1) { -+ return (void)fuse_reply_err(req, errno); -+ } - -- fuse_reply_attr(req, &buf, lo->timeout); -+ fuse_reply_attr(req, &buf, lo->timeout); - } - - static int utimensat_empty_nofollow(struct lo_inode *inode, -- const struct timespec *tv) -+ const struct timespec *tv) - { -- int res; -- char procname[64]; -- -- if (inode->is_symlink) { -- res = utimensat(inode->fd, "", tv, -- AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -- if (res == -1 && errno == EINVAL) { -- /* Sorry, no race free way to set times on symlink. */ -- errno = EPERM; -- } -- return res; -- } -- sprintf(procname, "/proc/self/fd/%i", inode->fd); -- -- return utimensat(AT_FDCWD, procname, tv, 0); -+ int res; -+ char procname[64]; -+ -+ if (inode->is_symlink) { -+ res = utimensat(inode->fd, "", tv, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -+ if (res == -1 && errno == EINVAL) { -+ /* Sorry, no race free way to set times on symlink. */ -+ errno = EPERM; -+ } -+ return res; -+ } -+ sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ -+ return utimensat(AT_FDCWD, procname, tv, 0); - } - - static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, -- int valid, struct fuse_file_info *fi) -+ int valid, struct fuse_file_info *fi) - { -- int saverr; -- char procname[64]; -- struct lo_inode *inode = lo_inode(req, ino); -- int ifd = inode->fd; -- int res; -- -- if (valid & FUSE_SET_ATTR_MODE) { -- if (fi) { -- res = fchmod(fi->fh, attr->st_mode); -- } else { -- sprintf(procname, "/proc/self/fd/%i", ifd); -- res = chmod(procname, attr->st_mode); -- } -- if (res == -1) -- goto out_err; -- } -- if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) { -- uid_t uid = (valid & FUSE_SET_ATTR_UID) ? -- attr->st_uid : (uid_t) -1; -- gid_t gid = (valid & FUSE_SET_ATTR_GID) ? -- attr->st_gid : (gid_t) -1; -- -- res = fchownat(ifd, "", uid, gid, -- AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -- if (res == -1) -- goto out_err; -- } -- if (valid & FUSE_SET_ATTR_SIZE) { -- if (fi) { -- res = ftruncate(fi->fh, attr->st_size); -- } else { -- sprintf(procname, "/proc/self/fd/%i", ifd); -- res = truncate(procname, attr->st_size); -- } -- if (res == -1) -- goto out_err; -- } -- if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { -- struct timespec tv[2]; -- -- tv[0].tv_sec = 0; -- tv[1].tv_sec = 0; -- tv[0].tv_nsec = UTIME_OMIT; -- tv[1].tv_nsec = UTIME_OMIT; -- -- if (valid & FUSE_SET_ATTR_ATIME_NOW) -- tv[0].tv_nsec = UTIME_NOW; -- else if (valid & FUSE_SET_ATTR_ATIME) -- tv[0] = attr->st_atim; -- -- if (valid & FUSE_SET_ATTR_MTIME_NOW) -- tv[1].tv_nsec = UTIME_NOW; -- else if (valid & FUSE_SET_ATTR_MTIME) -- tv[1] = attr->st_mtim; -- -- if (fi) -- res = futimens(fi->fh, tv); -- else -- res = utimensat_empty_nofollow(inode, tv); -- if (res == -1) -- goto out_err; -- } -- -- return lo_getattr(req, ino, fi); -+ int saverr; -+ char procname[64]; -+ struct lo_inode *inode = lo_inode(req, ino); -+ int ifd = inode->fd; -+ int res; -+ -+ if (valid & FUSE_SET_ATTR_MODE) { -+ if (fi) { -+ res = fchmod(fi->fh, attr->st_mode); -+ } else { -+ sprintf(procname, "/proc/self/fd/%i", ifd); -+ res = chmod(procname, attr->st_mode); -+ } -+ if (res == -1) { -+ goto out_err; -+ } -+ } -+ if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) { -+ uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t)-1; -+ gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t)-1; -+ -+ res = fchownat(ifd, "", uid, gid, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -+ if (res == -1) { -+ goto out_err; -+ } -+ } -+ if (valid & FUSE_SET_ATTR_SIZE) { -+ if (fi) { -+ res = ftruncate(fi->fh, attr->st_size); -+ } else { -+ sprintf(procname, "/proc/self/fd/%i", ifd); -+ res = truncate(procname, attr->st_size); -+ } -+ if (res == -1) { -+ goto out_err; -+ } -+ } -+ if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) { -+ struct timespec tv[2]; -+ -+ tv[0].tv_sec = 0; -+ tv[1].tv_sec = 0; -+ tv[0].tv_nsec = UTIME_OMIT; -+ tv[1].tv_nsec = UTIME_OMIT; -+ -+ if (valid & FUSE_SET_ATTR_ATIME_NOW) { -+ tv[0].tv_nsec = UTIME_NOW; -+ } else if (valid & FUSE_SET_ATTR_ATIME) { -+ tv[0] = attr->st_atim; -+ } -+ -+ if (valid & FUSE_SET_ATTR_MTIME_NOW) { -+ tv[1].tv_nsec = UTIME_NOW; -+ } else if (valid & FUSE_SET_ATTR_MTIME) { -+ tv[1] = attr->st_mtim; -+ } -+ -+ if (fi) { -+ res = futimens(fi->fh, tv); -+ } else { -+ res = utimensat_empty_nofollow(inode, tv); -+ } -+ if (res == -1) { -+ goto out_err; -+ } -+ } -+ -+ return lo_getattr(req, ino, fi); - - out_err: -- saverr = errno; -- fuse_reply_err(req, saverr); -+ saverr = errno; -+ fuse_reply_err(req, saverr); - } - - static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st) - { -- struct lo_inode *p; -- struct lo_inode *ret = NULL; -- -- pthread_mutex_lock(&lo->mutex); -- for (p = lo->root.next; p != &lo->root; p = p->next) { -- if (p->ino == st->st_ino && p->dev == st->st_dev) { -- assert(p->refcount > 0); -- ret = p; -- ret->refcount++; -- break; -- } -- } -- pthread_mutex_unlock(&lo->mutex); -- return ret; -+ struct lo_inode *p; -+ struct lo_inode *ret = NULL; -+ -+ pthread_mutex_lock(&lo->mutex); -+ for (p = lo->root.next; p != &lo->root; p = p->next) { -+ if (p->ino == st->st_ino && p->dev == st->st_dev) { -+ assert(p->refcount > 0); -+ ret = p; -+ ret->refcount++; -+ break; -+ } -+ } -+ pthread_mutex_unlock(&lo->mutex); -+ return ret; - } - - static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, -- struct fuse_entry_param *e) -+ struct fuse_entry_param *e) - { -- int newfd; -- int res; -- int saverr; -- struct lo_data *lo = lo_data(req); -- struct lo_inode *inode; -- -- memset(e, 0, sizeof(*e)); -- e->attr_timeout = lo->timeout; -- e->entry_timeout = lo->timeout; -- -- newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW); -- if (newfd == -1) -- goto out_err; -- -- res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -- if (res == -1) -- goto out_err; -- -- inode = lo_find(lo_data(req), &e->attr); -- if (inode) { -- close(newfd); -- newfd = -1; -- } else { -- struct lo_inode *prev, *next; -- -- saverr = ENOMEM; -- inode = calloc(1, sizeof(struct lo_inode)); -- if (!inode) -- goto out_err; -- -- inode->is_symlink = S_ISLNK(e->attr.st_mode); -- inode->refcount = 1; -- inode->fd = newfd; -- inode->ino = e->attr.st_ino; -- inode->dev = e->attr.st_dev; -- -- pthread_mutex_lock(&lo->mutex); -- prev = &lo->root; -- next = prev->next; -- next->prev = inode; -- inode->next = next; -- inode->prev = prev; -- prev->next = inode; -- pthread_mutex_unlock(&lo->mutex); -- } -- e->ino = (uintptr_t) inode; -- -- if (lo_debug(req)) -- fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", -- (unsigned long long) parent, name, (unsigned long long) e->ino); -- -- return 0; -+ int newfd; -+ int res; -+ int saverr; -+ struct lo_data *lo = lo_data(req); -+ struct lo_inode *inode; -+ -+ memset(e, 0, sizeof(*e)); -+ e->attr_timeout = lo->timeout; -+ e->entry_timeout = lo->timeout; -+ -+ newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW); -+ if (newfd == -1) { -+ goto out_err; -+ } -+ -+ res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -+ if (res == -1) { -+ goto out_err; -+ } -+ -+ inode = lo_find(lo_data(req), &e->attr); -+ if (inode) { -+ close(newfd); -+ newfd = -1; -+ } else { -+ struct lo_inode *prev, *next; -+ -+ saverr = ENOMEM; -+ inode = calloc(1, sizeof(struct lo_inode)); -+ if (!inode) { -+ goto out_err; -+ } -+ -+ inode->is_symlink = S_ISLNK(e->attr.st_mode); -+ inode->refcount = 1; -+ inode->fd = newfd; -+ inode->ino = e->attr.st_ino; -+ inode->dev = e->attr.st_dev; -+ -+ pthread_mutex_lock(&lo->mutex); -+ prev = &lo->root; -+ next = prev->next; -+ next->prev = inode; -+ inode->next = next; -+ inode->prev = prev; -+ prev->next = inode; -+ pthread_mutex_unlock(&lo->mutex); -+ } -+ e->ino = (uintptr_t)inode; -+ -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", -+ (unsigned long long)parent, name, (unsigned long long)e->ino); -+ } -+ -+ return 0; - - out_err: -- saverr = errno; -- if (newfd != -1) -- close(newfd); -- return saverr; -+ saverr = errno; -+ if (newfd != -1) { -+ close(newfd); -+ } -+ return saverr; - } - - static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) - { -- struct fuse_entry_param e; -- int err; -- -- if (lo_debug(req)) -- fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", -- parent, name); -- -- err = lo_do_lookup(req, parent, name, &e); -- if (err) -- fuse_reply_err(req, err); -- else -- fuse_reply_entry(req, &e); -+ struct fuse_entry_param e; -+ int err; -+ -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", -+ parent, name); -+ } -+ -+ err = lo_do_lookup(req, parent, name, &e); -+ if (err) { -+ fuse_reply_err(req, err); -+ } else { -+ fuse_reply_entry(req, &e); -+ } - } - - static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, -- const char *name, mode_t mode, dev_t rdev, -- const char *link) -+ const char *name, mode_t mode, dev_t rdev, -+ const char *link) - { -- int res; -- int saverr; -- struct lo_inode *dir = lo_inode(req, parent); -- struct fuse_entry_param e; -+ int res; -+ int saverr; -+ struct lo_inode *dir = lo_inode(req, parent); -+ struct fuse_entry_param e; - -- saverr = ENOMEM; -+ saverr = ENOMEM; - -- res = mknod_wrapper(dir->fd, name, link, mode, rdev); -+ res = mknod_wrapper(dir->fd, name, link, mode, rdev); - -- saverr = errno; -- if (res == -1) -- goto out; -+ saverr = errno; -+ if (res == -1) { -+ goto out; -+ } - -- saverr = lo_do_lookup(req, parent, name, &e); -- if (saverr) -- goto out; -+ saverr = lo_do_lookup(req, parent, name, &e); -+ if (saverr) { -+ goto out; -+ } - -- if (lo_debug(req)) -- fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", -- (unsigned long long) parent, name, (unsigned long long) e.ino); -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", -+ (unsigned long long)parent, name, (unsigned long long)e.ino); -+ } - -- fuse_reply_entry(req, &e); -- return; -+ fuse_reply_entry(req, &e); -+ return; - - out: -- fuse_reply_err(req, saverr); -+ fuse_reply_err(req, saverr); - } - --static void lo_mknod(fuse_req_t req, fuse_ino_t parent, -- const char *name, mode_t mode, dev_t rdev) -+static void lo_mknod(fuse_req_t req, fuse_ino_t parent, const char *name, -+ mode_t mode, dev_t rdev) - { -- lo_mknod_symlink(req, parent, name, mode, rdev, NULL); -+ lo_mknod_symlink(req, parent, name, mode, rdev, NULL); - } - - static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, -- mode_t mode) -+ mode_t mode) - { -- lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL); -+ lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL); - } - --static void lo_symlink(fuse_req_t req, const char *link, -- fuse_ino_t parent, const char *name) -+static void lo_symlink(fuse_req_t req, const char *link, fuse_ino_t parent, -+ const char *name) - { -- lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link); -+ lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link); - } - - static int linkat_empty_nofollow(struct lo_inode *inode, int dfd, -- const char *name) -+ const char *name) - { -- int res; -- char procname[64]; -+ int res; -+ char procname[64]; - -- if (inode->is_symlink) { -- res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH); -- if (res == -1 && (errno == ENOENT || errno == EINVAL)) { -- /* Sorry, no race free way to hard-link a symlink. */ -- errno = EPERM; -- } -- return res; -- } -+ if (inode->is_symlink) { -+ res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH); -+ if (res == -1 && (errno == ENOENT || errno == EINVAL)) { -+ /* Sorry, no race free way to hard-link a symlink. */ -+ errno = EPERM; -+ } -+ return res; -+ } - -- sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ sprintf(procname, "/proc/self/fd/%i", inode->fd); - -- return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW); -+ return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW); - } - - static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, -- const char *name) -+ const char *name) - { -- int res; -- struct lo_data *lo = lo_data(req); -- struct lo_inode *inode = lo_inode(req, ino); -- struct fuse_entry_param e; -- int saverr; -- -- memset(&e, 0, sizeof(struct fuse_entry_param)); -- e.attr_timeout = lo->timeout; -- e.entry_timeout = lo->timeout; -- -- res = linkat_empty_nofollow(inode, lo_fd(req, parent), name); -- if (res == -1) -- goto out_err; -- -- res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -- if (res == -1) -- goto out_err; -- -- pthread_mutex_lock(&lo->mutex); -- inode->refcount++; -- pthread_mutex_unlock(&lo->mutex); -- e.ino = (uintptr_t) inode; -- -- if (lo_debug(req)) -- fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", -- (unsigned long long) parent, name, -- (unsigned long long) e.ino); -- -- fuse_reply_entry(req, &e); -- return; -+ int res; -+ struct lo_data *lo = lo_data(req); -+ struct lo_inode *inode = lo_inode(req, ino); -+ struct fuse_entry_param e; -+ int saverr; -+ -+ memset(&e, 0, sizeof(struct fuse_entry_param)); -+ e.attr_timeout = lo->timeout; -+ e.entry_timeout = lo->timeout; -+ -+ res = linkat_empty_nofollow(inode, lo_fd(req, parent), name); -+ if (res == -1) { -+ goto out_err; -+ } -+ -+ res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -+ if (res == -1) { -+ goto out_err; -+ } -+ -+ pthread_mutex_lock(&lo->mutex); -+ inode->refcount++; -+ pthread_mutex_unlock(&lo->mutex); -+ e.ino = (uintptr_t)inode; -+ -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", -+ (unsigned long long)parent, name, (unsigned long long)e.ino); -+ } -+ -+ fuse_reply_entry(req, &e); -+ return; - - out_err: -- saverr = errno; -- fuse_reply_err(req, saverr); -+ saverr = errno; -+ fuse_reply_err(req, saverr); - } - - static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) - { -- int res; -+ int res; - -- res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); -+ res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); - -- fuse_reply_err(req, res == -1 ? errno : 0); -+ fuse_reply_err(req, res == -1 ? errno : 0); - } - - static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, -- fuse_ino_t newparent, const char *newname, -- unsigned int flags) -+ fuse_ino_t newparent, const char *newname, -+ unsigned int flags) - { -- int res; -+ int res; - -- if (flags) { -- fuse_reply_err(req, EINVAL); -- return; -- } -+ if (flags) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - -- res = renameat(lo_fd(req, parent), name, -- lo_fd(req, newparent), newname); -+ res = renameat(lo_fd(req, parent), name, lo_fd(req, newparent), newname); - -- fuse_reply_err(req, res == -1 ? errno : 0); -+ fuse_reply_err(req, res == -1 ? errno : 0); - } - - static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) - { -- int res; -+ int res; - -- res = unlinkat(lo_fd(req, parent), name, 0); -+ res = unlinkat(lo_fd(req, parent), name, 0); - -- fuse_reply_err(req, res == -1 ? errno : 0); -+ fuse_reply_err(req, res == -1 ? errno : 0); - } - - static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) - { -- if (!inode) -- return; -- -- pthread_mutex_lock(&lo->mutex); -- assert(inode->refcount >= n); -- inode->refcount -= n; -- if (!inode->refcount) { -- struct lo_inode *prev, *next; -- -- prev = inode->prev; -- next = inode->next; -- next->prev = prev; -- prev->next = next; -- -- pthread_mutex_unlock(&lo->mutex); -- close(inode->fd); -- free(inode); -- -- } else { -- pthread_mutex_unlock(&lo->mutex); -- } -+ if (!inode) { -+ return; -+ } -+ -+ pthread_mutex_lock(&lo->mutex); -+ assert(inode->refcount >= n); -+ inode->refcount -= n; -+ if (!inode->refcount) { -+ struct lo_inode *prev, *next; -+ -+ prev = inode->prev; -+ next = inode->next; -+ next->prev = prev; -+ prev->next = next; -+ -+ pthread_mutex_unlock(&lo->mutex); -+ close(inode->fd); -+ free(inode); -+ -+ } else { -+ pthread_mutex_unlock(&lo->mutex); -+ } - } - - static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) - { -- struct lo_data *lo = lo_data(req); -- struct lo_inode *inode = lo_inode(req, ino); -+ struct lo_data *lo = lo_data(req); -+ struct lo_inode *inode = lo_inode(req, ino); - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", -- (unsigned long long) ino, -- (unsigned long long) inode->refcount, -- (unsigned long long) nlookup); -- } -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", -+ (unsigned long long)ino, (unsigned long long)inode->refcount, -+ (unsigned long long)nlookup); -+ } - -- unref_inode(lo, inode, nlookup); -+ unref_inode(lo, inode, nlookup); - } - - static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) - { -- lo_forget_one(req, ino, nlookup); -- fuse_reply_none(req); -+ lo_forget_one(req, ino, nlookup); -+ fuse_reply_none(req); - } - - static void lo_forget_multi(fuse_req_t req, size_t count, -- struct fuse_forget_data *forgets) -+ struct fuse_forget_data *forgets) - { -- int i; -+ int i; - -- for (i = 0; i < count; i++) -- lo_forget_one(req, forgets[i].ino, forgets[i].nlookup); -- fuse_reply_none(req); -+ for (i = 0; i < count; i++) { -+ lo_forget_one(req, forgets[i].ino, forgets[i].nlookup); -+ } -+ fuse_reply_none(req); - } - - static void lo_readlink(fuse_req_t req, fuse_ino_t ino) - { -- char buf[PATH_MAX + 1]; -- int res; -+ char buf[PATH_MAX + 1]; -+ int res; - -- res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf)); -- if (res == -1) -- return (void) fuse_reply_err(req, errno); -+ res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf)); -+ if (res == -1) { -+ return (void)fuse_reply_err(req, errno); -+ } - -- if (res == sizeof(buf)) -- return (void) fuse_reply_err(req, ENAMETOOLONG); -+ if (res == sizeof(buf)) { -+ return (void)fuse_reply_err(req, ENAMETOOLONG); -+ } - -- buf[res] = '\0'; -+ buf[res] = '\0'; - -- fuse_reply_readlink(req, buf); -+ fuse_reply_readlink(req, buf); - } - - struct lo_dirp { -- DIR *dp; -- struct dirent *entry; -- off_t offset; -+ DIR *dp; -+ struct dirent *entry; -+ off_t offset; - }; - - static struct lo_dirp *lo_dirp(struct fuse_file_info *fi) - { -- return (struct lo_dirp *) (uintptr_t) fi->fh; -+ return (struct lo_dirp *)(uintptr_t)fi->fh; - } - --static void lo_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) -+static void lo_opendir(fuse_req_t req, fuse_ino_t ino, -+ struct fuse_file_info *fi) - { -- int error = ENOMEM; -- struct lo_data *lo = lo_data(req); -- struct lo_dirp *d; -- int fd; -- -- d = calloc(1, sizeof(struct lo_dirp)); -- if (d == NULL) -- goto out_err; -- -- fd = openat(lo_fd(req, ino), ".", O_RDONLY); -- if (fd == -1) -- goto out_errno; -- -- d->dp = fdopendir(fd); -- if (d->dp == NULL) -- goto out_errno; -- -- d->offset = 0; -- d->entry = NULL; -- -- fi->fh = (uintptr_t) d; -- if (lo->cache == CACHE_ALWAYS) -- fi->keep_cache = 1; -- fuse_reply_open(req, fi); -- return; -+ int error = ENOMEM; -+ struct lo_data *lo = lo_data(req); -+ struct lo_dirp *d; -+ int fd; -+ -+ d = calloc(1, sizeof(struct lo_dirp)); -+ if (d == NULL) { -+ goto out_err; -+ } -+ -+ fd = openat(lo_fd(req, ino), ".", O_RDONLY); -+ if (fd == -1) { -+ goto out_errno; -+ } -+ -+ d->dp = fdopendir(fd); -+ if (d->dp == NULL) { -+ goto out_errno; -+ } -+ -+ d->offset = 0; -+ d->entry = NULL; -+ -+ fi->fh = (uintptr_t)d; -+ if (lo->cache == CACHE_ALWAYS) { -+ fi->keep_cache = 1; -+ } -+ fuse_reply_open(req, fi); -+ return; - - out_errno: -- error = errno; -+ error = errno; - out_err: -- if (d) { -- if (fd != -1) -- close(fd); -- free(d); -- } -- fuse_reply_err(req, error); -+ if (d) { -+ if (fd != -1) { -+ close(fd); -+ } -+ free(d); -+ } -+ fuse_reply_err(req, error); - } - - static int is_dot_or_dotdot(const char *name) - { -- return name[0] == '.' && (name[1] == '\0' || -- (name[1] == '.' && name[2] == '\0')); -+ return name[0] == '.' && -+ (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); - } - - static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, -- off_t offset, struct fuse_file_info *fi, int plus) -+ off_t offset, struct fuse_file_info *fi, int plus) - { -- struct lo_dirp *d = lo_dirp(fi); -- char *buf; -- char *p; -- size_t rem = size; -- int err; -- -- (void) ino; -- -- buf = calloc(1, size); -- if (!buf) { -- err = ENOMEM; -- goto error; -- } -- p = buf; -- -- if (offset != d->offset) { -- seekdir(d->dp, offset); -- d->entry = NULL; -- d->offset = offset; -- } -- while (1) { -- size_t entsize; -- off_t nextoff; -- const char *name; -- -- if (!d->entry) { -- errno = 0; -- d->entry = readdir(d->dp); -- if (!d->entry) { -- if (errno) { // Error -- err = errno; -- goto error; -- } else { // End of stream -- break; -- } -- } -- } -- nextoff = d->entry->d_off; -- name = d->entry->d_name; -- fuse_ino_t entry_ino = 0; -- if (plus) { -- struct fuse_entry_param e; -- if (is_dot_or_dotdot(name)) { -- e = (struct fuse_entry_param) { -- .attr.st_ino = d->entry->d_ino, -- .attr.st_mode = d->entry->d_type << 12, -- }; -- } else { -- err = lo_do_lookup(req, ino, name, &e); -- if (err) -- goto error; -- entry_ino = e.ino; -- } -- -- entsize = fuse_add_direntry_plus(req, p, rem, name, -- &e, nextoff); -- } else { -- struct stat st = { -- .st_ino = d->entry->d_ino, -- .st_mode = d->entry->d_type << 12, -- }; -- entsize = fuse_add_direntry(req, p, rem, name, -- &st, nextoff); -- } -- if (entsize > rem) { -- if (entry_ino != 0) -- lo_forget_one(req, entry_ino, 1); -- break; -- } -- -- p += entsize; -- rem -= entsize; -- -- d->entry = NULL; -- d->offset = nextoff; -- } -+ struct lo_dirp *d = lo_dirp(fi); -+ char *buf; -+ char *p; -+ size_t rem = size; -+ int err; -+ -+ (void)ino; -+ -+ buf = calloc(1, size); -+ if (!buf) { -+ err = ENOMEM; -+ goto error; -+ } -+ p = buf; -+ -+ if (offset != d->offset) { -+ seekdir(d->dp, offset); -+ d->entry = NULL; -+ d->offset = offset; -+ } -+ while (1) { -+ size_t entsize; -+ off_t nextoff; -+ const char *name; -+ -+ if (!d->entry) { -+ errno = 0; -+ d->entry = readdir(d->dp); -+ if (!d->entry) { -+ if (errno) { /* Error */ -+ err = errno; -+ goto error; -+ } else { /* End of stream */ -+ break; -+ } -+ } -+ } -+ nextoff = d->entry->d_off; -+ name = d->entry->d_name; -+ fuse_ino_t entry_ino = 0; -+ if (plus) { -+ struct fuse_entry_param e; -+ if (is_dot_or_dotdot(name)) { -+ e = (struct fuse_entry_param){ -+ .attr.st_ino = d->entry->d_ino, -+ .attr.st_mode = d->entry->d_type << 12, -+ }; -+ } else { -+ err = lo_do_lookup(req, ino, name, &e); -+ if (err) { -+ goto error; -+ } -+ entry_ino = e.ino; -+ } -+ -+ entsize = fuse_add_direntry_plus(req, p, rem, name, &e, nextoff); -+ } else { -+ struct stat st = { -+ .st_ino = d->entry->d_ino, -+ .st_mode = d->entry->d_type << 12, -+ }; -+ entsize = fuse_add_direntry(req, p, rem, name, &st, nextoff); -+ } -+ if (entsize > rem) { -+ if (entry_ino != 0) { -+ lo_forget_one(req, entry_ino, 1); -+ } -+ break; -+ } -+ -+ p += entsize; -+ rem -= entsize; -+ -+ d->entry = NULL; -+ d->offset = nextoff; -+ } - - err = 0; - error: -- // If there's an error, we can only signal it if we haven't stored -- // any entries yet - otherwise we'd end up with wrong lookup -- // counts for the entries that are already in the buffer. So we -- // return what we've collected until that point. -- if (err && rem == size) -- fuse_reply_err(req, err); -- else -- fuse_reply_buf(req, buf, size - rem); -+ /* -+ * If there's an error, we can only signal it if we haven't stored -+ * any entries yet - otherwise we'd end up with wrong lookup -+ * counts for the entries that are already in the buffer. So we -+ * return what we've collected until that point. -+ */ -+ if (err && rem == size) { -+ fuse_reply_err(req, err); -+ } else { -+ fuse_reply_buf(req, buf, size - rem); -+ } - free(buf); - } - - static void lo_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, -- off_t offset, struct fuse_file_info *fi) -+ off_t offset, struct fuse_file_info *fi) - { -- lo_do_readdir(req, ino, size, offset, fi, 0); -+ lo_do_readdir(req, ino, size, offset, fi, 0); - } - - static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, -- off_t offset, struct fuse_file_info *fi) -+ off_t offset, struct fuse_file_info *fi) - { -- lo_do_readdir(req, ino, size, offset, fi, 1); -+ lo_do_readdir(req, ino, size, offset, fi, 1); - } - --static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) -+static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, -+ struct fuse_file_info *fi) - { -- struct lo_dirp *d = lo_dirp(fi); -- (void) ino; -- closedir(d->dp); -- free(d); -- fuse_reply_err(req, 0); -+ struct lo_dirp *d = lo_dirp(fi); -+ (void)ino; -+ closedir(d->dp); -+ free(d); -+ fuse_reply_err(req, 0); - } - - static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, -- mode_t mode, struct fuse_file_info *fi) -+ mode_t mode, struct fuse_file_info *fi) - { -- int fd; -- struct lo_data *lo = lo_data(req); -- struct fuse_entry_param e; -- int err; -- -- if (lo_debug(req)) -- fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", -- parent, name); -- -- fd = openat(lo_fd(req, parent), name, -- (fi->flags | O_CREAT) & ~O_NOFOLLOW, mode); -- if (fd == -1) -- return (void) fuse_reply_err(req, errno); -- -- fi->fh = fd; -- if (lo->cache == CACHE_NEVER) -- fi->direct_io = 1; -- else if (lo->cache == CACHE_ALWAYS) -- fi->keep_cache = 1; -- -- err = lo_do_lookup(req, parent, name, &e); -- if (err) -- fuse_reply_err(req, err); -- else -- fuse_reply_create(req, &e, fi); -+ int fd; -+ struct lo_data *lo = lo_data(req); -+ struct fuse_entry_param e; -+ int err; -+ -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", -+ parent, name); -+ } -+ -+ fd = openat(lo_fd(req, parent), name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, -+ mode); -+ if (fd == -1) { -+ return (void)fuse_reply_err(req, errno); -+ } -+ -+ fi->fh = fd; -+ if (lo->cache == CACHE_NEVER) { -+ fi->direct_io = 1; -+ } else if (lo->cache == CACHE_ALWAYS) { -+ fi->keep_cache = 1; -+ } -+ -+ err = lo_do_lookup(req, parent, name, &e); -+ if (err) { -+ fuse_reply_err(req, err); -+ } else { -+ fuse_reply_create(req, &e, fi); -+ } - } - - static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, -- struct fuse_file_info *fi) -+ struct fuse_file_info *fi) - { -- int res; -- int fd = dirfd(lo_dirp(fi)->dp); -- (void) ino; -- if (datasync) -- res = fdatasync(fd); -- else -- res = fsync(fd); -- fuse_reply_err(req, res == -1 ? errno : 0); -+ int res; -+ int fd = dirfd(lo_dirp(fi)->dp); -+ (void)ino; -+ if (datasync) { -+ res = fdatasync(fd); -+ } else { -+ res = fsync(fd); -+ } -+ fuse_reply_err(req, res == -1 ? errno : 0); - } - - static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) - { -- int fd; -- char buf[64]; -- struct lo_data *lo = lo_data(req); -- -- if (lo_debug(req)) -- fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", -- ino, fi->flags); -- -- /* With writeback cache, kernel may send read requests even -- when userspace opened write-only */ -- if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) { -- fi->flags &= ~O_ACCMODE; -- fi->flags |= O_RDWR; -- } -- -- /* With writeback cache, O_APPEND is handled by the kernel. -- This breaks atomicity (since the file may change in the -- underlying filesystem, so that the kernel's idea of the -- end of the file isn't accurate anymore). In this example, -- we just accept that. A more rigorous filesystem may want -- to return an error here */ -- if (lo->writeback && (fi->flags & O_APPEND)) -- fi->flags &= ~O_APPEND; -- -- sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino)); -- fd = open(buf, fi->flags & ~O_NOFOLLOW); -- if (fd == -1) -- return (void) fuse_reply_err(req, errno); -- -- fi->fh = fd; -- if (lo->cache == CACHE_NEVER) -- fi->direct_io = 1; -- else if (lo->cache == CACHE_ALWAYS) -- fi->keep_cache = 1; -- fuse_reply_open(req, fi); -+ int fd; -+ char buf[64]; -+ struct lo_data *lo = lo_data(req); -+ -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino, -+ fi->flags); -+ } -+ -+ /* -+ * With writeback cache, kernel may send read requests even -+ * when userspace opened write-only -+ */ -+ if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) { -+ fi->flags &= ~O_ACCMODE; -+ fi->flags |= O_RDWR; -+ } -+ -+ /* -+ * With writeback cache, O_APPEND is handled by the kernel. -+ * This breaks atomicity (since the file may change in the -+ * underlying filesystem, so that the kernel's idea of the -+ * end of the file isn't accurate anymore). In this example, -+ * we just accept that. A more rigorous filesystem may want -+ * to return an error here -+ */ -+ if (lo->writeback && (fi->flags & O_APPEND)) { -+ fi->flags &= ~O_APPEND; -+ } -+ -+ sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino)); -+ fd = open(buf, fi->flags & ~O_NOFOLLOW); -+ if (fd == -1) { -+ return (void)fuse_reply_err(req, errno); -+ } -+ -+ fi->fh = fd; -+ if (lo->cache == CACHE_NEVER) { -+ fi->direct_io = 1; -+ } else if (lo->cache == CACHE_ALWAYS) { -+ fi->keep_cache = 1; -+ } -+ fuse_reply_open(req, fi); - } - --static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) -+static void lo_release(fuse_req_t req, fuse_ino_t ino, -+ struct fuse_file_info *fi) - { -- (void) ino; -+ (void)ino; - -- close(fi->fh); -- fuse_reply_err(req, 0); -+ close(fi->fh); -+ fuse_reply_err(req, 0); - } - - static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) - { -- int res; -- (void) ino; -- res = close(dup(fi->fh)); -- fuse_reply_err(req, res == -1 ? errno : 0); -+ int res; -+ (void)ino; -+ res = close(dup(fi->fh)); -+ fuse_reply_err(req, res == -1 ? errno : 0); - } - - static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, -- struct fuse_file_info *fi) -+ struct fuse_file_info *fi) - { -- int res; -- (void) ino; -- if (datasync) -- res = fdatasync(fi->fh); -- else -- res = fsync(fi->fh); -- fuse_reply_err(req, res == -1 ? errno : 0); -+ int res; -+ (void)ino; -+ if (datasync) { -+ res = fdatasync(fi->fh); -+ } else { -+ res = fsync(fi->fh); -+ } -+ fuse_reply_err(req, res == -1 ? errno : 0); - } - --static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, -- off_t offset, struct fuse_file_info *fi) -+static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, -+ struct fuse_file_info *fi) - { -- struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); -+ struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); - -- if (lo_debug(req)) -- fuse_log(FUSE_LOG_DEBUG, "lo_read(ino=%" PRIu64 ", size=%zd, " -- "off=%lu)\n", ino, size, (unsigned long) offset); -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, -+ "lo_read(ino=%" PRIu64 ", size=%zd, " -+ "off=%lu)\n", -+ ino, size, (unsigned long)offset); -+ } - -- buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; -- buf.buf[0].fd = fi->fh; -- buf.buf[0].pos = offset; -+ buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; -+ buf.buf[0].fd = fi->fh; -+ buf.buf[0].pos = offset; - -- fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE); -+ fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE); - } - - static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, -- struct fuse_bufvec *in_buf, off_t off, -- struct fuse_file_info *fi) -+ struct fuse_bufvec *in_buf, off_t off, -+ struct fuse_file_info *fi) - { -- (void) ino; -- ssize_t res; -- struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); -- -- out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; -- out_buf.buf[0].fd = fi->fh; -- out_buf.buf[0].pos = off; -- -- if (lo_debug(req)) -- fuse_log(FUSE_LOG_DEBUG, "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n", -- ino, out_buf.buf[0].size, (unsigned long) off); -- -- res = fuse_buf_copy(&out_buf, in_buf, 0); -- if(res < 0) -- fuse_reply_err(req, -res); -- else -- fuse_reply_write(req, (size_t) res); -+ (void)ino; -+ ssize_t res; -+ struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); -+ -+ out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; -+ out_buf.buf[0].fd = fi->fh; -+ out_buf.buf[0].pos = off; -+ -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, -+ "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n", ino, -+ out_buf.buf[0].size, (unsigned long)off); -+ } -+ -+ res = fuse_buf_copy(&out_buf, in_buf, 0); -+ if (res < 0) { -+ fuse_reply_err(req, -res); -+ } else { -+ fuse_reply_write(req, (size_t)res); -+ } - } - - static void lo_statfs(fuse_req_t req, fuse_ino_t ino) - { -- int res; -- struct statvfs stbuf; -- -- res = fstatvfs(lo_fd(req, ino), &stbuf); -- if (res == -1) -- fuse_reply_err(req, errno); -- else -- fuse_reply_statfs(req, &stbuf); -+ int res; -+ struct statvfs stbuf; -+ -+ res = fstatvfs(lo_fd(req, ino), &stbuf); -+ if (res == -1) { -+ fuse_reply_err(req, errno); -+ } else { -+ fuse_reply_statfs(req, &stbuf); -+ } - } - --static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, -- off_t offset, off_t length, struct fuse_file_info *fi) -+static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, -+ off_t length, struct fuse_file_info *fi) - { -- int err = EOPNOTSUPP; -- (void) ino; -+ int err = EOPNOTSUPP; -+ (void)ino; - - #ifdef HAVE_FALLOCATE -- err = fallocate(fi->fh, mode, offset, length); -- if (err < 0) -- err = errno; -+ err = fallocate(fi->fh, mode, offset, length); -+ if (err < 0) { -+ err = errno; -+ } - - #elif defined(HAVE_POSIX_FALLOCATE) -- if (mode) { -- fuse_reply_err(req, EOPNOTSUPP); -- return; -- } -+ if (mode) { -+ fuse_reply_err(req, EOPNOTSUPP); -+ return; -+ } - -- err = posix_fallocate(fi->fh, offset, length); -+ err = posix_fallocate(fi->fh, offset, length); - #endif - -- fuse_reply_err(req, err); -+ fuse_reply_err(req, err); - } - - static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, -- int op) -+ int op) - { -- int res; -- (void) ino; -+ int res; -+ (void)ino; - -- res = flock(fi->fh, op); -+ res = flock(fi->fh, op); - -- fuse_reply_err(req, res == -1 ? errno : 0); -+ fuse_reply_err(req, res == -1 ? errno : 0); - } - - static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, -- size_t size) -+ size_t size) - { -- char *value = NULL; -- char procname[64]; -- struct lo_inode *inode = lo_inode(req, ino); -- ssize_t ret; -- int saverr; -- -- saverr = ENOSYS; -- if (!lo_data(req)->xattr) -- goto out; -- -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", -- ino, name, size); -- } -- -- if (inode->is_symlink) { -- /* Sorry, no race free way to getxattr on symlink. */ -- saverr = EPERM; -- goto out; -- } -- -- sprintf(procname, "/proc/self/fd/%i", inode->fd); -- -- if (size) { -- value = malloc(size); -- if (!value) -- goto out_err; -- -- ret = getxattr(procname, name, value, size); -- if (ret == -1) -- goto out_err; -- saverr = 0; -- if (ret == 0) -- goto out; -- -- fuse_reply_buf(req, value, ret); -- } else { -- ret = getxattr(procname, name, NULL, 0); -- if (ret == -1) -- goto out_err; -- -- fuse_reply_xattr(req, ret); -- } -+ char *value = NULL; -+ char procname[64]; -+ struct lo_inode *inode = lo_inode(req, ino); -+ ssize_t ret; -+ int saverr; -+ -+ saverr = ENOSYS; -+ if (!lo_data(req)->xattr) { -+ goto out; -+ } -+ -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, -+ "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", ino, name, -+ size); -+ } -+ -+ if (inode->is_symlink) { -+ /* Sorry, no race free way to getxattr on symlink. */ -+ saverr = EPERM; -+ goto out; -+ } -+ -+ sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ -+ if (size) { -+ value = malloc(size); -+ if (!value) { -+ goto out_err; -+ } -+ -+ ret = getxattr(procname, name, value, size); -+ if (ret == -1) { -+ goto out_err; -+ } -+ saverr = 0; -+ if (ret == 0) { -+ goto out; -+ } -+ -+ fuse_reply_buf(req, value, ret); -+ } else { -+ ret = getxattr(procname, name, NULL, 0); -+ if (ret == -1) { -+ goto out_err; -+ } -+ -+ fuse_reply_xattr(req, ret); -+ } - out_free: -- free(value); -- return; -+ free(value); -+ return; - - out_err: -- saverr = errno; -+ saverr = errno; - out: -- fuse_reply_err(req, saverr); -- goto out_free; -+ fuse_reply_err(req, saverr); -+ goto out_free; - } - - static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) - { -- char *value = NULL; -- char procname[64]; -- struct lo_inode *inode = lo_inode(req, ino); -- ssize_t ret; -- int saverr; -- -- saverr = ENOSYS; -- if (!lo_data(req)->xattr) -- goto out; -- -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n", -- ino, size); -- } -- -- if (inode->is_symlink) { -- /* Sorry, no race free way to listxattr on symlink. */ -- saverr = EPERM; -- goto out; -- } -- -- sprintf(procname, "/proc/self/fd/%i", inode->fd); -- -- if (size) { -- value = malloc(size); -- if (!value) -- goto out_err; -- -- ret = listxattr(procname, value, size); -- if (ret == -1) -- goto out_err; -- saverr = 0; -- if (ret == 0) -- goto out; -- -- fuse_reply_buf(req, value, ret); -- } else { -- ret = listxattr(procname, NULL, 0); -- if (ret == -1) -- goto out_err; -- -- fuse_reply_xattr(req, ret); -- } -+ char *value = NULL; -+ char procname[64]; -+ struct lo_inode *inode = lo_inode(req, ino); -+ ssize_t ret; -+ int saverr; -+ -+ saverr = ENOSYS; -+ if (!lo_data(req)->xattr) { -+ goto out; -+ } -+ -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n", -+ ino, size); -+ } -+ -+ if (inode->is_symlink) { -+ /* Sorry, no race free way to listxattr on symlink. */ -+ saverr = EPERM; -+ goto out; -+ } -+ -+ sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ -+ if (size) { -+ value = malloc(size); -+ if (!value) { -+ goto out_err; -+ } -+ -+ ret = listxattr(procname, value, size); -+ if (ret == -1) { -+ goto out_err; -+ } -+ saverr = 0; -+ if (ret == 0) { -+ goto out; -+ } -+ -+ fuse_reply_buf(req, value, ret); -+ } else { -+ ret = listxattr(procname, NULL, 0); -+ if (ret == -1) { -+ goto out_err; -+ } -+ -+ fuse_reply_xattr(req, ret); -+ } - out_free: -- free(value); -- return; -+ free(value); -+ return; - - out_err: -- saverr = errno; -+ saverr = errno; - out: -- fuse_reply_err(req, saverr); -- goto out_free; -+ fuse_reply_err(req, saverr); -+ goto out_free; - } - - static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, -- const char *value, size_t size, int flags) -+ const char *value, size_t size, int flags) - { -- char procname[64]; -- struct lo_inode *inode = lo_inode(req, ino); -- ssize_t ret; -- int saverr; -+ char procname[64]; -+ struct lo_inode *inode = lo_inode(req, ino); -+ ssize_t ret; -+ int saverr; - -- saverr = ENOSYS; -- if (!lo_data(req)->xattr) -- goto out; -+ saverr = ENOSYS; -+ if (!lo_data(req)->xattr) { -+ goto out; -+ } - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n", -- ino, name, value, size); -- } -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, -+ "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n", -+ ino, name, value, size); -+ } - -- if (inode->is_symlink) { -- /* Sorry, no race free way to setxattr on symlink. */ -- saverr = EPERM; -- goto out; -- } -+ if (inode->is_symlink) { -+ /* Sorry, no race free way to setxattr on symlink. */ -+ saverr = EPERM; -+ goto out; -+ } - -- sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ sprintf(procname, "/proc/self/fd/%i", inode->fd); - -- ret = setxattr(procname, name, value, size, flags); -- saverr = ret == -1 ? errno : 0; -+ ret = setxattr(procname, name, value, size, flags); -+ saverr = ret == -1 ? errno : 0; - - out: -- fuse_reply_err(req, saverr); -+ fuse_reply_err(req, saverr); - } - - static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) - { -- char procname[64]; -- struct lo_inode *inode = lo_inode(req, ino); -- ssize_t ret; -- int saverr; -+ char procname[64]; -+ struct lo_inode *inode = lo_inode(req, ino); -+ ssize_t ret; -+ int saverr; - -- saverr = ENOSYS; -- if (!lo_data(req)->xattr) -- goto out; -+ saverr = ENOSYS; -+ if (!lo_data(req)->xattr) { -+ goto out; -+ } - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", -- ino, name); -- } -+ if (lo_debug(req)) { -+ fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", -+ ino, name); -+ } - -- if (inode->is_symlink) { -- /* Sorry, no race free way to setxattr on symlink. */ -- saverr = EPERM; -- goto out; -- } -+ if (inode->is_symlink) { -+ /* Sorry, no race free way to setxattr on symlink. */ -+ saverr = EPERM; -+ goto out; -+ } - -- sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ sprintf(procname, "/proc/self/fd/%i", inode->fd); - -- ret = removexattr(procname, name); -- saverr = ret == -1 ? errno : 0; -+ ret = removexattr(procname, name); -+ saverr = ret == -1 ? errno : 0; - - out: -- fuse_reply_err(req, saverr); -+ fuse_reply_err(req, saverr); - } - - #ifdef HAVE_COPY_FILE_RANGE - static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, -- struct fuse_file_info *fi_in, -- fuse_ino_t ino_out, off_t off_out, -- struct fuse_file_info *fi_out, size_t len, -- int flags) -+ struct fuse_file_info *fi_in, fuse_ino_t ino_out, -+ off_t off_out, struct fuse_file_info *fi_out, -+ size_t len, int flags) - { -- ssize_t res; -- -- if (lo_debug(req)) -- fuse_log(FUSE_LOG_DEBUG, "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, " -- "off=%lu, ino=%" PRIu64 "/fd=%lu, " -- "off=%lu, size=%zd, flags=0x%x)\n", -- ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out, -- len, flags); -- -- res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len, -- flags); -- if (res < 0) -- fuse_reply_err(req, -errno); -- else -- fuse_reply_write(req, res); -+ ssize_t res; -+ -+ if (lo_debug(req)) -+ fuse_log(FUSE_LOG_DEBUG, -+ "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, " -+ "off=%lu, ino=%" PRIu64 "/fd=%lu, " -+ "off=%lu, size=%zd, flags=0x%x)\n", -+ ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out, len, -+ flags); -+ -+ res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len, flags); -+ if (res < 0) { -+ fuse_reply_err(req, -errno); -+ } else { -+ fuse_reply_write(req, res); -+ } - } - #endif - - static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, -- struct fuse_file_info *fi) -+ struct fuse_file_info *fi) - { -- off_t res; -- -- (void)ino; -- res = lseek(fi->fh, off, whence); -- if (res != -1) -- fuse_reply_lseek(req, res); -- else -- fuse_reply_err(req, errno); -+ off_t res; -+ -+ (void)ino; -+ res = lseek(fi->fh, off, whence); -+ if (res != -1) { -+ fuse_reply_lseek(req, res); -+ } else { -+ fuse_reply_err(req, errno); -+ } - } - - static struct fuse_lowlevel_ops lo_oper = { -- .init = lo_init, -- .lookup = lo_lookup, -- .mkdir = lo_mkdir, -- .mknod = lo_mknod, -- .symlink = lo_symlink, -- .link = lo_link, -- .unlink = lo_unlink, -- .rmdir = lo_rmdir, -- .rename = lo_rename, -- .forget = lo_forget, -- .forget_multi = lo_forget_multi, -- .getattr = lo_getattr, -- .setattr = lo_setattr, -- .readlink = lo_readlink, -- .opendir = lo_opendir, -- .readdir = lo_readdir, -- .readdirplus = lo_readdirplus, -- .releasedir = lo_releasedir, -- .fsyncdir = lo_fsyncdir, -- .create = lo_create, -- .open = lo_open, -- .release = lo_release, -- .flush = lo_flush, -- .fsync = lo_fsync, -- .read = lo_read, -- .write_buf = lo_write_buf, -- .statfs = lo_statfs, -- .fallocate = lo_fallocate, -- .flock = lo_flock, -- .getxattr = lo_getxattr, -- .listxattr = lo_listxattr, -- .setxattr = lo_setxattr, -- .removexattr = lo_removexattr, -+ .init = lo_init, -+ .lookup = lo_lookup, -+ .mkdir = lo_mkdir, -+ .mknod = lo_mknod, -+ .symlink = lo_symlink, -+ .link = lo_link, -+ .unlink = lo_unlink, -+ .rmdir = lo_rmdir, -+ .rename = lo_rename, -+ .forget = lo_forget, -+ .forget_multi = lo_forget_multi, -+ .getattr = lo_getattr, -+ .setattr = lo_setattr, -+ .readlink = lo_readlink, -+ .opendir = lo_opendir, -+ .readdir = lo_readdir, -+ .readdirplus = lo_readdirplus, -+ .releasedir = lo_releasedir, -+ .fsyncdir = lo_fsyncdir, -+ .create = lo_create, -+ .open = lo_open, -+ .release = lo_release, -+ .flush = lo_flush, -+ .fsync = lo_fsync, -+ .read = lo_read, -+ .write_buf = lo_write_buf, -+ .statfs = lo_statfs, -+ .fallocate = lo_fallocate, -+ .flock = lo_flock, -+ .getxattr = lo_getxattr, -+ .listxattr = lo_listxattr, -+ .setxattr = lo_setxattr, -+ .removexattr = lo_removexattr, - #ifdef HAVE_COPY_FILE_RANGE -- .copy_file_range = lo_copy_file_range, -+ .copy_file_range = lo_copy_file_range, - #endif -- .lseek = lo_lseek, -+ .lseek = lo_lseek, - }; - - int main(int argc, char *argv[]) - { -- struct fuse_args args = FUSE_ARGS_INIT(argc, argv); -- struct fuse_session *se; -- struct fuse_cmdline_opts opts; -- struct lo_data lo = { .debug = 0, -- .writeback = 0 }; -- int ret = -1; -- -- /* Don't mask creation mode, kernel already did that */ -- umask(0); -- -- pthread_mutex_init(&lo.mutex, NULL); -- lo.root.next = lo.root.prev = &lo.root; -- lo.root.fd = -1; -- lo.cache = CACHE_NORMAL; -- -- if (fuse_parse_cmdline(&args, &opts) != 0) -- return 1; -- if (opts.show_help) { -- printf("usage: %s [options] \n\n", argv[0]); -- fuse_cmdline_help(); -- fuse_lowlevel_help(); -- ret = 0; -- goto err_out1; -- } else if (opts.show_version) { -- fuse_lowlevel_version(); -- ret = 0; -- goto err_out1; -- } -- -- if(opts.mountpoint == NULL) { -- printf("usage: %s [options] \n", argv[0]); -- printf(" %s --help\n", argv[0]); -- ret = 1; -- goto err_out1; -- } -- -- if (fuse_opt_parse(&args, &lo, lo_opts, NULL)== -1) -- return 1; -- -- lo.debug = opts.debug; -- lo.root.refcount = 2; -- if (lo.source) { -- struct stat stat; -- int res; -- -- res = lstat(lo.source, &stat); -- if (res == -1) { -- fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n", -- lo.source); -- exit(1); -- } -- if (!S_ISDIR(stat.st_mode)) { -- fuse_log(FUSE_LOG_ERR, "source is not a directory\n"); -- exit(1); -- } -- -- } else { -- lo.source = "/"; -- } -- lo.root.is_symlink = false; -- if (!lo.timeout_set) { -- switch (lo.cache) { -- case CACHE_NEVER: -- lo.timeout = 0.0; -- break; -- -- case CACHE_NORMAL: -- lo.timeout = 1.0; -- break; -- -- case CACHE_ALWAYS: -- lo.timeout = 86400.0; -- break; -- } -- } else if (lo.timeout < 0) { -- fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n", -- lo.timeout); -- exit(1); -- } -- -- lo.root.fd = open(lo.source, O_PATH); -- if (lo.root.fd == -1) { -- fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", -- lo.source); -- exit(1); -- } -- -- se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); -- if (se == NULL) -- goto err_out1; -- -- if (fuse_set_signal_handlers(se) != 0) -- goto err_out2; -- -- if (fuse_session_mount(se, opts.mountpoint) != 0) -- goto err_out3; -- -- fuse_daemonize(opts.foreground); -- -- /* Block until ctrl+c or fusermount -u */ -- if (opts.singlethread) -- ret = fuse_session_loop(se); -- else -- ret = fuse_session_loop_mt(se, opts.clone_fd); -- -- fuse_session_unmount(se); -+ struct fuse_args args = FUSE_ARGS_INIT(argc, argv); -+ struct fuse_session *se; -+ struct fuse_cmdline_opts opts; -+ struct lo_data lo = { .debug = 0, .writeback = 0 }; -+ int ret = -1; -+ -+ /* Don't mask creation mode, kernel already did that */ -+ umask(0); -+ -+ pthread_mutex_init(&lo.mutex, NULL); -+ lo.root.next = lo.root.prev = &lo.root; -+ lo.root.fd = -1; -+ lo.cache = CACHE_NORMAL; -+ -+ if (fuse_parse_cmdline(&args, &opts) != 0) { -+ return 1; -+ } -+ if (opts.show_help) { -+ printf("usage: %s [options] \n\n", argv[0]); -+ fuse_cmdline_help(); -+ fuse_lowlevel_help(); -+ ret = 0; -+ goto err_out1; -+ } else if (opts.show_version) { -+ fuse_lowlevel_version(); -+ ret = 0; -+ goto err_out1; -+ } -+ -+ if (opts.mountpoint == NULL) { -+ printf("usage: %s [options] \n", argv[0]); -+ printf(" %s --help\n", argv[0]); -+ ret = 1; -+ goto err_out1; -+ } -+ -+ if (fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1) { -+ return 1; -+ } -+ -+ lo.debug = opts.debug; -+ lo.root.refcount = 2; -+ if (lo.source) { -+ struct stat stat; -+ int res; -+ -+ res = lstat(lo.source, &stat); -+ if (res == -1) { -+ fuse_log(FUSE_LOG_ERR, "failed to stat source (\"%s\"): %m\n", -+ lo.source); -+ exit(1); -+ } -+ if (!S_ISDIR(stat.st_mode)) { -+ fuse_log(FUSE_LOG_ERR, "source is not a directory\n"); -+ exit(1); -+ } -+ -+ } else { -+ lo.source = "/"; -+ } -+ lo.root.is_symlink = false; -+ if (!lo.timeout_set) { -+ switch (lo.cache) { -+ case CACHE_NEVER: -+ lo.timeout = 0.0; -+ break; -+ -+ case CACHE_NORMAL: -+ lo.timeout = 1.0; -+ break; -+ -+ case CACHE_ALWAYS: -+ lo.timeout = 86400.0; -+ break; -+ } -+ } else if (lo.timeout < 0) { -+ fuse_log(FUSE_LOG_ERR, "timeout is negative (%lf)\n", lo.timeout); -+ exit(1); -+ } -+ -+ lo.root.fd = open(lo.source, O_PATH); -+ if (lo.root.fd == -1) { -+ fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", lo.source); -+ exit(1); -+ } -+ -+ se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); -+ if (se == NULL) { -+ goto err_out1; -+ } -+ -+ if (fuse_set_signal_handlers(se) != 0) { -+ goto err_out2; -+ } -+ -+ if (fuse_session_mount(se, opts.mountpoint) != 0) { -+ goto err_out3; -+ } -+ -+ fuse_daemonize(opts.foreground); -+ -+ /* Block until ctrl+c or fusermount -u */ -+ if (opts.singlethread) { -+ ret = fuse_session_loop(se); -+ } else { -+ ret = fuse_session_loop_mt(se, opts.clone_fd); -+ } -+ -+ fuse_session_unmount(se); - err_out3: -- fuse_remove_signal_handlers(se); -+ fuse_remove_signal_handlers(se); - err_out2: -- fuse_session_destroy(se); -+ fuse_session_destroy(se); - err_out1: -- free(opts.mountpoint); -- fuse_opt_free_args(&args); -+ free(opts.mountpoint); -+ fuse_opt_free_args(&args); - -- if (lo.root.fd >= 0) -- close(lo.root.fd); -+ if (lo.root.fd >= 0) { -+ close(lo.root.fd); -+ } - -- return ret ? 1 : 0; -+ return ret ? 1 : 0; - } diff --git a/0017-virtiofsd-remove-mountpoint-dummy-argument.patch b/0017-virtiofsd-remove-mountpoint-dummy-argument.patch deleted file mode 100644 index 5b73fa9..0000000 --- a/0017-virtiofsd-remove-mountpoint-dummy-argument.patch +++ /dev/null @@ -1,143 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:00:46 +0000 -Subject: [PATCH] virtiofsd: remove mountpoint dummy argument -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Classic FUSE file system daemons take a mountpoint argument but -virtiofsd exposes a vhost-user UNIX domain socket instead. The -mountpoint argument is not used by virtiofsd but the user is still -required to pass a dummy argument on the command-line. - -Remove the mountpoint argument to clean up the command-line. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 67aab02272f6cb47c56420f60b370c184961b5ca) ---- - tools/virtiofsd/fuse_lowlevel.c | 2 +- - tools/virtiofsd/fuse_lowlevel.h | 4 +--- - tools/virtiofsd/helper.c | 20 +++----------------- - tools/virtiofsd/passthrough_ll.c | 12 ++---------- - 4 files changed, 7 insertions(+), 31 deletions(-) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 5c9cb52f2a..2f32c68161 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -2455,7 +2455,7 @@ out1: - return NULL; - } - --int fuse_session_mount(struct fuse_session *se, const char *mountpoint) -+int fuse_session_mount(struct fuse_session *se) - { - int fd; - -diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h -index adb9054bb1..8d8909b35d 100644 ---- a/tools/virtiofsd/fuse_lowlevel.h -+++ b/tools/virtiofsd/fuse_lowlevel.h -@@ -1863,7 +1863,6 @@ struct fuse_cmdline_opts { - int foreground; - int debug; - int nodefault_subtype; -- char *mountpoint; - int show_version; - int show_help; - unsigned int max_idle_threads; -@@ -1924,12 +1923,11 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, - /** - * Mount a FUSE file system. - * -- * @param mountpoint the mount point path - * @param se session object - * - * @return 0 on success, -1 on failure. - **/ --int fuse_session_mount(struct fuse_session *se, const char *mountpoint); -+int fuse_session_mount(struct fuse_session *se); - - /** - * Enter a single threaded, blocking event loop. -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index 5711dd2660..5e6f2051a7 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -140,27 +140,13 @@ void fuse_cmdline_help(void) - static int fuse_helper_opt_proc(void *data, const char *arg, int key, - struct fuse_args *outargs) - { -+ (void)data; - (void)outargs; -- struct fuse_cmdline_opts *opts = data; - - switch (key) { - case FUSE_OPT_KEY_NONOPT: -- if (!opts->mountpoint) { -- if (fuse_mnt_parse_fuse_fd(arg) != -1) { -- return fuse_opt_add_opt(&opts->mountpoint, arg); -- } -- -- char mountpoint[PATH_MAX] = ""; -- if (realpath(arg, mountpoint) == NULL) { -- fuse_log(FUSE_LOG_ERR, "fuse: bad mount point `%s': %s\n", arg, -- strerror(errno)); -- return -1; -- } -- return fuse_opt_add_opt(&opts->mountpoint, mountpoint); -- } else { -- fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg); -- return -1; -- } -+ fuse_log(FUSE_LOG_ERR, "fuse: invalid argument `%s'\n", arg); -+ return -1; - - default: - /* Pass through unknown options */ -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index c5850ef803..9377718d9d 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1297,7 +1297,7 @@ int main(int argc, char *argv[]) - return 1; - } - if (opts.show_help) { -- printf("usage: %s [options] \n\n", argv[0]); -+ printf("usage: %s [options]\n\n", argv[0]); - fuse_cmdline_help(); - fuse_lowlevel_help(); - ret = 0; -@@ -1308,13 +1308,6 @@ int main(int argc, char *argv[]) - goto err_out1; - } - -- if (opts.mountpoint == NULL) { -- printf("usage: %s [options] \n", argv[0]); -- printf(" %s --help\n", argv[0]); -- ret = 1; -- goto err_out1; -- } -- - if (fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1) { - return 1; - } -@@ -1374,7 +1367,7 @@ int main(int argc, char *argv[]) - goto err_out2; - } - -- if (fuse_session_mount(se, opts.mountpoint) != 0) { -+ if (fuse_session_mount(se) != 0) { - goto err_out3; - } - -@@ -1393,7 +1386,6 @@ err_out3: - err_out2: - fuse_session_destroy(se); - err_out1: -- free(opts.mountpoint); - fuse_opt_free_args(&args); - - if (lo.root.fd >= 0) { diff --git a/0018-virtiofsd-remove-unused-notify-reply-support.patch b/0018-virtiofsd-remove-unused-notify-reply-support.patch deleted file mode 100644 index da2fc17..0000000 --- a/0018-virtiofsd-remove-unused-notify-reply-support.patch +++ /dev/null @@ -1,278 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:00:47 +0000 -Subject: [PATCH] virtiofsd: remove unused notify reply support -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Notify reply support is unused by virtiofsd. The code would need to be -updated to validate input buffer sizes. Remove this unused code since -changes to it are untestable. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 64c6f408a29ef03e9b8da9f5a5d8fd511b0d801e) ---- - tools/virtiofsd/fuse_lowlevel.c | 147 +------------------------------- - tools/virtiofsd/fuse_lowlevel.h | 47 ---------- - 2 files changed, 1 insertion(+), 193 deletions(-) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 2f32c68161..eb0ec49d38 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -31,12 +31,6 @@ - #define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) - #define OFFSET_MAX 0x7fffffffffffffffLL - --#define container_of(ptr, type, member) \ -- ({ \ -- const typeof(((type *)0)->member) *__mptr = (ptr); \ -- (type *)((char *)__mptr - offsetof(type, member)); \ -- }) -- - struct fuse_pollhandle { - uint64_t kh; - struct fuse_session *se; -@@ -1862,52 +1856,6 @@ static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - send_reply_ok(req, NULL, 0); - } - --static void list_del_nreq(struct fuse_notify_req *nreq) --{ -- struct fuse_notify_req *prev = nreq->prev; -- struct fuse_notify_req *next = nreq->next; -- prev->next = next; -- next->prev = prev; --} -- --static void list_add_nreq(struct fuse_notify_req *nreq, -- struct fuse_notify_req *next) --{ -- struct fuse_notify_req *prev = next->prev; -- nreq->next = next; -- nreq->prev = prev; -- prev->next = nreq; -- next->prev = nreq; --} -- --static void list_init_nreq(struct fuse_notify_req *nreq) --{ -- nreq->next = nreq; -- nreq->prev = nreq; --} -- --static void do_notify_reply(fuse_req_t req, fuse_ino_t nodeid, -- const void *inarg, const struct fuse_buf *buf) --{ -- struct fuse_session *se = req->se; -- struct fuse_notify_req *nreq; -- struct fuse_notify_req *head; -- -- pthread_mutex_lock(&se->lock); -- head = &se->notify_list; -- for (nreq = head->next; nreq != head; nreq = nreq->next) { -- if (nreq->unique == req->unique) { -- list_del_nreq(nreq); -- break; -- } -- } -- pthread_mutex_unlock(&se->lock); -- -- if (nreq != head) { -- nreq->reply(nreq, req, nodeid, inarg, buf); -- } --} -- - static int send_notify_iov(struct fuse_session *se, int notify_code, - struct iovec *iov, int count) - { -@@ -2059,95 +2007,6 @@ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, - return res; - } - --struct fuse_retrieve_req { -- struct fuse_notify_req nreq; -- void *cookie; --}; -- --static void fuse_ll_retrieve_reply(struct fuse_notify_req *nreq, fuse_req_t req, -- fuse_ino_t ino, const void *inarg, -- const struct fuse_buf *ibuf) --{ -- struct fuse_session *se = req->se; -- struct fuse_retrieve_req *rreq = -- container_of(nreq, struct fuse_retrieve_req, nreq); -- const struct fuse_notify_retrieve_in *arg = inarg; -- struct fuse_bufvec bufv = { -- .buf[0] = *ibuf, -- .count = 1, -- }; -- -- if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) { -- bufv.buf[0].mem = PARAM(arg); -- } -- -- bufv.buf[0].size -= -- sizeof(struct fuse_in_header) + sizeof(struct fuse_notify_retrieve_in); -- -- if (bufv.buf[0].size < arg->size) { -- fuse_log(FUSE_LOG_ERR, "fuse: retrieve reply: buffer size too small\n"); -- fuse_reply_none(req); -- goto out; -- } -- bufv.buf[0].size = arg->size; -- -- if (se->op.retrieve_reply) { -- se->op.retrieve_reply(req, rreq->cookie, ino, arg->offset, &bufv); -- } else { -- fuse_reply_none(req); -- } --out: -- free(rreq); --} -- --int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, -- size_t size, off_t offset, void *cookie) --{ -- struct fuse_notify_retrieve_out outarg; -- struct iovec iov[2]; -- struct fuse_retrieve_req *rreq; -- int err; -- -- if (!se) { -- return -EINVAL; -- } -- -- if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) { -- return -ENOSYS; -- } -- -- rreq = malloc(sizeof(*rreq)); -- if (rreq == NULL) { -- return -ENOMEM; -- } -- -- pthread_mutex_lock(&se->lock); -- rreq->cookie = cookie; -- rreq->nreq.unique = se->notify_ctr++; -- rreq->nreq.reply = fuse_ll_retrieve_reply; -- list_add_nreq(&rreq->nreq, &se->notify_list); -- pthread_mutex_unlock(&se->lock); -- -- outarg.notify_unique = rreq->nreq.unique; -- outarg.nodeid = ino; -- outarg.offset = offset; -- outarg.size = size; -- outarg.padding = 0; -- -- iov[1].iov_base = &outarg; -- iov[1].iov_len = sizeof(outarg); -- -- err = send_notify_iov(se, FUSE_NOTIFY_RETRIEVE, iov, 2); -- if (err) { -- pthread_mutex_lock(&se->lock); -- list_del_nreq(&rreq->nreq); -- pthread_mutex_unlock(&se->lock); -- free(rreq); -- } -- -- return err; --} -- - void *fuse_req_userdata(fuse_req_t req) - { - return req->se->userdata; -@@ -2226,7 +2085,7 @@ static struct { - [FUSE_POLL] = { do_poll, "POLL" }, - [FUSE_FALLOCATE] = { do_fallocate, "FALLOCATE" }, - [FUSE_DESTROY] = { do_destroy, "DESTROY" }, -- [FUSE_NOTIFY_REPLY] = { (void *)1, "NOTIFY_REPLY" }, -+ [FUSE_NOTIFY_REPLY] = { NULL, "NOTIFY_REPLY" }, - [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" }, - [FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS" }, - [FUSE_RENAME2] = { do_rename2, "RENAME2" }, -@@ -2333,8 +2192,6 @@ void fuse_session_process_buf_int(struct fuse_session *se, - inarg = (void *)&in[1]; - if (in->opcode == FUSE_WRITE && se->op.write_buf) { - do_write_buf(req, in->nodeid, inarg, buf); -- } else if (in->opcode == FUSE_NOTIFY_REPLY) { -- do_notify_reply(req, in->nodeid, inarg, buf); - } else { - fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); - } -@@ -2437,8 +2294,6 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, - - list_init_req(&se->list); - list_init_req(&se->interrupts); -- list_init_nreq(&se->notify_list); -- se->notify_ctr = 1; - fuse_mutex_init(&se->lock); - - memcpy(&se->op, op, op_size); -diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h -index 8d8909b35d..12a84b460f 100644 ---- a/tools/virtiofsd/fuse_lowlevel.h -+++ b/tools/virtiofsd/fuse_lowlevel.h -@@ -1084,21 +1084,6 @@ struct fuse_lowlevel_ops { - void (*write_buf)(fuse_req_t req, fuse_ino_t ino, struct fuse_bufvec *bufv, - off_t off, struct fuse_file_info *fi); - -- /** -- * Callback function for the retrieve request -- * -- * Valid replies: -- * fuse_reply_none -- * -- * @param req request handle -- * @param cookie user data supplied to fuse_lowlevel_notify_retrieve() -- * @param ino the inode number supplied to fuse_lowlevel_notify_retrieve() -- * @param offset the offset supplied to fuse_lowlevel_notify_retrieve() -- * @param bufv the buffer containing the returned data -- */ -- void (*retrieve_reply)(fuse_req_t req, void *cookie, fuse_ino_t ino, -- off_t offset, struct fuse_bufvec *bufv); -- - /** - * Forget about multiple inodes - * -@@ -1726,38 +1711,6 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, - int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, - off_t offset, struct fuse_bufvec *bufv, - enum fuse_buf_copy_flags flags); --/** -- * Retrieve data from the kernel buffers -- * -- * Retrieve data in the kernel buffers belonging to the given inode. -- * If successful then the retrieve_reply() method will be called with -- * the returned data. -- * -- * Only present pages are returned in the retrieve reply. Retrieving -- * stops when it finds a non-present page and only data prior to that -- * is returned. -- * -- * If this function returns an error, then the retrieve will not be -- * completed and no reply will be sent. -- * -- * This function doesn't change the dirty state of pages in the kernel -- * buffer. For dirty pages the write() method will be called -- * regardless of having been retrieved previously. -- * -- * Added in FUSE protocol version 7.15. If the kernel does not support -- * this (or a newer) version, the function will return -ENOSYS and do -- * nothing. -- * -- * @param se the session object -- * @param ino the inode number -- * @param size the number of bytes to retrieve -- * @param offset the starting offset into the file to retrieve from -- * @param cookie user data to supply to the reply callback -- * @return zero for success, -errno for failure -- */ --int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino, -- size_t size, off_t offset, void *cookie); -- - - /* - * Utility functions diff --git a/0019-virtiofsd-Remove-unused-enum-fuse_buf_copy_flags.patch b/0019-virtiofsd-Remove-unused-enum-fuse_buf_copy_flags.patch deleted file mode 100644 index f60bab0..0000000 --- a/0019-virtiofsd-Remove-unused-enum-fuse_buf_copy_flags.patch +++ /dev/null @@ -1,252 +0,0 @@ -From: Xiao Yang -Date: Mon, 27 Jan 2020 19:00:48 +0000 -Subject: [PATCH] virtiofsd: Remove unused enum fuse_buf_copy_flags - -Signed-off-by: Xiao Yang -Reviewed-by: Stefan Hajnoczi -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 8c3fe75e0308ba2f01d160ace534b7e386cea808) ---- - tools/virtiofsd/buffer.c | 7 +++-- - tools/virtiofsd/fuse_common.h | 46 +------------------------------- - tools/virtiofsd/fuse_lowlevel.c | 13 ++++----- - tools/virtiofsd/fuse_lowlevel.h | 35 ++---------------------- - tools/virtiofsd/passthrough_ll.c | 4 +-- - 5 files changed, 13 insertions(+), 92 deletions(-) - -diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c -index 5df946c82c..4d507f3302 100644 ---- a/tools/virtiofsd/buffer.c -+++ b/tools/virtiofsd/buffer.c -@@ -171,7 +171,7 @@ static ssize_t fuse_buf_fd_to_fd(const struct fuse_buf *dst, size_t dst_off, - - static ssize_t fuse_buf_copy_one(const struct fuse_buf *dst, size_t dst_off, - const struct fuse_buf *src, size_t src_off, -- size_t len, enum fuse_buf_copy_flags flags) -+ size_t len) - { - int src_is_fd = src->flags & FUSE_BUF_IS_FD; - int dst_is_fd = dst->flags & FUSE_BUF_IS_FD; -@@ -224,8 +224,7 @@ static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len) - return 1; - } - --ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv, -- enum fuse_buf_copy_flags flags) -+ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv) - { - size_t copied = 0; - -@@ -249,7 +248,7 @@ ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv, - dst_len = dst->size - dstv->off; - len = min_size(src_len, dst_len); - -- res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len, flags); -+ res = fuse_buf_copy_one(dst, dstv->off, src, srcv->off, len); - if (res < 0) { - if (!copied) { - return res; -diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h -index bd9bf861f0..0cb33acc2f 100644 ---- a/tools/virtiofsd/fuse_common.h -+++ b/tools/virtiofsd/fuse_common.h -@@ -604,48 +604,6 @@ enum fuse_buf_flags { - FUSE_BUF_FD_RETRY = (1 << 3), - }; - --/** -- * Buffer copy flags -- */ --enum fuse_buf_copy_flags { -- /** -- * Don't use splice(2) -- * -- * Always fall back to using read and write instead of -- * splice(2) to copy data from one file descriptor to another. -- * -- * If this flag is not set, then only fall back if splice is -- * unavailable. -- */ -- FUSE_BUF_NO_SPLICE = (1 << 1), -- -- /** -- * Force splice -- * -- * Always use splice(2) to copy data from one file descriptor -- * to another. If splice is not available, return -EINVAL. -- */ -- FUSE_BUF_FORCE_SPLICE = (1 << 2), -- -- /** -- * Try to move data with splice. -- * -- * If splice is used, try to move pages from the source to the -- * destination instead of copying. See documentation of -- * SPLICE_F_MOVE in splice(2) man page. -- */ -- FUSE_BUF_SPLICE_MOVE = (1 << 3), -- -- /** -- * Don't block on the pipe when copying data with splice -- * -- * Makes the operations on the pipe non-blocking (if the pipe -- * is full or empty). See SPLICE_F_NONBLOCK in the splice(2) -- * man page. -- */ -- FUSE_BUF_SPLICE_NONBLOCK = (1 << 4), --}; -- - /** - * Single data buffer - * -@@ -741,11 +699,9 @@ size_t fuse_buf_size(const struct fuse_bufvec *bufv); - * - * @param dst destination buffer vector - * @param src source buffer vector -- * @param flags flags controlling the copy - * @return actual number of bytes copied or -errno on error - */ --ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src, -- enum fuse_buf_copy_flags flags); -+ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src); - - /* - * Signal handling -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index eb0ec49d38..3da80de233 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -490,16 +490,14 @@ static int fuse_send_data_iov_fallback(struct fuse_session *se, - - static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, - struct iovec *iov, int iov_count, -- struct fuse_bufvec *buf, unsigned int flags) -+ struct fuse_bufvec *buf) - { - size_t len = fuse_buf_size(buf); -- (void)flags; - - return fuse_send_data_iov_fallback(se, ch, iov, iov_count, buf, len); - } - --int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, -- enum fuse_buf_copy_flags flags) -+int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv) - { - struct iovec iov[2]; - struct fuse_out_header out; -@@ -511,7 +509,7 @@ int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, - out.unique = req->unique; - out.error = 0; - -- res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv, flags); -+ res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv); - if (res <= 0) { - fuse_free_req(req); - return res; -@@ -1969,8 +1967,7 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, - } - - int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, -- off_t offset, struct fuse_bufvec *bufv, -- enum fuse_buf_copy_flags flags) -+ off_t offset, struct fuse_bufvec *bufv) - { - struct fuse_out_header out; - struct fuse_notify_store_out outarg; -@@ -1999,7 +1996,7 @@ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, - iov[1].iov_base = &outarg; - iov[1].iov_len = sizeof(outarg); - -- res = fuse_send_data_iov(se, NULL, iov, 2, bufv, flags); -+ res = fuse_send_data_iov(se, NULL, iov, 2, bufv); - if (res > 0) { - res = -res; - } -diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h -index 12a84b460f..2fa225d40b 100644 ---- a/tools/virtiofsd/fuse_lowlevel.h -+++ b/tools/virtiofsd/fuse_lowlevel.h -@@ -1363,33 +1363,6 @@ int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size); - /** - * Reply with data copied/moved from buffer(s) - * -- * Zero copy data transfer ("splicing") will be used under -- * the following circumstances: -- * -- * 1. FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.want, and -- * 2. the kernel supports splicing from the fuse device -- * (FUSE_CAP_SPLICE_WRITE is set in fuse_conn_info.capable), and -- * 3. *flags* does not contain FUSE_BUF_NO_SPLICE -- * 4. The amount of data that is provided in file-descriptor backed -- * buffers (i.e., buffers for which bufv[n].flags == FUSE_BUF_FD) -- * is at least twice the page size. -- * -- * In order for SPLICE_F_MOVE to be used, the following additional -- * conditions have to be fulfilled: -- * -- * 1. FUSE_CAP_SPLICE_MOVE is set in fuse_conn_info.want, and -- * 2. the kernel supports it (i.e, FUSE_CAP_SPLICE_MOVE is set in -- fuse_conn_info.capable), and -- * 3. *flags* contains FUSE_BUF_SPLICE_MOVE -- * -- * Note that, if splice is used, the data is actually spliced twice: -- * once into a temporary pipe (to prepend header data), and then again -- * into the kernel. If some of the provided buffers are memory-backed, -- * the data in them is copied in step one and spliced in step two. -- * -- * The FUSE_BUF_SPLICE_FORCE_SPLICE and FUSE_BUF_SPLICE_NONBLOCK flags -- * are silently ignored. -- * - * Possible requests: - * read, readdir, getxattr, listxattr - * -@@ -1400,11 +1373,9 @@ int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size); - * - * @param req request handle - * @param bufv buffer vector -- * @param flags flags controlling the copy - * @return zero for success, -errno for failure to send reply - */ --int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv, -- enum fuse_buf_copy_flags flags); -+int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv); - - /** - * Reply with data vector -@@ -1705,12 +1676,10 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, - * @param ino the inode number - * @param offset the starting offset into the file to store to - * @param bufv buffer vector -- * @param flags flags controlling the copy - * @return zero for success, -errno for failure - */ - int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, -- off_t offset, struct fuse_bufvec *bufv, -- enum fuse_buf_copy_flags flags); -+ off_t offset, struct fuse_bufvec *bufv); - - /* - * Utility functions -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 9377718d9d..126a56ccbd 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -931,7 +931,7 @@ static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, - buf.buf[0].fd = fi->fh; - buf.buf[0].pos = offset; - -- fuse_reply_data(req, &buf, FUSE_BUF_SPLICE_MOVE); -+ fuse_reply_data(req, &buf); - } - - static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, -@@ -952,7 +952,7 @@ static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, - out_buf.buf[0].size, (unsigned long)off); - } - -- res = fuse_buf_copy(&out_buf, in_buf, 0); -+ res = fuse_buf_copy(&out_buf, in_buf); - if (res < 0) { - fuse_reply_err(req, -res); - } else { diff --git a/0020-virtiofsd-Fix-fuse_daemonize-ignored-return-values.patch b/0020-virtiofsd-Fix-fuse_daemonize-ignored-return-values.patch deleted file mode 100644 index 086f244..0000000 --- a/0020-virtiofsd-Fix-fuse_daemonize-ignored-return-values.patch +++ /dev/null @@ -1,104 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:00:49 +0000 -Subject: [PATCH] virtiofsd: Fix fuse_daemonize ignored return values -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -QEMU's compiler enables warnings/errors for ignored values -and the (void) trick used in the fuse code isn't enough. -Turn all the return values into a return value on the function. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Reviewed-by: Philippe Mathieu-Daudé -Tested-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 30d8e49760712d65697ea517c53671bd1d214fc7) ---- - tools/virtiofsd/helper.c | 33 ++++++++++++++++++++++----------- - 1 file changed, 22 insertions(+), 11 deletions(-) - -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index 5e6f2051a7..d9227d7367 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -10,12 +10,10 @@ - * See the file COPYING.LIB. - */ - --#include "config.h" - #include "fuse_i.h" - #include "fuse_lowlevel.h" - #include "fuse_misc.h" - #include "fuse_opt.h" --#include "mount_util.h" - - #include - #include -@@ -171,6 +169,7 @@ int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts) - - int fuse_daemonize(int foreground) - { -+ int ret = 0, rett; - if (!foreground) { - int nullfd; - int waiter[2]; -@@ -192,8 +191,8 @@ int fuse_daemonize(int foreground) - case 0: - break; - default: -- (void)read(waiter[0], &completed, sizeof(completed)); -- _exit(0); -+ _exit(read(waiter[0], &completed, -+ sizeof(completed) != sizeof(completed))); - } - - if (setsid() == -1) { -@@ -201,13 +200,22 @@ int fuse_daemonize(int foreground) - return -1; - } - -- (void)chdir("/"); -+ ret = chdir("/"); - - nullfd = open("/dev/null", O_RDWR, 0); - if (nullfd != -1) { -- (void)dup2(nullfd, 0); -- (void)dup2(nullfd, 1); -- (void)dup2(nullfd, 2); -+ rett = dup2(nullfd, 0); -+ if (!ret) { -+ ret = rett; -+ } -+ rett = dup2(nullfd, 1); -+ if (!ret) { -+ ret = rett; -+ } -+ rett = dup2(nullfd, 2); -+ if (!ret) { -+ ret = rett; -+ } - if (nullfd > 2) { - close(nullfd); - } -@@ -215,13 +223,16 @@ int fuse_daemonize(int foreground) - - /* Propagate completion of daemon initialization */ - completed = 1; -- (void)write(waiter[1], &completed, sizeof(completed)); -+ rett = write(waiter[1], &completed, sizeof(completed)); -+ if (!ret) { -+ ret = rett; -+ } - close(waiter[0]); - close(waiter[1]); - } else { -- (void)chdir("/"); -+ ret = chdir("/"); - } -- return 0; -+ return ret; - } - - void fuse_apply_conn_info_opts(struct fuse_conn_info_opts *opts, diff --git a/0021-virtiofsd-Fix-common-header-and-define-for-QEMU-buil.patch b/0021-virtiofsd-Fix-common-header-and-define-for-QEMU-buil.patch deleted file mode 100644 index 942c006..0000000 --- a/0021-virtiofsd-Fix-common-header-and-define-for-QEMU-buil.patch +++ /dev/null @@ -1,147 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:00:50 +0000 -Subject: [PATCH] virtiofsd: Fix common header and define for QEMU builds -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -All of the fuse files include config.h and define GNU_SOURCE -where we don't have either under our build - remove them. -Fixup path to the kernel's fuse.h in the QEMUs world. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Philippe Mathieu-Daudé -Tested-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 09863ebc7e32a107235b3c815ad54d26cc64f07a) ---- - tools/virtiofsd/buffer.c | 4 +--- - tools/virtiofsd/fuse_i.h | 3 +++ - tools/virtiofsd/fuse_log.c | 1 + - tools/virtiofsd/fuse_lowlevel.c | 6 ++---- - tools/virtiofsd/fuse_opt.c | 2 +- - tools/virtiofsd/fuse_signals.c | 2 +- - tools/virtiofsd/helper.c | 1 + - tools/virtiofsd/passthrough_ll.c | 8 ++------ - 8 files changed, 12 insertions(+), 15 deletions(-) - -diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c -index 4d507f3302..772efa922d 100644 ---- a/tools/virtiofsd/buffer.c -+++ b/tools/virtiofsd/buffer.c -@@ -9,9 +9,7 @@ - * See the file COPYING.LIB - */ - --#define _GNU_SOURCE -- --#include "config.h" -+#include "qemu/osdep.h" - #include "fuse_i.h" - #include "fuse_lowlevel.h" - #include -diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h -index e63cb58388..bae06992e0 100644 ---- a/tools/virtiofsd/fuse_i.h -+++ b/tools/virtiofsd/fuse_i.h -@@ -6,6 +6,9 @@ - * See the file COPYING.LIB - */ - -+#define FUSE_USE_VERSION 31 -+ -+ - #include "fuse.h" - #include "fuse_lowlevel.h" - -diff --git a/tools/virtiofsd/fuse_log.c b/tools/virtiofsd/fuse_log.c -index 11345f9ec8..c301ff6da1 100644 ---- a/tools/virtiofsd/fuse_log.c -+++ b/tools/virtiofsd/fuse_log.c -@@ -8,6 +8,7 @@ - * See the file COPYING.LIB - */ - -+#include "qemu/osdep.h" - #include "fuse_log.h" - - #include -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 3da80de233..07fb8a6095 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -9,11 +9,9 @@ - * See the file COPYING.LIB - */ - --#define _GNU_SOURCE -- --#include "config.h" -+#include "qemu/osdep.h" - #include "fuse_i.h" --#include "fuse_kernel.h" -+#include "standard-headers/linux/fuse.h" - #include "fuse_misc.h" - #include "fuse_opt.h" - -diff --git a/tools/virtiofsd/fuse_opt.c b/tools/virtiofsd/fuse_opt.c -index edd36f4a3b..28922361a2 100644 ---- a/tools/virtiofsd/fuse_opt.c -+++ b/tools/virtiofsd/fuse_opt.c -@@ -9,8 +9,8 @@ - * See the file COPYING.LIB - */ - -+#include "qemu/osdep.h" - #include "fuse_opt.h" --#include "config.h" - #include "fuse_i.h" - #include "fuse_misc.h" - -diff --git a/tools/virtiofsd/fuse_signals.c b/tools/virtiofsd/fuse_signals.c -index 19d6791cb9..dc7c8ac025 100644 ---- a/tools/virtiofsd/fuse_signals.c -+++ b/tools/virtiofsd/fuse_signals.c -@@ -8,7 +8,7 @@ - * See the file COPYING.LIB - */ - --#include "config.h" -+#include "qemu/osdep.h" - #include "fuse_i.h" - #include "fuse_lowlevel.h" - -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index d9227d7367..9333691525 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -10,6 +10,7 @@ - * See the file COPYING.LIB. - */ - -+#include "qemu/osdep.h" - #include "fuse_i.h" - #include "fuse_lowlevel.h" - #include "fuse_misc.h" -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 126a56ccbd..322a889cdf 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -35,15 +35,11 @@ - * \include passthrough_ll.c - */ - --#define _GNU_SOURCE --#define FUSE_USE_VERSION 31 -- --#include "config.h" -- -+#include "qemu/osdep.h" -+#include "fuse_lowlevel.h" - #include - #include - #include --#include - #include - #include - #include diff --git a/0022-virtiofsd-Trim-out-compatibility-code.patch b/0022-virtiofsd-Trim-out-compatibility-code.patch deleted file mode 100644 index 5da556a..0000000 --- a/0022-virtiofsd-Trim-out-compatibility-code.patch +++ /dev/null @@ -1,529 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:00:51 +0000 -Subject: [PATCH] virtiofsd: Trim out compatibility code -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -virtiofsd only supports major=7, minor>=31; trim out a lot of -old compatibility code. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 72c42e2d65510e073cf78fdc924d121c77fa0080) ---- - tools/virtiofsd/fuse_lowlevel.c | 330 ++++++++++++-------------------- - 1 file changed, 119 insertions(+), 211 deletions(-) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 07fb8a6095..514d79cb24 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -387,16 +387,7 @@ static void fill_open(struct fuse_open_out *arg, const struct fuse_file_info *f) - int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e) - { - struct fuse_entry_out arg; -- size_t size = req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ENTRY_OUT_SIZE : -- sizeof(arg); -- -- /* -- * before ABI 7.4 e->ino == 0 was invalid, only ENOENT meant -- * negative entry -- */ -- if (!e->ino && req->se->conn.proto_minor < 4) { -- return fuse_reply_err(req, ENOENT); -- } -+ size_t size = sizeof(arg); - - memset(&arg, 0, sizeof(arg)); - fill_entry(&arg, e); -@@ -407,9 +398,7 @@ int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, - const struct fuse_file_info *f) - { - char buf[sizeof(struct fuse_entry_out) + sizeof(struct fuse_open_out)]; -- size_t entrysize = req->se->conn.proto_minor < 9 ? -- FUSE_COMPAT_ENTRY_OUT_SIZE : -- sizeof(struct fuse_entry_out); -+ size_t entrysize = sizeof(struct fuse_entry_out); - struct fuse_entry_out *earg = (struct fuse_entry_out *)buf; - struct fuse_open_out *oarg = (struct fuse_open_out *)(buf + entrysize); - -@@ -423,8 +412,7 @@ int fuse_reply_attr(fuse_req_t req, const struct stat *attr, - double attr_timeout) - { - struct fuse_attr_out arg; -- size_t size = -- req->se->conn.proto_minor < 9 ? FUSE_COMPAT_ATTR_OUT_SIZE : sizeof(arg); -+ size_t size = sizeof(arg); - - memset(&arg, 0, sizeof(arg)); - arg.attr_valid = calc_timeout_sec(attr_timeout); -@@ -519,8 +507,7 @@ int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv) - int fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf) - { - struct fuse_statfs_out arg; -- size_t size = -- req->se->conn.proto_minor < 4 ? FUSE_COMPAT_STATFS_SIZE : sizeof(arg); -+ size_t size = sizeof(arg); - - memset(&arg, 0, sizeof(arg)); - convert_statfs(stbuf, &arg.st); -@@ -604,45 +591,31 @@ int fuse_reply_ioctl_retry(fuse_req_t req, const struct iovec *in_iov, - iov[count].iov_len = sizeof(arg); - count++; - -- if (req->se->conn.proto_minor < 16) { -- if (in_count) { -- iov[count].iov_base = (void *)in_iov; -- iov[count].iov_len = sizeof(in_iov[0]) * in_count; -- count++; -- } -+ /* Can't handle non-compat 64bit ioctls on 32bit */ -+ if (sizeof(void *) == 4 && req->ioctl_64bit) { -+ res = fuse_reply_err(req, EINVAL); -+ goto out; -+ } - -- if (out_count) { -- iov[count].iov_base = (void *)out_iov; -- iov[count].iov_len = sizeof(out_iov[0]) * out_count; -- count++; -+ if (in_count) { -+ in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count); -+ if (!in_fiov) { -+ goto enomem; - } -- } else { -- /* Can't handle non-compat 64bit ioctls on 32bit */ -- if (sizeof(void *) == 4 && req->ioctl_64bit) { -- res = fuse_reply_err(req, EINVAL); -- goto out; -- } -- -- if (in_count) { -- in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count); -- if (!in_fiov) { -- goto enomem; -- } - -- iov[count].iov_base = (void *)in_fiov; -- iov[count].iov_len = sizeof(in_fiov[0]) * in_count; -- count++; -+ iov[count].iov_base = (void *)in_fiov; -+ iov[count].iov_len = sizeof(in_fiov[0]) * in_count; -+ count++; -+ } -+ if (out_count) { -+ out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count); -+ if (!out_fiov) { -+ goto enomem; - } -- if (out_count) { -- out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count); -- if (!out_fiov) { -- goto enomem; -- } - -- iov[count].iov_base = (void *)out_fiov; -- iov[count].iov_len = sizeof(out_fiov[0]) * out_count; -- count++; -- } -+ iov[count].iov_base = (void *)out_fiov; -+ iov[count].iov_len = sizeof(out_fiov[0]) * out_count; -+ count++; - } - - res = send_reply_iov(req, 0, iov, count); -@@ -784,14 +757,12 @@ static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - struct fuse_file_info *fip = NULL; - struct fuse_file_info fi; - -- if (req->se->conn.proto_minor >= 9) { -- struct fuse_getattr_in *arg = (struct fuse_getattr_in *)inarg; -+ struct fuse_getattr_in *arg = (struct fuse_getattr_in *)inarg; - -- if (arg->getattr_flags & FUSE_GETATTR_FH) { -- memset(&fi, 0, sizeof(fi)); -- fi.fh = arg->fh; -- fip = &fi; -- } -+ if (arg->getattr_flags & FUSE_GETATTR_FH) { -+ memset(&fi, 0, sizeof(fi)); -+ fi.fh = arg->fh; -+ fip = &fi; - } - - if (req->se->op.getattr) { -@@ -856,11 +827,7 @@ static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - struct fuse_mknod_in *arg = (struct fuse_mknod_in *)inarg; - char *name = PARAM(arg); - -- if (req->se->conn.proto_minor >= 12) { -- req->ctx.umask = arg->umask; -- } else { -- name = (char *)inarg + FUSE_COMPAT_MKNOD_IN_SIZE; -- } -+ req->ctx.umask = arg->umask; - - if (req->se->op.mknod) { - req->se->op.mknod(req, nodeid, name, arg->mode, arg->rdev); -@@ -873,9 +840,7 @@ static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - { - struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *)inarg; - -- if (req->se->conn.proto_minor >= 12) { -- req->ctx.umask = arg->umask; -- } -+ req->ctx.umask = arg->umask; - - if (req->se->op.mkdir) { - req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode); -@@ -967,11 +932,7 @@ static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - memset(&fi, 0, sizeof(fi)); - fi.flags = arg->flags; - -- if (req->se->conn.proto_minor >= 12) { -- req->ctx.umask = arg->umask; -- } else { -- name = (char *)inarg + sizeof(struct fuse_open_in); -- } -+ req->ctx.umask = arg->umask; - - req->se->op.create(req, nodeid, name, arg->mode, &fi); - } else { -@@ -1003,10 +964,8 @@ static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; -- if (req->se->conn.proto_minor >= 9) { -- fi.lock_owner = arg->lock_owner; -- fi.flags = arg->flags; -- } -+ fi.lock_owner = arg->lock_owner; -+ fi.flags = arg->flags; - req->se->op.read(req, nodeid, arg->size, arg->offset, &fi); - } else { - fuse_reply_err(req, ENOSYS); -@@ -1023,13 +982,9 @@ static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - fi.fh = arg->fh; - fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0; - -- if (req->se->conn.proto_minor < 9) { -- param = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE; -- } else { -- fi.lock_owner = arg->lock_owner; -- fi.flags = arg->flags; -- param = PARAM(arg); -- } -+ fi.lock_owner = arg->lock_owner; -+ fi.flags = arg->flags; -+ param = PARAM(arg); - - if (req->se->op.write) { - req->se->op.write(req, nodeid, param, arg->size, arg->offset, &fi); -@@ -1053,21 +1008,14 @@ static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, - fi.fh = arg->fh; - fi.writepage = arg->write_flags & FUSE_WRITE_CACHE; - -- if (se->conn.proto_minor < 9) { -- bufv.buf[0].mem = ((char *)arg) + FUSE_COMPAT_WRITE_IN_SIZE; -- bufv.buf[0].size -= -- sizeof(struct fuse_in_header) + FUSE_COMPAT_WRITE_IN_SIZE; -- assert(!(bufv.buf[0].flags & FUSE_BUF_IS_FD)); -- } else { -- fi.lock_owner = arg->lock_owner; -- fi.flags = arg->flags; -- if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) { -- bufv.buf[0].mem = PARAM(arg); -- } -- -- bufv.buf[0].size -= -- sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); -+ fi.lock_owner = arg->lock_owner; -+ fi.flags = arg->flags; -+ if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) { -+ bufv.buf[0].mem = PARAM(arg); - } -+ -+ bufv.buf[0].size -= -+ sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); - if (bufv.buf[0].size < arg->size) { - fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n"); - fuse_reply_err(req, EIO); -@@ -1086,9 +1034,7 @@ static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; - fi.flush = 1; -- if (req->se->conn.proto_minor >= 7) { -- fi.lock_owner = arg->lock_owner; -- } -+ fi.lock_owner = arg->lock_owner; - - if (req->se->op.flush) { - req->se->op.flush(req, nodeid, &fi); -@@ -1105,10 +1051,8 @@ static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - memset(&fi, 0, sizeof(fi)); - fi.flags = arg->flags; - fi.fh = arg->fh; -- if (req->se->conn.proto_minor >= 8) { -- fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; -- fi.lock_owner = arg->lock_owner; -- } -+ fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; -+ fi.lock_owner = arg->lock_owner; - if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) { - fi.flock_release = 1; - fi.lock_owner = arg->lock_owner; -@@ -1477,8 +1421,7 @@ static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; - -- if (sizeof(void *) == 4 && req->se->conn.proto_minor >= 16 && -- !(flags & FUSE_IOCTL_32BIT)) { -+ if (sizeof(void *) == 4 && !(flags & FUSE_IOCTL_32BIT)) { - req->ioctl_64bit = 1; - } - -@@ -1603,7 +1546,7 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - outarg.major = FUSE_KERNEL_VERSION; - outarg.minor = FUSE_KERNEL_MINOR_VERSION; - -- if (arg->major < 7) { -+ if (arg->major < 7 || (arg->major == 7 && arg->minor < 31)) { - fuse_log(FUSE_LOG_ERR, "fuse: unsupported protocol version: %u.%u\n", - arg->major, arg->minor); - fuse_reply_err(req, EPROTO); -@@ -1616,81 +1559,71 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - return; - } - -- if (arg->minor >= 6) { -- if (arg->max_readahead < se->conn.max_readahead) { -- se->conn.max_readahead = arg->max_readahead; -- } -- if (arg->flags & FUSE_ASYNC_READ) { -- se->conn.capable |= FUSE_CAP_ASYNC_READ; -- } -- if (arg->flags & FUSE_POSIX_LOCKS) { -- se->conn.capable |= FUSE_CAP_POSIX_LOCKS; -- } -- if (arg->flags & FUSE_ATOMIC_O_TRUNC) { -- se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC; -- } -- if (arg->flags & FUSE_EXPORT_SUPPORT) { -- se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT; -- } -- if (arg->flags & FUSE_DONT_MASK) { -- se->conn.capable |= FUSE_CAP_DONT_MASK; -- } -- if (arg->flags & FUSE_FLOCK_LOCKS) { -- se->conn.capable |= FUSE_CAP_FLOCK_LOCKS; -- } -- if (arg->flags & FUSE_AUTO_INVAL_DATA) { -- se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA; -- } -- if (arg->flags & FUSE_DO_READDIRPLUS) { -- se->conn.capable |= FUSE_CAP_READDIRPLUS; -- } -- if (arg->flags & FUSE_READDIRPLUS_AUTO) { -- se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO; -- } -- if (arg->flags & FUSE_ASYNC_DIO) { -- se->conn.capable |= FUSE_CAP_ASYNC_DIO; -- } -- if (arg->flags & FUSE_WRITEBACK_CACHE) { -- se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE; -- } -- if (arg->flags & FUSE_NO_OPEN_SUPPORT) { -- se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT; -- } -- if (arg->flags & FUSE_PARALLEL_DIROPS) { -- se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS; -- } -- if (arg->flags & FUSE_POSIX_ACL) { -- se->conn.capable |= FUSE_CAP_POSIX_ACL; -- } -- if (arg->flags & FUSE_HANDLE_KILLPRIV) { -- se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV; -- } -- if (arg->flags & FUSE_NO_OPENDIR_SUPPORT) { -- se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT; -- } -- if (!(arg->flags & FUSE_MAX_PAGES)) { -- size_t max_bufsize = -- FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() + -- FUSE_BUFFER_HEADER_SIZE; -- if (bufsize > max_bufsize) { -- bufsize = max_bufsize; -- } -+ if (arg->max_readahead < se->conn.max_readahead) { -+ se->conn.max_readahead = arg->max_readahead; -+ } -+ if (arg->flags & FUSE_ASYNC_READ) { -+ se->conn.capable |= FUSE_CAP_ASYNC_READ; -+ } -+ if (arg->flags & FUSE_POSIX_LOCKS) { -+ se->conn.capable |= FUSE_CAP_POSIX_LOCKS; -+ } -+ if (arg->flags & FUSE_ATOMIC_O_TRUNC) { -+ se->conn.capable |= FUSE_CAP_ATOMIC_O_TRUNC; -+ } -+ if (arg->flags & FUSE_EXPORT_SUPPORT) { -+ se->conn.capable |= FUSE_CAP_EXPORT_SUPPORT; -+ } -+ if (arg->flags & FUSE_DONT_MASK) { -+ se->conn.capable |= FUSE_CAP_DONT_MASK; -+ } -+ if (arg->flags & FUSE_FLOCK_LOCKS) { -+ se->conn.capable |= FUSE_CAP_FLOCK_LOCKS; -+ } -+ if (arg->flags & FUSE_AUTO_INVAL_DATA) { -+ se->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA; -+ } -+ if (arg->flags & FUSE_DO_READDIRPLUS) { -+ se->conn.capable |= FUSE_CAP_READDIRPLUS; -+ } -+ if (arg->flags & FUSE_READDIRPLUS_AUTO) { -+ se->conn.capable |= FUSE_CAP_READDIRPLUS_AUTO; -+ } -+ if (arg->flags & FUSE_ASYNC_DIO) { -+ se->conn.capable |= FUSE_CAP_ASYNC_DIO; -+ } -+ if (arg->flags & FUSE_WRITEBACK_CACHE) { -+ se->conn.capable |= FUSE_CAP_WRITEBACK_CACHE; -+ } -+ if (arg->flags & FUSE_NO_OPEN_SUPPORT) { -+ se->conn.capable |= FUSE_CAP_NO_OPEN_SUPPORT; -+ } -+ if (arg->flags & FUSE_PARALLEL_DIROPS) { -+ se->conn.capable |= FUSE_CAP_PARALLEL_DIROPS; -+ } -+ if (arg->flags & FUSE_POSIX_ACL) { -+ se->conn.capable |= FUSE_CAP_POSIX_ACL; -+ } -+ if (arg->flags & FUSE_HANDLE_KILLPRIV) { -+ se->conn.capable |= FUSE_CAP_HANDLE_KILLPRIV; -+ } -+ if (arg->flags & FUSE_NO_OPENDIR_SUPPORT) { -+ se->conn.capable |= FUSE_CAP_NO_OPENDIR_SUPPORT; -+ } -+ if (!(arg->flags & FUSE_MAX_PAGES)) { -+ size_t max_bufsize = FUSE_DEFAULT_MAX_PAGES_PER_REQ * getpagesize() + -+ FUSE_BUFFER_HEADER_SIZE; -+ if (bufsize > max_bufsize) { -+ bufsize = max_bufsize; - } -- } else { -- se->conn.max_readahead = 0; - } -- -- if (se->conn.proto_minor >= 14) { - #ifdef HAVE_SPLICE - #ifdef HAVE_VMSPLICE -- se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; -+ se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE; - #endif -- se->conn.capable |= FUSE_CAP_SPLICE_READ; -+ se->conn.capable |= FUSE_CAP_SPLICE_READ; - #endif -- } -- if (se->conn.proto_minor >= 18) { -- se->conn.capable |= FUSE_CAP_IOCTL_DIR; -- } -+ se->conn.capable |= FUSE_CAP_IOCTL_DIR; - - /* - * Default settings for modern filesystems. -@@ -1797,24 +1730,20 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - outarg.max_readahead = se->conn.max_readahead; - outarg.max_write = se->conn.max_write; -- if (se->conn.proto_minor >= 13) { -- if (se->conn.max_background >= (1 << 16)) { -- se->conn.max_background = (1 << 16) - 1; -- } -- if (se->conn.congestion_threshold > se->conn.max_background) { -- se->conn.congestion_threshold = se->conn.max_background; -- } -- if (!se->conn.congestion_threshold) { -- se->conn.congestion_threshold = se->conn.max_background * 3 / 4; -- } -- -- outarg.max_background = se->conn.max_background; -- outarg.congestion_threshold = se->conn.congestion_threshold; -+ if (se->conn.max_background >= (1 << 16)) { -+ se->conn.max_background = (1 << 16) - 1; -+ } -+ if (se->conn.congestion_threshold > se->conn.max_background) { -+ se->conn.congestion_threshold = se->conn.max_background; - } -- if (se->conn.proto_minor >= 23) { -- outarg.time_gran = se->conn.time_gran; -+ if (!se->conn.congestion_threshold) { -+ se->conn.congestion_threshold = se->conn.max_background * 3 / 4; - } - -+ outarg.max_background = se->conn.max_background; -+ outarg.congestion_threshold = se->conn.congestion_threshold; -+ outarg.time_gran = se->conn.time_gran; -+ - if (se->debug) { - fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, - outarg.minor); -@@ -1828,11 +1757,6 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - outarg.congestion_threshold); - fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", outarg.time_gran); - } -- if (arg->minor < 5) { -- outargsize = FUSE_COMPAT_INIT_OUT_SIZE; -- } else if (arg->minor < 23) { -- outargsize = FUSE_COMPAT_22_INIT_OUT_SIZE; -- } - - send_reply_ok(req, &outarg, outargsize); - } -@@ -1896,10 +1820,6 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, - return -EINVAL; - } - -- if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) { -- return -ENOSYS; -- } -- - outarg.ino = ino; - outarg.off = off; - outarg.len = len; -@@ -1920,10 +1840,6 @@ int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, - return -EINVAL; - } - -- if (se->conn.proto_major < 6 || se->conn.proto_minor < 12) { -- return -ENOSYS; -- } -- - outarg.parent = parent; - outarg.namelen = namelen; - outarg.padding = 0; -@@ -1947,10 +1863,6 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, - return -EINVAL; - } - -- if (se->conn.proto_major < 6 || se->conn.proto_minor < 18) { -- return -ENOSYS; -- } -- - outarg.parent = parent; - outarg.child = child; - outarg.namelen = namelen; -@@ -1977,10 +1889,6 @@ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, - return -EINVAL; - } - -- if (se->conn.proto_major < 6 || se->conn.proto_minor < 15) { -- return -ENOSYS; -- } -- - out.unique = 0; - out.error = FUSE_NOTIFY_STORE; - diff --git a/0023-vitriofsd-passthrough_ll-fix-fallocate-ifdefs.patch b/0023-vitriofsd-passthrough_ll-fix-fallocate-ifdefs.patch deleted file mode 100644 index 529af3b..0000000 --- a/0023-vitriofsd-passthrough_ll-fix-fallocate-ifdefs.patch +++ /dev/null @@ -1,36 +0,0 @@ -From: Xiao Yang -Date: Mon, 27 Jan 2020 19:00:52 +0000 -Subject: [PATCH] vitriofsd/passthrough_ll: fix fallocate() ifdefs - -1) Use correct CONFIG_FALLOCATE macro to check if fallocate() is supported.(i.e configure - script sets CONFIG_FALLOCATE intead of HAVE_FALLOCATE if fallocate() is supported) -2) Replace HAVE_POSIX_FALLOCATE with CONFIG_POSIX_FALLOCATE. - -Signed-off-by: Xiao Yang -Signed-off-by: Dr. David Alan Gilbert - Merged from two of Xiao Yang's patches -(cherry picked from commit 9776457ca6f05d5900e27decb1dba2ffddf95a22) ---- - tools/virtiofsd/passthrough_ll.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 322a889cdf..6c4da18075 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -975,13 +975,13 @@ static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, - int err = EOPNOTSUPP; - (void)ino; - --#ifdef HAVE_FALLOCATE -+#ifdef CONFIG_FALLOCATE - err = fallocate(fi->fh, mode, offset, length); - if (err < 0) { - err = errno; - } - --#elif defined(HAVE_POSIX_FALLOCATE) -+#elif defined(CONFIG_POSIX_FALLOCATE) - if (mode) { - fuse_reply_err(req, EOPNOTSUPP); - return; diff --git a/0024-virtiofsd-Make-fsync-work-even-if-only-inode-is-pass.patch b/0024-virtiofsd-Make-fsync-work-even-if-only-inode-is-pass.patch deleted file mode 100644 index 119bcdb..0000000 --- a/0024-virtiofsd-Make-fsync-work-even-if-only-inode-is-pass.patch +++ /dev/null @@ -1,79 +0,0 @@ -From: Vivek Goyal -Date: Mon, 27 Jan 2020 19:00:53 +0000 -Subject: [PATCH] virtiofsd: Make fsync work even if only inode is passed in -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -If caller has not sent file handle in request, then using inode, retrieve -the fd opened using O_PATH and use that to open file again and issue -fsync. This will be needed when dax_flush() calls fsync. At that time -we only have inode information (and not file). - -Signed-off-by: Vivek Goyal -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 1b209805f8159c3f4d89ddb9390a5f64887cebff) ---- - tools/virtiofsd/fuse_lowlevel.c | 6 +++++- - tools/virtiofsd/passthrough_ll.c | 28 ++++++++++++++++++++++++++-- - 2 files changed, 31 insertions(+), 3 deletions(-) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 514d79cb24..8552cfb8af 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -1075,7 +1075,11 @@ static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - fi.fh = arg->fh; - - if (req->se->op.fsync) { -- req->se->op.fsync(req, nodeid, datasync, &fi); -+ if (fi.fh == (uint64_t)-1) { -+ req->se->op.fsync(req, nodeid, datasync, NULL); -+ } else { -+ req->se->op.fsync(req, nodeid, datasync, &fi); -+ } - } else { - fuse_reply_err(req, ENOSYS); - } -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 6c4da18075..26ac87013b 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -903,10 +903,34 @@ static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, - { - int res; - (void)ino; -+ int fd; -+ char *buf; -+ -+ fuse_log(FUSE_LOG_DEBUG, "lo_fsync(ino=%" PRIu64 ", fi=0x%p)\n", ino, -+ (void *)fi); -+ -+ if (!fi) { -+ res = asprintf(&buf, "/proc/self/fd/%i", lo_fd(req, ino)); -+ if (res == -1) { -+ return (void)fuse_reply_err(req, errno); -+ } -+ -+ fd = open(buf, O_RDWR); -+ free(buf); -+ if (fd == -1) { -+ return (void)fuse_reply_err(req, errno); -+ } -+ } else { -+ fd = fi->fh; -+ } -+ - if (datasync) { -- res = fdatasync(fi->fh); -+ res = fdatasync(fd); - } else { -- res = fsync(fi->fh); -+ res = fsync(fd); -+ } -+ if (!fi) { -+ close(fd); - } - fuse_reply_err(req, res == -1 ? errno : 0); - } diff --git a/0025-virtiofsd-Add-options-for-virtio.patch b/0025-virtiofsd-Add-options-for-virtio.patch deleted file mode 100644 index f0193f8..0000000 --- a/0025-virtiofsd-Add-options-for-virtio.patch +++ /dev/null @@ -1,84 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:00:54 +0000 -Subject: [PATCH] virtiofsd: Add options for virtio - -Add options to specify parameters for virtio-fs paths, i.e. - - ./virtiofsd -o vhost_user_socket=/tmp/vhostqemu - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Misono Tomohiro -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 205de006aab8dcbe546a7e3a51d295c2d05e654b) ---- - tools/virtiofsd/fuse_i.h | 1 + - tools/virtiofsd/fuse_lowlevel.c | 11 ++++++++--- - tools/virtiofsd/helper.c | 14 +++++++------- - 3 files changed, 16 insertions(+), 10 deletions(-) - -diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h -index bae06992e0..26b1a7da88 100644 ---- a/tools/virtiofsd/fuse_i.h -+++ b/tools/virtiofsd/fuse_i.h -@@ -63,6 +63,7 @@ struct fuse_session { - struct fuse_notify_req notify_list; - size_t bufsize; - int error; -+ char *vu_socket_path; - }; - - struct fuse_chan { -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 8552cfb8af..17e8718283 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -2115,8 +2115,11 @@ reply_err: - } - - static const struct fuse_opt fuse_ll_opts[] = { -- LL_OPTION("debug", debug, 1), LL_OPTION("-d", debug, 1), -- LL_OPTION("--debug", debug, 1), LL_OPTION("allow_root", deny_others, 1), -+ LL_OPTION("debug", debug, 1), -+ LL_OPTION("-d", debug, 1), -+ LL_OPTION("--debug", debug, 1), -+ LL_OPTION("allow_root", deny_others, 1), -+ LL_OPTION("--socket-path=%s", vu_socket_path, 0), - FUSE_OPT_END - }; - -@@ -2132,7 +2135,9 @@ void fuse_lowlevel_help(void) - * These are not all options, but the ones that are - * potentially of interest to an end-user - */ -- printf(" -o allow_root allow access by root\n"); -+ printf( -+ " -o allow_root allow access by root\n" -+ " --socket-path=PATH path for the vhost-user socket\n"); - } - - void fuse_session_destroy(struct fuse_session *se) -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index 9333691525..676032e71f 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -127,13 +127,13 @@ static const struct fuse_opt conn_info_opt_spec[] = { - - void fuse_cmdline_help(void) - { -- printf( -- " -h --help print help\n" -- " -V --version print version\n" -- " -d -o debug enable debug output (implies -f)\n" -- " -f foreground operation\n" -- " -o max_idle_threads the maximum number of idle worker threads\n" -- " allowed (default: 10)\n"); -+ printf(" -h --help print help\n" -+ " -V --version print version\n" -+ " -d -o debug enable debug output (implies -f)\n" -+ " -f foreground operation\n" -+ " -o max_idle_threads the maximum number of idle worker " -+ "threads\n" -+ " allowed (default: 10)\n"); - } - - static int fuse_helper_opt_proc(void *data, const char *arg, int key, diff --git a/0026-virtiofsd-add-o-source-PATH-to-help-output.patch b/0026-virtiofsd-add-o-source-PATH-to-help-output.patch deleted file mode 100644 index 43f1c59..0000000 --- a/0026-virtiofsd-add-o-source-PATH-to-help-output.patch +++ /dev/null @@ -1,30 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:00:55 +0000 -Subject: [PATCH] virtiofsd: add -o source=PATH to help output -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The -o source=PATH option will be used by most command-line invocations. -Let's document it! - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 4ff075f72be2f489c8998ae492ec5cdbbbd73e07) ---- - tools/virtiofsd/passthrough_ll.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 26ac87013b..fc9b264d56 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1319,6 +1319,7 @@ int main(int argc, char *argv[]) - if (opts.show_help) { - printf("usage: %s [options]\n\n", argv[0]); - fuse_cmdline_help(); -+ printf(" -o source=PATH shared directory tree\n"); - fuse_lowlevel_help(); - ret = 0; - goto err_out1; diff --git a/0027-virtiofsd-Open-vhost-connection-instead-of-mounting.patch b/0027-virtiofsd-Open-vhost-connection-instead-of-mounting.patch deleted file mode 100644 index 01e1514..0000000 --- a/0027-virtiofsd-Open-vhost-connection-instead-of-mounting.patch +++ /dev/null @@ -1,241 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:00:56 +0000 -Subject: [PATCH] virtiofsd: Open vhost connection instead of mounting -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -When run with vhost-user options we conect to the QEMU instead -via a socket. Start this off by creating the socket. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Reviewed-by: Misono Tomohiro -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit d14bf584dd965821e80d14c16d9292a464b1ab85) ---- - tools/virtiofsd/fuse_i.h | 7 ++- - tools/virtiofsd/fuse_lowlevel.c | 55 +++-------------------- - tools/virtiofsd/fuse_virtio.c | 79 +++++++++++++++++++++++++++++++++ - tools/virtiofsd/fuse_virtio.h | 23 ++++++++++ - 4 files changed, 114 insertions(+), 50 deletions(-) - create mode 100644 tools/virtiofsd/fuse_virtio.c - create mode 100644 tools/virtiofsd/fuse_virtio.h - -diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h -index 26b1a7da88..82d6ac7115 100644 ---- a/tools/virtiofsd/fuse_i.h -+++ b/tools/virtiofsd/fuse_i.h -@@ -6,9 +6,10 @@ - * See the file COPYING.LIB - */ - --#define FUSE_USE_VERSION 31 -- -+#ifndef FUSE_I_H -+#define FUSE_I_H - -+#define FUSE_USE_VERSION 31 - #include "fuse.h" - #include "fuse_lowlevel.h" - -@@ -101,3 +102,5 @@ void fuse_session_process_buf_int(struct fuse_session *se, - - /* room needed in buffer to accommodate header */ - #define FUSE_BUFFER_HEADER_SIZE 0x1000 -+ -+#endif -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 17e8718283..5df124e64b 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -14,6 +14,7 @@ - #include "standard-headers/linux/fuse.h" - #include "fuse_misc.h" - #include "fuse_opt.h" -+#include "fuse_virtio.h" - - #include - #include -@@ -2202,6 +2203,11 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, - goto out4; - } - -+ if (!se->vu_socket_path) { -+ fprintf(stderr, "fuse: missing -o vhost_user_socket option\n"); -+ goto out4; -+ } -+ - se->bufsize = FUSE_MAX_MAX_PAGES * getpagesize() + FUSE_BUFFER_HEADER_SIZE; - - list_init_req(&se->list); -@@ -2224,54 +2230,7 @@ out1: - - int fuse_session_mount(struct fuse_session *se) - { -- int fd; -- -- /* -- * Make sure file descriptors 0, 1 and 2 are open, otherwise chaos -- * would ensue. -- */ -- do { -- fd = open("/dev/null", O_RDWR); -- if (fd > 2) { -- close(fd); -- } -- } while (fd >= 0 && fd <= 2); -- -- /* -- * To allow FUSE daemons to run without privileges, the caller may open -- * /dev/fuse before launching the file system and pass on the file -- * descriptor by specifying /dev/fd/N as the mount point. Note that the -- * parent process takes care of performing the mount in this case. -- */ -- fd = fuse_mnt_parse_fuse_fd(mountpoint); -- if (fd != -1) { -- if (fcntl(fd, F_GETFD) == -1) { -- fuse_log(FUSE_LOG_ERR, "fuse: Invalid file descriptor /dev/fd/%u\n", -- fd); -- return -1; -- } -- se->fd = fd; -- return 0; -- } -- -- /* Open channel */ -- fd = fuse_kern_mount(mountpoint, se->mo); -- if (fd == -1) { -- return -1; -- } -- se->fd = fd; -- -- /* Save mountpoint */ -- se->mountpoint = strdup(mountpoint); -- if (se->mountpoint == NULL) { -- goto error_out; -- } -- -- return 0; -- --error_out: -- fuse_kern_unmount(mountpoint, fd); -- return -1; -+ return virtio_session_mount(se); - } - - int fuse_session_fd(struct fuse_session *se) -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -new file mode 100644 -index 0000000000..cbef6ffdda ---- /dev/null -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -0,0 +1,79 @@ -+/* -+ * virtio-fs glue for FUSE -+ * Copyright (C) 2018 Red Hat, Inc. and/or its affiliates -+ * -+ * Authors: -+ * Dave Gilbert -+ * -+ * Implements the glue between libfuse and libvhost-user -+ * -+ * This program can be distributed under the terms of the GNU LGPLv2. -+ * See the file COPYING.LIB -+ */ -+ -+#include "fuse_i.h" -+#include "standard-headers/linux/fuse.h" -+#include "fuse_misc.h" -+#include "fuse_opt.h" -+#include "fuse_virtio.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+/* From spec */ -+struct virtio_fs_config { -+ char tag[36]; -+ uint32_t num_queues; -+}; -+ -+int virtio_session_mount(struct fuse_session *se) -+{ -+ struct sockaddr_un un; -+ mode_t old_umask; -+ -+ if (strlen(se->vu_socket_path) >= sizeof(un.sun_path)) { -+ fuse_log(FUSE_LOG_ERR, "Socket path too long\n"); -+ return -1; -+ } -+ -+ se->fd = -1; -+ -+ /* -+ * Create the Unix socket to communicate with qemu -+ * based on QEMU's vhost-user-bridge -+ */ -+ unlink(se->vu_socket_path); -+ strcpy(un.sun_path, se->vu_socket_path); -+ size_t addr_len = sizeof(un); -+ -+ int listen_sock = socket(AF_UNIX, SOCK_STREAM, 0); -+ if (listen_sock == -1) { -+ fuse_log(FUSE_LOG_ERR, "vhost socket creation: %m\n"); -+ return -1; -+ } -+ un.sun_family = AF_UNIX; -+ -+ /* -+ * Unfortunately bind doesn't let you set the mask on the socket, -+ * so set umask to 077 and restore it later. -+ */ -+ old_umask = umask(0077); -+ if (bind(listen_sock, (struct sockaddr *)&un, addr_len) == -1) { -+ fuse_log(FUSE_LOG_ERR, "vhost socket bind: %m\n"); -+ umask(old_umask); -+ return -1; -+ } -+ umask(old_umask); -+ -+ if (listen(listen_sock, 1) == -1) { -+ fuse_log(FUSE_LOG_ERR, "vhost socket listen: %m\n"); -+ return -1; -+ } -+ -+ return -1; -+} -diff --git a/tools/virtiofsd/fuse_virtio.h b/tools/virtiofsd/fuse_virtio.h -new file mode 100644 -index 0000000000..8f2edb69ca ---- /dev/null -+++ b/tools/virtiofsd/fuse_virtio.h -@@ -0,0 +1,23 @@ -+/* -+ * virtio-fs glue for FUSE -+ * Copyright (C) 2018 Red Hat, Inc. and/or its affiliates -+ * -+ * Authors: -+ * Dave Gilbert -+ * -+ * Implements the glue between libfuse and libvhost-user -+ * -+ * This program can be distributed under the terms of the GNU LGPLv2. -+ * See the file COPYING.LIB -+ */ -+ -+#ifndef FUSE_VIRTIO_H -+#define FUSE_VIRTIO_H -+ -+#include "fuse_i.h" -+ -+struct fuse_session; -+ -+int virtio_session_mount(struct fuse_session *se); -+ -+#endif diff --git a/0028-virtiofsd-Start-wiring-up-vhost-user.patch b/0028-virtiofsd-Start-wiring-up-vhost-user.patch deleted file mode 100644 index 1fdd8a4..0000000 --- a/0028-virtiofsd-Start-wiring-up-vhost-user.patch +++ /dev/null @@ -1,231 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:00:57 +0000 -Subject: [PATCH] virtiofsd: Start wiring up vhost-user -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Listen on our unix socket for the connection from QEMU, when we get it -initialise vhost-user and dive into our own loop variant (currently -dummy). - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit f6f3573c6f271af5ded63ce28589a113f7205c72) ---- - tools/virtiofsd/fuse_i.h | 4 ++ - tools/virtiofsd/fuse_lowlevel.c | 5 ++ - tools/virtiofsd/fuse_lowlevel.h | 7 +++ - tools/virtiofsd/fuse_virtio.c | 87 +++++++++++++++++++++++++++++++- - tools/virtiofsd/fuse_virtio.h | 2 + - tools/virtiofsd/passthrough_ll.c | 7 +-- - 6 files changed, 106 insertions(+), 6 deletions(-) - -diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h -index 82d6ac7115..ec04449069 100644 ---- a/tools/virtiofsd/fuse_i.h -+++ b/tools/virtiofsd/fuse_i.h -@@ -13,6 +13,8 @@ - #include "fuse.h" - #include "fuse_lowlevel.h" - -+struct fv_VuDev; -+ - struct fuse_req { - struct fuse_session *se; - uint64_t unique; -@@ -65,6 +67,8 @@ struct fuse_session { - size_t bufsize; - int error; - char *vu_socket_path; -+ int vu_socketfd; -+ struct fv_VuDev *virtio_dev; - }; - - struct fuse_chan { -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 5df124e64b..af09fa2b94 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -2242,6 +2242,11 @@ void fuse_session_unmount(struct fuse_session *se) - { - } - -+int fuse_lowlevel_is_virtio(struct fuse_session *se) -+{ -+ return se->vu_socket_path != NULL; -+} -+ - #ifdef linux - int fuse_req_getgroups(fuse_req_t req, int size, gid_t list[]) - { -diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h -index 2fa225d40b..f6b34700af 100644 ---- a/tools/virtiofsd/fuse_lowlevel.h -+++ b/tools/virtiofsd/fuse_lowlevel.h -@@ -1755,6 +1755,13 @@ void fuse_req_interrupt_func(fuse_req_t req, fuse_interrupt_func_t func, - */ - int fuse_req_interrupted(fuse_req_t req); - -+/** -+ * Check if the session is connected via virtio -+ * -+ * @param se session object -+ * @return 1 if the session is a virtio session -+ */ -+int fuse_lowlevel_is_virtio(struct fuse_session *se); - - /* - * Inquiry functions -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index cbef6ffdda..2ae3c764dd 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -19,18 +19,78 @@ - - #include - #include -+#include - #include - #include - #include - #include - #include - -+#include "contrib/libvhost-user/libvhost-user.h" -+ -+/* -+ * We pass the dev element into libvhost-user -+ * and then use it to get back to the outer -+ * container for other data. -+ */ -+struct fv_VuDev { -+ VuDev dev; -+ struct fuse_session *se; -+}; -+ - /* From spec */ - struct virtio_fs_config { - char tag[36]; - uint32_t num_queues; - }; - -+/* -+ * Callback from libvhost-user if there's a new fd we're supposed to listen -+ * to, typically a queue kick? -+ */ -+static void fv_set_watch(VuDev *dev, int fd, int condition, vu_watch_cb cb, -+ void *data) -+{ -+ fuse_log(FUSE_LOG_WARNING, "%s: TODO! fd=%d\n", __func__, fd); -+} -+ -+/* -+ * Callback from libvhost-user if we're no longer supposed to listen on an fd -+ */ -+static void fv_remove_watch(VuDev *dev, int fd) -+{ -+ fuse_log(FUSE_LOG_WARNING, "%s: TODO! fd=%d\n", __func__, fd); -+} -+ -+/* Callback from libvhost-user to panic */ -+static void fv_panic(VuDev *dev, const char *err) -+{ -+ fuse_log(FUSE_LOG_ERR, "%s: libvhost-user: %s\n", __func__, err); -+ /* TODO: Allow reconnects?? */ -+ exit(EXIT_FAILURE); -+} -+ -+static bool fv_queue_order(VuDev *dev, int qidx) -+{ -+ return false; -+} -+ -+static const VuDevIface fv_iface = { -+ /* TODO: Add other callbacks */ -+ .queue_is_processed_in_order = fv_queue_order, -+}; -+ -+int virtio_loop(struct fuse_session *se) -+{ -+ fuse_log(FUSE_LOG_INFO, "%s: Entry\n", __func__); -+ -+ while (1) { -+ /* TODO: Add stuffing */ -+ } -+ -+ fuse_log(FUSE_LOG_INFO, "%s: Exit\n", __func__); -+} -+ - int virtio_session_mount(struct fuse_session *se) - { - struct sockaddr_un un; -@@ -75,5 +135,30 @@ int virtio_session_mount(struct fuse_session *se) - return -1; - } - -- return -1; -+ fuse_log(FUSE_LOG_INFO, "%s: Waiting for vhost-user socket connection...\n", -+ __func__); -+ int data_sock = accept(listen_sock, NULL, NULL); -+ if (data_sock == -1) { -+ fuse_log(FUSE_LOG_ERR, "vhost socket accept: %m\n"); -+ close(listen_sock); -+ return -1; -+ } -+ close(listen_sock); -+ fuse_log(FUSE_LOG_INFO, "%s: Received vhost-user socket connection\n", -+ __func__); -+ -+ /* TODO: Some cleanup/deallocation! */ -+ se->virtio_dev = calloc(sizeof(struct fv_VuDev), 1); -+ if (!se->virtio_dev) { -+ fuse_log(FUSE_LOG_ERR, "%s: virtio_dev calloc failed\n", __func__); -+ close(data_sock); -+ return -1; -+ } -+ -+ se->vu_socketfd = data_sock; -+ se->virtio_dev->se = se; -+ vu_init(&se->virtio_dev->dev, 2, se->vu_socketfd, fv_panic, fv_set_watch, -+ fv_remove_watch, &fv_iface); -+ -+ return 0; - } -diff --git a/tools/virtiofsd/fuse_virtio.h b/tools/virtiofsd/fuse_virtio.h -index 8f2edb69ca..23026d6e4c 100644 ---- a/tools/virtiofsd/fuse_virtio.h -+++ b/tools/virtiofsd/fuse_virtio.h -@@ -20,4 +20,6 @@ struct fuse_session; - - int virtio_session_mount(struct fuse_session *se); - -+int virtio_loop(struct fuse_session *se); -+ - #endif -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index fc9b264d56..037c5d7b26 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -36,6 +36,7 @@ - */ - - #include "qemu/osdep.h" -+#include "fuse_virtio.h" - #include "fuse_lowlevel.h" - #include - #include -@@ -1395,11 +1396,7 @@ int main(int argc, char *argv[]) - fuse_daemonize(opts.foreground); - - /* Block until ctrl+c or fusermount -u */ -- if (opts.singlethread) { -- ret = fuse_session_loop(se); -- } else { -- ret = fuse_session_loop_mt(se, opts.clone_fd); -- } -+ ret = virtio_loop(se); - - fuse_session_unmount(se); - err_out3: diff --git a/0029-virtiofsd-Add-main-virtio-loop.patch b/0029-virtiofsd-Add-main-virtio-loop.patch deleted file mode 100644 index d8217c4..0000000 --- a/0029-virtiofsd-Add-main-virtio-loop.patch +++ /dev/null @@ -1,89 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:00:58 +0000 -Subject: [PATCH] virtiofsd: Add main virtio loop -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Processes incoming requests on the vhost-user fd. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 204d8ae57b3c57098642c79b3c03d42495149c09) ---- - tools/virtiofsd/fuse_virtio.c | 42 ++++++++++++++++++++++++++++++++--- - 1 file changed, 39 insertions(+), 3 deletions(-) - -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index 2ae3c764dd..1928a2025c 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -11,12 +11,14 @@ - * See the file COPYING.LIB - */ - -+#include "fuse_virtio.h" - #include "fuse_i.h" - #include "standard-headers/linux/fuse.h" - #include "fuse_misc.h" - #include "fuse_opt.h" --#include "fuse_virtio.h" - -+#include -+#include - #include - #include - #include -@@ -80,15 +82,49 @@ static const VuDevIface fv_iface = { - .queue_is_processed_in_order = fv_queue_order, - }; - -+/* -+ * Main loop; this mostly deals with events on the vhost-user -+ * socket itself, and not actual fuse data. -+ */ - int virtio_loop(struct fuse_session *se) - { - fuse_log(FUSE_LOG_INFO, "%s: Entry\n", __func__); - -- while (1) { -- /* TODO: Add stuffing */ -+ while (!fuse_session_exited(se)) { -+ struct pollfd pf[1]; -+ pf[0].fd = se->vu_socketfd; -+ pf[0].events = POLLIN; -+ pf[0].revents = 0; -+ -+ fuse_log(FUSE_LOG_DEBUG, "%s: Waiting for VU event\n", __func__); -+ int poll_res = ppoll(pf, 1, NULL, NULL); -+ -+ if (poll_res == -1) { -+ if (errno == EINTR) { -+ fuse_log(FUSE_LOG_INFO, "%s: ppoll interrupted, going around\n", -+ __func__); -+ continue; -+ } -+ fuse_log(FUSE_LOG_ERR, "virtio_loop ppoll: %m\n"); -+ break; -+ } -+ assert(poll_res == 1); -+ if (pf[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { -+ fuse_log(FUSE_LOG_ERR, "%s: Unexpected poll revents %x\n", __func__, -+ pf[0].revents); -+ break; -+ } -+ assert(pf[0].revents & POLLIN); -+ fuse_log(FUSE_LOG_DEBUG, "%s: Got VU event\n", __func__); -+ if (!vu_dispatch(&se->virtio_dev->dev)) { -+ fuse_log(FUSE_LOG_ERR, "%s: vu_dispatch failed\n", __func__); -+ break; -+ } - } - - fuse_log(FUSE_LOG_INFO, "%s: Exit\n", __func__); -+ -+ return 0; - } - - int virtio_session_mount(struct fuse_session *se) diff --git a/0030-virtiofsd-get-set-features-callbacks.patch b/0030-virtiofsd-get-set-features-callbacks.patch deleted file mode 100644 index cd07092..0000000 --- a/0030-virtiofsd-get-set-features-callbacks.patch +++ /dev/null @@ -1,50 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:00:59 +0000 -Subject: [PATCH] virtiofsd: get/set features callbacks -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add the get/set features callbacks. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit f2cef5fb9ae20136ca18d16328787b69b3abfa18) ---- - tools/virtiofsd/fuse_virtio.c | 15 ++++++++++++++- - 1 file changed, 14 insertions(+), 1 deletion(-) - -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index 1928a2025c..4819e56568 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -46,6 +46,17 @@ struct virtio_fs_config { - uint32_t num_queues; - }; - -+/* Callback from libvhost-user */ -+static uint64_t fv_get_features(VuDev *dev) -+{ -+ return 1ULL << VIRTIO_F_VERSION_1; -+} -+ -+/* Callback from libvhost-user */ -+static void fv_set_features(VuDev *dev, uint64_t features) -+{ -+} -+ - /* - * Callback from libvhost-user if there's a new fd we're supposed to listen - * to, typically a queue kick? -@@ -78,7 +89,9 @@ static bool fv_queue_order(VuDev *dev, int qidx) - } - - static const VuDevIface fv_iface = { -- /* TODO: Add other callbacks */ -+ .get_features = fv_get_features, -+ .set_features = fv_set_features, -+ - .queue_is_processed_in_order = fv_queue_order, - }; - diff --git a/0031-virtiofsd-Start-queue-threads.patch b/0031-virtiofsd-Start-queue-threads.patch deleted file mode 100644 index f463a47..0000000 --- a/0031-virtiofsd-Start-queue-threads.patch +++ /dev/null @@ -1,148 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:00 +0000 -Subject: [PATCH] virtiofsd: Start queue threads -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Start a thread for each queue when we get notified it's been started. - -Signed-off-by: Dr. David Alan Gilbert -fix by: -Signed-off-by: Jun Piao -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit e4c55a3c144493b436e40031e2eed61a84eca47b) ---- - tools/virtiofsd/fuse_virtio.c | 89 +++++++++++++++++++++++++++++++++++ - 1 file changed, 89 insertions(+) - -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index 4819e56568..2a94bb3cca 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -11,6 +11,7 @@ - * See the file COPYING.LIB - */ - -+#include "qemu/osdep.h" - #include "fuse_virtio.h" - #include "fuse_i.h" - #include "standard-headers/linux/fuse.h" -@@ -30,6 +31,15 @@ - - #include "contrib/libvhost-user/libvhost-user.h" - -+struct fv_QueueInfo { -+ pthread_t thread; -+ struct fv_VuDev *virtio_dev; -+ -+ /* Our queue index, corresponds to array position */ -+ int qidx; -+ int kick_fd; -+}; -+ - /* - * We pass the dev element into libvhost-user - * and then use it to get back to the outer -@@ -38,6 +48,13 @@ - struct fv_VuDev { - VuDev dev; - struct fuse_session *se; -+ -+ /* -+ * The following pair of fields are only accessed in the main -+ * virtio_loop -+ */ -+ size_t nqueues; -+ struct fv_QueueInfo **qi; - }; - - /* From spec */ -@@ -83,6 +100,75 @@ static void fv_panic(VuDev *dev, const char *err) - exit(EXIT_FAILURE); - } - -+static void *fv_queue_thread(void *opaque) -+{ -+ struct fv_QueueInfo *qi = opaque; -+ fuse_log(FUSE_LOG_INFO, "%s: Start for queue %d kick_fd %d\n", __func__, -+ qi->qidx, qi->kick_fd); -+ while (1) { -+ /* TODO */ -+ } -+ -+ return NULL; -+} -+ -+/* Callback from libvhost-user on start or stop of a queue */ -+static void fv_queue_set_started(VuDev *dev, int qidx, bool started) -+{ -+ struct fv_VuDev *vud = container_of(dev, struct fv_VuDev, dev); -+ struct fv_QueueInfo *ourqi; -+ -+ fuse_log(FUSE_LOG_INFO, "%s: qidx=%d started=%d\n", __func__, qidx, -+ started); -+ assert(qidx >= 0); -+ -+ /* -+ * Ignore additional request queues for now. passthrough_ll.c must be -+ * audited for thread-safety issues first. It was written with a -+ * well-behaved client in mind and may not protect against all types of -+ * races yet. -+ */ -+ if (qidx > 1) { -+ fuse_log(FUSE_LOG_ERR, -+ "%s: multiple request queues not yet implemented, please only " -+ "configure 1 request queue\n", -+ __func__); -+ exit(EXIT_FAILURE); -+ } -+ -+ if (started) { -+ /* Fire up a thread to watch this queue */ -+ if (qidx >= vud->nqueues) { -+ vud->qi = realloc(vud->qi, (qidx + 1) * sizeof(vud->qi[0])); -+ assert(vud->qi); -+ memset(vud->qi + vud->nqueues, 0, -+ sizeof(vud->qi[0]) * (1 + (qidx - vud->nqueues))); -+ vud->nqueues = qidx + 1; -+ } -+ if (!vud->qi[qidx]) { -+ vud->qi[qidx] = calloc(sizeof(struct fv_QueueInfo), 1); -+ assert(vud->qi[qidx]); -+ vud->qi[qidx]->virtio_dev = vud; -+ vud->qi[qidx]->qidx = qidx; -+ } else { -+ /* Shouldn't have been started */ -+ assert(vud->qi[qidx]->kick_fd == -1); -+ } -+ ourqi = vud->qi[qidx]; -+ ourqi->kick_fd = dev->vq[qidx].kick_fd; -+ if (pthread_create(&ourqi->thread, NULL, fv_queue_thread, ourqi)) { -+ fuse_log(FUSE_LOG_ERR, "%s: Failed to create thread for queue %d\n", -+ __func__, qidx); -+ assert(0); -+ } -+ } else { -+ /* TODO: Kill the thread */ -+ assert(qidx < vud->nqueues); -+ ourqi = vud->qi[qidx]; -+ ourqi->kick_fd = -1; -+ } -+} -+ - static bool fv_queue_order(VuDev *dev, int qidx) - { - return false; -@@ -92,6 +178,9 @@ static const VuDevIface fv_iface = { - .get_features = fv_get_features, - .set_features = fv_set_features, - -+ /* Don't need process message, we've not got any at vhost-user level */ -+ .queue_set_started = fv_queue_set_started, -+ - .queue_is_processed_in_order = fv_queue_order, - }; - diff --git a/0032-virtiofsd-Poll-kick_fd-for-queue.patch b/0032-virtiofsd-Poll-kick_fd-for-queue.patch deleted file mode 100644 index 085ca83..0000000 --- a/0032-virtiofsd-Poll-kick_fd-for-queue.patch +++ /dev/null @@ -1,81 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:01 +0000 -Subject: [PATCH] virtiofsd: Poll kick_fd for queue -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -In the queue thread poll the kick_fd we're passed. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 5dcd1f56141378226d33dc3df68ec57913e0aa04) ---- - tools/virtiofsd/fuse_virtio.c | 40 ++++++++++++++++++++++++++++++++++- - 1 file changed, 39 insertions(+), 1 deletion(-) - -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index 2a94bb3cca..05e7258712 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -24,6 +24,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -100,13 +101,50 @@ static void fv_panic(VuDev *dev, const char *err) - exit(EXIT_FAILURE); - } - -+/* Thread function for individual queues, created when a queue is 'started' */ - static void *fv_queue_thread(void *opaque) - { - struct fv_QueueInfo *qi = opaque; - fuse_log(FUSE_LOG_INFO, "%s: Start for queue %d kick_fd %d\n", __func__, - qi->qidx, qi->kick_fd); - while (1) { -- /* TODO */ -+ struct pollfd pf[1]; -+ pf[0].fd = qi->kick_fd; -+ pf[0].events = POLLIN; -+ pf[0].revents = 0; -+ -+ fuse_log(FUSE_LOG_DEBUG, "%s: Waiting for Queue %d event\n", __func__, -+ qi->qidx); -+ int poll_res = ppoll(pf, 1, NULL, NULL); -+ -+ if (poll_res == -1) { -+ if (errno == EINTR) { -+ fuse_log(FUSE_LOG_INFO, "%s: ppoll interrupted, going around\n", -+ __func__); -+ continue; -+ } -+ fuse_log(FUSE_LOG_ERR, "fv_queue_thread ppoll: %m\n"); -+ break; -+ } -+ assert(poll_res == 1); -+ if (pf[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { -+ fuse_log(FUSE_LOG_ERR, "%s: Unexpected poll revents %x Queue %d\n", -+ __func__, pf[0].revents, qi->qidx); -+ break; -+ } -+ assert(pf[0].revents & POLLIN); -+ fuse_log(FUSE_LOG_DEBUG, "%s: Got queue event on Queue %d\n", __func__, -+ qi->qidx); -+ -+ eventfd_t evalue; -+ if (eventfd_read(qi->kick_fd, &evalue)) { -+ fuse_log(FUSE_LOG_ERR, "Eventfd_read for queue: %m\n"); -+ break; -+ } -+ if (qi->virtio_dev->se->debug) { -+ fprintf(stderr, "%s: Queue %d gave evalue: %zx\n", __func__, -+ qi->qidx, (size_t)evalue); -+ } - } - - return NULL; diff --git a/0033-virtiofsd-Start-reading-commands-from-queue.patch b/0033-virtiofsd-Start-reading-commands-from-queue.patch deleted file mode 100644 index 8b652e5..0000000 --- a/0033-virtiofsd-Start-reading-commands-from-queue.patch +++ /dev/null @@ -1,184 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:02 +0000 -Subject: [PATCH] virtiofsd: Start reading commands from queue -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Pop queue elements off queues, copy the data from them and -pass that to fuse. - - Note: 'out' in a VuVirtqElement is from QEMU - 'in' in libfuse is into the daemon - - So we read from the out iov's to get a fuse_in_header - -When we get a kick we've got to read all the elements until the queue -is empty. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit b509e1228b3e5eb83c14819045988999fc2dbd1b) ---- - tools/virtiofsd/fuse_i.h | 2 + - tools/virtiofsd/fuse_virtio.c | 99 +++++++++++++++++++++++++++++++++-- - 2 files changed, 98 insertions(+), 3 deletions(-) - -diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h -index ec04449069..1126723d18 100644 ---- a/tools/virtiofsd/fuse_i.h -+++ b/tools/virtiofsd/fuse_i.h -@@ -14,6 +14,7 @@ - #include "fuse_lowlevel.h" - - struct fv_VuDev; -+struct fv_QueueInfo; - - struct fuse_req { - struct fuse_session *se; -@@ -75,6 +76,7 @@ struct fuse_chan { - pthread_mutex_t lock; - int ctr; - int fd; -+ struct fv_QueueInfo *qi; - }; - - /** -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index 05e7258712..3841b20129 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -12,6 +12,7 @@ - */ - - #include "qemu/osdep.h" -+#include "qemu/iov.h" - #include "fuse_virtio.h" - #include "fuse_i.h" - #include "standard-headers/linux/fuse.h" -@@ -32,6 +33,7 @@ - - #include "contrib/libvhost-user/libvhost-user.h" - -+struct fv_VuDev; - struct fv_QueueInfo { - pthread_t thread; - struct fv_VuDev *virtio_dev; -@@ -101,10 +103,41 @@ static void fv_panic(VuDev *dev, const char *err) - exit(EXIT_FAILURE); - } - -+/* -+ * Copy from an iovec into a fuse_buf (memory only) -+ * Caller must ensure there is space -+ */ -+static void copy_from_iov(struct fuse_buf *buf, size_t out_num, -+ const struct iovec *out_sg) -+{ -+ void *dest = buf->mem; -+ -+ while (out_num) { -+ size_t onelen = out_sg->iov_len; -+ memcpy(dest, out_sg->iov_base, onelen); -+ dest += onelen; -+ out_sg++; -+ out_num--; -+ } -+} -+ - /* Thread function for individual queues, created when a queue is 'started' */ - static void *fv_queue_thread(void *opaque) - { - struct fv_QueueInfo *qi = opaque; -+ struct VuDev *dev = &qi->virtio_dev->dev; -+ struct VuVirtq *q = vu_get_queue(dev, qi->qidx); -+ struct fuse_session *se = qi->virtio_dev->se; -+ struct fuse_chan ch; -+ struct fuse_buf fbuf; -+ -+ fbuf.mem = NULL; -+ fbuf.flags = 0; -+ -+ fuse_mutex_init(&ch.lock); -+ ch.fd = (int)0xdaff0d111; -+ ch.qi = qi; -+ - fuse_log(FUSE_LOG_INFO, "%s: Start for queue %d kick_fd %d\n", __func__, - qi->qidx, qi->kick_fd); - while (1) { -@@ -141,11 +174,71 @@ static void *fv_queue_thread(void *opaque) - fuse_log(FUSE_LOG_ERR, "Eventfd_read for queue: %m\n"); - break; - } -- if (qi->virtio_dev->se->debug) { -- fprintf(stderr, "%s: Queue %d gave evalue: %zx\n", __func__, -- qi->qidx, (size_t)evalue); -+ /* out is from guest, in is too guest */ -+ unsigned int in_bytes, out_bytes; -+ vu_queue_get_avail_bytes(dev, q, &in_bytes, &out_bytes, ~0, ~0); -+ -+ fuse_log(FUSE_LOG_DEBUG, -+ "%s: Queue %d gave evalue: %zx available: in: %u out: %u\n", -+ __func__, qi->qidx, (size_t)evalue, in_bytes, out_bytes); -+ -+ while (1) { -+ /* -+ * An element contains one request and the space to send our -+ * response They're spread over multiple descriptors in a -+ * scatter/gather set and we can't trust the guest to keep them -+ * still; so copy in/out. -+ */ -+ VuVirtqElement *elem = vu_queue_pop(dev, q, sizeof(VuVirtqElement)); -+ if (!elem) { -+ break; -+ } -+ -+ if (!fbuf.mem) { -+ fbuf.mem = malloc(se->bufsize); -+ assert(fbuf.mem); -+ assert(se->bufsize > sizeof(struct fuse_in_header)); -+ } -+ /* The 'out' part of the elem is from qemu */ -+ unsigned int out_num = elem->out_num; -+ struct iovec *out_sg = elem->out_sg; -+ size_t out_len = iov_size(out_sg, out_num); -+ fuse_log(FUSE_LOG_DEBUG, -+ "%s: elem %d: with %d out desc of length %zd\n", __func__, -+ elem->index, out_num, out_len); -+ -+ /* -+ * The elem should contain a 'fuse_in_header' (in to fuse) -+ * plus the data based on the len in the header. -+ */ -+ if (out_len < sizeof(struct fuse_in_header)) { -+ fuse_log(FUSE_LOG_ERR, "%s: elem %d too short for in_header\n", -+ __func__, elem->index); -+ assert(0); /* TODO */ -+ } -+ if (out_len > se->bufsize) { -+ fuse_log(FUSE_LOG_ERR, "%s: elem %d too large for buffer\n", -+ __func__, elem->index); -+ assert(0); /* TODO */ -+ } -+ copy_from_iov(&fbuf, out_num, out_sg); -+ fbuf.size = out_len; -+ -+ /* TODO! Endianness of header */ -+ -+ /* TODO: Fixup fuse_send_msg */ -+ /* TODO: Add checks for fuse_session_exited */ -+ fuse_session_process_buf_int(se, &fbuf, &ch); -+ -+ /* TODO: vu_queue_push(dev, q, elem, qi->write_count); */ -+ vu_queue_notify(dev, q); -+ -+ free(elem); -+ elem = NULL; - } - } -+ pthread_mutex_destroy(&ch.lock); -+ free(fbuf.mem); - - return NULL; - } diff --git a/0034-virtiofsd-Send-replies-to-messages.patch b/0034-virtiofsd-Send-replies-to-messages.patch deleted file mode 100644 index 66b9376..0000000 --- a/0034-virtiofsd-Send-replies-to-messages.patch +++ /dev/null @@ -1,183 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:03 +0000 -Subject: [PATCH] virtiofsd: Send replies to messages -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Route fuse out messages back through the same queue elements -that had the command that triggered the request. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit df57ba919ec3edef9cc208d35685095e6e92713e) ---- - tools/virtiofsd/fuse_lowlevel.c | 4 ++ - tools/virtiofsd/fuse_virtio.c | 107 ++++++++++++++++++++++++++++++-- - tools/virtiofsd/fuse_virtio.h | 4 ++ - 3 files changed, 111 insertions(+), 4 deletions(-) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index af09fa2b94..380d93bd01 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -171,6 +171,10 @@ static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, - } - } - -+ if (fuse_lowlevel_is_virtio(se)) { -+ return virtio_send_msg(se, ch, iov, count); -+ } -+ - abort(); /* virtio should have taken it before here */ - return 0; - } -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index 3841b20129..05d0e29f12 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -41,6 +41,9 @@ struct fv_QueueInfo { - /* Our queue index, corresponds to array position */ - int qidx; - int kick_fd; -+ -+ /* The element for the command currently being processed */ -+ VuVirtqElement *qe; - }; - - /* -@@ -121,6 +124,105 @@ static void copy_from_iov(struct fuse_buf *buf, size_t out_num, - } - } - -+/* -+ * Copy from one iov to another, the given number of bytes -+ * The caller must have checked sizes. -+ */ -+static void copy_iov(struct iovec *src_iov, int src_count, -+ struct iovec *dst_iov, int dst_count, size_t to_copy) -+{ -+ size_t dst_offset = 0; -+ /* Outer loop copies 'src' elements */ -+ while (to_copy) { -+ assert(src_count); -+ size_t src_len = src_iov[0].iov_len; -+ size_t src_offset = 0; -+ -+ if (src_len > to_copy) { -+ src_len = to_copy; -+ } -+ /* Inner loop copies contents of one 'src' to maybe multiple dst. */ -+ while (src_len) { -+ assert(dst_count); -+ size_t dst_len = dst_iov[0].iov_len - dst_offset; -+ if (dst_len > src_len) { -+ dst_len = src_len; -+ } -+ -+ memcpy(dst_iov[0].iov_base + dst_offset, -+ src_iov[0].iov_base + src_offset, dst_len); -+ src_len -= dst_len; -+ to_copy -= dst_len; -+ src_offset += dst_len; -+ dst_offset += dst_len; -+ -+ assert(dst_offset <= dst_iov[0].iov_len); -+ if (dst_offset == dst_iov[0].iov_len) { -+ dst_offset = 0; -+ dst_iov++; -+ dst_count--; -+ } -+ } -+ src_iov++; -+ src_count--; -+ } -+} -+ -+/* -+ * Called back by ll whenever it wants to send a reply/message back -+ * The 1st element of the iov starts with the fuse_out_header -+ * 'unique'==0 means it's a notify message. -+ */ -+int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, -+ struct iovec *iov, int count) -+{ -+ VuVirtqElement *elem; -+ VuVirtq *q; -+ -+ assert(count >= 1); -+ assert(iov[0].iov_len >= sizeof(struct fuse_out_header)); -+ -+ struct fuse_out_header *out = iov[0].iov_base; -+ /* TODO: Endianness! */ -+ -+ size_t tosend_len = iov_size(iov, count); -+ -+ /* unique == 0 is notification, which we don't support */ -+ assert(out->unique); -+ /* For virtio we always have ch */ -+ assert(ch); -+ elem = ch->qi->qe; -+ q = &ch->qi->virtio_dev->dev.vq[ch->qi->qidx]; -+ -+ /* The 'in' part of the elem is to qemu */ -+ unsigned int in_num = elem->in_num; -+ struct iovec *in_sg = elem->in_sg; -+ size_t in_len = iov_size(in_sg, in_num); -+ fuse_log(FUSE_LOG_DEBUG, "%s: elem %d: with %d in desc of length %zd\n", -+ __func__, elem->index, in_num, in_len); -+ -+ /* -+ * The elem should have room for a 'fuse_out_header' (out from fuse) -+ * plus the data based on the len in the header. -+ */ -+ if (in_len < sizeof(struct fuse_out_header)) { -+ fuse_log(FUSE_LOG_ERR, "%s: elem %d too short for out_header\n", -+ __func__, elem->index); -+ return -E2BIG; -+ } -+ if (in_len < tosend_len) { -+ fuse_log(FUSE_LOG_ERR, "%s: elem %d too small for data len %zd\n", -+ __func__, elem->index, tosend_len); -+ return -E2BIG; -+ } -+ -+ copy_iov(iov, count, in_sg, in_num, tosend_len); -+ vu_queue_push(&se->virtio_dev->dev, q, elem, tosend_len); -+ vu_queue_notify(&se->virtio_dev->dev, q); -+ -+ return 0; -+} -+ - /* Thread function for individual queues, created when a queue is 'started' */ - static void *fv_queue_thread(void *opaque) - { -@@ -226,13 +328,10 @@ static void *fv_queue_thread(void *opaque) - - /* TODO! Endianness of header */ - -- /* TODO: Fixup fuse_send_msg */ - /* TODO: Add checks for fuse_session_exited */ - fuse_session_process_buf_int(se, &fbuf, &ch); - -- /* TODO: vu_queue_push(dev, q, elem, qi->write_count); */ -- vu_queue_notify(dev, q); -- -+ qi->qe = NULL; - free(elem); - elem = NULL; - } -diff --git a/tools/virtiofsd/fuse_virtio.h b/tools/virtiofsd/fuse_virtio.h -index 23026d6e4c..135a14875a 100644 ---- a/tools/virtiofsd/fuse_virtio.h -+++ b/tools/virtiofsd/fuse_virtio.h -@@ -22,4 +22,8 @@ int virtio_session_mount(struct fuse_session *se); - - int virtio_loop(struct fuse_session *se); - -+ -+int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, -+ struct iovec *iov, int count); -+ - #endif diff --git a/0035-virtiofsd-Keep-track-of-replies.patch b/0035-virtiofsd-Keep-track-of-replies.patch deleted file mode 100644 index 874a16d..0000000 --- a/0035-virtiofsd-Keep-track-of-replies.patch +++ /dev/null @@ -1,100 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:04 +0000 -Subject: [PATCH] virtiofsd: Keep track of replies -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Keep track of whether we sent a reply to a request; this is a bit -paranoid but it means: - a) We should always recycle an element even if there was an error - in the request - b) Never try and send two replies on one queue element - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 2f65e69a7f22da8d20c747f34f339ebb40a0634f) ---- - tools/virtiofsd/fuse_virtio.c | 23 ++++++++++++++++++++--- - 1 file changed, 20 insertions(+), 3 deletions(-) - -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index 05d0e29f12..f1adeb6345 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -44,6 +44,7 @@ struct fv_QueueInfo { - - /* The element for the command currently being processed */ - VuVirtqElement *qe; -+ bool reply_sent; - }; - - /* -@@ -178,6 +179,7 @@ int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, - { - VuVirtqElement *elem; - VuVirtq *q; -+ int ret = 0; - - assert(count >= 1); - assert(iov[0].iov_len >= sizeof(struct fuse_out_header)); -@@ -191,6 +193,7 @@ int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, - assert(out->unique); - /* For virtio we always have ch */ - assert(ch); -+ assert(!ch->qi->reply_sent); - elem = ch->qi->qe; - q = &ch->qi->virtio_dev->dev.vq[ch->qi->qidx]; - -@@ -208,19 +211,23 @@ int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, - if (in_len < sizeof(struct fuse_out_header)) { - fuse_log(FUSE_LOG_ERR, "%s: elem %d too short for out_header\n", - __func__, elem->index); -- return -E2BIG; -+ ret = -E2BIG; -+ goto err; - } - if (in_len < tosend_len) { - fuse_log(FUSE_LOG_ERR, "%s: elem %d too small for data len %zd\n", - __func__, elem->index, tosend_len); -- return -E2BIG; -+ ret = -E2BIG; -+ goto err; - } - - copy_iov(iov, count, in_sg, in_num, tosend_len); - vu_queue_push(&se->virtio_dev->dev, q, elem, tosend_len); - vu_queue_notify(&se->virtio_dev->dev, q); -+ ch->qi->reply_sent = true; - -- return 0; -+err: -+ return ret; - } - - /* Thread function for individual queues, created when a queue is 'started' */ -@@ -296,6 +303,9 @@ static void *fv_queue_thread(void *opaque) - break; - } - -+ qi->qe = elem; -+ qi->reply_sent = false; -+ - if (!fbuf.mem) { - fbuf.mem = malloc(se->bufsize); - assert(fbuf.mem); -@@ -331,6 +341,13 @@ static void *fv_queue_thread(void *opaque) - /* TODO: Add checks for fuse_session_exited */ - fuse_session_process_buf_int(se, &fbuf, &ch); - -+ if (!qi->reply_sent) { -+ fuse_log(FUSE_LOG_DEBUG, "%s: elem %d no reply sent\n", -+ __func__, elem->index); -+ /* I think we've still got to recycle the element */ -+ vu_queue_push(dev, q, elem, 0); -+ vu_queue_notify(dev, q); -+ } - qi->qe = NULL; - free(elem); - elem = NULL; diff --git a/0036-virtiofsd-Add-Makefile-wiring-for-virtiofsd-contrib.patch b/0036-virtiofsd-Add-Makefile-wiring-for-virtiofsd-contrib.patch deleted file mode 100644 index a52d5dd..0000000 --- a/0036-virtiofsd-Add-Makefile-wiring-for-virtiofsd-contrib.patch +++ /dev/null @@ -1,90 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:05 +0000 -Subject: [PATCH] virtiofsd: Add Makefile wiring for virtiofsd contrib -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Wire up the building of the virtiofsd in tools. - -virtiofsd relies on Linux-specific system calls and seccomp. Anyone -wishing to port it to other host operating systems should do so -carefully and without reducing security. - -Only allow building on Linux hosts. - -Signed-off-by: Dr. David Alan Gilbert -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Liam Merwick -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 81bfc42dcf473bc8d3790622633410da72d8e622) ---- - Makefile | 10 ++++++++++ - Makefile.objs | 1 + - tools/virtiofsd/Makefile.objs | 9 +++++++++ - 3 files changed, 20 insertions(+) - create mode 100644 tools/virtiofsd/Makefile.objs - -diff --git a/Makefile b/Makefile -index b437a346d7..10fd31e705 100644 ---- a/Makefile -+++ b/Makefile -@@ -330,6 +330,10 @@ endif - endif - endif - -+ifdef CONFIG_LINUX -+HELPERS-y += virtiofsd$(EXESUF) -+endif -+ - # Sphinx does not allow building manuals into the same directory as - # the source files, so if we're doing an in-tree QEMU build we must - # build the manuals into a subdirectory (and then install them from -@@ -430,6 +434,7 @@ dummy := $(call unnest-vars,, \ - elf2dmp-obj-y \ - ivshmem-client-obj-y \ - ivshmem-server-obj-y \ -+ virtiofsd-obj-y \ - rdmacm-mux-obj-y \ - libvhost-user-obj-y \ - vhost-user-scsi-obj-y \ -@@ -674,6 +679,11 @@ rdmacm-mux$(EXESUF): LIBS += "-libumad" - rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) - $(call LINK, $^) - -+ifdef CONFIG_LINUX # relies on Linux-specific syscalls -+virtiofsd$(EXESUF): $(virtiofsd-obj-y) libvhost-user.a $(COMMON_LDADDS) -+ $(call LINK, $^) -+endif -+ - vhost-user-gpu$(EXESUF): $(vhost-user-gpu-obj-y) $(libvhost-user-obj-y) libqemuutil.a libqemustub.a - $(call LINK, $^) - -diff --git a/Makefile.objs b/Makefile.objs -index 11ba1a36bd..b5f667a4ba 100644 ---- a/Makefile.objs -+++ b/Makefile.objs -@@ -125,6 +125,7 @@ vhost-user-blk-obj-y = contrib/vhost-user-blk/ - rdmacm-mux-obj-y = contrib/rdmacm-mux/ - vhost-user-input-obj-y = contrib/vhost-user-input/ - vhost-user-gpu-obj-y = contrib/vhost-user-gpu/ -+virtiofsd-obj-y = tools/virtiofsd/ - - ###################################################################### - trace-events-subdirs = -diff --git a/tools/virtiofsd/Makefile.objs b/tools/virtiofsd/Makefile.objs -new file mode 100644 -index 0000000000..45a807500d ---- /dev/null -+++ b/tools/virtiofsd/Makefile.objs -@@ -0,0 +1,9 @@ -+virtiofsd-obj-y = buffer.o \ -+ fuse_opt.o \ -+ fuse_log.o \ -+ fuse_lowlevel.o \ -+ fuse_signals.o \ -+ fuse_virtio.o \ -+ helper.o \ -+ passthrough_ll.o -+ diff --git a/0037-virtiofsd-Fast-path-for-virtio-read.patch b/0037-virtiofsd-Fast-path-for-virtio-read.patch deleted file mode 100644 index 200f1da..0000000 --- a/0037-virtiofsd-Fast-path-for-virtio-read.patch +++ /dev/null @@ -1,220 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:06 +0000 -Subject: [PATCH] virtiofsd: Fast path for virtio read - -Readv the data straight into the guests buffer. - -Signed-off-by: Dr. David Alan Gilbert -With fix by: -Signed-off-by: Eryu Guan -Reviewed-by: Masayoshi Mizuma -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit eb49d187ef5134483a34c970bbfece28aaa686a7) ---- - tools/virtiofsd/fuse_lowlevel.c | 5 + - tools/virtiofsd/fuse_virtio.c | 162 ++++++++++++++++++++++++++++++++ - tools/virtiofsd/fuse_virtio.h | 4 + - 3 files changed, 171 insertions(+) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 380d93bd01..4f4684d942 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -475,6 +475,11 @@ static int fuse_send_data_iov_fallback(struct fuse_session *se, - return fuse_send_msg(se, ch, iov, iov_count); - } - -+ if (fuse_lowlevel_is_virtio(se) && buf->count == 1 && -+ buf->buf[0].flags == (FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK)) { -+ return virtio_send_data_iov(se, ch, iov, iov_count, buf, len); -+ } -+ - abort(); /* Will have taken vhost path */ - return 0; - } -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index f1adeb6345..7e2711b504 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -230,6 +230,168 @@ err: - return ret; - } - -+/* -+ * Callback from fuse_send_data_iov_* when it's virtio and the buffer -+ * is a single FD with FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK -+ * We need send the iov and then the buffer. -+ * Return 0 on success -+ */ -+int virtio_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, -+ struct iovec *iov, int count, struct fuse_bufvec *buf, -+ size_t len) -+{ -+ int ret = 0; -+ VuVirtqElement *elem; -+ VuVirtq *q; -+ -+ assert(count >= 1); -+ assert(iov[0].iov_len >= sizeof(struct fuse_out_header)); -+ -+ struct fuse_out_header *out = iov[0].iov_base; -+ /* TODO: Endianness! */ -+ -+ size_t iov_len = iov_size(iov, count); -+ size_t tosend_len = iov_len + len; -+ -+ out->len = tosend_len; -+ -+ fuse_log(FUSE_LOG_DEBUG, "%s: count=%d len=%zd iov_len=%zd\n", __func__, -+ count, len, iov_len); -+ -+ /* unique == 0 is notification which we don't support */ -+ assert(out->unique); -+ -+ /* For virtio we always have ch */ -+ assert(ch); -+ assert(!ch->qi->reply_sent); -+ elem = ch->qi->qe; -+ q = &ch->qi->virtio_dev->dev.vq[ch->qi->qidx]; -+ -+ /* The 'in' part of the elem is to qemu */ -+ unsigned int in_num = elem->in_num; -+ struct iovec *in_sg = elem->in_sg; -+ size_t in_len = iov_size(in_sg, in_num); -+ fuse_log(FUSE_LOG_DEBUG, "%s: elem %d: with %d in desc of length %zd\n", -+ __func__, elem->index, in_num, in_len); -+ -+ /* -+ * The elem should have room for a 'fuse_out_header' (out from fuse) -+ * plus the data based on the len in the header. -+ */ -+ if (in_len < sizeof(struct fuse_out_header)) { -+ fuse_log(FUSE_LOG_ERR, "%s: elem %d too short for out_header\n", -+ __func__, elem->index); -+ ret = E2BIG; -+ goto err; -+ } -+ if (in_len < tosend_len) { -+ fuse_log(FUSE_LOG_ERR, "%s: elem %d too small for data len %zd\n", -+ __func__, elem->index, tosend_len); -+ ret = E2BIG; -+ goto err; -+ } -+ -+ /* TODO: Limit to 'len' */ -+ -+ /* First copy the header data from iov->in_sg */ -+ copy_iov(iov, count, in_sg, in_num, iov_len); -+ -+ /* -+ * Build a copy of the the in_sg iov so we can skip bits in it, -+ * including changing the offsets -+ */ -+ struct iovec *in_sg_cpy = calloc(sizeof(struct iovec), in_num); -+ assert(in_sg_cpy); -+ memcpy(in_sg_cpy, in_sg, sizeof(struct iovec) * in_num); -+ /* These get updated as we skip */ -+ struct iovec *in_sg_ptr = in_sg_cpy; -+ int in_sg_cpy_count = in_num; -+ -+ /* skip over parts of in_sg that contained the header iov */ -+ size_t skip_size = iov_len; -+ -+ size_t in_sg_left = 0; -+ do { -+ while (skip_size != 0 && in_sg_cpy_count) { -+ if (skip_size >= in_sg_ptr[0].iov_len) { -+ skip_size -= in_sg_ptr[0].iov_len; -+ in_sg_ptr++; -+ in_sg_cpy_count--; -+ } else { -+ in_sg_ptr[0].iov_len -= skip_size; -+ in_sg_ptr[0].iov_base += skip_size; -+ break; -+ } -+ } -+ -+ int i; -+ for (i = 0, in_sg_left = 0; i < in_sg_cpy_count; i++) { -+ in_sg_left += in_sg_ptr[i].iov_len; -+ } -+ fuse_log(FUSE_LOG_DEBUG, -+ "%s: after skip skip_size=%zd in_sg_cpy_count=%d " -+ "in_sg_left=%zd\n", -+ __func__, skip_size, in_sg_cpy_count, in_sg_left); -+ ret = preadv(buf->buf[0].fd, in_sg_ptr, in_sg_cpy_count, -+ buf->buf[0].pos); -+ -+ if (ret == -1) { -+ ret = errno; -+ fuse_log(FUSE_LOG_DEBUG, "%s: preadv failed (%m) len=%zd\n", -+ __func__, len); -+ free(in_sg_cpy); -+ goto err; -+ } -+ fuse_log(FUSE_LOG_DEBUG, "%s: preadv ret=%d len=%zd\n", __func__, -+ ret, len); -+ if (ret < len && ret) { -+ fuse_log(FUSE_LOG_DEBUG, "%s: ret < len\n", __func__); -+ /* Skip over this much next time around */ -+ skip_size = ret; -+ buf->buf[0].pos += ret; -+ len -= ret; -+ -+ /* Lets do another read */ -+ continue; -+ } -+ if (!ret) { -+ /* EOF case? */ -+ fuse_log(FUSE_LOG_DEBUG, "%s: !ret in_sg_left=%zd\n", __func__, -+ in_sg_left); -+ break; -+ } -+ if (ret != len) { -+ fuse_log(FUSE_LOG_DEBUG, "%s: ret!=len\n", __func__); -+ ret = EIO; -+ free(in_sg_cpy); -+ goto err; -+ } -+ in_sg_left -= ret; -+ len -= ret; -+ } while (in_sg_left); -+ free(in_sg_cpy); -+ -+ /* Need to fix out->len on EOF */ -+ if (len) { -+ struct fuse_out_header *out_sg = in_sg[0].iov_base; -+ -+ tosend_len -= len; -+ out_sg->len = tosend_len; -+ } -+ -+ ret = 0; -+ -+ vu_queue_push(&se->virtio_dev->dev, q, elem, tosend_len); -+ vu_queue_notify(&se->virtio_dev->dev, q); -+ -+err: -+ if (ret == 0) { -+ ch->qi->reply_sent = true; -+ } -+ -+ return ret; -+} -+ - /* Thread function for individual queues, created when a queue is 'started' */ - static void *fv_queue_thread(void *opaque) - { -diff --git a/tools/virtiofsd/fuse_virtio.h b/tools/virtiofsd/fuse_virtio.h -index 135a14875a..cc676b9193 100644 ---- a/tools/virtiofsd/fuse_virtio.h -+++ b/tools/virtiofsd/fuse_virtio.h -@@ -26,4 +26,8 @@ int virtio_loop(struct fuse_session *se); - int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, - struct iovec *iov, int count); - -+int virtio_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, -+ struct iovec *iov, int count, -+ struct fuse_bufvec *buf, size_t len); -+ - #endif diff --git a/0038-virtiofsd-add-fd-FDNUM-fd-passing-option.patch b/0038-virtiofsd-add-fd-FDNUM-fd-passing-option.patch deleted file mode 100644 index 2947b73..0000000 --- a/0038-virtiofsd-add-fd-FDNUM-fd-passing-option.patch +++ /dev/null @@ -1,154 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:07 +0000 -Subject: [PATCH] virtiofsd: add --fd=FDNUM fd passing option -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Although --socket-path=PATH is useful for manual invocations, management -tools typically create the UNIX domain socket themselves and pass it to -the vhost-user device backend. This way QEMU can be launched -immediately with a valid socket. No waiting for the vhost-user device -backend is required when fd passing is used. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit cee8e35d4386e34bf79c3ca2aab7f7b1bb48cf8d) ---- - tools/virtiofsd/fuse_i.h | 1 + - tools/virtiofsd/fuse_lowlevel.c | 16 ++++++++++++---- - tools/virtiofsd/fuse_virtio.c | 31 +++++++++++++++++++++++++------ - 3 files changed, 38 insertions(+), 10 deletions(-) - -diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h -index 1126723d18..45995f3246 100644 ---- a/tools/virtiofsd/fuse_i.h -+++ b/tools/virtiofsd/fuse_i.h -@@ -68,6 +68,7 @@ struct fuse_session { - size_t bufsize; - int error; - char *vu_socket_path; -+ int vu_listen_fd; - int vu_socketfd; - struct fv_VuDev *virtio_dev; - }; -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 4f4684d942..95f4db8fcf 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -2130,6 +2130,7 @@ static const struct fuse_opt fuse_ll_opts[] = { - LL_OPTION("--debug", debug, 1), - LL_OPTION("allow_root", deny_others, 1), - LL_OPTION("--socket-path=%s", vu_socket_path, 0), -+ LL_OPTION("--fd=%d", vu_listen_fd, 0), - FUSE_OPT_END - }; - -@@ -2147,7 +2148,8 @@ void fuse_lowlevel_help(void) - */ - printf( - " -o allow_root allow access by root\n" -- " --socket-path=PATH path for the vhost-user socket\n"); -+ " --socket-path=PATH path for the vhost-user socket\n" -+ " --fd=FDNUM fd number of vhost-user socket\n"); - } - - void fuse_session_destroy(struct fuse_session *se) -@@ -2191,6 +2193,7 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, - goto out1; - } - se->fd = -1; -+ se->vu_listen_fd = -1; - se->conn.max_write = UINT_MAX; - se->conn.max_readahead = UINT_MAX; - -@@ -2212,8 +2215,13 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, - goto out4; - } - -- if (!se->vu_socket_path) { -- fprintf(stderr, "fuse: missing -o vhost_user_socket option\n"); -+ if (!se->vu_socket_path && se->vu_listen_fd < 0) { -+ fuse_log(FUSE_LOG_ERR, "fuse: missing --socket-path or --fd option\n"); -+ goto out4; -+ } -+ if (se->vu_socket_path && se->vu_listen_fd >= 0) { -+ fuse_log(FUSE_LOG_ERR, -+ "fuse: --socket-path and --fd cannot be given together\n"); - goto out4; - } - -@@ -2253,7 +2261,7 @@ void fuse_session_unmount(struct fuse_session *se) - - int fuse_lowlevel_is_virtio(struct fuse_session *se) - { -- return se->vu_socket_path != NULL; -+ return !!se->virtio_dev; - } - - #ifdef linux -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index 7e2711b504..635f87756a 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -638,18 +638,21 @@ int virtio_loop(struct fuse_session *se) - return 0; - } - --int virtio_session_mount(struct fuse_session *se) -+static int fv_create_listen_socket(struct fuse_session *se) - { - struct sockaddr_un un; - mode_t old_umask; - -+ /* Nothing to do if fd is already initialized */ -+ if (se->vu_listen_fd >= 0) { -+ return 0; -+ } -+ - if (strlen(se->vu_socket_path) >= sizeof(un.sun_path)) { - fuse_log(FUSE_LOG_ERR, "Socket path too long\n"); - return -1; - } - -- se->fd = -1; -- - /* - * Create the Unix socket to communicate with qemu - * based on QEMU's vhost-user-bridge -@@ -682,15 +685,31 @@ int virtio_session_mount(struct fuse_session *se) - return -1; - } - -+ se->vu_listen_fd = listen_sock; -+ return 0; -+} -+ -+int virtio_session_mount(struct fuse_session *se) -+{ -+ int ret; -+ -+ ret = fv_create_listen_socket(se); -+ if (ret < 0) { -+ return ret; -+ } -+ -+ se->fd = -1; -+ - fuse_log(FUSE_LOG_INFO, "%s: Waiting for vhost-user socket connection...\n", - __func__); -- int data_sock = accept(listen_sock, NULL, NULL); -+ int data_sock = accept(se->vu_listen_fd, NULL, NULL); - if (data_sock == -1) { - fuse_log(FUSE_LOG_ERR, "vhost socket accept: %m\n"); -- close(listen_sock); -+ close(se->vu_listen_fd); - return -1; - } -- close(listen_sock); -+ close(se->vu_listen_fd); -+ se->vu_listen_fd = -1; - fuse_log(FUSE_LOG_INFO, "%s: Received vhost-user socket connection\n", - __func__); - diff --git a/0039-virtiofsd-make-f-foreground-the-default.patch b/0039-virtiofsd-make-f-foreground-the-default.patch deleted file mode 100644 index 2712afc..0000000 --- a/0039-virtiofsd-make-f-foreground-the-default.patch +++ /dev/null @@ -1,60 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:08 +0000 -Subject: [PATCH] virtiofsd: make -f (foreground) the default -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -According to vhost-user.rst "Backend program conventions", backend -programs should run in the foregound by default. Follow the -conventions so libvirt and other management tools can control virtiofsd -in a standard way. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 0bbd31753714ac2899efda0f0de31e353e965789) ---- - tools/virtiofsd/helper.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index 676032e71f..a3645fc807 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -29,6 +29,11 @@ - { \ - t, offsetof(struct fuse_cmdline_opts, p), 1 \ - } -+#define FUSE_HELPER_OPT_VALUE(t, p, v) \ -+ { \ -+ t, offsetof(struct fuse_cmdline_opts, p), v \ -+ } -+ - - static const struct fuse_opt fuse_helper_opts[] = { - FUSE_HELPER_OPT("-h", show_help), -@@ -42,6 +47,7 @@ static const struct fuse_opt fuse_helper_opts[] = { - FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), - FUSE_OPT_KEY("debug", FUSE_OPT_KEY_KEEP), - FUSE_HELPER_OPT("-f", foreground), -+ FUSE_HELPER_OPT_VALUE("--daemonize", foreground, 0), - FUSE_HELPER_OPT("fsname=", nodefault_subtype), - FUSE_OPT_KEY("fsname=", FUSE_OPT_KEY_KEEP), - FUSE_HELPER_OPT("subtype=", nodefault_subtype), -@@ -131,6 +137,7 @@ void fuse_cmdline_help(void) - " -V --version print version\n" - " -d -o debug enable debug output (implies -f)\n" - " -f foreground operation\n" -+ " --daemonize run in background\n" - " -o max_idle_threads the maximum number of idle worker " - "threads\n" - " allowed (default: 10)\n"); -@@ -158,6 +165,7 @@ int fuse_parse_cmdline(struct fuse_args *args, struct fuse_cmdline_opts *opts) - memset(opts, 0, sizeof(struct fuse_cmdline_opts)); - - opts->max_idle_threads = 10; -+ opts->foreground = 1; - - if (fuse_opt_parse(args, opts, fuse_helper_opts, fuse_helper_opt_proc) == - -1) { diff --git a/0040-virtiofsd-add-vhost-user.json-file.patch b/0040-virtiofsd-add-vhost-user.json-file.patch deleted file mode 100644 index 0af1555..0000000 --- a/0040-virtiofsd-add-vhost-user.json-file.patch +++ /dev/null @@ -1,57 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:09 +0000 -Subject: [PATCH] virtiofsd: add vhost-user.json file -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Install a vhost-user.json file describing virtiofsd. This allows -libvirt and other management tools to enumerate vhost-user backend -programs. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 315616ed50ba15a5d7236ade8a402a93898202de) ---- - .gitignore | 1 + - Makefile | 1 + - tools/virtiofsd/50-qemu-virtiofsd.json.in | 5 +++++ - 3 files changed, 7 insertions(+) - create mode 100644 tools/virtiofsd/50-qemu-virtiofsd.json.in - -diff --git a/.gitignore b/.gitignore -index 7de868d1ea..c56ec1d122 100644 ---- a/.gitignore -+++ b/.gitignore -@@ -6,6 +6,7 @@ - /config-target.* - /config.status - /config-temp -+/tools/virtiofsd/50-qemu-virtiofsd.json - /elf2dmp - /trace-events-all - /trace/generated-events.h -diff --git a/Makefile b/Makefile -index 10fd31e705..aebb57aed8 100644 ---- a/Makefile -+++ b/Makefile -@@ -332,6 +332,7 @@ endif - - ifdef CONFIG_LINUX - HELPERS-y += virtiofsd$(EXESUF) -+vhost-user-json-y += tools/virtiofsd/50-qemu-virtiofsd.json - endif - - # Sphinx does not allow building manuals into the same directory as -diff --git a/tools/virtiofsd/50-qemu-virtiofsd.json.in b/tools/virtiofsd/50-qemu-virtiofsd.json.in -new file mode 100644 -index 0000000000..9bcd86f8dc ---- /dev/null -+++ b/tools/virtiofsd/50-qemu-virtiofsd.json.in -@@ -0,0 +1,5 @@ -+{ -+ "description": "QEMU virtiofsd vhost-user-fs", -+ "type": "fs", -+ "binary": "@libexecdir@/virtiofsd" -+} diff --git a/0041-virtiofsd-add-print-capabilities-option.patch b/0041-virtiofsd-add-print-capabilities-option.patch deleted file mode 100644 index f7bd267..0000000 --- a/0041-virtiofsd-add-print-capabilities-option.patch +++ /dev/null @@ -1,105 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:10 +0000 -Subject: [PATCH] virtiofsd: add --print-capabilities option -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add the --print-capabilities option as per vhost-user.rst "Backend -programs conventions". Currently there are no advertised features. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 45018fbb0a73ce66fd3dd87ecd2872b45658add4) ---- - docs/interop/vhost-user.json | 4 +++- - tools/virtiofsd/fuse_lowlevel.h | 1 + - tools/virtiofsd/helper.c | 2 ++ - tools/virtiofsd/passthrough_ll.c | 12 ++++++++++++ - 4 files changed, 18 insertions(+), 1 deletion(-) - -diff --git a/docs/interop/vhost-user.json b/docs/interop/vhost-user.json -index da6aaf51c8..d4ea1f7ac5 100644 ---- a/docs/interop/vhost-user.json -+++ b/docs/interop/vhost-user.json -@@ -31,6 +31,7 @@ - # @rproc-serial: virtio remoteproc serial link - # @scsi: virtio scsi - # @vsock: virtio vsock transport -+# @fs: virtio fs (since 4.2) - # - # Since: 4.0 - ## -@@ -50,7 +51,8 @@ - 'rpmsg', - 'rproc-serial', - 'scsi', -- 'vsock' -+ 'vsock', -+ 'fs' - ] - } - -diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h -index f6b34700af..0d61df8110 100644 ---- a/tools/virtiofsd/fuse_lowlevel.h -+++ b/tools/virtiofsd/fuse_lowlevel.h -@@ -1794,6 +1794,7 @@ struct fuse_cmdline_opts { - int nodefault_subtype; - int show_version; - int show_help; -+ int print_capabilities; - unsigned int max_idle_threads; - }; - -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index a3645fc807..b8ec5ac8dc 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -40,6 +40,7 @@ static const struct fuse_opt fuse_helper_opts[] = { - FUSE_HELPER_OPT("--help", show_help), - FUSE_HELPER_OPT("-V", show_version), - FUSE_HELPER_OPT("--version", show_version), -+ FUSE_HELPER_OPT("--print-capabilities", print_capabilities), - FUSE_HELPER_OPT("-d", debug), - FUSE_HELPER_OPT("debug", debug), - FUSE_HELPER_OPT("-d", foreground), -@@ -135,6 +136,7 @@ void fuse_cmdline_help(void) - { - printf(" -h --help print help\n" - " -V --version print version\n" -+ " --print-capabilities print vhost-user.json\n" - " -d -o debug enable debug output (implies -f)\n" - " -f foreground operation\n" - " --daemonize run in background\n" -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 037c5d7b26..cd27c09f59 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1298,6 +1298,14 @@ static struct fuse_lowlevel_ops lo_oper = { - .lseek = lo_lseek, - }; - -+/* Print vhost-user.json backend program capabilities */ -+static void print_capabilities(void) -+{ -+ printf("{\n"); -+ printf(" \"type\": \"fs\"\n"); -+ printf("}\n"); -+} -+ - int main(int argc, char *argv[]) - { - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); -@@ -1328,6 +1336,10 @@ int main(int argc, char *argv[]) - fuse_lowlevel_version(); - ret = 0; - goto err_out1; -+ } else if (opts.print_capabilities) { -+ print_capabilities(); -+ ret = 0; -+ goto err_out1; - } - - if (fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1) { diff --git a/0042-virtiofs-Add-maintainers-entry.patch b/0042-virtiofs-Add-maintainers-entry.patch deleted file mode 100644 index 224e64b..0000000 --- a/0042-virtiofs-Add-maintainers-entry.patch +++ /dev/null @@ -1,36 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:11 +0000 -Subject: [PATCH] virtiofs: Add maintainers entry -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Reviewed-by: Philippe Mathieu-Daudé -Tested-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit bad7d2c3ad1af9344df035aedaf8e0967a543070) ---- - MAINTAINERS | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/MAINTAINERS b/MAINTAINERS -index 5e5e3e52d6..d1b3e262d2 100644 ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -1575,6 +1575,14 @@ T: git https://github.com/cohuck/qemu.git s390-next - T: git https://github.com/borntraeger/qemu.git s390-next - L: qemu-s390x@nongnu.org - -+virtiofs -+M: Dr. David Alan Gilbert -+M: Stefan Hajnoczi -+S: Supported -+F: tools/virtiofsd/* -+F: hw/virtio/vhost-user-fs* -+F: include/hw/virtio/vhost-user-fs.h -+ - virtio-input - M: Gerd Hoffmann - S: Maintained diff --git a/0043-virtiofsd-passthrough_ll-create-new-files-in-caller-.patch b/0043-virtiofsd-passthrough_ll-create-new-files-in-caller-.patch deleted file mode 100644 index 9412335..0000000 --- a/0043-virtiofsd-passthrough_ll-create-new-files-in-caller-.patch +++ /dev/null @@ -1,179 +0,0 @@ -From: Vivek Goyal -Date: Mon, 27 Jan 2020 19:01:12 +0000 -Subject: [PATCH] virtiofsd: passthrough_ll: create new files in caller's - context - -We need to create files in the caller's context. Otherwise after -creating a file, the caller might not be able to do file operations on -that file. - -Changed effective uid/gid to caller's uid/gid, create file and then -switch back to uid/gid 0. - -Use syscall(setresuid, ...) otherwise glibc does some magic to change EUID -in all threads, which is not what we want. - -Signed-off-by: Vivek Goyal -Signed-off-by: Miklos Szeredi -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 929cfb7a9a1b101cdfc9ac19807ecab4c81a13e4) ---- - tools/virtiofsd/passthrough_ll.c | 96 ++++++++++++++++++++++++++++++-- - 1 file changed, 91 insertions(+), 5 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index cd27c09f59..5e061797d4 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -50,6 +50,7 @@ - #include - #include - #include -+#include - #include - #include - -@@ -83,6 +84,11 @@ struct lo_inode { - uint64_t refcount; /* protected by lo->mutex */ - }; - -+struct lo_cred { -+ uid_t euid; -+ gid_t egid; -+}; -+ - enum { - CACHE_NEVER, - CACHE_NORMAL, -@@ -383,6 +389,69 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) - } - } - -+/* -+ * On some archs, setres*id is limited to 2^16 but they -+ * provide setres*id32 variants that allow 2^32. -+ * Others just let setres*id do 2^32 anyway. -+ */ -+#ifdef SYS_setresgid32 -+#define OURSYS_setresgid SYS_setresgid32 -+#else -+#define OURSYS_setresgid SYS_setresgid -+#endif -+ -+#ifdef SYS_setresuid32 -+#define OURSYS_setresuid SYS_setresuid32 -+#else -+#define OURSYS_setresuid SYS_setresuid -+#endif -+ -+/* -+ * Change to uid/gid of caller so that file is created with -+ * ownership of caller. -+ * TODO: What about selinux context? -+ */ -+static int lo_change_cred(fuse_req_t req, struct lo_cred *old) -+{ -+ int res; -+ -+ old->euid = geteuid(); -+ old->egid = getegid(); -+ -+ res = syscall(OURSYS_setresgid, -1, fuse_req_ctx(req)->gid, -1); -+ if (res == -1) { -+ return errno; -+ } -+ -+ res = syscall(OURSYS_setresuid, -1, fuse_req_ctx(req)->uid, -1); -+ if (res == -1) { -+ int errno_save = errno; -+ -+ syscall(OURSYS_setresgid, -1, old->egid, -1); -+ return errno_save; -+ } -+ -+ return 0; -+} -+ -+/* Regain Privileges */ -+static void lo_restore_cred(struct lo_cred *old) -+{ -+ int res; -+ -+ res = syscall(OURSYS_setresuid, -1, old->euid, -1); -+ if (res == -1) { -+ fuse_log(FUSE_LOG_ERR, "seteuid(%u): %m\n", old->euid); -+ exit(1); -+ } -+ -+ res = syscall(OURSYS_setresgid, -1, old->egid, -1); -+ if (res == -1) { -+ fuse_log(FUSE_LOG_ERR, "setegid(%u): %m\n", old->egid); -+ exit(1); -+ } -+} -+ - static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, - const char *name, mode_t mode, dev_t rdev, - const char *link) -@@ -391,12 +460,21 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, - int saverr; - struct lo_inode *dir = lo_inode(req, parent); - struct fuse_entry_param e; -+ struct lo_cred old = {}; - - saverr = ENOMEM; - -+ saverr = lo_change_cred(req, &old); -+ if (saverr) { -+ goto out; -+ } -+ - res = mknod_wrapper(dir->fd, name, link, mode, rdev); - - saverr = errno; -+ -+ lo_restore_cred(&old); -+ - if (res == -1) { - goto out; - } -@@ -794,26 +872,34 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, - struct lo_data *lo = lo_data(req); - struct fuse_entry_param e; - int err; -+ struct lo_cred old = {}; - - if (lo_debug(req)) { - fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", - parent, name); - } - -+ err = lo_change_cred(req, &old); -+ if (err) { -+ goto out; -+ } -+ - fd = openat(lo_fd(req, parent), name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, - mode); -- if (fd == -1) { -- return (void)fuse_reply_err(req, errno); -- } -+ err = fd == -1 ? errno : 0; -+ lo_restore_cred(&old); - -- fi->fh = fd; -+ if (!err) { -+ fi->fh = fd; -+ err = lo_do_lookup(req, parent, name, &e); -+ } - if (lo->cache == CACHE_NEVER) { - fi->direct_io = 1; - } else if (lo->cache == CACHE_ALWAYS) { - fi->keep_cache = 1; - } - -- err = lo_do_lookup(req, parent, name, &e); -+out: - if (err) { - fuse_reply_err(req, err); - } else { diff --git a/0044-virtiofsd-passthrough_ll-add-lo_map-for-ino-fh-indir.patch b/0044-virtiofsd-passthrough_ll-add-lo_map-for-ino-fh-indir.patch deleted file mode 100644 index 43ee290..0000000 --- a/0044-virtiofsd-passthrough_ll-add-lo_map-for-ino-fh-indir.patch +++ /dev/null @@ -1,162 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:13 +0000 -Subject: [PATCH] virtiofsd: passthrough_ll: add lo_map for ino/fh indirection - -A layer of indirection is needed because passthrough_ll cannot expose -pointers or file descriptor numbers to untrusted clients. Malicious -clients could send invalid pointers or file descriptors in order to -crash or exploit the file system daemon. - -lo_map provides an integer key->value mapping. This will be used for -ino and fh fields in the patches that follow. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Masayoshi Mizuma -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 25c135727b08dca90f00094e522a69170b13dfac) ---- - tools/virtiofsd/passthrough_ll.c | 124 +++++++++++++++++++++++++++++++ - 1 file changed, 124 insertions(+) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 5e061797d4..e83a976587 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -74,6 +74,21 @@ struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { - }; - #endif - -+struct lo_map_elem { -+ union { -+ /* Element values will go here... */ -+ ssize_t freelist; -+ }; -+ bool in_use; -+}; -+ -+/* Maps FUSE fh or ino values to internal objects */ -+struct lo_map { -+ struct lo_map_elem *elems; -+ size_t nelems; -+ ssize_t freelist; -+}; -+ - struct lo_inode { - struct lo_inode *next; /* protected by lo->mutex */ - struct lo_inode *prev; /* protected by lo->mutex */ -@@ -130,6 +145,115 @@ static struct lo_data *lo_data(fuse_req_t req) - return (struct lo_data *)fuse_req_userdata(req); - } - -+__attribute__((unused)) static void lo_map_init(struct lo_map *map) -+{ -+ map->elems = NULL; -+ map->nelems = 0; -+ map->freelist = -1; -+} -+ -+__attribute__((unused)) static void lo_map_destroy(struct lo_map *map) -+{ -+ free(map->elems); -+} -+ -+static int lo_map_grow(struct lo_map *map, size_t new_nelems) -+{ -+ struct lo_map_elem *new_elems; -+ size_t i; -+ -+ if (new_nelems <= map->nelems) { -+ return 1; -+ } -+ -+ new_elems = realloc(map->elems, sizeof(map->elems[0]) * new_nelems); -+ if (!new_elems) { -+ return 0; -+ } -+ -+ for (i = map->nelems; i < new_nelems; i++) { -+ new_elems[i].freelist = i + 1; -+ new_elems[i].in_use = false; -+ } -+ new_elems[new_nelems - 1].freelist = -1; -+ -+ map->elems = new_elems; -+ map->freelist = map->nelems; -+ map->nelems = new_nelems; -+ return 1; -+} -+ -+__attribute__((unused)) static struct lo_map_elem * -+lo_map_alloc_elem(struct lo_map *map) -+{ -+ struct lo_map_elem *elem; -+ -+ if (map->freelist == -1 && !lo_map_grow(map, map->nelems + 256)) { -+ return NULL; -+ } -+ -+ elem = &map->elems[map->freelist]; -+ map->freelist = elem->freelist; -+ -+ elem->in_use = true; -+ -+ return elem; -+} -+ -+__attribute__((unused)) static struct lo_map_elem * -+lo_map_reserve(struct lo_map *map, size_t key) -+{ -+ ssize_t *prev; -+ -+ if (!lo_map_grow(map, key + 1)) { -+ return NULL; -+ } -+ -+ for (prev = &map->freelist; *prev != -1; -+ prev = &map->elems[*prev].freelist) { -+ if (*prev == key) { -+ struct lo_map_elem *elem = &map->elems[key]; -+ -+ *prev = elem->freelist; -+ elem->in_use = true; -+ return elem; -+ } -+ } -+ return NULL; -+} -+ -+__attribute__((unused)) static struct lo_map_elem * -+lo_map_get(struct lo_map *map, size_t key) -+{ -+ if (key >= map->nelems) { -+ return NULL; -+ } -+ if (!map->elems[key].in_use) { -+ return NULL; -+ } -+ return &map->elems[key]; -+} -+ -+__attribute__((unused)) static void lo_map_remove(struct lo_map *map, -+ size_t key) -+{ -+ struct lo_map_elem *elem; -+ -+ if (key >= map->nelems) { -+ return; -+ } -+ -+ elem = &map->elems[key]; -+ if (!elem->in_use) { -+ return; -+ } -+ -+ elem->in_use = false; -+ -+ elem->freelist = map->freelist; -+ map->freelist = key; -+} -+ - static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) - { - if (ino == FUSE_ROOT_ID) { diff --git a/0045-virtiofsd-passthrough_ll-add-ino_map-to-hide-lo_inod.patch b/0045-virtiofsd-passthrough_ll-add-ino_map-to-hide-lo_inod.patch deleted file mode 100644 index 9fb606a..0000000 --- a/0045-virtiofsd-passthrough_ll-add-ino_map-to-hide-lo_inod.patch +++ /dev/null @@ -1,376 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:14 +0000 -Subject: [PATCH] virtiofsd: passthrough_ll: add ino_map to hide lo_inode - pointers - -Do not expose lo_inode pointers to clients. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Masayoshi Mizuma -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 92fb57b83cdbfc4bf53c0c46a3d0bcbc36e64126) ---- - tools/virtiofsd/passthrough_ll.c | 144 ++++++++++++++++++++++++------- - 1 file changed, 114 insertions(+), 30 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index e83a976587..a3ebf74eab 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -57,8 +57,8 @@ - #include "passthrough_helpers.h" - - /* -- * We are re-using pointers to our `struct lo_inode` and `struct -- * lo_dirp` elements as inodes. This means that we must be able to -+ * We are re-using pointers to our `struct lo_inode` -+ * elements as inodes. This means that we must be able to - * store uintptr_t values in a fuse_ino_t variable. The following - * incantation checks this condition at compile time. - */ -@@ -76,7 +76,7 @@ struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { - - struct lo_map_elem { - union { -- /* Element values will go here... */ -+ struct lo_inode *inode; - ssize_t freelist; - }; - bool in_use; -@@ -97,6 +97,7 @@ struct lo_inode { - ino_t ino; - dev_t dev; - uint64_t refcount; /* protected by lo->mutex */ -+ fuse_ino_t fuse_ino; - }; - - struct lo_cred { -@@ -121,6 +122,7 @@ struct lo_data { - int cache; - int timeout_set; - struct lo_inode root; /* protected by lo->mutex */ -+ struct lo_map ino_map; /* protected by lo->mutex */ - }; - - static const struct fuse_opt lo_opts[] = { -@@ -145,14 +147,14 @@ static struct lo_data *lo_data(fuse_req_t req) - return (struct lo_data *)fuse_req_userdata(req); - } - --__attribute__((unused)) static void lo_map_init(struct lo_map *map) -+static void lo_map_init(struct lo_map *map) - { - map->elems = NULL; - map->nelems = 0; - map->freelist = -1; - } - --__attribute__((unused)) static void lo_map_destroy(struct lo_map *map) -+static void lo_map_destroy(struct lo_map *map) - { - free(map->elems); - } -@@ -183,8 +185,7 @@ static int lo_map_grow(struct lo_map *map, size_t new_nelems) - return 1; - } - --__attribute__((unused)) static struct lo_map_elem * --lo_map_alloc_elem(struct lo_map *map) -+static struct lo_map_elem *lo_map_alloc_elem(struct lo_map *map) - { - struct lo_map_elem *elem; - -@@ -200,8 +201,7 @@ lo_map_alloc_elem(struct lo_map *map) - return elem; - } - --__attribute__((unused)) static struct lo_map_elem * --lo_map_reserve(struct lo_map *map, size_t key) -+static struct lo_map_elem *lo_map_reserve(struct lo_map *map, size_t key) - { - ssize_t *prev; - -@@ -222,8 +222,7 @@ lo_map_reserve(struct lo_map *map, size_t key) - return NULL; - } - --__attribute__((unused)) static struct lo_map_elem * --lo_map_get(struct lo_map *map, size_t key) -+static struct lo_map_elem *lo_map_get(struct lo_map *map, size_t key) - { - if (key >= map->nelems) { - return NULL; -@@ -234,8 +233,7 @@ lo_map_get(struct lo_map *map, size_t key) - return &map->elems[key]; - } - --__attribute__((unused)) static void lo_map_remove(struct lo_map *map, -- size_t key) -+static void lo_map_remove(struct lo_map *map, size_t key) - { - struct lo_map_elem *elem; - -@@ -254,18 +252,40 @@ __attribute__((unused)) static void lo_map_remove(struct lo_map *map, - map->freelist = key; - } - -+/* Assumes lo->mutex is held */ -+static ssize_t lo_add_inode_mapping(fuse_req_t req, struct lo_inode *inode) -+{ -+ struct lo_map_elem *elem; -+ -+ elem = lo_map_alloc_elem(&lo_data(req)->ino_map); -+ if (!elem) { -+ return -1; -+ } -+ -+ elem->inode = inode; -+ return elem - lo_data(req)->ino_map.elems; -+} -+ - static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) - { -- if (ino == FUSE_ROOT_ID) { -- return &lo_data(req)->root; -- } else { -- return (struct lo_inode *)(uintptr_t)ino; -+ struct lo_data *lo = lo_data(req); -+ struct lo_map_elem *elem; -+ -+ pthread_mutex_lock(&lo->mutex); -+ elem = lo_map_get(&lo->ino_map, ino); -+ pthread_mutex_unlock(&lo->mutex); -+ -+ if (!elem) { -+ return NULL; - } -+ -+ return elem->inode; - } - - static int lo_fd(fuse_req_t req, fuse_ino_t ino) - { -- return lo_inode(req, ino)->fd; -+ struct lo_inode *inode = lo_inode(req, ino); -+ return inode ? inode->fd : -1; - } - - static bool lo_debug(fuse_req_t req) -@@ -337,10 +357,18 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, - { - int saverr; - char procname[64]; -- struct lo_inode *inode = lo_inode(req, ino); -- int ifd = inode->fd; -+ struct lo_inode *inode; -+ int ifd; - int res; - -+ inode = lo_inode(req, ino); -+ if (!inode) { -+ fuse_reply_err(req, EBADF); -+ return; -+ } -+ -+ ifd = inode->fd; -+ - if (valid & FUSE_SET_ATTR_MODE) { - if (fi) { - res = fchmod(fi->fh, attr->st_mode); -@@ -470,6 +498,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - inode->dev = e->attr.st_dev; - - pthread_mutex_lock(&lo->mutex); -+ inode->fuse_ino = lo_add_inode_mapping(req, inode); - prev = &lo->root; - next = prev->next; - next->prev = inode; -@@ -478,7 +507,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - prev->next = inode; - pthread_mutex_unlock(&lo->mutex); - } -- e->ino = (uintptr_t)inode; -+ e->ino = inode->fuse_ino; - - if (lo_debug(req)) { - fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", -@@ -582,10 +611,16 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, - { - int res; - int saverr; -- struct lo_inode *dir = lo_inode(req, parent); -+ struct lo_inode *dir; - struct fuse_entry_param e; - struct lo_cred old = {}; - -+ dir = lo_inode(req, parent); -+ if (!dir) { -+ fuse_reply_err(req, EBADF); -+ return; -+ } -+ - saverr = ENOMEM; - - saverr = lo_change_cred(req, &old); -@@ -663,10 +698,16 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, - { - int res; - struct lo_data *lo = lo_data(req); -- struct lo_inode *inode = lo_inode(req, ino); -+ struct lo_inode *inode; - struct fuse_entry_param e; - int saverr; - -+ inode = lo_inode(req, ino); -+ if (!inode) { -+ fuse_reply_err(req, EBADF); -+ return; -+ } -+ - memset(&e, 0, sizeof(struct fuse_entry_param)); - e.attr_timeout = lo->timeout; - e.entry_timeout = lo->timeout; -@@ -684,7 +725,7 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, - pthread_mutex_lock(&lo->mutex); - inode->refcount++; - pthread_mutex_unlock(&lo->mutex); -- e.ino = (uintptr_t)inode; -+ e.ino = inode->fuse_ino; - - if (lo_debug(req)) { - fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", -@@ -750,10 +791,10 @@ static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) - next->prev = prev; - prev->next = next; - -+ lo_map_remove(&lo->ino_map, inode->fuse_ino); - pthread_mutex_unlock(&lo->mutex); - close(inode->fd); - free(inode); -- - } else { - pthread_mutex_unlock(&lo->mutex); - } -@@ -762,7 +803,12 @@ static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) - static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) - { - struct lo_data *lo = lo_data(req); -- struct lo_inode *inode = lo_inode(req, ino); -+ struct lo_inode *inode; -+ -+ inode = lo_inode(req, ino); -+ if (!inode) { -+ return; -+ } - - if (lo_debug(req)) { - fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", -@@ -1244,10 +1290,16 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, - { - char *value = NULL; - char procname[64]; -- struct lo_inode *inode = lo_inode(req, ino); -+ struct lo_inode *inode; - ssize_t ret; - int saverr; - -+ inode = lo_inode(req, ino); -+ if (!inode) { -+ fuse_reply_err(req, EBADF); -+ return; -+ } -+ - saverr = ENOSYS; - if (!lo_data(req)->xattr) { - goto out; -@@ -1306,10 +1358,16 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) - { - char *value = NULL; - char procname[64]; -- struct lo_inode *inode = lo_inode(req, ino); -+ struct lo_inode *inode; - ssize_t ret; - int saverr; - -+ inode = lo_inode(req, ino); -+ if (!inode) { -+ fuse_reply_err(req, EBADF); -+ return; -+ } -+ - saverr = ENOSYS; - if (!lo_data(req)->xattr) { - goto out; -@@ -1367,10 +1425,16 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, - const char *value, size_t size, int flags) - { - char procname[64]; -- struct lo_inode *inode = lo_inode(req, ino); -+ struct lo_inode *inode; - ssize_t ret; - int saverr; - -+ inode = lo_inode(req, ino); -+ if (!inode) { -+ fuse_reply_err(req, EBADF); -+ return; -+ } -+ - saverr = ENOSYS; - if (!lo_data(req)->xattr) { - goto out; -@@ -1400,10 +1464,16 @@ out: - static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) - { - char procname[64]; -- struct lo_inode *inode = lo_inode(req, ino); -+ struct lo_inode *inode; - ssize_t ret; - int saverr; - -+ inode = lo_inode(req, ino); -+ if (!inode) { -+ fuse_reply_err(req, EBADF); -+ return; -+ } -+ - saverr = ENOSYS; - if (!lo_data(req)->xattr) { - goto out; -@@ -1522,6 +1592,7 @@ int main(int argc, char *argv[]) - struct fuse_session *se; - struct fuse_cmdline_opts opts; - struct lo_data lo = { .debug = 0, .writeback = 0 }; -+ struct lo_map_elem *root_elem; - int ret = -1; - - /* Don't mask creation mode, kernel already did that */ -@@ -1530,8 +1601,19 @@ int main(int argc, char *argv[]) - pthread_mutex_init(&lo.mutex, NULL); - lo.root.next = lo.root.prev = &lo.root; - lo.root.fd = -1; -+ lo.root.fuse_ino = FUSE_ROOT_ID; - lo.cache = CACHE_NORMAL; - -+ /* -+ * Set up the ino map like this: -+ * [0] Reserved (will not be used) -+ * [1] Root inode -+ */ -+ lo_map_init(&lo.ino_map); -+ lo_map_reserve(&lo.ino_map, 0)->in_use = false; -+ root_elem = lo_map_reserve(&lo.ino_map, lo.root.fuse_ino); -+ root_elem->inode = &lo.root; -+ - if (fuse_parse_cmdline(&args, &opts) != 0) { - return 1; - } -@@ -1628,6 +1710,8 @@ err_out2: - err_out1: - fuse_opt_free_args(&args); - -+ lo_map_destroy(&lo.ino_map); -+ - if (lo.root.fd >= 0) { - close(lo.root.fd); - } diff --git a/0046-virtiofsd-passthrough_ll-add-dirp_map-to-hide-lo_dir.patch b/0046-virtiofsd-passthrough_ll-add-dirp_map-to-hide-lo_dir.patch deleted file mode 100644 index 1e4ed85..0000000 --- a/0046-virtiofsd-passthrough_ll-add-dirp_map-to-hide-lo_dir.patch +++ /dev/null @@ -1,222 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:15 +0000 -Subject: [PATCH] virtiofsd: passthrough_ll: add dirp_map to hide lo_dirp - pointers -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Do not expose lo_dirp pointers to clients. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit b39bce121bfad8757eec0ee41f14607b883935d3) ---- - tools/virtiofsd/passthrough_ll.c | 103 +++++++++++++++++++++++-------- - 1 file changed, 76 insertions(+), 27 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index a3ebf74eab..5f5a72fdbb 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -56,27 +56,10 @@ - - #include "passthrough_helpers.h" - --/* -- * We are re-using pointers to our `struct lo_inode` -- * elements as inodes. This means that we must be able to -- * store uintptr_t values in a fuse_ino_t variable. The following -- * incantation checks this condition at compile time. -- */ --#if defined(__GNUC__) && \ -- (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 6) && \ -- !defined __cplusplus --_Static_assert(sizeof(fuse_ino_t) >= sizeof(uintptr_t), -- "fuse_ino_t too small to hold uintptr_t values!"); --#else --struct _uintptr_to_must_hold_fuse_ino_t_dummy_struct { -- unsigned _uintptr_to_must_hold_fuse_ino_t -- : ((sizeof(fuse_ino_t) >= sizeof(uintptr_t)) ? 1 : -1); --}; --#endif -- - struct lo_map_elem { - union { - struct lo_inode *inode; -+ struct lo_dirp *dirp; - ssize_t freelist; - }; - bool in_use; -@@ -123,6 +106,7 @@ struct lo_data { - int timeout_set; - struct lo_inode root; /* protected by lo->mutex */ - struct lo_map ino_map; /* protected by lo->mutex */ -+ struct lo_map dirp_map; /* protected by lo->mutex */ - }; - - static const struct fuse_opt lo_opts[] = { -@@ -252,6 +236,20 @@ static void lo_map_remove(struct lo_map *map, size_t key) - map->freelist = key; - } - -+/* Assumes lo->mutex is held */ -+static ssize_t lo_add_dirp_mapping(fuse_req_t req, struct lo_dirp *dirp) -+{ -+ struct lo_map_elem *elem; -+ -+ elem = lo_map_alloc_elem(&lo_data(req)->dirp_map); -+ if (!elem) { -+ return -1; -+ } -+ -+ elem->dirp = dirp; -+ return elem - lo_data(req)->dirp_map.elems; -+} -+ - /* Assumes lo->mutex is held */ - static ssize_t lo_add_inode_mapping(fuse_req_t req, struct lo_inode *inode) - { -@@ -861,9 +859,19 @@ struct lo_dirp { - off_t offset; - }; - --static struct lo_dirp *lo_dirp(struct fuse_file_info *fi) -+static struct lo_dirp *lo_dirp(fuse_req_t req, struct fuse_file_info *fi) - { -- return (struct lo_dirp *)(uintptr_t)fi->fh; -+ struct lo_data *lo = lo_data(req); -+ struct lo_map_elem *elem; -+ -+ pthread_mutex_lock(&lo->mutex); -+ elem = lo_map_get(&lo->dirp_map, fi->fh); -+ pthread_mutex_unlock(&lo->mutex); -+ if (!elem) { -+ return NULL; -+ } -+ -+ return elem->dirp; - } - - static void lo_opendir(fuse_req_t req, fuse_ino_t ino, -@@ -873,6 +881,7 @@ static void lo_opendir(fuse_req_t req, fuse_ino_t ino, - struct lo_data *lo = lo_data(req); - struct lo_dirp *d; - int fd; -+ ssize_t fh; - - d = calloc(1, sizeof(struct lo_dirp)); - if (d == NULL) { -@@ -892,7 +901,14 @@ static void lo_opendir(fuse_req_t req, fuse_ino_t ino, - d->offset = 0; - d->entry = NULL; - -- fi->fh = (uintptr_t)d; -+ pthread_mutex_lock(&lo->mutex); -+ fh = lo_add_dirp_mapping(req, d); -+ pthread_mutex_unlock(&lo->mutex); -+ if (fh == -1) { -+ goto out_err; -+ } -+ -+ fi->fh = fh; - if (lo->cache == CACHE_ALWAYS) { - fi->keep_cache = 1; - } -@@ -903,6 +919,9 @@ out_errno: - error = errno; - out_err: - if (d) { -+ if (d->dp) { -+ closedir(d->dp); -+ } - if (fd != -1) { - close(fd); - } -@@ -920,17 +939,21 @@ static int is_dot_or_dotdot(const char *name) - static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, - off_t offset, struct fuse_file_info *fi, int plus) - { -- struct lo_dirp *d = lo_dirp(fi); -- char *buf; -+ struct lo_dirp *d; -+ char *buf = NULL; - char *p; - size_t rem = size; -- int err; -+ int err = ENOMEM; - - (void)ino; - -+ d = lo_dirp(req, fi); -+ if (!d) { -+ goto error; -+ } -+ - buf = calloc(1, size); - if (!buf) { -- err = ENOMEM; - goto error; - } - p = buf; -@@ -1028,8 +1051,21 @@ static void lo_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size, - static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info *fi) - { -- struct lo_dirp *d = lo_dirp(fi); -+ struct lo_data *lo = lo_data(req); -+ struct lo_dirp *d; -+ - (void)ino; -+ -+ d = lo_dirp(req, fi); -+ if (!d) { -+ fuse_reply_err(req, EBADF); -+ return; -+ } -+ -+ pthread_mutex_lock(&lo->mutex); -+ lo_map_remove(&lo->dirp_map, fi->fh); -+ pthread_mutex_unlock(&lo->mutex); -+ - closedir(d->dp); - free(d); - fuse_reply_err(req, 0); -@@ -1081,8 +1117,18 @@ static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, - struct fuse_file_info *fi) - { - int res; -- int fd = dirfd(lo_dirp(fi)->dp); -+ struct lo_dirp *d; -+ int fd; -+ - (void)ino; -+ -+ d = lo_dirp(req, fi); -+ if (!d) { -+ fuse_reply_err(req, EBADF); -+ return; -+ } -+ -+ fd = dirfd(d->dp); - if (datasync) { - res = fdatasync(fd); - } else { -@@ -1614,6 +1660,8 @@ int main(int argc, char *argv[]) - root_elem = lo_map_reserve(&lo.ino_map, lo.root.fuse_ino); - root_elem->inode = &lo.root; - -+ lo_map_init(&lo.dirp_map); -+ - if (fuse_parse_cmdline(&args, &opts) != 0) { - return 1; - } -@@ -1710,6 +1758,7 @@ err_out2: - err_out1: - fuse_opt_free_args(&args); - -+ lo_map_destroy(&lo.dirp_map); - lo_map_destroy(&lo.ino_map); - - if (lo.root.fd >= 0) { diff --git a/0047-virtiofsd-passthrough_ll-add-fd_map-to-hide-file-des.patch b/0047-virtiofsd-passthrough_ll-add-fd_map-to-hide-file-des.patch deleted file mode 100644 index 485a73b..0000000 --- a/0047-virtiofsd-passthrough_ll-add-fd_map-to-hide-file-des.patch +++ /dev/null @@ -1,308 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:16 +0000 -Subject: [PATCH] virtiofsd: passthrough_ll: add fd_map to hide file - descriptors - -Do not expose file descriptor numbers to clients. This prevents the -abuse of internal file descriptors (like stdin/stdout). - -Signed-off-by: Stefan Hajnoczi -Fix from: -Signed-off-by: Xiao Yang -dgilbert: - Added lseek -Reviewed-by: Masayoshi Mizuma -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 73b4d19dfc4248a74c1f3e511cfa934681d9c602) ---- - tools/virtiofsd/passthrough_ll.c | 116 +++++++++++++++++++++++++------ - 1 file changed, 94 insertions(+), 22 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 5f5a72fdbb..9815bfa5c5 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -60,6 +60,7 @@ struct lo_map_elem { - union { - struct lo_inode *inode; - struct lo_dirp *dirp; -+ int fd; - ssize_t freelist; - }; - bool in_use; -@@ -107,6 +108,7 @@ struct lo_data { - struct lo_inode root; /* protected by lo->mutex */ - struct lo_map ino_map; /* protected by lo->mutex */ - struct lo_map dirp_map; /* protected by lo->mutex */ -+ struct lo_map fd_map; /* protected by lo->mutex */ - }; - - static const struct fuse_opt lo_opts[] = { -@@ -236,6 +238,20 @@ static void lo_map_remove(struct lo_map *map, size_t key) - map->freelist = key; - } - -+/* Assumes lo->mutex is held */ -+static ssize_t lo_add_fd_mapping(fuse_req_t req, int fd) -+{ -+ struct lo_map_elem *elem; -+ -+ elem = lo_map_alloc_elem(&lo_data(req)->fd_map); -+ if (!elem) { -+ return -1; -+ } -+ -+ elem->fd = fd; -+ return elem - lo_data(req)->fd_map.elems; -+} -+ - /* Assumes lo->mutex is held */ - static ssize_t lo_add_dirp_mapping(fuse_req_t req, struct lo_dirp *dirp) - { -@@ -350,6 +366,22 @@ static int utimensat_empty_nofollow(struct lo_inode *inode, - return utimensat(AT_FDCWD, procname, tv, 0); - } - -+static int lo_fi_fd(fuse_req_t req, struct fuse_file_info *fi) -+{ -+ struct lo_data *lo = lo_data(req); -+ struct lo_map_elem *elem; -+ -+ pthread_mutex_lock(&lo->mutex); -+ elem = lo_map_get(&lo->fd_map, fi->fh); -+ pthread_mutex_unlock(&lo->mutex); -+ -+ if (!elem) { -+ return -1; -+ } -+ -+ return elem->fd; -+} -+ - static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, - int valid, struct fuse_file_info *fi) - { -@@ -358,6 +390,7 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, - struct lo_inode *inode; - int ifd; - int res; -+ int fd; - - inode = lo_inode(req, ino); - if (!inode) { -@@ -367,9 +400,14 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, - - ifd = inode->fd; - -+ /* If fi->fh is invalid we'll report EBADF later */ -+ if (fi) { -+ fd = lo_fi_fd(req, fi); -+ } -+ - if (valid & FUSE_SET_ATTR_MODE) { - if (fi) { -- res = fchmod(fi->fh, attr->st_mode); -+ res = fchmod(fd, attr->st_mode); - } else { - sprintf(procname, "/proc/self/fd/%i", ifd); - res = chmod(procname, attr->st_mode); -@@ -389,7 +427,7 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, - } - if (valid & FUSE_SET_ATTR_SIZE) { - if (fi) { -- res = ftruncate(fi->fh, attr->st_size); -+ res = ftruncate(fd, attr->st_size); - } else { - sprintf(procname, "/proc/self/fd/%i", ifd); - res = truncate(procname, attr->st_size); -@@ -419,7 +457,7 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, - } - - if (fi) { -- res = futimens(fi->fh, tv); -+ res = futimens(fd, tv); - } else { - res = utimensat_empty_nofollow(inode, tv); - } -@@ -1096,7 +1134,18 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, - lo_restore_cred(&old); - - if (!err) { -- fi->fh = fd; -+ ssize_t fh; -+ -+ pthread_mutex_lock(&lo->mutex); -+ fh = lo_add_fd_mapping(req, fd); -+ pthread_mutex_unlock(&lo->mutex); -+ if (fh == -1) { -+ close(fd); -+ fuse_reply_err(req, ENOMEM); -+ return; -+ } -+ -+ fi->fh = fh; - err = lo_do_lookup(req, parent, name, &e); - } - if (lo->cache == CACHE_NEVER) { -@@ -1140,6 +1189,7 @@ static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, - static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) - { - int fd; -+ ssize_t fh; - char buf[64]; - struct lo_data *lo = lo_data(req); - -@@ -1175,7 +1225,16 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) - return (void)fuse_reply_err(req, errno); - } - -- fi->fh = fd; -+ pthread_mutex_lock(&lo->mutex); -+ fh = lo_add_fd_mapping(req, fd); -+ pthread_mutex_unlock(&lo->mutex); -+ if (fh == -1) { -+ close(fd); -+ fuse_reply_err(req, ENOMEM); -+ return; -+ } -+ -+ fi->fh = fh; - if (lo->cache == CACHE_NEVER) { - fi->direct_io = 1; - } else if (lo->cache == CACHE_ALWAYS) { -@@ -1187,9 +1246,18 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) - static void lo_release(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info *fi) - { -+ struct lo_data *lo = lo_data(req); -+ int fd; -+ - (void)ino; - -- close(fi->fh); -+ fd = lo_fi_fd(req, fi); -+ -+ pthread_mutex_lock(&lo->mutex); -+ lo_map_remove(&lo->fd_map, fi->fh); -+ pthread_mutex_unlock(&lo->mutex); -+ -+ close(fd); - fuse_reply_err(req, 0); - } - -@@ -1197,7 +1265,7 @@ static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) - { - int res; - (void)ino; -- res = close(dup(fi->fh)); -+ res = close(dup(lo_fi_fd(req, fi))); - fuse_reply_err(req, res == -1 ? errno : 0); - } - -@@ -1224,7 +1292,7 @@ static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, - return (void)fuse_reply_err(req, errno); - } - } else { -- fd = fi->fh; -+ fd = lo_fi_fd(req, fi); - } - - if (datasync) { -@@ -1251,7 +1319,7 @@ static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, - } - - buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; -- buf.buf[0].fd = fi->fh; -+ buf.buf[0].fd = lo_fi_fd(req, fi); - buf.buf[0].pos = offset; - - fuse_reply_data(req, &buf); -@@ -1266,7 +1334,7 @@ static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, - struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); - - out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; -- out_buf.buf[0].fd = fi->fh; -+ out_buf.buf[0].fd = lo_fi_fd(req, fi); - out_buf.buf[0].pos = off; - - if (lo_debug(req)) { -@@ -1303,7 +1371,7 @@ static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, - (void)ino; - - #ifdef CONFIG_FALLOCATE -- err = fallocate(fi->fh, mode, offset, length); -+ err = fallocate(lo_fi_fd(req, fi), mode, offset, length); - if (err < 0) { - err = errno; - } -@@ -1314,7 +1382,7 @@ static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode, off_t offset, - return; - } - -- err = posix_fallocate(fi->fh, offset, length); -+ err = posix_fallocate(lo_fi_fd(req, fi), offset, length); - #endif - - fuse_reply_err(req, err); -@@ -1326,7 +1394,7 @@ static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, - int res; - (void)ino; - -- res = flock(fi->fh, op); -+ res = flock(lo_fi_fd(req, fi), op); - - fuse_reply_err(req, res == -1 ? errno : 0); - } -@@ -1551,17 +1619,19 @@ static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, - off_t off_out, struct fuse_file_info *fi_out, - size_t len, int flags) - { -+ int in_fd, out_fd; - ssize_t res; - -- if (lo_debug(req)) -- fuse_log(FUSE_LOG_DEBUG, -- "lo_copy_file_range(ino=%" PRIu64 "/fd=%lu, " -- "off=%lu, ino=%" PRIu64 "/fd=%lu, " -- "off=%lu, size=%zd, flags=0x%x)\n", -- ino_in, fi_in->fh, off_in, ino_out, fi_out->fh, off_out, len, -- flags); -+ in_fd = lo_fi_fd(req, fi_in); -+ out_fd = lo_fi_fd(req, fi_out); -+ -+ fuse_log(FUSE_LOG_DEBUG, -+ "lo_copy_file_range(ino=%" PRIu64 "/fd=%d, " -+ "off=%lu, ino=%" PRIu64 "/fd=%d, " -+ "off=%lu, size=%zd, flags=0x%x)\n", -+ ino_in, in_fd, off_in, ino_out, out_fd, off_out, len, flags); - -- res = copy_file_range(fi_in->fh, &off_in, fi_out->fh, &off_out, len, flags); -+ res = copy_file_range(in_fd, &off_in, out_fd, &off_out, len, flags); - if (res < 0) { - fuse_reply_err(req, -errno); - } else { -@@ -1576,7 +1646,7 @@ static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, - off_t res; - - (void)ino; -- res = lseek(fi->fh, off, whence); -+ res = lseek(lo_fi_fd(req, fi), off, whence); - if (res != -1) { - fuse_reply_lseek(req, res); - } else { -@@ -1661,6 +1731,7 @@ int main(int argc, char *argv[]) - root_elem->inode = &lo.root; - - lo_map_init(&lo.dirp_map); -+ lo_map_init(&lo.fd_map); - - if (fuse_parse_cmdline(&args, &opts) != 0) { - return 1; -@@ -1758,6 +1829,7 @@ err_out2: - err_out1: - fuse_opt_free_args(&args); - -+ lo_map_destroy(&lo.fd_map); - lo_map_destroy(&lo.dirp_map); - lo_map_destroy(&lo.ino_map); - diff --git a/0048-virtiofsd-passthrough_ll-add-fallback-for-racy-ops.patch b/0048-virtiofsd-passthrough_ll-add-fallback-for-racy-ops.patch deleted file mode 100644 index f843056..0000000 --- a/0048-virtiofsd-passthrough_ll-add-fallback-for-racy-ops.patch +++ /dev/null @@ -1,284 +0,0 @@ -From: Miklos Szeredi -Date: Mon, 27 Jan 2020 19:01:17 +0000 -Subject: [PATCH] virtiofsd: passthrough_ll: add fallback for racy ops - -We have two operations that cannot be done race-free on a symlink in -certain cases: utimes and link. - -Add racy fallback for these if the race-free method doesn't work. We do -our best to avoid races even in this case: - - - get absolute path by reading /proc/self/fd/NN symlink - - - lookup parent directory: after this we are safe against renames in - ancestors - - - lookup name in parent directory, and verify that we got to the original - inode, if not retry the whole thing - -Both utimes(2) and link(2) hold i_lock on the inode across the operation, -so a racing rename/delete by this fuse instance is not possible, only from -other entities changing the filesystem. - -If the "norace" option is given, then disable the racy fallbacks. - -Signed-off-by: Miklos Szeredi -Reviewed-by: Masayoshi Mizuma -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 5fe319a7b19c9c328e6e061bffcf1ff6cc8b89ce) ---- - tools/virtiofsd/helper.c | 5 +- - tools/virtiofsd/passthrough_ll.c | 157 +++++++++++++++++++++++++++---- - 2 files changed, 145 insertions(+), 17 deletions(-) - -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index b8ec5ac8dc..5531425223 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -142,7 +142,10 @@ void fuse_cmdline_help(void) - " --daemonize run in background\n" - " -o max_idle_threads the maximum number of idle worker " - "threads\n" -- " allowed (default: 10)\n"); -+ " allowed (default: 10)\n" -+ " -o norace disable racy fallback\n" -+ " default: false\n" -+ ); - } - - static int fuse_helper_opt_proc(void *data, const char *arg, int key, -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 9815bfa5c5..ac380efcb1 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -98,6 +98,7 @@ enum { - struct lo_data { - pthread_mutex_t mutex; - int debug; -+ int norace; - int writeback; - int flock; - int xattr; -@@ -124,10 +125,15 @@ static const struct fuse_opt lo_opts[] = { - { "cache=never", offsetof(struct lo_data, cache), CACHE_NEVER }, - { "cache=auto", offsetof(struct lo_data, cache), CACHE_NORMAL }, - { "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS }, -- -+ { "norace", offsetof(struct lo_data, norace), 1 }, - FUSE_OPT_END - }; - -+static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n); -+ -+static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st); -+ -+ - static struct lo_data *lo_data(fuse_req_t req) - { - return (struct lo_data *)fuse_req_userdata(req); -@@ -347,23 +353,127 @@ static void lo_getattr(fuse_req_t req, fuse_ino_t ino, - fuse_reply_attr(req, &buf, lo->timeout); - } - --static int utimensat_empty_nofollow(struct lo_inode *inode, -- const struct timespec *tv) -+static int lo_parent_and_name(struct lo_data *lo, struct lo_inode *inode, -+ char path[PATH_MAX], struct lo_inode **parent) - { -- int res; - char procname[64]; -+ char *last; -+ struct stat stat; -+ struct lo_inode *p; -+ int retries = 2; -+ int res; -+ -+retry: -+ sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ -+ res = readlink(procname, path, PATH_MAX); -+ if (res < 0) { -+ fuse_log(FUSE_LOG_WARNING, "%s: readlink failed: %m\n", __func__); -+ goto fail_noretry; -+ } -+ -+ if (res >= PATH_MAX) { -+ fuse_log(FUSE_LOG_WARNING, "%s: readlink overflowed\n", __func__); -+ goto fail_noretry; -+ } -+ path[res] = '\0'; -+ -+ last = strrchr(path, '/'); -+ if (last == NULL) { -+ /* Shouldn't happen */ -+ fuse_log( -+ FUSE_LOG_WARNING, -+ "%s: INTERNAL ERROR: bad path read from proc\n", __func__); -+ goto fail_noretry; -+ } -+ if (last == path) { -+ p = &lo->root; -+ pthread_mutex_lock(&lo->mutex); -+ p->refcount++; -+ pthread_mutex_unlock(&lo->mutex); -+ } else { -+ *last = '\0'; -+ res = fstatat(AT_FDCWD, last == path ? "/" : path, &stat, 0); -+ if (res == -1) { -+ if (!retries) { -+ fuse_log(FUSE_LOG_WARNING, -+ "%s: failed to stat parent: %m\n", __func__); -+ } -+ goto fail; -+ } -+ p = lo_find(lo, &stat); -+ if (p == NULL) { -+ if (!retries) { -+ fuse_log(FUSE_LOG_WARNING, -+ "%s: failed to find parent\n", __func__); -+ } -+ goto fail; -+ } -+ } -+ last++; -+ res = fstatat(p->fd, last, &stat, AT_SYMLINK_NOFOLLOW); -+ if (res == -1) { -+ if (!retries) { -+ fuse_log(FUSE_LOG_WARNING, -+ "%s: failed to stat last\n", __func__); -+ } -+ goto fail_unref; -+ } -+ if (stat.st_dev != inode->dev || stat.st_ino != inode->ino) { -+ if (!retries) { -+ fuse_log(FUSE_LOG_WARNING, -+ "%s: failed to match last\n", __func__); -+ } -+ goto fail_unref; -+ } -+ *parent = p; -+ memmove(path, last, strlen(last) + 1); -+ -+ return 0; -+ -+fail_unref: -+ unref_inode(lo, p, 1); -+fail: -+ if (retries) { -+ retries--; -+ goto retry; -+ } -+fail_noretry: -+ errno = EIO; -+ return -1; -+} -+ -+static int utimensat_empty(struct lo_data *lo, struct lo_inode *inode, -+ const struct timespec *tv) -+{ -+ int res; -+ struct lo_inode *parent; -+ char path[PATH_MAX]; - - if (inode->is_symlink) { -- res = utimensat(inode->fd, "", tv, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -+ res = utimensat(inode->fd, "", tv, AT_EMPTY_PATH); - if (res == -1 && errno == EINVAL) { - /* Sorry, no race free way to set times on symlink. */ -- errno = EPERM; -+ if (lo->norace) { -+ errno = EPERM; -+ } else { -+ goto fallback; -+ } - } - return res; - } -- sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ sprintf(path, "/proc/self/fd/%i", inode->fd); - -- return utimensat(AT_FDCWD, procname, tv, 0); -+ return utimensat(AT_FDCWD, path, tv, 0); -+ -+fallback: -+ res = lo_parent_and_name(lo, inode, path, &parent); -+ if (res != -1) { -+ res = utimensat(parent->fd, path, tv, AT_SYMLINK_NOFOLLOW); -+ unref_inode(lo, parent, 1); -+ } -+ -+ return res; - } - - static int lo_fi_fd(fuse_req_t req, struct fuse_file_info *fi) -@@ -387,6 +497,7 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, - { - int saverr; - char procname[64]; -+ struct lo_data *lo = lo_data(req); - struct lo_inode *inode; - int ifd; - int res; -@@ -459,7 +570,7 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, - if (fi) { - res = futimens(fd, tv); - } else { -- res = utimensat_empty_nofollow(inode, tv); -+ res = utimensat_empty(lo, inode, tv); - } - if (res == -1) { - goto out_err; -@@ -709,24 +820,38 @@ static void lo_symlink(fuse_req_t req, const char *link, fuse_ino_t parent, - lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link); - } - --static int linkat_empty_nofollow(struct lo_inode *inode, int dfd, -- const char *name) -+static int linkat_empty_nofollow(struct lo_data *lo, struct lo_inode *inode, -+ int dfd, const char *name) - { - int res; -- char procname[64]; -+ struct lo_inode *parent; -+ char path[PATH_MAX]; - - if (inode->is_symlink) { - res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH); - if (res == -1 && (errno == ENOENT || errno == EINVAL)) { - /* Sorry, no race free way to hard-link a symlink. */ -- errno = EPERM; -+ if (lo->norace) { -+ errno = EPERM; -+ } else { -+ goto fallback; -+ } - } - return res; - } - -- sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ sprintf(path, "/proc/self/fd/%i", inode->fd); -+ -+ return linkat(AT_FDCWD, path, dfd, name, AT_SYMLINK_FOLLOW); -+ -+fallback: -+ res = lo_parent_and_name(lo, inode, path, &parent); -+ if (res != -1) { -+ res = linkat(parent->fd, path, dfd, name, 0); -+ unref_inode(lo, parent, 1); -+ } - -- return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW); -+ return res; - } - - static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, -@@ -748,7 +873,7 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, - e.attr_timeout = lo->timeout; - e.entry_timeout = lo->timeout; - -- res = linkat_empty_nofollow(inode, lo_fd(req, parent), name); -+ res = linkat_empty_nofollow(lo, inode, lo_fd(req, parent), name); - if (res == -1) { - goto out_err; - } diff --git a/0049-virtiofsd-validate-path-components.patch b/0049-virtiofsd-validate-path-components.patch deleted file mode 100644 index 1e5bd2b..0000000 --- a/0049-virtiofsd-validate-path-components.patch +++ /dev/null @@ -1,148 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:18 +0000 -Subject: [PATCH] virtiofsd: validate path components -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Several FUSE requests contain single path components. A correct FUSE -client sends well-formed path components but there is currently no input -validation in case something went wrong or the client is malicious. - -Refuse ".", "..", and paths containing '/' when we expect a path -component. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 25dae28c58d7e706b5d5db99042c9db3cef2e657) ---- - tools/virtiofsd/passthrough_ll.c | 59 ++++++++++++++++++++++++++++---- - 1 file changed, 53 insertions(+), 6 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index ac380efcb1..e375406160 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -133,6 +133,21 @@ static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n); - - static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st); - -+static int is_dot_or_dotdot(const char *name) -+{ -+ return name[0] == '.' && -+ (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); -+} -+ -+/* Is `path` a single path component that is not "." or ".."? */ -+static int is_safe_path_component(const char *path) -+{ -+ if (strchr(path, '/')) { -+ return 0; -+ } -+ -+ return !is_dot_or_dotdot(path); -+} - - static struct lo_data *lo_data(fuse_req_t req) - { -@@ -681,6 +696,15 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) - parent, name); - } - -+ /* -+ * Don't use is_safe_path_component(), allow "." and ".." for NFS export -+ * support. -+ */ -+ if (strchr(name, '/')) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - err = lo_do_lookup(req, parent, name, &e); - if (err) { - fuse_reply_err(req, err); -@@ -762,6 +786,11 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, - struct fuse_entry_param e; - struct lo_cred old = {}; - -+ if (!is_safe_path_component(name)) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - dir = lo_inode(req, parent); - if (!dir) { - fuse_reply_err(req, EBADF); -@@ -863,6 +892,11 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, - struct fuse_entry_param e; - int saverr; - -+ if (!is_safe_path_component(name)) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - inode = lo_inode(req, ino); - if (!inode) { - fuse_reply_err(req, EBADF); -@@ -904,6 +938,10 @@ out_err: - static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) - { - int res; -+ if (!is_safe_path_component(name)) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); - -@@ -916,6 +954,11 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, - { - int res; - -+ if (!is_safe_path_component(name) || !is_safe_path_component(newname)) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - if (flags) { - fuse_reply_err(req, EINVAL); - return; -@@ -930,6 +973,11 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) - { - int res; - -+ if (!is_safe_path_component(name)) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - res = unlinkat(lo_fd(req, parent), name, 0); - - fuse_reply_err(req, res == -1 ? errno : 0); -@@ -1093,12 +1141,6 @@ out_err: - fuse_reply_err(req, error); - } - --static int is_dot_or_dotdot(const char *name) --{ -- return name[0] == '.' && -- (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')); --} -- - static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, - off_t offset, struct fuse_file_info *fi, int plus) - { -@@ -1248,6 +1290,11 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, - parent, name); - } - -+ if (!is_safe_path_component(name)) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - err = lo_change_cred(req, &old); - if (err) { - goto out; diff --git a/0050-virtiofsd-Plumb-fuse_bufvec-through-to-do_write_buf.patch b/0050-virtiofsd-Plumb-fuse_bufvec-through-to-do_write_buf.patch deleted file mode 100644 index 451e3b9..0000000 --- a/0050-virtiofsd-Plumb-fuse_bufvec-through-to-do_write_buf.patch +++ /dev/null @@ -1,149 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:19 +0000 -Subject: [PATCH] virtiofsd: Plumb fuse_bufvec through to do_write_buf - -Let fuse_session_process_buf_int take a fuse_bufvec * instead of a -fuse_buf; and then through to do_write_buf - where in the best -case it can pass that straight through to op.write_buf without copying -(other than skipping a header). - -Signed-off-by: Dr. David Alan Gilbert -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Masayoshi Mizuma -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 469f9d2fc405b0508e6cf1b4b5bbcadfc82064e5) ---- - tools/virtiofsd/fuse_i.h | 2 +- - tools/virtiofsd/fuse_lowlevel.c | 61 ++++++++++++++++++++++----------- - tools/virtiofsd/fuse_virtio.c | 3 +- - 3 files changed, 44 insertions(+), 22 deletions(-) - -diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h -index 45995f3246..a20854f1c4 100644 ---- a/tools/virtiofsd/fuse_i.h -+++ b/tools/virtiofsd/fuse_i.h -@@ -100,7 +100,7 @@ int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, - void fuse_free_req(fuse_req_t req); - - void fuse_session_process_buf_int(struct fuse_session *se, -- const struct fuse_buf *buf, -+ struct fuse_bufvec *bufv, - struct fuse_chan *ch); - - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 95f4db8fcf..7e10995adc 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -1004,11 +1004,12 @@ static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - - static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, -- const struct fuse_buf *ibuf) -+ struct fuse_bufvec *ibufv) - { - struct fuse_session *se = req->se; -- struct fuse_bufvec bufv = { -- .buf[0] = *ibuf, -+ struct fuse_bufvec *pbufv = ibufv; -+ struct fuse_bufvec tmpbufv = { -+ .buf[0] = ibufv->buf[0], - .count = 1, - }; - struct fuse_write_in *arg = (struct fuse_write_in *)inarg; -@@ -1018,22 +1019,31 @@ static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, - fi.fh = arg->fh; - fi.writepage = arg->write_flags & FUSE_WRITE_CACHE; - -- fi.lock_owner = arg->lock_owner; -- fi.flags = arg->flags; -- if (!(bufv.buf[0].flags & FUSE_BUF_IS_FD)) { -- bufv.buf[0].mem = PARAM(arg); -- } -- -- bufv.buf[0].size -= -- sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); -- if (bufv.buf[0].size < arg->size) { -- fuse_log(FUSE_LOG_ERR, "fuse: do_write_buf: buffer size too small\n"); -- fuse_reply_err(req, EIO); -- return; -+ if (ibufv->count == 1) { -+ fi.lock_owner = arg->lock_owner; -+ fi.flags = arg->flags; -+ if (!(tmpbufv.buf[0].flags & FUSE_BUF_IS_FD)) { -+ tmpbufv.buf[0].mem = PARAM(arg); -+ } -+ tmpbufv.buf[0].size -= -+ sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); -+ if (tmpbufv.buf[0].size < arg->size) { -+ fuse_log(FUSE_LOG_ERR, -+ "fuse: do_write_buf: buffer size too small\n"); -+ fuse_reply_err(req, EIO); -+ return; -+ } -+ tmpbufv.buf[0].size = arg->size; -+ pbufv = &tmpbufv; -+ } else { -+ /* -+ * Input bufv contains the headers in the first element -+ * and the data in the rest, we need to skip that first element -+ */ -+ ibufv->buf[0].size = 0; - } -- bufv.buf[0].size = arg->size; - -- se->op.write_buf(req, nodeid, &bufv, arg->offset, &fi); -+ se->op.write_buf(req, nodeid, pbufv, arg->offset, &fi); - } - - static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -@@ -2024,13 +2034,24 @@ static const char *opname(enum fuse_opcode opcode) - void fuse_session_process_buf(struct fuse_session *se, - const struct fuse_buf *buf) - { -- fuse_session_process_buf_int(se, buf, NULL); -+ struct fuse_bufvec bufv = { .buf[0] = *buf, .count = 1 }; -+ fuse_session_process_buf_int(se, &bufv, NULL); - } - -+/* -+ * Restriction: -+ * bufv is normally a single entry buffer, except for a write -+ * where (if it's in memory) then the bufv may be multiple entries, -+ * where the first entry contains all headers and subsequent entries -+ * contain data -+ * bufv shall not use any offsets etc to make the data anything -+ * other than contiguous starting from 0. -+ */ - void fuse_session_process_buf_int(struct fuse_session *se, -- const struct fuse_buf *buf, -+ struct fuse_bufvec *bufv, - struct fuse_chan *ch) - { -+ const struct fuse_buf *buf = bufv->buf; - struct fuse_in_header *in; - const void *inarg; - struct fuse_req *req; -@@ -2108,7 +2129,7 @@ void fuse_session_process_buf_int(struct fuse_session *se, - - inarg = (void *)&in[1]; - if (in->opcode == FUSE_WRITE && se->op.write_buf) { -- do_write_buf(req, in->nodeid, inarg, buf); -+ do_write_buf(req, in->nodeid, inarg, bufv); - } else { - fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); - } -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index 635f87756a..fd588a4829 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -501,7 +501,8 @@ static void *fv_queue_thread(void *opaque) - /* TODO! Endianness of header */ - - /* TODO: Add checks for fuse_session_exited */ -- fuse_session_process_buf_int(se, &fbuf, &ch); -+ struct fuse_bufvec bufv = { .buf[0] = fbuf, .count = 1 }; -+ fuse_session_process_buf_int(se, &bufv, &ch); - - if (!qi->reply_sent) { - fuse_log(FUSE_LOG_DEBUG, "%s: elem %d no reply sent\n", diff --git a/0051-virtiofsd-Pass-write-iov-s-all-the-way-through.patch b/0051-virtiofsd-Pass-write-iov-s-all-the-way-through.patch deleted file mode 100644 index d32681d..0000000 --- a/0051-virtiofsd-Pass-write-iov-s-all-the-way-through.patch +++ /dev/null @@ -1,121 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:20 +0000 -Subject: [PATCH] virtiofsd: Pass write iov's all the way through - -Pass the write iov pointing to guest RAM all the way through rather -than copying the data. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Xiao Yang -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit e17f7a580e2c599330ad3a6946be615ca2fe97d9) ---- - tools/virtiofsd/fuse_virtio.c | 79 ++++++++++++++++++++++++++++++++--- - 1 file changed, 73 insertions(+), 6 deletions(-) - -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index fd588a4829..872968f2c8 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -454,6 +454,10 @@ static void *fv_queue_thread(void *opaque) - __func__, qi->qidx, (size_t)evalue, in_bytes, out_bytes); - - while (1) { -+ bool allocated_bufv = false; -+ struct fuse_bufvec bufv; -+ struct fuse_bufvec *pbufv; -+ - /* - * An element contains one request and the space to send our - * response They're spread over multiple descriptors in a -@@ -495,14 +499,76 @@ static void *fv_queue_thread(void *opaque) - __func__, elem->index); - assert(0); /* TODO */ - } -- copy_from_iov(&fbuf, out_num, out_sg); -- fbuf.size = out_len; -+ /* Copy just the first element and look at it */ -+ copy_from_iov(&fbuf, 1, out_sg); -+ -+ if (out_num > 2 && -+ out_sg[0].iov_len == sizeof(struct fuse_in_header) && -+ ((struct fuse_in_header *)fbuf.mem)->opcode == FUSE_WRITE && -+ out_sg[1].iov_len == sizeof(struct fuse_write_in)) { -+ /* -+ * For a write we don't actually need to copy the -+ * data, we can just do it straight out of guest memory -+ * but we must still copy the headers in case the guest -+ * was nasty and changed them while we were using them. -+ */ -+ fuse_log(FUSE_LOG_DEBUG, "%s: Write special case\n", __func__); -+ -+ /* copy the fuse_write_in header after the fuse_in_header */ -+ fbuf.mem += out_sg->iov_len; -+ copy_from_iov(&fbuf, 1, out_sg + 1); -+ fbuf.mem -= out_sg->iov_len; -+ fbuf.size = out_sg[0].iov_len + out_sg[1].iov_len; -+ -+ /* Allocate the bufv, with space for the rest of the iov */ -+ allocated_bufv = true; -+ pbufv = malloc(sizeof(struct fuse_bufvec) + -+ sizeof(struct fuse_buf) * (out_num - 2)); -+ if (!pbufv) { -+ vu_queue_unpop(dev, q, elem, 0); -+ free(elem); -+ fuse_log(FUSE_LOG_ERR, "%s: pbufv malloc failed\n", -+ __func__); -+ goto out; -+ } -+ -+ pbufv->count = 1; -+ pbufv->buf[0] = fbuf; -+ -+ size_t iovindex, pbufvindex; -+ iovindex = 2; /* 2 headers, separate iovs */ -+ pbufvindex = 1; /* 2 headers, 1 fusebuf */ -+ -+ for (; iovindex < out_num; iovindex++, pbufvindex++) { -+ pbufv->count++; -+ pbufv->buf[pbufvindex].pos = ~0; /* Dummy */ -+ pbufv->buf[pbufvindex].flags = 0; -+ pbufv->buf[pbufvindex].mem = out_sg[iovindex].iov_base; -+ pbufv->buf[pbufvindex].size = out_sg[iovindex].iov_len; -+ } -+ } else { -+ /* Normal (non fast write) path */ -+ -+ /* Copy the rest of the buffer */ -+ fbuf.mem += out_sg->iov_len; -+ copy_from_iov(&fbuf, out_num - 1, out_sg + 1); -+ fbuf.mem -= out_sg->iov_len; -+ fbuf.size = out_len; - -- /* TODO! Endianness of header */ -+ /* TODO! Endianness of header */ - -- /* TODO: Add checks for fuse_session_exited */ -- struct fuse_bufvec bufv = { .buf[0] = fbuf, .count = 1 }; -- fuse_session_process_buf_int(se, &bufv, &ch); -+ /* TODO: Add checks for fuse_session_exited */ -+ bufv.buf[0] = fbuf; -+ bufv.count = 1; -+ pbufv = &bufv; -+ } -+ pbufv->idx = 0; -+ pbufv->off = 0; -+ fuse_session_process_buf_int(se, pbufv, &ch); -+ -+ if (allocated_bufv) { -+ free(pbufv); -+ } - - if (!qi->reply_sent) { - fuse_log(FUSE_LOG_DEBUG, "%s: elem %d no reply sent\n", -@@ -516,6 +582,7 @@ static void *fv_queue_thread(void *opaque) - elem = NULL; - } - } -+out: - pthread_mutex_destroy(&ch.lock); - free(fbuf.mem); - diff --git a/0052-virtiofsd-add-fuse_mbuf_iter-API.patch b/0052-virtiofsd-add-fuse_mbuf_iter-API.patch deleted file mode 100644 index 1358aa8..0000000 --- a/0052-virtiofsd-add-fuse_mbuf_iter-API.patch +++ /dev/null @@ -1,115 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:21 +0000 -Subject: [PATCH] virtiofsd: add fuse_mbuf_iter API - -Introduce an API for consuming bytes from a buffer with size checks. -All FUSE operations will be converted to use this safe API instead of -void *inarg. - -Signed-off-by: Stefan Hajnoczi -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit dad157e880416ab3a0e45beaa0e81977516568bc) ---- - tools/virtiofsd/buffer.c | 28 ++++++++++++++++++++ - tools/virtiofsd/fuse_common.h | 49 ++++++++++++++++++++++++++++++++++- - 2 files changed, 76 insertions(+), 1 deletion(-) - -diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c -index 772efa922d..42a608f6bd 100644 ---- a/tools/virtiofsd/buffer.c -+++ b/tools/virtiofsd/buffer.c -@@ -267,3 +267,31 @@ ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv) - - return copied; - } -+ -+void *fuse_mbuf_iter_advance(struct fuse_mbuf_iter *iter, size_t len) -+{ -+ void *ptr; -+ -+ if (len > iter->size - iter->pos) { -+ return NULL; -+ } -+ -+ ptr = iter->mem + iter->pos; -+ iter->pos += len; -+ return ptr; -+} -+ -+const char *fuse_mbuf_iter_advance_str(struct fuse_mbuf_iter *iter) -+{ -+ const char *str = iter->mem + iter->pos; -+ size_t remaining = iter->size - iter->pos; -+ size_t i; -+ -+ for (i = 0; i < remaining; i++) { -+ if (str[i] == '\0') { -+ iter->pos += i + 1; -+ return str; -+ } -+ } -+ return NULL; -+} -diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h -index 0cb33acc2f..f8f6433743 100644 ---- a/tools/virtiofsd/fuse_common.h -+++ b/tools/virtiofsd/fuse_common.h -@@ -703,10 +703,57 @@ size_t fuse_buf_size(const struct fuse_bufvec *bufv); - */ - ssize_t fuse_buf_copy(struct fuse_bufvec *dst, struct fuse_bufvec *src); - -+/** -+ * Memory buffer iterator -+ * -+ */ -+struct fuse_mbuf_iter { -+ /** -+ * Data pointer -+ */ -+ void *mem; -+ -+ /** -+ * Total length, in bytes -+ */ -+ size_t size; -+ -+ /** -+ * Offset from start of buffer -+ */ -+ size_t pos; -+}; -+ -+/* Initialize memory buffer iterator from a fuse_buf */ -+#define FUSE_MBUF_ITER_INIT(fbuf) \ -+ ((struct fuse_mbuf_iter){ \ -+ .mem = fbuf->mem, \ -+ .size = fbuf->size, \ -+ .pos = 0, \ -+ }) -+ -+/** -+ * Consume bytes from a memory buffer iterator -+ * -+ * @param iter memory buffer iterator -+ * @param len number of bytes to consume -+ * @return pointer to start of consumed bytes or -+ * NULL if advancing beyond end of buffer -+ */ -+void *fuse_mbuf_iter_advance(struct fuse_mbuf_iter *iter, size_t len); -+ -+/** -+ * Consume a NUL-terminated string from a memory buffer iterator -+ * -+ * @param iter memory buffer iterator -+ * @return pointer to the string or -+ * NULL if advancing beyond end of buffer or there is no NUL-terminator -+ */ -+const char *fuse_mbuf_iter_advance_str(struct fuse_mbuf_iter *iter); -+ - /* - * Signal handling - */ -- - /** - * Exit session on HUP, TERM and INT signals and ignore PIPE signal - * diff --git a/0053-virtiofsd-validate-input-buffer-sizes-in-do_write_bu.patch b/0053-virtiofsd-validate-input-buffer-sizes-in-do_write_bu.patch deleted file mode 100644 index d5ad1dd..0000000 --- a/0053-virtiofsd-validate-input-buffer-sizes-in-do_write_bu.patch +++ /dev/null @@ -1,117 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:22 +0000 -Subject: [PATCH] virtiofsd: validate input buffer sizes in do_write_buf() - -There is a small change in behavior: if fuse_write_in->size doesn't -match the input buffer size then the request is failed. Previously -write requests with 1 fuse_buf element would truncate to -fuse_write_in->size. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Sergio Lopez -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 0ba8c3c6fce8fe949d59c1fd84d98d220ef9e759) ---- - tools/virtiofsd/fuse_lowlevel.c | 49 ++++++++++++++++++++------------- - 1 file changed, 30 insertions(+), 19 deletions(-) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 7e10995adc..611e8b0354 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -1003,8 +1003,8 @@ static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, -- struct fuse_bufvec *ibufv) -+static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter, struct fuse_bufvec *ibufv) - { - struct fuse_session *se = req->se; - struct fuse_bufvec *pbufv = ibufv; -@@ -1012,28 +1012,27 @@ static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, - .buf[0] = ibufv->buf[0], - .count = 1, - }; -- struct fuse_write_in *arg = (struct fuse_write_in *)inarg; -+ struct fuse_write_in *arg; -+ size_t arg_size = sizeof(*arg); - struct fuse_file_info fi; - - memset(&fi, 0, sizeof(fi)); -+ -+ arg = fuse_mbuf_iter_advance(iter, arg_size); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ -+ fi.lock_owner = arg->lock_owner; -+ fi.flags = arg->flags; - fi.fh = arg->fh; - fi.writepage = arg->write_flags & FUSE_WRITE_CACHE; - - if (ibufv->count == 1) { -- fi.lock_owner = arg->lock_owner; -- fi.flags = arg->flags; -- if (!(tmpbufv.buf[0].flags & FUSE_BUF_IS_FD)) { -- tmpbufv.buf[0].mem = PARAM(arg); -- } -- tmpbufv.buf[0].size -= -- sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in); -- if (tmpbufv.buf[0].size < arg->size) { -- fuse_log(FUSE_LOG_ERR, -- "fuse: do_write_buf: buffer size too small\n"); -- fuse_reply_err(req, EIO); -- return; -- } -- tmpbufv.buf[0].size = arg->size; -+ assert(!(tmpbufv.buf[0].flags & FUSE_BUF_IS_FD)); -+ tmpbufv.buf[0].mem = ((char *)arg) + arg_size; -+ tmpbufv.buf[0].size -= sizeof(struct fuse_in_header) + arg_size; - pbufv = &tmpbufv; - } else { - /* -@@ -1043,6 +1042,13 @@ static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, const void *inarg, - ibufv->buf[0].size = 0; - } - -+ if (fuse_buf_size(pbufv) != arg->size) { -+ fuse_log(FUSE_LOG_ERR, -+ "fuse: do_write_buf: buffer size doesn't match arg->size\n"); -+ fuse_reply_err(req, EIO); -+ return; -+ } -+ - se->op.write_buf(req, nodeid, pbufv, arg->offset, &fi); - } - -@@ -2052,12 +2058,17 @@ void fuse_session_process_buf_int(struct fuse_session *se, - struct fuse_chan *ch) - { - const struct fuse_buf *buf = bufv->buf; -+ struct fuse_mbuf_iter iter = FUSE_MBUF_ITER_INIT(buf); - struct fuse_in_header *in; - const void *inarg; - struct fuse_req *req; - int err; - -- in = buf->mem; -+ /* The first buffer must be a memory buffer */ -+ assert(!(buf->flags & FUSE_BUF_IS_FD)); -+ -+ in = fuse_mbuf_iter_advance(&iter, sizeof(*in)); -+ assert(in); /* caller guarantees the input buffer is large enough */ - - if (se->debug) { - fuse_log(FUSE_LOG_DEBUG, -@@ -2129,7 +2140,7 @@ void fuse_session_process_buf_int(struct fuse_session *se, - - inarg = (void *)&in[1]; - if (in->opcode == FUSE_WRITE && se->op.write_buf) { -- do_write_buf(req, in->nodeid, inarg, bufv); -+ do_write_buf(req, in->nodeid, &iter, bufv); - } else { - fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); - } diff --git a/0054-virtiofsd-check-input-buffer-size-in-fuse_lowlevel.c.patch b/0054-virtiofsd-check-input-buffer-size-in-fuse_lowlevel.c.patch deleted file mode 100644 index 6746752..0000000 --- a/0054-virtiofsd-check-input-buffer-size-in-fuse_lowlevel.c.patch +++ /dev/null @@ -1,1091 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:23 +0000 -Subject: [PATCH] virtiofsd: check input buffer size in fuse_lowlevel.c ops - -Each FUSE operation involves parsing the input buffer. Currently the -code assumes the input buffer is large enough for the expected -arguments. This patch uses fuse_mbuf_iter to check the size. - -Most operations are simple to convert. Some are more complicated due to -variable-length inputs or different sizes depending on the protocol -version. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Sergio Lopez -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 70995754416eb4491c31607fe380a83cfd25a087) ---- - tools/virtiofsd/fuse_lowlevel.c | 581 +++++++++++++++++++++++++------- - 1 file changed, 456 insertions(+), 125 deletions(-) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 611e8b0354..02e1d83038 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -19,6 +19,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -27,7 +28,6 @@ - #include - - --#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg))) - #define OFFSET_MAX 0x7fffffffffffffffLL - - struct fuse_pollhandle { -@@ -706,9 +706,14 @@ int fuse_reply_lseek(fuse_req_t req, off_t off) - return send_reply_ok(req, &arg, sizeof(arg)); - } - --static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- char *name = (char *)inarg; -+ const char *name = fuse_mbuf_iter_advance_str(iter); -+ if (!name) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - if (req->se->op.lookup) { - req->se->op.lookup(req, nodeid, name); -@@ -717,9 +722,16 @@ static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_forget(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_forget_in *arg = (struct fuse_forget_in *)inarg; -+ struct fuse_forget_in *arg; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - if (req->se->op.forget) { - req->se->op.forget(req, nodeid, arg->nlookup); -@@ -729,20 +741,48 @@ static void do_forget(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - - static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid, -- const void *inarg) -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_batch_forget_in *arg = (void *)inarg; -- struct fuse_forget_one *param = (void *)PARAM(arg); -- unsigned int i; -+ struct fuse_batch_forget_in *arg; -+ struct fuse_forget_data *forgets; -+ size_t scount; - - (void)nodeid; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_none(req); -+ return; -+ } -+ -+ /* -+ * Prevent integer overflow. The compiler emits the following warning -+ * unless we use the scount local variable: -+ * -+ * error: comparison is always false due to limited range of data type -+ * [-Werror=type-limits] -+ * -+ * This may be true on 64-bit hosts but we need this check for 32-bit -+ * hosts. -+ */ -+ scount = arg->count; -+ if (scount > SIZE_MAX / sizeof(forgets[0])) { -+ fuse_reply_none(req); -+ return; -+ } -+ -+ forgets = fuse_mbuf_iter_advance(iter, arg->count * sizeof(forgets[0])); -+ if (!forgets) { -+ fuse_reply_none(req); -+ return; -+ } -+ - if (req->se->op.forget_multi) { -- req->se->op.forget_multi(req, arg->count, -- (struct fuse_forget_data *)param); -+ req->se->op.forget_multi(req, arg->count, forgets); - } else if (req->se->op.forget) { -+ unsigned int i; -+ - for (i = 0; i < arg->count; i++) { -- struct fuse_forget_one *forget = ¶m[i]; - struct fuse_req *dummy_req; - - dummy_req = fuse_ll_alloc_req(req->se); -@@ -754,7 +794,7 @@ static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid, - dummy_req->ctx = req->ctx; - dummy_req->ch = NULL; - -- req->se->op.forget(dummy_req, forget->nodeid, forget->nlookup); -+ req->se->op.forget(dummy_req, forgets[i].ino, forgets[i].nlookup); - } - fuse_reply_none(req); - } else { -@@ -762,12 +802,19 @@ static void do_batch_forget(fuse_req_t req, fuse_ino_t nodeid, - } - } - --static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { - struct fuse_file_info *fip = NULL; - struct fuse_file_info fi; - -- struct fuse_getattr_in *arg = (struct fuse_getattr_in *)inarg; -+ struct fuse_getattr_in *arg; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - if (arg->getattr_flags & FUSE_GETATTR_FH) { - memset(&fi, 0, sizeof(fi)); -@@ -782,14 +829,21 @@ static void do_getattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_setattr_in *arg = (struct fuse_setattr_in *)inarg; -- - if (req->se->op.setattr) { -+ struct fuse_setattr_in *arg; - struct fuse_file_info *fi = NULL; - struct fuse_file_info fi_store; - struct stat stbuf; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - memset(&stbuf, 0, sizeof(stbuf)); - convert_attr(arg, &stbuf); - if (arg->valid & FATTR_FH) { -@@ -810,9 +864,16 @@ static void do_setattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_access(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_access_in *arg = (struct fuse_access_in *)inarg; -+ struct fuse_access_in *arg; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - if (req->se->op.access) { - req->se->op.access(req, nodeid, arg->mask); -@@ -821,9 +882,10 @@ static void do_access(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- (void)inarg; -+ (void)iter; - - if (req->se->op.readlink) { - req->se->op.readlink(req, nodeid); -@@ -832,10 +894,18 @@ static void do_readlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_mknod_in *arg = (struct fuse_mknod_in *)inarg; -- char *name = PARAM(arg); -+ struct fuse_mknod_in *arg; -+ const char *name; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ name = fuse_mbuf_iter_advance_str(iter); -+ if (!arg || !name) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - req->ctx.umask = arg->umask; - -@@ -846,22 +916,37 @@ static void do_mknod(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_mkdir(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_mkdir_in *arg = (struct fuse_mkdir_in *)inarg; -+ struct fuse_mkdir_in *arg; -+ const char *name; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ name = fuse_mbuf_iter_advance_str(iter); -+ if (!arg || !name) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - req->ctx.umask = arg->umask; - - if (req->se->op.mkdir) { -- req->se->op.mkdir(req, nodeid, PARAM(arg), arg->mode); -+ req->se->op.mkdir(req, nodeid, name, arg->mode); - } else { - fuse_reply_err(req, ENOSYS); - } - } - --static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- char *name = (char *)inarg; -+ const char *name = fuse_mbuf_iter_advance_str(iter); -+ -+ if (!name) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - if (req->se->op.unlink) { - req->se->op.unlink(req, nodeid, name); -@@ -870,9 +955,15 @@ static void do_unlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- char *name = (char *)inarg; -+ const char *name = fuse_mbuf_iter_advance_str(iter); -+ -+ if (!name) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - if (req->se->op.rmdir) { - req->se->op.rmdir(req, nodeid, name); -@@ -881,10 +972,16 @@ static void do_rmdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- char *name = (char *)inarg; -- char *linkname = ((char *)inarg) + strlen((char *)inarg) + 1; -+ const char *name = fuse_mbuf_iter_advance_str(iter); -+ const char *linkname = fuse_mbuf_iter_advance_str(iter); -+ -+ if (!name || !linkname) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - if (req->se->op.symlink) { - req->se->op.symlink(req, linkname, nodeid, name); -@@ -893,11 +990,20 @@ static void do_symlink(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_rename(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_rename_in *arg = (struct fuse_rename_in *)inarg; -- char *oldname = PARAM(arg); -- char *newname = oldname + strlen(oldname) + 1; -+ struct fuse_rename_in *arg; -+ const char *oldname; -+ const char *newname; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ oldname = fuse_mbuf_iter_advance_str(iter); -+ newname = fuse_mbuf_iter_advance_str(iter); -+ if (!arg || !oldname || !newname) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - if (req->se->op.rename) { - req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, 0); -@@ -906,11 +1012,20 @@ static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_rename2_in *arg = (struct fuse_rename2_in *)inarg; -- char *oldname = PARAM(arg); -- char *newname = oldname + strlen(oldname) + 1; -+ struct fuse_rename2_in *arg; -+ const char *oldname; -+ const char *newname; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ oldname = fuse_mbuf_iter_advance_str(iter); -+ newname = fuse_mbuf_iter_advance_str(iter); -+ if (!arg || !oldname || !newname) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - if (req->se->op.rename) { - req->se->op.rename(req, nodeid, oldname, arg->newdir, newname, -@@ -920,24 +1035,38 @@ static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_link(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_link(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_link_in *arg = (struct fuse_link_in *)inarg; -+ struct fuse_link_in *arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ const char *name = fuse_mbuf_iter_advance_str(iter); -+ -+ if (!arg || !name) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - if (req->se->op.link) { -- req->se->op.link(req, arg->oldnodeid, nodeid, PARAM(arg)); -+ req->se->op.link(req, arg->oldnodeid, nodeid, name); - } else { - fuse_reply_err(req, ENOSYS); - } - } - --static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_create(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_create_in *arg = (struct fuse_create_in *)inarg; -- - if (req->se->op.create) { -+ struct fuse_create_in *arg; - struct fuse_file_info fi; -- char *name = PARAM(arg); -+ const char *name; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ name = fuse_mbuf_iter_advance_str(iter); -+ if (!arg || !name) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - memset(&fi, 0, sizeof(fi)); - fi.flags = arg->flags; -@@ -950,11 +1079,18 @@ static void do_create(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_open(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_open_in *arg = (struct fuse_open_in *)inarg; -+ struct fuse_open_in *arg; - struct fuse_file_info fi; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - memset(&fi, 0, sizeof(fi)); - fi.flags = arg->flags; - -@@ -965,13 +1101,15 @@ static void do_open(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_read(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_read_in *arg = (struct fuse_read_in *)inarg; -- - if (req->se->op.read) { -+ struct fuse_read_in *arg; - struct fuse_file_info fi; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; - fi.lock_owner = arg->lock_owner; -@@ -982,11 +1120,24 @@ static void do_read(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_write(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_write_in *arg = (struct fuse_write_in *)inarg; -+ struct fuse_write_in *arg; - struct fuse_file_info fi; -- char *param; -+ const char *param; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ -+ param = fuse_mbuf_iter_advance(iter, arg->size); -+ if (!param) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; -@@ -994,7 +1145,6 @@ static void do_write(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - - fi.lock_owner = arg->lock_owner; - fi.flags = arg->flags; -- param = PARAM(arg); - - if (req->se->op.write) { - req->se->op.write(req, nodeid, param, arg->size, arg->offset, &fi); -@@ -1052,11 +1202,18 @@ static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, - se->op.write_buf(req, nodeid, pbufv, arg->offset, &fi); - } - --static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_flush(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_flush_in *arg = (struct fuse_flush_in *)inarg; -+ struct fuse_flush_in *arg; - struct fuse_file_info fi; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; - fi.flush = 1; -@@ -1069,19 +1226,26 @@ static void do_flush(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_release(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_release_in *arg = (struct fuse_release_in *)inarg; -+ struct fuse_release_in *arg; - struct fuse_file_info fi; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - memset(&fi, 0, sizeof(fi)); - fi.flags = arg->flags; - fi.fh = arg->fh; - fi.flush = (arg->release_flags & FUSE_RELEASE_FLUSH) ? 1 : 0; - fi.lock_owner = arg->lock_owner; -+ - if (arg->release_flags & FUSE_RELEASE_FLOCK_UNLOCK) { - fi.flock_release = 1; -- fi.lock_owner = arg->lock_owner; - } - - if (req->se->op.release) { -@@ -1091,11 +1255,19 @@ static void do_release(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_fsync_in *arg = (struct fuse_fsync_in *)inarg; -+ struct fuse_fsync_in *arg; - struct fuse_file_info fi; -- int datasync = arg->fsync_flags & 1; -+ int datasync; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ datasync = arg->fsync_flags & 1; - - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; -@@ -1111,11 +1283,18 @@ static void do_fsync(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_open_in *arg = (struct fuse_open_in *)inarg; -+ struct fuse_open_in *arg; - struct fuse_file_info fi; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - memset(&fi, 0, sizeof(fi)); - fi.flags = arg->flags; - -@@ -1126,11 +1305,18 @@ static void do_opendir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_read_in *arg = (struct fuse_read_in *)inarg; -+ struct fuse_read_in *arg; - struct fuse_file_info fi; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; - -@@ -1141,11 +1327,18 @@ static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_read_in *arg = (struct fuse_read_in *)inarg; -+ struct fuse_read_in *arg; - struct fuse_file_info fi; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; - -@@ -1156,11 +1349,18 @@ static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_release_in *arg = (struct fuse_release_in *)inarg; -+ struct fuse_release_in *arg; - struct fuse_file_info fi; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - memset(&fi, 0, sizeof(fi)); - fi.flags = arg->flags; - fi.fh = arg->fh; -@@ -1172,11 +1372,19 @@ static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_fsync_in *arg = (struct fuse_fsync_in *)inarg; -+ struct fuse_fsync_in *arg; - struct fuse_file_info fi; -- int datasync = arg->fsync_flags & 1; -+ int datasync; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ datasync = arg->fsync_flags & 1; - - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; -@@ -1188,10 +1396,11 @@ static void do_fsyncdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { - (void)nodeid; -- (void)inarg; -+ (void)iter; - - if (req->se->op.statfs) { - req->se->op.statfs(req, nodeid); -@@ -1204,11 +1413,25 @@ static void do_statfs(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_setxattr_in *arg = (struct fuse_setxattr_in *)inarg; -- char *name = PARAM(arg); -- char *value = name + strlen(name) + 1; -+ struct fuse_setxattr_in *arg; -+ const char *name; -+ const char *value; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ name = fuse_mbuf_iter_advance_str(iter); -+ if (!arg || !name) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ -+ value = fuse_mbuf_iter_advance(iter, arg->size); -+ if (!value) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - if (req->se->op.setxattr) { - req->se->op.setxattr(req, nodeid, name, value, arg->size, arg->flags); -@@ -1217,20 +1440,36 @@ static void do_setxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_getxattr(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *)inarg; -+ struct fuse_getxattr_in *arg; -+ const char *name; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ name = fuse_mbuf_iter_advance_str(iter); -+ if (!arg || !name) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - if (req->se->op.getxattr) { -- req->se->op.getxattr(req, nodeid, PARAM(arg), arg->size); -+ req->se->op.getxattr(req, nodeid, name, arg->size); - } else { - fuse_reply_err(req, ENOSYS); - } - } - --static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_getxattr_in *arg = (struct fuse_getxattr_in *)inarg; -+ struct fuse_getxattr_in *arg; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - if (req->se->op.listxattr) { - req->se->op.listxattr(req, nodeid, arg->size); -@@ -1239,9 +1478,15 @@ static void do_listxattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_removexattr(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- char *name = (char *)inarg; -+ const char *name = fuse_mbuf_iter_advance_str(iter); -+ -+ if (!name) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - if (req->se->op.removexattr) { - req->se->op.removexattr(req, nodeid, name); -@@ -1265,12 +1510,19 @@ static void convert_fuse_file_lock(struct fuse_file_lock *fl, - flock->l_pid = fl->pid; - } - --static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_lk_in *arg = (struct fuse_lk_in *)inarg; -+ struct fuse_lk_in *arg; - struct fuse_file_info fi; - struct flock flock; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; - fi.lock_owner = arg->owner; -@@ -1284,12 +1536,18 @@ static void do_getlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - - static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, -- const void *inarg, int sleep) -+ struct fuse_mbuf_iter *iter, int sleep) - { -- struct fuse_lk_in *arg = (struct fuse_lk_in *)inarg; -+ struct fuse_lk_in *arg; - struct fuse_file_info fi; - struct flock flock; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; - fi.lock_owner = arg->owner; -@@ -1327,14 +1585,16 @@ static void do_setlk_common(fuse_req_t req, fuse_ino_t nodeid, - } - } - --static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_setlk(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- do_setlk_common(req, nodeid, inarg, 0); -+ do_setlk_common(req, nodeid, iter, 0); - } - --static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_setlkw(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- do_setlk_common(req, nodeid, inarg, 1); -+ do_setlk_common(req, nodeid, iter, 1); - } - - static int find_interrupted(struct fuse_session *se, struct fuse_req *req) -@@ -1379,12 +1639,20 @@ static int find_interrupted(struct fuse_session *se, struct fuse_req *req) - return 0; - } - --static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_interrupt_in *arg = (struct fuse_interrupt_in *)inarg; -+ struct fuse_interrupt_in *arg; - struct fuse_session *se = req->se; - - (void)nodeid; -+ -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - if (se->debug) { - fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n", - (unsigned long long)arg->unique); -@@ -1425,9 +1693,15 @@ static struct fuse_req *check_interrupt(struct fuse_session *se, - } - } - --static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_bmap_in *arg = (struct fuse_bmap_in *)inarg; -+ struct fuse_bmap_in *arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - - if (req->se->op.bmap) { - req->se->op.bmap(req, nodeid, arg->blocksize, arg->block); -@@ -1436,18 +1710,34 @@ static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *)inarg; -- unsigned int flags = arg->flags; -- void *in_buf = arg->in_size ? PARAM(arg) : NULL; -+ struct fuse_ioctl_in *arg; -+ unsigned int flags; -+ void *in_buf = NULL; - struct fuse_file_info fi; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ -+ flags = arg->flags; - if (flags & FUSE_IOCTL_DIR && !(req->se->conn.want & FUSE_CAP_IOCTL_DIR)) { - fuse_reply_err(req, ENOTTY); - return; - } - -+ if (arg->in_size) { -+ in_buf = fuse_mbuf_iter_advance(iter, arg->in_size); -+ if (!in_buf) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ } -+ - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; - -@@ -1468,11 +1758,18 @@ void fuse_pollhandle_destroy(struct fuse_pollhandle *ph) - free(ph); - } - --static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_poll(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_poll_in *arg = (struct fuse_poll_in *)inarg; -+ struct fuse_poll_in *arg; - struct fuse_file_info fi; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; - fi.poll_events = arg->events; -@@ -1496,11 +1793,18 @@ static void do_poll(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_fallocate_in *arg = (struct fuse_fallocate_in *)inarg; -+ struct fuse_fallocate_in *arg; - struct fuse_file_info fi; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; - -@@ -1513,12 +1817,17 @@ static void do_fallocate(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - - static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, -- const void *inarg) -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_copy_file_range_in *arg = -- (struct fuse_copy_file_range_in *)inarg; -+ struct fuse_copy_file_range_in *arg; - struct fuse_file_info fi_in, fi_out; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ - memset(&fi_in, 0, sizeof(fi_in)); - fi_in.fh = arg->fh_in; - -@@ -1535,11 +1844,17 @@ static void do_copy_file_range(fuse_req_t req, fuse_ino_t nodeid_in, - } - } - --static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_lseek_in *arg = (struct fuse_lseek_in *)inarg; -+ struct fuse_lseek_in *arg; - struct fuse_file_info fi; - -+ arg = fuse_mbuf_iter_advance(iter, sizeof(*arg)); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; - -@@ -1550,15 +1865,33 @@ static void do_lseek(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - } - } - --static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_init(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { -- struct fuse_init_in *arg = (struct fuse_init_in *)inarg; -+ size_t compat_size = offsetof(struct fuse_init_in, max_readahead); -+ struct fuse_init_in *arg; - struct fuse_init_out outarg; - struct fuse_session *se = req->se; - size_t bufsize = se->bufsize; - size_t outargsize = sizeof(outarg); - - (void)nodeid; -+ -+ /* First consume the old fields... */ -+ arg = fuse_mbuf_iter_advance(iter, compat_size); -+ if (!arg) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ -+ /* ...and now consume the new fields. */ -+ if (arg->major == 7 && arg->minor >= 6) { -+ if (!fuse_mbuf_iter_advance(iter, sizeof(*arg) - compat_size)) { -+ fuse_reply_err(req, EINVAL); -+ return; -+ } -+ } -+ - if (se->debug) { - fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor); - if (arg->major == 7 && arg->minor >= 6) { -@@ -1791,12 +2124,13 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) - send_reply_ok(req, &outarg, outargsize); - } - --static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, const void *inarg) -+static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, -+ struct fuse_mbuf_iter *iter) - { - struct fuse_session *se = req->se; - - (void)nodeid; -- (void)inarg; -+ (void)iter; - - se->got_destroy = 1; - if (se->op.destroy) { -@@ -1976,7 +2310,7 @@ int fuse_req_interrupted(fuse_req_t req) - } - - static struct { -- void (*func)(fuse_req_t, fuse_ino_t, const void *); -+ void (*func)(fuse_req_t, fuse_ino_t, struct fuse_mbuf_iter *); - const char *name; - } fuse_ll_ops[] = { - [FUSE_LOOKUP] = { do_lookup, "LOOKUP" }, -@@ -2060,7 +2394,6 @@ void fuse_session_process_buf_int(struct fuse_session *se, - const struct fuse_buf *buf = bufv->buf; - struct fuse_mbuf_iter iter = FUSE_MBUF_ITER_INIT(buf); - struct fuse_in_header *in; -- const void *inarg; - struct fuse_req *req; - int err; - -@@ -2138,13 +2471,11 @@ void fuse_session_process_buf_int(struct fuse_session *se, - } - } - -- inarg = (void *)&in[1]; - if (in->opcode == FUSE_WRITE && se->op.write_buf) { - do_write_buf(req, in->nodeid, &iter, bufv); - } else { -- fuse_ll_ops[in->opcode].func(req, in->nodeid, inarg); -+ fuse_ll_ops[in->opcode].func(req, in->nodeid, &iter); - } -- - return; - - reply_err: diff --git a/0055-virtiofsd-prevent-.-escape-in-lo_do_lookup.patch b/0055-virtiofsd-prevent-.-escape-in-lo_do_lookup.patch deleted file mode 100644 index 00894ad..0000000 --- a/0055-virtiofsd-prevent-.-escape-in-lo_do_lookup.patch +++ /dev/null @@ -1,35 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:24 +0000 -Subject: [PATCH] virtiofsd: prevent ".." escape in lo_do_lookup() - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Sergio Lopez -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 854684bc0b3d63eb90b3abdfe471c2e4271ef176) ---- - tools/virtiofsd/passthrough_ll.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index e375406160..79d5966eea 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -624,12 +624,17 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - int res; - int saverr; - struct lo_data *lo = lo_data(req); -- struct lo_inode *inode; -+ struct lo_inode *inode, *dir = lo_inode(req, parent); - - memset(e, 0, sizeof(*e)); - e->attr_timeout = lo->timeout; - e->entry_timeout = lo->timeout; - -+ /* Do not allow escaping root directory */ -+ if (dir == &lo->root && strcmp(name, "..") == 0) { -+ name = "."; -+ } -+ - newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW); - if (newfd == -1) { - goto out_err; diff --git a/0056-virtiofsd-prevent-.-escape-in-lo_do_readdir.patch b/0056-virtiofsd-prevent-.-escape-in-lo_do_readdir.patch deleted file mode 100644 index 47845ba..0000000 --- a/0056-virtiofsd-prevent-.-escape-in-lo_do_readdir.patch +++ /dev/null @@ -1,89 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:25 +0000 -Subject: [PATCH] virtiofsd: prevent ".." escape in lo_do_readdir() - -Construct a fake dirent for the root directory's ".." entry. This hides -the parent directory from the FUSE client. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Sergio Lopez -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 752272da2b68a2312f0e11fc5303015a6c3ee1ac) ---- - tools/virtiofsd/passthrough_ll.c | 36 +++++++++++++++++++------------- - 1 file changed, 22 insertions(+), 14 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 79d5966eea..e3d65c3676 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1149,19 +1149,25 @@ out_err: - static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, - off_t offset, struct fuse_file_info *fi, int plus) - { -+ struct lo_data *lo = lo_data(req); - struct lo_dirp *d; -+ struct lo_inode *dinode; - char *buf = NULL; - char *p; - size_t rem = size; -- int err = ENOMEM; -+ int err = EBADF; - -- (void)ino; -+ dinode = lo_inode(req, ino); -+ if (!dinode) { -+ goto error; -+ } - - d = lo_dirp(req, fi); - if (!d) { - goto error; - } - -+ err = ENOMEM; - buf = calloc(1, size); - if (!buf) { - goto error; -@@ -1192,15 +1198,21 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, - } - nextoff = d->entry->d_off; - name = d->entry->d_name; -+ - fuse_ino_t entry_ino = 0; -+ struct fuse_entry_param e = (struct fuse_entry_param){ -+ .attr.st_ino = d->entry->d_ino, -+ .attr.st_mode = d->entry->d_type << 12, -+ }; -+ -+ /* Hide root's parent directory */ -+ if (dinode == &lo->root && strcmp(name, "..") == 0) { -+ e.attr.st_ino = lo->root.ino; -+ e.attr.st_mode = DT_DIR << 12; -+ } -+ - if (plus) { -- struct fuse_entry_param e; -- if (is_dot_or_dotdot(name)) { -- e = (struct fuse_entry_param){ -- .attr.st_ino = d->entry->d_ino, -- .attr.st_mode = d->entry->d_type << 12, -- }; -- } else { -+ if (!is_dot_or_dotdot(name)) { - err = lo_do_lookup(req, ino, name, &e); - if (err) { - goto error; -@@ -1210,11 +1222,7 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, - - entsize = fuse_add_direntry_plus(req, p, rem, name, &e, nextoff); - } else { -- struct stat st = { -- .st_ino = d->entry->d_ino, -- .st_mode = d->entry->d_type << 12, -- }; -- entsize = fuse_add_direntry(req, p, rem, name, &st, nextoff); -+ entsize = fuse_add_direntry(req, p, rem, name, &e.attr, nextoff); - } - if (entsize > rem) { - if (entry_ino != 0) { diff --git a/0057-virtiofsd-use-proc-self-fd-O_PATH-file-descriptor.patch b/0057-virtiofsd-use-proc-self-fd-O_PATH-file-descriptor.patch deleted file mode 100644 index d3473e0..0000000 --- a/0057-virtiofsd-use-proc-self-fd-O_PATH-file-descriptor.patch +++ /dev/null @@ -1,374 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:26 +0000 -Subject: [PATCH] virtiofsd: use /proc/self/fd/ O_PATH file descriptor -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Sandboxing will remove /proc from the mount namespace so we can no -longer build string paths into "/proc/self/fd/...". - -Keep an O_PATH file descriptor so we can still re-open fds via -/proc/self/fd. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 9f59d175e2ca96f0b87f534dba69ea547dd35945) ---- - tools/virtiofsd/passthrough_ll.c | 130 ++++++++++++++++++++++++------- - 1 file changed, 103 insertions(+), 27 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index e3d65c3676..e2e2211ea1 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -110,6 +110,9 @@ struct lo_data { - struct lo_map ino_map; /* protected by lo->mutex */ - struct lo_map dirp_map; /* protected by lo->mutex */ - struct lo_map fd_map; /* protected by lo->mutex */ -+ -+ /* An O_PATH file descriptor to /proc/self/fd/ */ -+ int proc_self_fd; - }; - - static const struct fuse_opt lo_opts[] = { -@@ -379,9 +382,9 @@ static int lo_parent_and_name(struct lo_data *lo, struct lo_inode *inode, - int res; - - retry: -- sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ sprintf(procname, "%i", inode->fd); - -- res = readlink(procname, path, PATH_MAX); -+ res = readlinkat(lo->proc_self_fd, procname, path, PATH_MAX); - if (res < 0) { - fuse_log(FUSE_LOG_WARNING, "%s: readlink failed: %m\n", __func__); - goto fail_noretry; -@@ -477,9 +480,9 @@ static int utimensat_empty(struct lo_data *lo, struct lo_inode *inode, - } - return res; - } -- sprintf(path, "/proc/self/fd/%i", inode->fd); -+ sprintf(path, "%i", inode->fd); - -- return utimensat(AT_FDCWD, path, tv, 0); -+ return utimensat(lo->proc_self_fd, path, tv, 0); - - fallback: - res = lo_parent_and_name(lo, inode, path, &parent); -@@ -535,8 +538,8 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, - if (fi) { - res = fchmod(fd, attr->st_mode); - } else { -- sprintf(procname, "/proc/self/fd/%i", ifd); -- res = chmod(procname, attr->st_mode); -+ sprintf(procname, "%i", ifd); -+ res = fchmodat(lo->proc_self_fd, procname, attr->st_mode, 0); - } - if (res == -1) { - goto out_err; -@@ -552,11 +555,23 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, - } - } - if (valid & FUSE_SET_ATTR_SIZE) { -+ int truncfd; -+ - if (fi) { -- res = ftruncate(fd, attr->st_size); -+ truncfd = fd; - } else { -- sprintf(procname, "/proc/self/fd/%i", ifd); -- res = truncate(procname, attr->st_size); -+ sprintf(procname, "%i", ifd); -+ truncfd = openat(lo->proc_self_fd, procname, O_RDWR); -+ if (truncfd < 0) { -+ goto out_err; -+ } -+ } -+ -+ res = ftruncate(truncfd, attr->st_size); -+ if (!fi) { -+ saverr = errno; -+ close(truncfd); -+ errno = saverr; - } - if (res == -1) { - goto out_err; -@@ -874,9 +889,9 @@ static int linkat_empty_nofollow(struct lo_data *lo, struct lo_inode *inode, - return res; - } - -- sprintf(path, "/proc/self/fd/%i", inode->fd); -+ sprintf(path, "%i", inode->fd); - -- return linkat(AT_FDCWD, path, dfd, name, AT_SYMLINK_FOLLOW); -+ return linkat(lo->proc_self_fd, path, dfd, name, AT_SYMLINK_FOLLOW); - - fallback: - res = lo_parent_and_name(lo, inode, path, &parent); -@@ -1404,8 +1419,8 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) - fi->flags &= ~O_APPEND; - } - -- sprintf(buf, "/proc/self/fd/%i", lo_fd(req, ino)); -- fd = open(buf, fi->flags & ~O_NOFOLLOW); -+ sprintf(buf, "%i", lo_fd(req, ino)); -+ fd = openat(lo->proc_self_fd, buf, fi->flags & ~O_NOFOLLOW); - if (fd == -1) { - return (void)fuse_reply_err(req, errno); - } -@@ -1458,7 +1473,6 @@ static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, - struct fuse_file_info *fi) - { - int res; -- (void)ino; - int fd; - char *buf; - -@@ -1466,12 +1480,14 @@ static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, - (void *)fi); - - if (!fi) { -- res = asprintf(&buf, "/proc/self/fd/%i", lo_fd(req, ino)); -+ struct lo_data *lo = lo_data(req); -+ -+ res = asprintf(&buf, "%i", lo_fd(req, ino)); - if (res == -1) { - return (void)fuse_reply_err(req, errno); - } - -- fd = open(buf, O_RDWR); -+ fd = openat(lo->proc_self_fd, buf, O_RDWR); - free(buf); - if (fd == -1) { - return (void)fuse_reply_err(req, errno); -@@ -1587,11 +1603,13 @@ static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, - static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, - size_t size) - { -+ struct lo_data *lo = lo_data(req); - char *value = NULL; - char procname[64]; - struct lo_inode *inode; - ssize_t ret; - int saverr; -+ int fd = -1; - - inode = lo_inode(req, ino); - if (!inode) { -@@ -1616,7 +1634,11 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, - goto out; - } - -- sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ sprintf(procname, "%i", inode->fd); -+ fd = openat(lo->proc_self_fd, procname, O_RDONLY); -+ if (fd < 0) { -+ goto out_err; -+ } - - if (size) { - value = malloc(size); -@@ -1624,7 +1646,7 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, - goto out_err; - } - -- ret = getxattr(procname, name, value, size); -+ ret = fgetxattr(fd, name, value, size); - if (ret == -1) { - goto out_err; - } -@@ -1635,7 +1657,7 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, - - fuse_reply_buf(req, value, ret); - } else { -- ret = getxattr(procname, name, NULL, 0); -+ ret = fgetxattr(fd, name, NULL, 0); - if (ret == -1) { - goto out_err; - } -@@ -1644,6 +1666,10 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, - } - out_free: - free(value); -+ -+ if (fd >= 0) { -+ close(fd); -+ } - return; - - out_err: -@@ -1655,11 +1681,13 @@ out: - - static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) - { -+ struct lo_data *lo = lo_data(req); - char *value = NULL; - char procname[64]; - struct lo_inode *inode; - ssize_t ret; - int saverr; -+ int fd = -1; - - inode = lo_inode(req, ino); - if (!inode) { -@@ -1683,7 +1711,11 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) - goto out; - } - -- sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ sprintf(procname, "%i", inode->fd); -+ fd = openat(lo->proc_self_fd, procname, O_RDONLY); -+ if (fd < 0) { -+ goto out_err; -+ } - - if (size) { - value = malloc(size); -@@ -1691,7 +1723,7 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) - goto out_err; - } - -- ret = listxattr(procname, value, size); -+ ret = flistxattr(fd, value, size); - if (ret == -1) { - goto out_err; - } -@@ -1702,7 +1734,7 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) - - fuse_reply_buf(req, value, ret); - } else { -- ret = listxattr(procname, NULL, 0); -+ ret = flistxattr(fd, NULL, 0); - if (ret == -1) { - goto out_err; - } -@@ -1711,6 +1743,10 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) - } - out_free: - free(value); -+ -+ if (fd >= 0) { -+ close(fd); -+ } - return; - - out_err: -@@ -1724,9 +1760,11 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, - const char *value, size_t size, int flags) - { - char procname[64]; -+ struct lo_data *lo = lo_data(req); - struct lo_inode *inode; - ssize_t ret; - int saverr; -+ int fd = -1; - - inode = lo_inode(req, ino); - if (!inode) { -@@ -1751,21 +1789,31 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, - goto out; - } - -- sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ sprintf(procname, "%i", inode->fd); -+ fd = openat(lo->proc_self_fd, procname, O_RDWR); -+ if (fd < 0) { -+ saverr = errno; -+ goto out; -+ } - -- ret = setxattr(procname, name, value, size, flags); -+ ret = fsetxattr(fd, name, value, size, flags); - saverr = ret == -1 ? errno : 0; - - out: -+ if (fd >= 0) { -+ close(fd); -+ } - fuse_reply_err(req, saverr); - } - - static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) - { - char procname[64]; -+ struct lo_data *lo = lo_data(req); - struct lo_inode *inode; - ssize_t ret; - int saverr; -+ int fd = -1; - - inode = lo_inode(req, ino); - if (!inode) { -@@ -1789,12 +1837,20 @@ static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) - goto out; - } - -- sprintf(procname, "/proc/self/fd/%i", inode->fd); -+ sprintf(procname, "%i", inode->fd); -+ fd = openat(lo->proc_self_fd, procname, O_RDWR); -+ if (fd < 0) { -+ saverr = errno; -+ goto out; -+ } - -- ret = removexattr(procname, name); -+ ret = fremovexattr(fd, name); - saverr = ret == -1 ? errno : 0; - - out: -+ if (fd >= 0) { -+ close(fd); -+ } - fuse_reply_err(req, saverr); - } - -@@ -1887,12 +1943,25 @@ static void print_capabilities(void) - printf("}\n"); - } - -+static void setup_proc_self_fd(struct lo_data *lo) -+{ -+ lo->proc_self_fd = open("/proc/self/fd", O_PATH); -+ if (lo->proc_self_fd == -1) { -+ fuse_log(FUSE_LOG_ERR, "open(/proc/self/fd, O_PATH): %m\n"); -+ exit(1); -+ } -+} -+ - int main(int argc, char *argv[]) - { - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); - struct fuse_session *se; - struct fuse_cmdline_opts opts; -- struct lo_data lo = { .debug = 0, .writeback = 0 }; -+ struct lo_data lo = { -+ .debug = 0, -+ .writeback = 0, -+ .proc_self_fd = -1, -+ }; - struct lo_map_elem *root_elem; - int ret = -1; - -@@ -2003,6 +2072,9 @@ int main(int argc, char *argv[]) - - fuse_daemonize(opts.foreground); - -+ /* Must be after daemonize to get the right /proc/self/fd */ -+ setup_proc_self_fd(&lo); -+ - /* Block until ctrl+c or fusermount -u */ - ret = virtio_loop(se); - -@@ -2018,6 +2090,10 @@ err_out1: - lo_map_destroy(&lo.dirp_map); - lo_map_destroy(&lo.ino_map); - -+ if (lo.proc_self_fd >= 0) { -+ close(lo.proc_self_fd); -+ } -+ - if (lo.root.fd >= 0) { - close(lo.root.fd); - } diff --git a/0058-virtiofsd-sandbox-mount-namespace.patch b/0058-virtiofsd-sandbox-mount-namespace.patch deleted file mode 100644 index 76fb892..0000000 --- a/0058-virtiofsd-sandbox-mount-namespace.patch +++ /dev/null @@ -1,150 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:27 +0000 -Subject: [PATCH] virtiofsd: sandbox mount namespace -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Use a mount namespace with the shared directory tree mounted at "/" and -no other mounts. - -This prevents symlink escape attacks because symlink targets are -resolved only against the shared directory and cannot go outside it. - -Signed-off-by: Stefan Hajnoczi -Signed-off-by: Peng Tao -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 5baa3b8e95064c2434bd9e2f312edd5e9ae275dc) ---- - tools/virtiofsd/passthrough_ll.c | 89 ++++++++++++++++++++++++++++++++ - 1 file changed, 89 insertions(+) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index e2e2211ea1..0570453eef 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -50,6 +50,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -1943,6 +1944,58 @@ static void print_capabilities(void) - printf("}\n"); - } - -+/* This magic is based on lxc's lxc_pivot_root() */ -+static void setup_pivot_root(const char *source) -+{ -+ int oldroot; -+ int newroot; -+ -+ oldroot = open("/", O_DIRECTORY | O_RDONLY | O_CLOEXEC); -+ if (oldroot < 0) { -+ fuse_log(FUSE_LOG_ERR, "open(/): %m\n"); -+ exit(1); -+ } -+ -+ newroot = open(source, O_DIRECTORY | O_RDONLY | O_CLOEXEC); -+ if (newroot < 0) { -+ fuse_log(FUSE_LOG_ERR, "open(%s): %m\n", source); -+ exit(1); -+ } -+ -+ if (fchdir(newroot) < 0) { -+ fuse_log(FUSE_LOG_ERR, "fchdir(newroot): %m\n"); -+ exit(1); -+ } -+ -+ if (syscall(__NR_pivot_root, ".", ".") < 0) { -+ fuse_log(FUSE_LOG_ERR, "pivot_root(., .): %m\n"); -+ exit(1); -+ } -+ -+ if (fchdir(oldroot) < 0) { -+ fuse_log(FUSE_LOG_ERR, "fchdir(oldroot): %m\n"); -+ exit(1); -+ } -+ -+ if (mount("", ".", "", MS_SLAVE | MS_REC, NULL) < 0) { -+ fuse_log(FUSE_LOG_ERR, "mount(., MS_SLAVE | MS_REC): %m\n"); -+ exit(1); -+ } -+ -+ if (umount2(".", MNT_DETACH) < 0) { -+ fuse_log(FUSE_LOG_ERR, "umount2(., MNT_DETACH): %m\n"); -+ exit(1); -+ } -+ -+ if (fchdir(newroot) < 0) { -+ fuse_log(FUSE_LOG_ERR, "fchdir(newroot): %m\n"); -+ exit(1); -+ } -+ -+ close(newroot); -+ close(oldroot); -+} -+ - static void setup_proc_self_fd(struct lo_data *lo) - { - lo->proc_self_fd = open("/proc/self/fd", O_PATH); -@@ -1952,6 +2005,39 @@ static void setup_proc_self_fd(struct lo_data *lo) - } - } - -+/* -+ * Make the source directory our root so symlinks cannot escape and no other -+ * files are accessible. -+ */ -+static void setup_mount_namespace(const char *source) -+{ -+ if (unshare(CLONE_NEWNS) != 0) { -+ fuse_log(FUSE_LOG_ERR, "unshare(CLONE_NEWNS): %m\n"); -+ exit(1); -+ } -+ -+ if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) < 0) { -+ fuse_log(FUSE_LOG_ERR, "mount(/, MS_REC|MS_PRIVATE): %m\n"); -+ exit(1); -+ } -+ -+ if (mount(source, source, NULL, MS_BIND, NULL) < 0) { -+ fuse_log(FUSE_LOG_ERR, "mount(%s, %s, MS_BIND): %m\n", source, source); -+ exit(1); -+ } -+ -+ setup_pivot_root(source); -+} -+ -+/* -+ * Lock down this process to prevent access to other processes or files outside -+ * source directory. This reduces the impact of arbitrary code execution bugs. -+ */ -+static void setup_sandbox(struct lo_data *lo) -+{ -+ setup_mount_namespace(lo->source); -+} -+ - int main(int argc, char *argv[]) - { - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); -@@ -2052,6 +2138,7 @@ int main(int argc, char *argv[]) - } - - lo.root.fd = open(lo.source, O_PATH); -+ - if (lo.root.fd == -1) { - fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", lo.source); - exit(1); -@@ -2075,6 +2162,8 @@ int main(int argc, char *argv[]) - /* Must be after daemonize to get the right /proc/self/fd */ - setup_proc_self_fd(&lo); - -+ setup_sandbox(&lo); -+ - /* Block until ctrl+c or fusermount -u */ - ret = virtio_loop(se); - diff --git a/0059-virtiofsd-move-to-an-empty-network-namespace.patch b/0059-virtiofsd-move-to-an-empty-network-namespace.patch deleted file mode 100644 index 043aa5f..0000000 --- a/0059-virtiofsd-move-to-an-empty-network-namespace.patch +++ /dev/null @@ -1,50 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:28 +0000 -Subject: [PATCH] virtiofsd: move to an empty network namespace -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -If the process is compromised there should be no network access. Use an -empty network namespace to sandbox networking. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit d74830d12ae233186ff74ddf64c552d26bb39e50) ---- - tools/virtiofsd/passthrough_ll.c | 14 ++++++++++++++ - 1 file changed, 14 insertions(+) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 0570453eef..27ab328722 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1944,6 +1944,19 @@ static void print_capabilities(void) - printf("}\n"); - } - -+/* -+ * Called after our UNIX domain sockets have been created, now we can move to -+ * an empty network namespace to prevent TCP/IP and other network activity in -+ * case this process is compromised. -+ */ -+static void setup_net_namespace(void) -+{ -+ if (unshare(CLONE_NEWNET) != 0) { -+ fuse_log(FUSE_LOG_ERR, "unshare(CLONE_NEWNET): %m\n"); -+ exit(1); -+ } -+} -+ - /* This magic is based on lxc's lxc_pivot_root() */ - static void setup_pivot_root(const char *source) - { -@@ -2035,6 +2048,7 @@ static void setup_mount_namespace(const char *source) - */ - static void setup_sandbox(struct lo_data *lo) - { -+ setup_net_namespace(); - setup_mount_namespace(lo->source); - } - diff --git a/0060-virtiofsd-move-to-a-new-pid-namespace.patch b/0060-virtiofsd-move-to-a-new-pid-namespace.patch deleted file mode 100644 index 043f962..0000000 --- a/0060-virtiofsd-move-to-a-new-pid-namespace.patch +++ /dev/null @@ -1,207 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:29 +0000 -Subject: [PATCH] virtiofsd: move to a new pid namespace -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -virtiofsd needs access to /proc/self/fd. Let's move to a new pid -namespace so that a compromised process cannot see another other -processes running on the system. - -One wrinkle in this approach: unshare(CLONE_NEWPID) affects *child* -processes and not the current process. Therefore we need to fork the -pid 1 process that will actually run virtiofsd and leave a parent in -waitpid(2). This is not the same thing as daemonization and parent -processes should not notice a difference. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 8e1d4ef231d8327be219f7aea7aa15d181375bbc) ---- - tools/virtiofsd/passthrough_ll.c | 134 ++++++++++++++++++++----------- - 1 file changed, 86 insertions(+), 48 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 27ab328722..0947d14e5b 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -51,7 +51,10 @@ - #include - #include - #include -+#include - #include -+#include -+#include - #include - #include - -@@ -1945,24 +1948,95 @@ static void print_capabilities(void) - } - - /* -- * Called after our UNIX domain sockets have been created, now we can move to -- * an empty network namespace to prevent TCP/IP and other network activity in -- * case this process is compromised. -+ * Move to a new mount, net, and pid namespaces to isolate this process. - */ --static void setup_net_namespace(void) -+static void setup_namespaces(struct lo_data *lo, struct fuse_session *se) - { -- if (unshare(CLONE_NEWNET) != 0) { -- fuse_log(FUSE_LOG_ERR, "unshare(CLONE_NEWNET): %m\n"); -+ pid_t child; -+ -+ /* -+ * Create a new pid namespace for *child* processes. We'll have to -+ * fork in order to enter the new pid namespace. A new mount namespace -+ * is also needed so that we can remount /proc for the new pid -+ * namespace. -+ * -+ * Our UNIX domain sockets have been created. Now we can move to -+ * an empty network namespace to prevent TCP/IP and other network -+ * activity in case this process is compromised. -+ */ -+ if (unshare(CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET) != 0) { -+ fuse_log(FUSE_LOG_ERR, "unshare(CLONE_NEWPID | CLONE_NEWNS): %m\n"); -+ exit(1); -+ } -+ -+ child = fork(); -+ if (child < 0) { -+ fuse_log(FUSE_LOG_ERR, "fork() failed: %m\n"); -+ exit(1); -+ } -+ if (child > 0) { -+ pid_t waited; -+ int wstatus; -+ -+ /* The parent waits for the child */ -+ do { -+ waited = waitpid(child, &wstatus, 0); -+ } while (waited < 0 && errno == EINTR && !se->exited); -+ -+ /* We were terminated by a signal, see fuse_signals.c */ -+ if (se->exited) { -+ exit(0); -+ } -+ -+ if (WIFEXITED(wstatus)) { -+ exit(WEXITSTATUS(wstatus)); -+ } -+ -+ exit(1); -+ } -+ -+ /* Send us SIGTERM when the parent thread terminates, see prctl(2) */ -+ prctl(PR_SET_PDEATHSIG, SIGTERM); -+ -+ /* -+ * If the mounts have shared propagation then we want to opt out so our -+ * mount changes don't affect the parent mount namespace. -+ */ -+ if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) < 0) { -+ fuse_log(FUSE_LOG_ERR, "mount(/, MS_REC|MS_SLAVE): %m\n"); -+ exit(1); -+ } -+ -+ /* The child must remount /proc to use the new pid namespace */ -+ if (mount("proc", "/proc", "proc", -+ MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_RELATIME, NULL) < 0) { -+ fuse_log(FUSE_LOG_ERR, "mount(/proc): %m\n"); -+ exit(1); -+ } -+ -+ /* Now we can get our /proc/self/fd directory file descriptor */ -+ lo->proc_self_fd = open("/proc/self/fd", O_PATH); -+ if (lo->proc_self_fd == -1) { -+ fuse_log(FUSE_LOG_ERR, "open(/proc/self/fd, O_PATH): %m\n"); - exit(1); - } - } - --/* This magic is based on lxc's lxc_pivot_root() */ --static void setup_pivot_root(const char *source) -+/* -+ * Make the source directory our root so symlinks cannot escape and no other -+ * files are accessible. Assumes unshare(CLONE_NEWNS) was already called. -+ */ -+static void setup_mounts(const char *source) - { - int oldroot; - int newroot; - -+ if (mount(source, source, NULL, MS_BIND, NULL) < 0) { -+ fuse_log(FUSE_LOG_ERR, "mount(%s, %s, MS_BIND): %m\n", source, source); -+ exit(1); -+ } -+ -+ /* This magic is based on lxc's lxc_pivot_root() */ - oldroot = open("/", O_DIRECTORY | O_RDONLY | O_CLOEXEC); - if (oldroot < 0) { - fuse_log(FUSE_LOG_ERR, "open(/): %m\n"); -@@ -2009,47 +2083,14 @@ static void setup_pivot_root(const char *source) - close(oldroot); - } - --static void setup_proc_self_fd(struct lo_data *lo) --{ -- lo->proc_self_fd = open("/proc/self/fd", O_PATH); -- if (lo->proc_self_fd == -1) { -- fuse_log(FUSE_LOG_ERR, "open(/proc/self/fd, O_PATH): %m\n"); -- exit(1); -- } --} -- --/* -- * Make the source directory our root so symlinks cannot escape and no other -- * files are accessible. -- */ --static void setup_mount_namespace(const char *source) --{ -- if (unshare(CLONE_NEWNS) != 0) { -- fuse_log(FUSE_LOG_ERR, "unshare(CLONE_NEWNS): %m\n"); -- exit(1); -- } -- -- if (mount(NULL, "/", NULL, MS_REC | MS_SLAVE, NULL) < 0) { -- fuse_log(FUSE_LOG_ERR, "mount(/, MS_REC|MS_PRIVATE): %m\n"); -- exit(1); -- } -- -- if (mount(source, source, NULL, MS_BIND, NULL) < 0) { -- fuse_log(FUSE_LOG_ERR, "mount(%s, %s, MS_BIND): %m\n", source, source); -- exit(1); -- } -- -- setup_pivot_root(source); --} -- - /* - * Lock down this process to prevent access to other processes or files outside - * source directory. This reduces the impact of arbitrary code execution bugs. - */ --static void setup_sandbox(struct lo_data *lo) -+static void setup_sandbox(struct lo_data *lo, struct fuse_session *se) - { -- setup_net_namespace(); -- setup_mount_namespace(lo->source); -+ setup_namespaces(lo, se); -+ setup_mounts(lo->source); - } - - int main(int argc, char *argv[]) -@@ -2173,10 +2214,7 @@ int main(int argc, char *argv[]) - - fuse_daemonize(opts.foreground); - -- /* Must be after daemonize to get the right /proc/self/fd */ -- setup_proc_self_fd(&lo); -- -- setup_sandbox(&lo); -+ setup_sandbox(&lo, se); - - /* Block until ctrl+c or fusermount -u */ - ret = virtio_loop(se); diff --git a/0061-virtiofsd-add-seccomp-whitelist.patch b/0061-virtiofsd-add-seccomp-whitelist.patch deleted file mode 100644 index 124cd4e..0000000 --- a/0061-virtiofsd-add-seccomp-whitelist.patch +++ /dev/null @@ -1,265 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:30 +0000 -Subject: [PATCH] virtiofsd: add seccomp whitelist - -Only allow system calls that are needed by virtiofsd. All other system -calls cause SIGSYS to be directed at the thread and the process will -coredump. - -Restricting system calls reduces the kernel attack surface and limits -what the process can do when compromised. - -Signed-off-by: Stefan Hajnoczi -with additional entries by: -Signed-off-by: Ganesh Maharaj Mahalingam -Signed-off-by: Masayoshi Mizuma -Signed-off-by: Misono Tomohiro -Signed-off-by: piaojun -Signed-off-by: Vivek Goyal -Signed-off-by: Eric Ren -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 4f8bde99c175ffd86b5125098a4707d43f5e80c6) ---- - Makefile | 5 +- - tools/virtiofsd/Makefile.objs | 5 +- - tools/virtiofsd/passthrough_ll.c | 2 + - tools/virtiofsd/seccomp.c | 151 +++++++++++++++++++++++++++++++ - tools/virtiofsd/seccomp.h | 14 +++ - 5 files changed, 174 insertions(+), 3 deletions(-) - create mode 100644 tools/virtiofsd/seccomp.c - create mode 100644 tools/virtiofsd/seccomp.h - -diff --git a/Makefile b/Makefile -index aebb57aed8..9a17e34603 100644 ---- a/Makefile -+++ b/Makefile -@@ -330,7 +330,7 @@ endif - endif - endif - --ifdef CONFIG_LINUX -+ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP),yy) - HELPERS-y += virtiofsd$(EXESUF) - vhost-user-json-y += tools/virtiofsd/50-qemu-virtiofsd.json - endif -@@ -680,7 +680,8 @@ rdmacm-mux$(EXESUF): LIBS += "-libumad" - rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) - $(call LINK, $^) - --ifdef CONFIG_LINUX # relies on Linux-specific syscalls -+# relies on Linux-specific syscalls -+ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP),yy) - virtiofsd$(EXESUF): $(virtiofsd-obj-y) libvhost-user.a $(COMMON_LDADDS) - $(call LINK, $^) - endif -diff --git a/tools/virtiofsd/Makefile.objs b/tools/virtiofsd/Makefile.objs -index 45a807500d..076f667e46 100644 ---- a/tools/virtiofsd/Makefile.objs -+++ b/tools/virtiofsd/Makefile.objs -@@ -5,5 +5,8 @@ virtiofsd-obj-y = buffer.o \ - fuse_signals.o \ - fuse_virtio.o \ - helper.o \ -- passthrough_ll.o -+ passthrough_ll.o \ -+ seccomp.o - -+seccomp.o-cflags := $(SECCOMP_CFLAGS) -+seccomp.o-libs := $(SECCOMP_LIBS) -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 0947d14e5b..bd8925bd83 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -59,6 +59,7 @@ - #include - - #include "passthrough_helpers.h" -+#include "seccomp.h" - - struct lo_map_elem { - union { -@@ -2091,6 +2092,7 @@ static void setup_sandbox(struct lo_data *lo, struct fuse_session *se) - { - setup_namespaces(lo, se); - setup_mounts(lo->source); -+ setup_seccomp(); - } - - int main(int argc, char *argv[]) -diff --git a/tools/virtiofsd/seccomp.c b/tools/virtiofsd/seccomp.c -new file mode 100644 -index 0000000000..691fb63dea ---- /dev/null -+++ b/tools/virtiofsd/seccomp.c -@@ -0,0 +1,151 @@ -+/* -+ * Seccomp sandboxing for virtiofsd -+ * -+ * Copyright (C) 2019 Red Hat, Inc. -+ * -+ * SPDX-License-Identifier: GPL-2.0-or-later -+ */ -+ -+#include "qemu/osdep.h" -+#include "seccomp.h" -+#include "fuse_i.h" -+#include "fuse_log.h" -+#include -+#include -+#include -+#include -+ -+/* Bodge for libseccomp 2.4.2 which broke ppoll */ -+#if !defined(__SNR_ppoll) && defined(__SNR_brk) -+#ifdef __NR_ppoll -+#define __SNR_ppoll __NR_ppoll -+#else -+#define __SNR_ppoll __PNR_ppoll -+#endif -+#endif -+ -+static const int syscall_whitelist[] = { -+ /* TODO ireg sem*() syscalls */ -+ SCMP_SYS(brk), -+ SCMP_SYS(capget), /* For CAP_FSETID */ -+ SCMP_SYS(capset), -+ SCMP_SYS(clock_gettime), -+ SCMP_SYS(clone), -+#ifdef __NR_clone3 -+ SCMP_SYS(clone3), -+#endif -+ SCMP_SYS(close), -+ SCMP_SYS(copy_file_range), -+ SCMP_SYS(dup), -+ SCMP_SYS(eventfd2), -+ SCMP_SYS(exit), -+ SCMP_SYS(exit_group), -+ SCMP_SYS(fallocate), -+ SCMP_SYS(fchmodat), -+ SCMP_SYS(fchownat), -+ SCMP_SYS(fcntl), -+ SCMP_SYS(fdatasync), -+ SCMP_SYS(fgetxattr), -+ SCMP_SYS(flistxattr), -+ SCMP_SYS(flock), -+ SCMP_SYS(fremovexattr), -+ SCMP_SYS(fsetxattr), -+ SCMP_SYS(fstat), -+ SCMP_SYS(fstatfs), -+ SCMP_SYS(fsync), -+ SCMP_SYS(ftruncate), -+ SCMP_SYS(futex), -+ SCMP_SYS(getdents), -+ SCMP_SYS(getdents64), -+ SCMP_SYS(getegid), -+ SCMP_SYS(geteuid), -+ SCMP_SYS(getpid), -+ SCMP_SYS(gettid), -+ SCMP_SYS(gettimeofday), -+ SCMP_SYS(linkat), -+ SCMP_SYS(lseek), -+ SCMP_SYS(madvise), -+ SCMP_SYS(mkdirat), -+ SCMP_SYS(mknodat), -+ SCMP_SYS(mmap), -+ SCMP_SYS(mprotect), -+ SCMP_SYS(mremap), -+ SCMP_SYS(munmap), -+ SCMP_SYS(newfstatat), -+ SCMP_SYS(open), -+ SCMP_SYS(openat), -+ SCMP_SYS(ppoll), -+ SCMP_SYS(prctl), /* TODO restrict to just PR_SET_NAME? */ -+ SCMP_SYS(preadv), -+ SCMP_SYS(pread64), -+ SCMP_SYS(pwritev), -+ SCMP_SYS(pwrite64), -+ SCMP_SYS(read), -+ SCMP_SYS(readlinkat), -+ SCMP_SYS(recvmsg), -+ SCMP_SYS(renameat), -+ SCMP_SYS(renameat2), -+ SCMP_SYS(rt_sigaction), -+ SCMP_SYS(rt_sigprocmask), -+ SCMP_SYS(rt_sigreturn), -+ SCMP_SYS(sendmsg), -+ SCMP_SYS(setresgid), -+ SCMP_SYS(setresuid), -+#ifdef __NR_setresgid32 -+ SCMP_SYS(setresgid32), -+#endif -+#ifdef __NR_setresuid32 -+ SCMP_SYS(setresuid32), -+#endif -+ SCMP_SYS(set_robust_list), -+ SCMP_SYS(symlinkat), -+ SCMP_SYS(time), /* Rarely needed, except on static builds */ -+ SCMP_SYS(tgkill), -+ SCMP_SYS(unlinkat), -+ SCMP_SYS(utimensat), -+ SCMP_SYS(write), -+ SCMP_SYS(writev), -+}; -+ -+void setup_seccomp(void) -+{ -+ scmp_filter_ctx ctx; -+ size_t i; -+ -+#ifdef SCMP_ACT_KILL_PROCESS -+ ctx = seccomp_init(SCMP_ACT_KILL_PROCESS); -+ /* Handle a newer libseccomp but an older kernel */ -+ if (!ctx && errno == EOPNOTSUPP) { -+ ctx = seccomp_init(SCMP_ACT_TRAP); -+ } -+#else -+ ctx = seccomp_init(SCMP_ACT_TRAP); -+#endif -+ if (!ctx) { -+ fuse_log(FUSE_LOG_ERR, "seccomp_init() failed\n"); -+ exit(1); -+ } -+ -+ for (i = 0; i < G_N_ELEMENTS(syscall_whitelist); i++) { -+ if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, -+ syscall_whitelist[i], 0) != 0) { -+ fuse_log(FUSE_LOG_ERR, "seccomp_rule_add syscall %d", -+ syscall_whitelist[i]); -+ exit(1); -+ } -+ } -+ -+ /* libvhost-user calls this for post-copy migration, we don't need it */ -+ if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOSYS), -+ SCMP_SYS(userfaultfd), 0) != 0) { -+ fuse_log(FUSE_LOG_ERR, "seccomp_rule_add userfaultfd failed\n"); -+ exit(1); -+ } -+ -+ if (seccomp_load(ctx) < 0) { -+ fuse_log(FUSE_LOG_ERR, "seccomp_load() failed\n"); -+ exit(1); -+ } -+ -+ seccomp_release(ctx); -+} -diff --git a/tools/virtiofsd/seccomp.h b/tools/virtiofsd/seccomp.h -new file mode 100644 -index 0000000000..86bce72652 ---- /dev/null -+++ b/tools/virtiofsd/seccomp.h -@@ -0,0 +1,14 @@ -+/* -+ * Seccomp sandboxing for virtiofsd -+ * -+ * Copyright (C) 2019 Red Hat, Inc. -+ * -+ * SPDX-License-Identifier: GPL-2.0-or-later -+ */ -+ -+#ifndef VIRTIOFSD_SECCOMP_H -+#define VIRTIOFSD_SECCOMP_H -+ -+void setup_seccomp(void); -+ -+#endif /* VIRTIOFSD_SECCOMP_H */ diff --git a/0062-virtiofsd-Parse-flag-FUSE_WRITE_KILL_PRIV.patch b/0062-virtiofsd-Parse-flag-FUSE_WRITE_KILL_PRIV.patch deleted file mode 100644 index 1670c83..0000000 --- a/0062-virtiofsd-Parse-flag-FUSE_WRITE_KILL_PRIV.patch +++ /dev/null @@ -1,57 +0,0 @@ -From: Vivek Goyal -Date: Mon, 27 Jan 2020 19:01:31 +0000 -Subject: [PATCH] virtiofsd: Parse flag FUSE_WRITE_KILL_PRIV - -Caller can set FUSE_WRITE_KILL_PRIV in write_flags. Parse it and pass it -to the filesystem. - -Signed-off-by: Vivek Goyal -Reviewed-by: Misono Tomohiro -Reviewed-by: Sergio Lopez -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit f779bc5265e7e7abb13a03d4bfbc74151afc15c2) ---- - tools/virtiofsd/fuse_common.h | 6 +++++- - tools/virtiofsd/fuse_lowlevel.c | 4 +++- - 2 files changed, 8 insertions(+), 2 deletions(-) - -diff --git a/tools/virtiofsd/fuse_common.h b/tools/virtiofsd/fuse_common.h -index f8f6433743..686c42c0a5 100644 ---- a/tools/virtiofsd/fuse_common.h -+++ b/tools/virtiofsd/fuse_common.h -@@ -93,8 +93,12 @@ struct fuse_file_info { - */ - unsigned int cache_readdir:1; - -+ /* Indicates that suid/sgid bits should be removed upon write */ -+ unsigned int kill_priv:1; -+ -+ - /** Padding. Reserved for future use*/ -- unsigned int padding:25; -+ unsigned int padding:24; - unsigned int padding2:32; - - /* -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 02e1d83038..2d6dc5a680 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -1142,6 +1142,7 @@ static void do_write(fuse_req_t req, fuse_ino_t nodeid, - memset(&fi, 0, sizeof(fi)); - fi.fh = arg->fh; - fi.writepage = (arg->write_flags & FUSE_WRITE_CACHE) != 0; -+ fi.kill_priv = !!(arg->write_flags & FUSE_WRITE_KILL_PRIV); - - fi.lock_owner = arg->lock_owner; - fi.flags = arg->flags; -@@ -1177,7 +1178,8 @@ static void do_write_buf(fuse_req_t req, fuse_ino_t nodeid, - fi.lock_owner = arg->lock_owner; - fi.flags = arg->flags; - fi.fh = arg->fh; -- fi.writepage = arg->write_flags & FUSE_WRITE_CACHE; -+ fi.writepage = !!(arg->write_flags & FUSE_WRITE_CACHE); -+ fi.kill_priv = !!(arg->write_flags & FUSE_WRITE_KILL_PRIV); - - if (ibufv->count == 1) { - assert(!(tmpbufv.buf[0].flags & FUSE_BUF_IS_FD)); diff --git a/0063-virtiofsd-cap-ng-helpers.patch b/0063-virtiofsd-cap-ng-helpers.patch deleted file mode 100644 index 7bc8ca9..0000000 --- a/0063-virtiofsd-cap-ng-helpers.patch +++ /dev/null @@ -1,159 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:32 +0000 -Subject: [PATCH] virtiofsd: cap-ng helpers -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -libcap-ng reads /proc during capng_get_caps_process, and virtiofsd's -sandboxing doesn't have /proc mounted; thus we have to do the -caps read before we sandbox it and save/restore the state. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 2405f3c0d19eb4d516a88aa4e5c54e5f9c6bbea3) ---- - Makefile | 4 +- - tools/virtiofsd/passthrough_ll.c | 72 ++++++++++++++++++++++++++++++++ - 2 files changed, 74 insertions(+), 2 deletions(-) - -diff --git a/Makefile b/Makefile -index 9a17e34603..14793cad11 100644 ---- a/Makefile -+++ b/Makefile -@@ -330,7 +330,7 @@ endif - endif - endif - --ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP),yy) -+ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy) - HELPERS-y += virtiofsd$(EXESUF) - vhost-user-json-y += tools/virtiofsd/50-qemu-virtiofsd.json - endif -@@ -681,7 +681,7 @@ rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) - $(call LINK, $^) - - # relies on Linux-specific syscalls --ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP),yy) -+ifeq ($(CONFIG_LINUX)$(CONFIG_SECCOMP)$(CONFIG_LIBCAP_NG),yyy) - virtiofsd$(EXESUF): $(virtiofsd-obj-y) libvhost-user.a $(COMMON_LDADDS) - $(call LINK, $^) - endif -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index bd8925bd83..97e7c75667 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -39,6 +39,7 @@ - #include "fuse_virtio.h" - #include "fuse_lowlevel.h" - #include -+#include - #include - #include - #include -@@ -139,6 +140,13 @@ static const struct fuse_opt lo_opts[] = { - - static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n); - -+static struct { -+ pthread_mutex_t mutex; -+ void *saved; -+} cap; -+/* That we loaded cap-ng in the current thread from the saved */ -+static __thread bool cap_loaded = 0; -+ - static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st); - - static int is_dot_or_dotdot(const char *name) -@@ -162,6 +170,37 @@ static struct lo_data *lo_data(fuse_req_t req) - return (struct lo_data *)fuse_req_userdata(req); - } - -+/* -+ * Load capng's state from our saved state if the current thread -+ * hadn't previously been loaded. -+ * returns 0 on success -+ */ -+static int load_capng(void) -+{ -+ if (!cap_loaded) { -+ pthread_mutex_lock(&cap.mutex); -+ capng_restore_state(&cap.saved); -+ /* -+ * restore_state free's the saved copy -+ * so make another. -+ */ -+ cap.saved = capng_save_state(); -+ if (!cap.saved) { -+ fuse_log(FUSE_LOG_ERR, "capng_save_state (thread)\n"); -+ return -EINVAL; -+ } -+ pthread_mutex_unlock(&cap.mutex); -+ -+ /* -+ * We want to use the loaded state for our pid, -+ * not the original -+ */ -+ capng_setpid(syscall(SYS_gettid)); -+ cap_loaded = true; -+ } -+ return 0; -+} -+ - static void lo_map_init(struct lo_map *map) - { - map->elems = NULL; -@@ -2023,6 +2062,35 @@ static void setup_namespaces(struct lo_data *lo, struct fuse_session *se) - } - } - -+/* -+ * Capture the capability state, we'll need to restore this for individual -+ * threads later; see load_capng. -+ */ -+static void setup_capng(void) -+{ -+ /* Note this accesses /proc so has to happen before the sandbox */ -+ if (capng_get_caps_process()) { -+ fuse_log(FUSE_LOG_ERR, "capng_get_caps_process\n"); -+ exit(1); -+ } -+ pthread_mutex_init(&cap.mutex, NULL); -+ pthread_mutex_lock(&cap.mutex); -+ cap.saved = capng_save_state(); -+ if (!cap.saved) { -+ fuse_log(FUSE_LOG_ERR, "capng_save_state\n"); -+ exit(1); -+ } -+ pthread_mutex_unlock(&cap.mutex); -+} -+ -+static void cleanup_capng(void) -+{ -+ free(cap.saved); -+ cap.saved = NULL; -+ pthread_mutex_destroy(&cap.mutex); -+} -+ -+ - /* - * Make the source directory our root so symlinks cannot escape and no other - * files are accessible. Assumes unshare(CLONE_NEWNS) was already called. -@@ -2216,12 +2284,16 @@ int main(int argc, char *argv[]) - - fuse_daemonize(opts.foreground); - -+ /* Must be before sandbox since it wants /proc */ -+ setup_capng(); -+ - setup_sandbox(&lo, se); - - /* Block until ctrl+c or fusermount -u */ - ret = virtio_loop(se); - - fuse_session_unmount(se); -+ cleanup_capng(); - err_out3: - fuse_remove_signal_handlers(se); - err_out2: diff --git a/0064-virtiofsd-Drop-CAP_FSETID-if-client-asked-for-it.patch b/0064-virtiofsd-Drop-CAP_FSETID-if-client-asked-for-it.patch deleted file mode 100644 index 13438cc..0000000 --- a/0064-virtiofsd-Drop-CAP_FSETID-if-client-asked-for-it.patch +++ /dev/null @@ -1,156 +0,0 @@ -From: Vivek Goyal -Date: Mon, 27 Jan 2020 19:01:33 +0000 -Subject: [PATCH] virtiofsd: Drop CAP_FSETID if client asked for it - -If client requested killing setuid/setgid bits on file being written, drop -CAP_FSETID capability so that setuid/setgid bits are cleared upon write -automatically. - -pjdfstest chown/12.t needs this. - -Signed-off-by: Vivek Goyal - dgilbert: reworked for libcap-ng -Reviewed-by: Misono Tomohiro -Reviewed-by: Sergio Lopez -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit ee88465224b3aed2596049caa28f86cbe0d5a3d0) ---- - tools/virtiofsd/passthrough_ll.c | 105 +++++++++++++++++++++++++++++++ - 1 file changed, 105 insertions(+) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 97e7c75667..d53cb1e005 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -201,6 +201,91 @@ static int load_capng(void) - return 0; - } - -+/* -+ * Helpers for dropping and regaining effective capabilities. Returns 0 -+ * on success, error otherwise -+ */ -+static int drop_effective_cap(const char *cap_name, bool *cap_dropped) -+{ -+ int cap, ret; -+ -+ cap = capng_name_to_capability(cap_name); -+ if (cap < 0) { -+ ret = errno; -+ fuse_log(FUSE_LOG_ERR, "capng_name_to_capability(%s) failed:%s\n", -+ cap_name, strerror(errno)); -+ goto out; -+ } -+ -+ if (load_capng()) { -+ ret = errno; -+ fuse_log(FUSE_LOG_ERR, "load_capng() failed\n"); -+ goto out; -+ } -+ -+ /* We dont have this capability in effective set already. */ -+ if (!capng_have_capability(CAPNG_EFFECTIVE, cap)) { -+ ret = 0; -+ goto out; -+ } -+ -+ if (capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, cap)) { -+ ret = errno; -+ fuse_log(FUSE_LOG_ERR, "capng_update(DROP,) failed\n"); -+ goto out; -+ } -+ -+ if (capng_apply(CAPNG_SELECT_CAPS)) { -+ ret = errno; -+ fuse_log(FUSE_LOG_ERR, "drop:capng_apply() failed\n"); -+ goto out; -+ } -+ -+ ret = 0; -+ if (cap_dropped) { -+ *cap_dropped = true; -+ } -+ -+out: -+ return ret; -+} -+ -+static int gain_effective_cap(const char *cap_name) -+{ -+ int cap; -+ int ret = 0; -+ -+ cap = capng_name_to_capability(cap_name); -+ if (cap < 0) { -+ ret = errno; -+ fuse_log(FUSE_LOG_ERR, "capng_name_to_capability(%s) failed:%s\n", -+ cap_name, strerror(errno)); -+ goto out; -+ } -+ -+ if (load_capng()) { -+ ret = errno; -+ fuse_log(FUSE_LOG_ERR, "load_capng() failed\n"); -+ goto out; -+ } -+ -+ if (capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, cap)) { -+ ret = errno; -+ fuse_log(FUSE_LOG_ERR, "capng_update(ADD,) failed\n"); -+ goto out; -+ } -+ -+ if (capng_apply(CAPNG_SELECT_CAPS)) { -+ ret = errno; -+ fuse_log(FUSE_LOG_ERR, "gain:capng_apply() failed\n"); -+ goto out; -+ } -+ ret = 0; -+ -+out: -+ return ret; -+} -+ - static void lo_map_init(struct lo_map *map) - { - map->elems = NULL; -@@ -1577,6 +1662,7 @@ static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, - (void)ino; - ssize_t res; - struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf)); -+ bool cap_fsetid_dropped = false; - - out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; - out_buf.buf[0].fd = lo_fi_fd(req, fi); -@@ -1588,12 +1674,31 @@ static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, - out_buf.buf[0].size, (unsigned long)off); - } - -+ /* -+ * If kill_priv is set, drop CAP_FSETID which should lead to kernel -+ * clearing setuid/setgid on file. -+ */ -+ if (fi->kill_priv) { -+ res = drop_effective_cap("FSETID", &cap_fsetid_dropped); -+ if (res != 0) { -+ fuse_reply_err(req, res); -+ return; -+ } -+ } -+ - res = fuse_buf_copy(&out_buf, in_buf); - if (res < 0) { - fuse_reply_err(req, -res); - } else { - fuse_reply_write(req, (size_t)res); - } -+ -+ if (cap_fsetid_dropped) { -+ res = gain_effective_cap("FSETID"); -+ if (res) { -+ fuse_log(FUSE_LOG_ERR, "Failed to gain CAP_FSETID\n"); -+ } -+ } - } - - static void lo_statfs(fuse_req_t req, fuse_ino_t ino) diff --git a/0065-virtiofsd-set-maximum-RLIMIT_NOFILE-limit.patch b/0065-virtiofsd-set-maximum-RLIMIT_NOFILE-limit.patch deleted file mode 100644 index 9782dc5..0000000 --- a/0065-virtiofsd-set-maximum-RLIMIT_NOFILE-limit.patch +++ /dev/null @@ -1,77 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:34 +0000 -Subject: [PATCH] virtiofsd: set maximum RLIMIT_NOFILE limit -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -virtiofsd can exceed the default open file descriptor limit easily on -most systems. Take advantage of the fact that it runs as root to raise -the limit. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 01a6dc95ec7f71eeff9963fe3cb03d85225fba3e) ---- - tools/virtiofsd/passthrough_ll.c | 32 ++++++++++++++++++++++++++++++++ - 1 file changed, 32 insertions(+) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index d53cb1e005..c281d817af 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -53,6 +53,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -2268,6 +2269,35 @@ static void setup_sandbox(struct lo_data *lo, struct fuse_session *se) - setup_seccomp(); - } - -+/* Raise the maximum number of open file descriptors */ -+static void setup_nofile_rlimit(void) -+{ -+ const rlim_t max_fds = 1000000; -+ struct rlimit rlim; -+ -+ if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) { -+ fuse_log(FUSE_LOG_ERR, "getrlimit(RLIMIT_NOFILE): %m\n"); -+ exit(1); -+ } -+ -+ if (rlim.rlim_cur >= max_fds) { -+ return; /* nothing to do */ -+ } -+ -+ rlim.rlim_cur = max_fds; -+ rlim.rlim_max = max_fds; -+ -+ if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) { -+ /* Ignore SELinux denials */ -+ if (errno == EPERM) { -+ return; -+ } -+ -+ fuse_log(FUSE_LOG_ERR, "setrlimit(RLIMIT_NOFILE): %m\n"); -+ exit(1); -+ } -+} -+ - int main(int argc, char *argv[]) - { - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); -@@ -2389,6 +2419,8 @@ int main(int argc, char *argv[]) - - fuse_daemonize(opts.foreground); - -+ setup_nofile_rlimit(); -+ - /* Must be before sandbox since it wants /proc */ - setup_capng(); - diff --git a/0066-virtiofsd-fix-libfuse-information-leaks.patch b/0066-virtiofsd-fix-libfuse-information-leaks.patch deleted file mode 100644 index 8a5b630..0000000 --- a/0066-virtiofsd-fix-libfuse-information-leaks.patch +++ /dev/null @@ -1,306 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:35 +0000 -Subject: [PATCH] virtiofsd: fix libfuse information leaks -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Some FUSE message replies contain padding fields that are not -initialized by libfuse. This is fine in traditional FUSE applications -because the kernel is trusted. virtiofsd does not trust the guest and -must not expose uninitialized memory. - -Use C struct initializers to automatically zero out memory. Not all of -these code changes are strictly necessary but they will prevent future -information leaks if the structs are extended. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 3db2876a0153ac7103c077c53090e020faffb3ea) ---- - tools/virtiofsd/fuse_lowlevel.c | 150 ++++++++++++++++---------------- - 1 file changed, 76 insertions(+), 74 deletions(-) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 2d6dc5a680..6ceb33d913 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -44,21 +44,23 @@ static __attribute__((constructor)) void fuse_ll_init_pagesize(void) - - static void convert_stat(const struct stat *stbuf, struct fuse_attr *attr) - { -- attr->ino = stbuf->st_ino; -- attr->mode = stbuf->st_mode; -- attr->nlink = stbuf->st_nlink; -- attr->uid = stbuf->st_uid; -- attr->gid = stbuf->st_gid; -- attr->rdev = stbuf->st_rdev; -- attr->size = stbuf->st_size; -- attr->blksize = stbuf->st_blksize; -- attr->blocks = stbuf->st_blocks; -- attr->atime = stbuf->st_atime; -- attr->mtime = stbuf->st_mtime; -- attr->ctime = stbuf->st_ctime; -- attr->atimensec = ST_ATIM_NSEC(stbuf); -- attr->mtimensec = ST_MTIM_NSEC(stbuf); -- attr->ctimensec = ST_CTIM_NSEC(stbuf); -+ *attr = (struct fuse_attr){ -+ .ino = stbuf->st_ino, -+ .mode = stbuf->st_mode, -+ .nlink = stbuf->st_nlink, -+ .uid = stbuf->st_uid, -+ .gid = stbuf->st_gid, -+ .rdev = stbuf->st_rdev, -+ .size = stbuf->st_size, -+ .blksize = stbuf->st_blksize, -+ .blocks = stbuf->st_blocks, -+ .atime = stbuf->st_atime, -+ .mtime = stbuf->st_mtime, -+ .ctime = stbuf->st_ctime, -+ .atimensec = ST_ATIM_NSEC(stbuf), -+ .mtimensec = ST_MTIM_NSEC(stbuf), -+ .ctimensec = ST_CTIM_NSEC(stbuf), -+ }; - } - - static void convert_attr(const struct fuse_setattr_in *attr, struct stat *stbuf) -@@ -183,16 +185,16 @@ static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, - int fuse_send_reply_iov_nofree(fuse_req_t req, int error, struct iovec *iov, - int count) - { -- struct fuse_out_header out; -+ struct fuse_out_header out = { -+ .unique = req->unique, -+ .error = error, -+ }; - - if (error <= -1000 || error > 0) { - fuse_log(FUSE_LOG_ERR, "fuse: bad error value: %i\n", error); - error = -ERANGE; - } - -- out.unique = req->unique; -- out.error = error; -- - iov[0].iov_base = &out; - iov[0].iov_len = sizeof(struct fuse_out_header); - -@@ -277,14 +279,16 @@ size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, - static void convert_statfs(const struct statvfs *stbuf, - struct fuse_kstatfs *kstatfs) - { -- kstatfs->bsize = stbuf->f_bsize; -- kstatfs->frsize = stbuf->f_frsize; -- kstatfs->blocks = stbuf->f_blocks; -- kstatfs->bfree = stbuf->f_bfree; -- kstatfs->bavail = stbuf->f_bavail; -- kstatfs->files = stbuf->f_files; -- kstatfs->ffree = stbuf->f_ffree; -- kstatfs->namelen = stbuf->f_namemax; -+ *kstatfs = (struct fuse_kstatfs){ -+ .bsize = stbuf->f_bsize, -+ .frsize = stbuf->f_frsize, -+ .blocks = stbuf->f_blocks, -+ .bfree = stbuf->f_bfree, -+ .bavail = stbuf->f_bavail, -+ .files = stbuf->f_files, -+ .ffree = stbuf->f_ffree, -+ .namelen = stbuf->f_namemax, -+ }; - } - - static int send_reply_ok(fuse_req_t req, const void *arg, size_t argsize) -@@ -328,12 +332,14 @@ static unsigned int calc_timeout_nsec(double t) - static void fill_entry(struct fuse_entry_out *arg, - const struct fuse_entry_param *e) - { -- arg->nodeid = e->ino; -- arg->generation = e->generation; -- arg->entry_valid = calc_timeout_sec(e->entry_timeout); -- arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout); -- arg->attr_valid = calc_timeout_sec(e->attr_timeout); -- arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout); -+ *arg = (struct fuse_entry_out){ -+ .nodeid = e->ino, -+ .generation = e->generation, -+ .entry_valid = calc_timeout_sec(e->entry_timeout), -+ .entry_valid_nsec = calc_timeout_nsec(e->entry_timeout), -+ .attr_valid = calc_timeout_sec(e->attr_timeout), -+ .attr_valid_nsec = calc_timeout_nsec(e->attr_timeout), -+ }; - convert_stat(&e->attr, &arg->attr); - } - -@@ -362,10 +368,12 @@ size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize, - fill_entry(&dp->entry_out, e); - - struct fuse_dirent *dirent = &dp->dirent; -- dirent->ino = e->attr.st_ino; -- dirent->off = off; -- dirent->namelen = namelen; -- dirent->type = (e->attr.st_mode & S_IFMT) >> 12; -+ *dirent = (struct fuse_dirent){ -+ .ino = e->attr.st_ino, -+ .off = off, -+ .namelen = namelen, -+ .type = (e->attr.st_mode & S_IFMT) >> 12, -+ }; - memcpy(dirent->name, name, namelen); - memset(dirent->name + namelen, 0, entlen_padded - entlen); - -@@ -496,15 +504,14 @@ static int fuse_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, - int fuse_reply_data(fuse_req_t req, struct fuse_bufvec *bufv) - { - struct iovec iov[2]; -- struct fuse_out_header out; -+ struct fuse_out_header out = { -+ .unique = req->unique, -+ }; - int res; - - iov[0].iov_base = &out; - iov[0].iov_len = sizeof(struct fuse_out_header); - -- out.unique = req->unique; -- out.error = 0; -- - res = fuse_send_data_iov(req->se, req->ch, iov, 1, bufv); - if (res <= 0) { - fuse_free_req(req); -@@ -2145,14 +2152,14 @@ static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, - static int send_notify_iov(struct fuse_session *se, int notify_code, - struct iovec *iov, int count) - { -- struct fuse_out_header out; -+ struct fuse_out_header out = { -+ .error = notify_code, -+ }; - - if (!se->got_init) { - return -ENOTCONN; - } - -- out.unique = 0; -- out.error = notify_code; - iov[0].iov_base = &out; - iov[0].iov_len = sizeof(struct fuse_out_header); - -@@ -2162,11 +2169,11 @@ static int send_notify_iov(struct fuse_session *se, int notify_code, - int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph) - { - if (ph != NULL) { -- struct fuse_notify_poll_wakeup_out outarg; -+ struct fuse_notify_poll_wakeup_out outarg = { -+ .kh = ph->kh, -+ }; - struct iovec iov[2]; - -- outarg.kh = ph->kh; -- - iov[1].iov_base = &outarg; - iov[1].iov_len = sizeof(outarg); - -@@ -2179,17 +2186,17 @@ int fuse_lowlevel_notify_poll(struct fuse_pollhandle *ph) - int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, - off_t off, off_t len) - { -- struct fuse_notify_inval_inode_out outarg; -+ struct fuse_notify_inval_inode_out outarg = { -+ .ino = ino, -+ .off = off, -+ .len = len, -+ }; - struct iovec iov[2]; - - if (!se) { - return -EINVAL; - } - -- outarg.ino = ino; -- outarg.off = off; -- outarg.len = len; -- - iov[1].iov_base = &outarg; - iov[1].iov_len = sizeof(outarg); - -@@ -2199,17 +2206,16 @@ int fuse_lowlevel_notify_inval_inode(struct fuse_session *se, fuse_ino_t ino, - int fuse_lowlevel_notify_inval_entry(struct fuse_session *se, fuse_ino_t parent, - const char *name, size_t namelen) - { -- struct fuse_notify_inval_entry_out outarg; -+ struct fuse_notify_inval_entry_out outarg = { -+ .parent = parent, -+ .namelen = namelen, -+ }; - struct iovec iov[3]; - - if (!se) { - return -EINVAL; - } - -- outarg.parent = parent; -- outarg.namelen = namelen; -- outarg.padding = 0; -- - iov[1].iov_base = &outarg; - iov[1].iov_len = sizeof(outarg); - iov[2].iov_base = (void *)name; -@@ -2222,18 +2228,17 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, - fuse_ino_t child, const char *name, - size_t namelen) - { -- struct fuse_notify_delete_out outarg; -+ struct fuse_notify_delete_out outarg = { -+ .parent = parent, -+ .child = child, -+ .namelen = namelen, -+ }; - struct iovec iov[3]; - - if (!se) { - return -EINVAL; - } - -- outarg.parent = parent; -- outarg.child = child; -- outarg.namelen = namelen; -- outarg.padding = 0; -- - iov[1].iov_base = &outarg; - iov[1].iov_len = sizeof(outarg); - iov[2].iov_base = (void *)name; -@@ -2245,24 +2250,21 @@ int fuse_lowlevel_notify_delete(struct fuse_session *se, fuse_ino_t parent, - int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino, - off_t offset, struct fuse_bufvec *bufv) - { -- struct fuse_out_header out; -- struct fuse_notify_store_out outarg; -+ struct fuse_out_header out = { -+ .error = FUSE_NOTIFY_STORE, -+ }; -+ struct fuse_notify_store_out outarg = { -+ .nodeid = ino, -+ .offset = offset, -+ .size = fuse_buf_size(bufv), -+ }; - struct iovec iov[3]; -- size_t size = fuse_buf_size(bufv); - int res; - - if (!se) { - return -EINVAL; - } - -- out.unique = 0; -- out.error = FUSE_NOTIFY_STORE; -- -- outarg.nodeid = ino; -- outarg.offset = offset; -- outarg.size = size; -- outarg.padding = 0; -- - iov[0].iov_base = &out; - iov[0].iov_len = sizeof(out); - iov[1].iov_base = &outarg; diff --git a/0067-virtiofsd-add-syslog-command-line-option.patch b/0067-virtiofsd-add-syslog-command-line-option.patch deleted file mode 100644 index 756de55..0000000 --- a/0067-virtiofsd-add-syslog-command-line-option.patch +++ /dev/null @@ -1,223 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:01:36 +0000 -Subject: [PATCH] virtiofsd: add --syslog command-line option -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Sometimes collecting output from stderr is inconvenient or does not fit -within the overall logging architecture. Add syslog(3) support for -cases where stderr cannot be used. - -Signed-off-by: Stefan Hajnoczi -dgilbert: Reworked as a logging function -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit f185621d41f03a23b55795b89e6584253fa23505) ---- - tools/virtiofsd/fuse_lowlevel.h | 1 + - tools/virtiofsd/helper.c | 2 ++ - tools/virtiofsd/passthrough_ll.c | 50 ++++++++++++++++++++++++++++++-- - tools/virtiofsd/seccomp.c | 32 ++++++++++++++------ - tools/virtiofsd/seccomp.h | 4 ++- - 5 files changed, 76 insertions(+), 13 deletions(-) - -diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h -index 0d61df8110..f2750bc189 100644 ---- a/tools/virtiofsd/fuse_lowlevel.h -+++ b/tools/virtiofsd/fuse_lowlevel.h -@@ -1795,6 +1795,7 @@ struct fuse_cmdline_opts { - int show_version; - int show_help; - int print_capabilities; -+ int syslog; - unsigned int max_idle_threads; - }; - -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index 5531425223..9692ef9f1f 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -54,6 +54,7 @@ static const struct fuse_opt fuse_helper_opts[] = { - FUSE_HELPER_OPT("subtype=", nodefault_subtype), - FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), - FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads), -+ FUSE_HELPER_OPT("--syslog", syslog), - FUSE_OPT_END - }; - -@@ -138,6 +139,7 @@ void fuse_cmdline_help(void) - " -V --version print version\n" - " --print-capabilities print vhost-user.json\n" - " -d -o debug enable debug output (implies -f)\n" -+ " --syslog log to syslog (default stderr)\n" - " -f foreground operation\n" - " --daemonize run in background\n" - " -o max_idle_threads the maximum number of idle worker " -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index c281d817af..0372aca143 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -58,6 +58,7 @@ - #include - #include - #include -+#include - #include - - #include "passthrough_helpers.h" -@@ -138,6 +139,7 @@ static const struct fuse_opt lo_opts[] = { - { "norace", offsetof(struct lo_data, norace), 1 }, - FUSE_OPT_END - }; -+static bool use_syslog = false; - - static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n); - -@@ -2262,11 +2264,12 @@ static void setup_mounts(const char *source) - * Lock down this process to prevent access to other processes or files outside - * source directory. This reduces the impact of arbitrary code execution bugs. - */ --static void setup_sandbox(struct lo_data *lo, struct fuse_session *se) -+static void setup_sandbox(struct lo_data *lo, struct fuse_session *se, -+ bool enable_syslog) - { - setup_namespaces(lo, se); - setup_mounts(lo->source); -- setup_seccomp(); -+ setup_seccomp(enable_syslog); - } - - /* Raise the maximum number of open file descriptors */ -@@ -2298,6 +2301,42 @@ static void setup_nofile_rlimit(void) - } - } - -+static void log_func(enum fuse_log_level level, const char *fmt, va_list ap) -+{ -+ if (use_syslog) { -+ int priority = LOG_ERR; -+ switch (level) { -+ case FUSE_LOG_EMERG: -+ priority = LOG_EMERG; -+ break; -+ case FUSE_LOG_ALERT: -+ priority = LOG_ALERT; -+ break; -+ case FUSE_LOG_CRIT: -+ priority = LOG_CRIT; -+ break; -+ case FUSE_LOG_ERR: -+ priority = LOG_ERR; -+ break; -+ case FUSE_LOG_WARNING: -+ priority = LOG_WARNING; -+ break; -+ case FUSE_LOG_NOTICE: -+ priority = LOG_NOTICE; -+ break; -+ case FUSE_LOG_INFO: -+ priority = LOG_INFO; -+ break; -+ case FUSE_LOG_DEBUG: -+ priority = LOG_DEBUG; -+ break; -+ } -+ vsyslog(priority, fmt, ap); -+ } else { -+ vfprintf(stderr, fmt, ap); -+ } -+} -+ - int main(int argc, char *argv[]) - { - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); -@@ -2336,6 +2375,11 @@ int main(int argc, char *argv[]) - if (fuse_parse_cmdline(&args, &opts) != 0) { - return 1; - } -+ fuse_set_log_func(log_func); -+ use_syslog = opts.syslog; -+ if (use_syslog) { -+ openlog("virtiofsd", LOG_PID, LOG_DAEMON); -+ } - if (opts.show_help) { - printf("usage: %s [options]\n\n", argv[0]); - fuse_cmdline_help(); -@@ -2424,7 +2468,7 @@ int main(int argc, char *argv[]) - /* Must be before sandbox since it wants /proc */ - setup_capng(); - -- setup_sandbox(&lo, se); -+ setup_sandbox(&lo, se, opts.syslog); - - /* Block until ctrl+c or fusermount -u */ - ret = virtio_loop(se); -diff --git a/tools/virtiofsd/seccomp.c b/tools/virtiofsd/seccomp.c -index 691fb63dea..2d9d4a7ec0 100644 ---- a/tools/virtiofsd/seccomp.c -+++ b/tools/virtiofsd/seccomp.c -@@ -107,11 +107,28 @@ static const int syscall_whitelist[] = { - SCMP_SYS(writev), - }; - --void setup_seccomp(void) -+/* Syscalls used when --syslog is enabled */ -+static const int syscall_whitelist_syslog[] = { -+ SCMP_SYS(sendto), -+}; -+ -+static void add_whitelist(scmp_filter_ctx ctx, const int syscalls[], size_t len) - { -- scmp_filter_ctx ctx; - size_t i; - -+ for (i = 0; i < len; i++) { -+ if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, syscalls[i], 0) != 0) { -+ fuse_log(FUSE_LOG_ERR, "seccomp_rule_add syscall %d failed\n", -+ syscalls[i]); -+ exit(1); -+ } -+ } -+} -+ -+void setup_seccomp(bool enable_syslog) -+{ -+ scmp_filter_ctx ctx; -+ - #ifdef SCMP_ACT_KILL_PROCESS - ctx = seccomp_init(SCMP_ACT_KILL_PROCESS); - /* Handle a newer libseccomp but an older kernel */ -@@ -126,13 +143,10 @@ void setup_seccomp(void) - exit(1); - } - -- for (i = 0; i < G_N_ELEMENTS(syscall_whitelist); i++) { -- if (seccomp_rule_add(ctx, SCMP_ACT_ALLOW, -- syscall_whitelist[i], 0) != 0) { -- fuse_log(FUSE_LOG_ERR, "seccomp_rule_add syscall %d", -- syscall_whitelist[i]); -- exit(1); -- } -+ add_whitelist(ctx, syscall_whitelist, G_N_ELEMENTS(syscall_whitelist)); -+ if (enable_syslog) { -+ add_whitelist(ctx, syscall_whitelist_syslog, -+ G_N_ELEMENTS(syscall_whitelist_syslog)); - } - - /* libvhost-user calls this for post-copy migration, we don't need it */ -diff --git a/tools/virtiofsd/seccomp.h b/tools/virtiofsd/seccomp.h -index 86bce72652..d47c8eade6 100644 ---- a/tools/virtiofsd/seccomp.h -+++ b/tools/virtiofsd/seccomp.h -@@ -9,6 +9,8 @@ - #ifndef VIRTIOFSD_SECCOMP_H - #define VIRTIOFSD_SECCOMP_H - --void setup_seccomp(void); -+#include -+ -+void setup_seccomp(bool enable_syslog); - - #endif /* VIRTIOFSD_SECCOMP_H */ diff --git a/0068-virtiofsd-print-log-only-when-priority-is-high-enoug.patch b/0068-virtiofsd-print-log-only-when-priority-is-high-enoug.patch deleted file mode 100644 index fa679f4..0000000 --- a/0068-virtiofsd-print-log-only-when-priority-is-high-enoug.patch +++ /dev/null @@ -1,451 +0,0 @@ -From: Eryu Guan -Date: Mon, 27 Jan 2020 19:01:37 +0000 -Subject: [PATCH] virtiofsd: print log only when priority is high enough -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Introduce "-o log_level=" command line option to specify current log -level (priority), valid values are "debug info warn err", e.g. - - ./virtiofsd -o log_level=debug ... - -So only log priority higher than "debug" will be printed to -stderr/syslog. And the default level is info. - -The "-o debug"/"-d" options are kept, and imply debug log level. - -Signed-off-by: Eryu Guan -dgilbert: Reworked for libfuse's log_func -Signed-off-by: Dr. David Alan Gilbert -with fix by: -Signed-off-by: Xiao Yang -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit d240314a1a18a1d914af1b5763fe8c9a572e6409) ---- - tools/virtiofsd/fuse_lowlevel.c | 75 ++++++++------------ - tools/virtiofsd/fuse_lowlevel.h | 1 + - tools/virtiofsd/helper.c | 8 ++- - tools/virtiofsd/passthrough_ll.c | 118 +++++++++++++------------------ - 4 files changed, 87 insertions(+), 115 deletions(-) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 6ceb33d913..a7a19685b5 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -158,19 +158,17 @@ static int fuse_send_msg(struct fuse_session *se, struct fuse_chan *ch, - struct fuse_out_header *out = iov[0].iov_base; - - out->len = iov_length(iov, count); -- if (se->debug) { -- if (out->unique == 0) { -- fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n", out->error, -- out->len); -- } else if (out->error) { -- fuse_log(FUSE_LOG_DEBUG, -- " unique: %llu, error: %i (%s), outsize: %i\n", -- (unsigned long long)out->unique, out->error, -- strerror(-out->error), out->len); -- } else { -- fuse_log(FUSE_LOG_DEBUG, " unique: %llu, success, outsize: %i\n", -- (unsigned long long)out->unique, out->len); -- } -+ if (out->unique == 0) { -+ fuse_log(FUSE_LOG_DEBUG, "NOTIFY: code=%d length=%u\n", out->error, -+ out->len); -+ } else if (out->error) { -+ fuse_log(FUSE_LOG_DEBUG, -+ " unique: %llu, error: %i (%s), outsize: %i\n", -+ (unsigned long long)out->unique, out->error, -+ strerror(-out->error), out->len); -+ } else { -+ fuse_log(FUSE_LOG_DEBUG, " unique: %llu, success, outsize: %i\n", -+ (unsigned long long)out->unique, out->len); - } - - if (fuse_lowlevel_is_virtio(se)) { -@@ -1662,10 +1660,8 @@ static void do_interrupt(fuse_req_t req, fuse_ino_t nodeid, - return; - } - -- if (se->debug) { -- fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n", -- (unsigned long long)arg->unique); -- } -+ fuse_log(FUSE_LOG_DEBUG, "INTERRUPT: %llu\n", -+ (unsigned long long)arg->unique); - - req->u.i.unique = arg->unique; - -@@ -1901,13 +1897,10 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, - } - } - -- if (se->debug) { -- fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor); -- if (arg->major == 7 && arg->minor >= 6) { -- fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags); -- fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n", -- arg->max_readahead); -- } -+ fuse_log(FUSE_LOG_DEBUG, "INIT: %u.%u\n", arg->major, arg->minor); -+ if (arg->major == 7 && arg->minor >= 6) { -+ fuse_log(FUSE_LOG_DEBUG, "flags=0x%08x\n", arg->flags); -+ fuse_log(FUSE_LOG_DEBUG, "max_readahead=0x%08x\n", arg->max_readahead); - } - se->conn.proto_major = arg->major; - se->conn.proto_minor = arg->minor; -@@ -2116,19 +2109,14 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, - outarg.congestion_threshold = se->conn.congestion_threshold; - outarg.time_gran = se->conn.time_gran; - -- if (se->debug) { -- fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, -- outarg.minor); -- fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); -- fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n", -- outarg.max_readahead); -- fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write); -- fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n", -- outarg.max_background); -- fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n", -- outarg.congestion_threshold); -- fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", outarg.time_gran); -- } -+ fuse_log(FUSE_LOG_DEBUG, " INIT: %u.%u\n", outarg.major, outarg.minor); -+ fuse_log(FUSE_LOG_DEBUG, " flags=0x%08x\n", outarg.flags); -+ fuse_log(FUSE_LOG_DEBUG, " max_readahead=0x%08x\n", outarg.max_readahead); -+ fuse_log(FUSE_LOG_DEBUG, " max_write=0x%08x\n", outarg.max_write); -+ fuse_log(FUSE_LOG_DEBUG, " max_background=%i\n", outarg.max_background); -+ fuse_log(FUSE_LOG_DEBUG, " congestion_threshold=%i\n", -+ outarg.congestion_threshold); -+ fuse_log(FUSE_LOG_DEBUG, " time_gran=%u\n", outarg.time_gran); - - send_reply_ok(req, &outarg, outargsize); - } -@@ -2407,14 +2395,11 @@ void fuse_session_process_buf_int(struct fuse_session *se, - in = fuse_mbuf_iter_advance(&iter, sizeof(*in)); - assert(in); /* caller guarantees the input buffer is large enough */ - -- if (se->debug) { -- fuse_log(FUSE_LOG_DEBUG, -- "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, " -- "pid: %u\n", -- (unsigned long long)in->unique, -- opname((enum fuse_opcode)in->opcode), in->opcode, -- (unsigned long long)in->nodeid, buf->size, in->pid); -- } -+ fuse_log( -+ FUSE_LOG_DEBUG, -+ "unique: %llu, opcode: %s (%i), nodeid: %llu, insize: %zu, pid: %u\n", -+ (unsigned long long)in->unique, opname((enum fuse_opcode)in->opcode), -+ in->opcode, (unsigned long long)in->nodeid, buf->size, in->pid); - - req = fuse_ll_alloc_req(se); - if (req == NULL) { -diff --git a/tools/virtiofsd/fuse_lowlevel.h b/tools/virtiofsd/fuse_lowlevel.h -index f2750bc189..138041e5f1 100644 ---- a/tools/virtiofsd/fuse_lowlevel.h -+++ b/tools/virtiofsd/fuse_lowlevel.h -@@ -1796,6 +1796,7 @@ struct fuse_cmdline_opts { - int show_help; - int print_capabilities; - int syslog; -+ int log_level; - unsigned int max_idle_threads; - }; - -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index 9692ef9f1f..6d50a46a7e 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -34,7 +34,6 @@ - t, offsetof(struct fuse_cmdline_opts, p), v \ - } - -- - static const struct fuse_opt fuse_helper_opts[] = { - FUSE_HELPER_OPT("-h", show_help), - FUSE_HELPER_OPT("--help", show_help), -@@ -55,6 +54,10 @@ static const struct fuse_opt fuse_helper_opts[] = { - FUSE_OPT_KEY("subtype=", FUSE_OPT_KEY_KEEP), - FUSE_HELPER_OPT("max_idle_threads=%u", max_idle_threads), - FUSE_HELPER_OPT("--syslog", syslog), -+ FUSE_HELPER_OPT_VALUE("log_level=debug", log_level, FUSE_LOG_DEBUG), -+ FUSE_HELPER_OPT_VALUE("log_level=info", log_level, FUSE_LOG_INFO), -+ FUSE_HELPER_OPT_VALUE("log_level=warn", log_level, FUSE_LOG_WARNING), -+ FUSE_HELPER_OPT_VALUE("log_level=err", log_level, FUSE_LOG_ERR), - FUSE_OPT_END - }; - -@@ -142,6 +145,9 @@ void fuse_cmdline_help(void) - " --syslog log to syslog (default stderr)\n" - " -f foreground operation\n" - " --daemonize run in background\n" -+ " -o log_level= log level, default to \"info\"\n" -+ " level could be one of \"debug, " -+ "info, warn, err\"\n" - " -o max_idle_threads the maximum number of idle worker " - "threads\n" - " allowed (default: 10)\n" -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 0372aca143..ff6910fd73 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -37,6 +37,7 @@ - - #include "qemu/osdep.h" - #include "fuse_virtio.h" -+#include "fuse_log.h" - #include "fuse_lowlevel.h" - #include - #include -@@ -140,6 +141,7 @@ static const struct fuse_opt lo_opts[] = { - FUSE_OPT_END - }; - static bool use_syslog = false; -+static int current_log_level; - - static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n); - -@@ -458,11 +460,6 @@ static int lo_fd(fuse_req_t req, fuse_ino_t ino) - return inode ? inode->fd : -1; - } - --static bool lo_debug(fuse_req_t req) --{ -- return lo_data(req)->debug != 0; --} -- - static void lo_init(void *userdata, struct fuse_conn_info *conn) - { - struct lo_data *lo = (struct lo_data *)userdata; -@@ -472,15 +469,11 @@ static void lo_init(void *userdata, struct fuse_conn_info *conn) - } - - if (lo->writeback && conn->capable & FUSE_CAP_WRITEBACK_CACHE) { -- if (lo->debug) { -- fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); -- } -+ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); - conn->want |= FUSE_CAP_WRITEBACK_CACHE; - } - if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { -- if (lo->debug) { -- fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); -- } -+ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); - conn->want |= FUSE_CAP_FLOCK_LOCKS; - } - } -@@ -823,10 +816,8 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - } - e->ino = inode->fuse_ino; - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", -- (unsigned long long)parent, name, (unsigned long long)e->ino); -- } -+ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long)parent, -+ name, (unsigned long long)e->ino); - - return 0; - -@@ -843,10 +834,8 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) - struct fuse_entry_param e; - int err; - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", -- parent, name); -- } -+ fuse_log(FUSE_LOG_DEBUG, "lo_lookup(parent=%" PRIu64 ", name=%s)\n", parent, -+ name); - - /* - * Don't use is_safe_path_component(), allow "." and ".." for NFS export -@@ -971,10 +960,8 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, - goto out; - } - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", -- (unsigned long long)parent, name, (unsigned long long)e.ino); -- } -+ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long)parent, -+ name, (unsigned long long)e.ino); - - fuse_reply_entry(req, &e); - return; -@@ -1074,10 +1061,8 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, - pthread_mutex_unlock(&lo->mutex); - e.ino = inode->fuse_ino; - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", -- (unsigned long long)parent, name, (unsigned long long)e.ino); -- } -+ fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long)parent, -+ name, (unsigned long long)e.ino); - - fuse_reply_entry(req, &e); - return; -@@ -1171,11 +1156,9 @@ static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) - return; - } - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", -- (unsigned long long)ino, (unsigned long long)inode->refcount, -- (unsigned long long)nlookup); -- } -+ fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", -+ (unsigned long long)ino, (unsigned long long)inode->refcount, -+ (unsigned long long)nlookup); - - unref_inode(lo, inode, nlookup); - } -@@ -1445,10 +1428,8 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, - int err; - struct lo_cred old = {}; - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", -- parent, name); -- } -+ fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", parent, -+ name); - - if (!is_safe_path_component(name)) { - fuse_reply_err(req, EINVAL); -@@ -1525,10 +1506,8 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) - char buf[64]; - struct lo_data *lo = lo_data(req); - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino, -- fi->flags); -- } -+ fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino, -+ fi->flags); - - /* - * With writeback cache, kernel may send read requests even -@@ -1644,12 +1623,10 @@ static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, - { - struct fuse_bufvec buf = FUSE_BUFVEC_INIT(size); - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, -- "lo_read(ino=%" PRIu64 ", size=%zd, " -- "off=%lu)\n", -- ino, size, (unsigned long)offset); -- } -+ fuse_log(FUSE_LOG_DEBUG, -+ "lo_read(ino=%" PRIu64 ", size=%zd, " -+ "off=%lu)\n", -+ ino, size, (unsigned long)offset); - - buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK; - buf.buf[0].fd = lo_fi_fd(req, fi); -@@ -1671,11 +1648,9 @@ static void lo_write_buf(fuse_req_t req, fuse_ino_t ino, - out_buf.buf[0].fd = lo_fi_fd(req, fi); - out_buf.buf[0].pos = off; - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, -- "lo_write(ino=%" PRIu64 ", size=%zd, off=%lu)\n", ino, -- out_buf.buf[0].size, (unsigned long)off); -- } -+ fuse_log(FUSE_LOG_DEBUG, -+ "lo_write_buf(ino=%" PRIu64 ", size=%zd, off=%lu)\n", ino, -+ out_buf.buf[0].size, (unsigned long)off); - - /* - * If kill_priv is set, drop CAP_FSETID which should lead to kernel -@@ -1774,11 +1749,8 @@ static void lo_getxattr(fuse_req_t req, fuse_ino_t ino, const char *name, - goto out; - } - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, -- "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", ino, name, -- size); -- } -+ fuse_log(FUSE_LOG_DEBUG, "lo_getxattr(ino=%" PRIu64 ", name=%s size=%zd)\n", -+ ino, name, size); - - if (inode->is_symlink) { - /* Sorry, no race free way to getxattr on symlink. */ -@@ -1852,10 +1824,8 @@ static void lo_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) - goto out; - } - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n", -- ino, size); -- } -+ fuse_log(FUSE_LOG_DEBUG, "lo_listxattr(ino=%" PRIu64 ", size=%zd)\n", ino, -+ size); - - if (inode->is_symlink) { - /* Sorry, no race free way to listxattr on symlink. */ -@@ -1929,11 +1899,8 @@ static void lo_setxattr(fuse_req_t req, fuse_ino_t ino, const char *name, - goto out; - } - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, -- "lo_setxattr(ino=%" PRIu64 ", name=%s value=%s size=%zd)\n", -- ino, name, value, size); -- } -+ fuse_log(FUSE_LOG_DEBUG, "lo_setxattr(ino=%" PRIu64 -+ ", name=%s value=%s size=%zd)\n", ino, name, value, size); - - if (inode->is_symlink) { - /* Sorry, no race free way to setxattr on symlink. */ -@@ -1978,10 +1945,8 @@ static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name) - goto out; - } - -- if (lo_debug(req)) { -- fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", -- ino, name); -- } -+ fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", ino, -+ name); - - if (inode->is_symlink) { - /* Sorry, no race free way to setxattr on symlink. */ -@@ -2303,6 +2268,10 @@ static void setup_nofile_rlimit(void) - - static void log_func(enum fuse_log_level level, const char *fmt, va_list ap) - { -+ if (current_log_level < level) { -+ return; -+ } -+ - if (use_syslog) { - int priority = LOG_ERR; - switch (level) { -@@ -2401,8 +2370,19 @@ int main(int argc, char *argv[]) - return 1; - } - -+ /* -+ * log_level is 0 if not configured via cmd options (0 is LOG_EMERG, -+ * and we don't use this log level). -+ */ -+ if (opts.log_level != 0) { -+ current_log_level = opts.log_level; -+ } - lo.debug = opts.debug; -+ if (lo.debug) { -+ current_log_level = FUSE_LOG_DEBUG; -+ } - lo.root.refcount = 2; -+ - if (lo.source) { - struct stat stat; - int res; diff --git a/0069-virtiofsd-Add-ID-to-the-log-with-FUSE_LOG_DEBUG-leve.patch b/0069-virtiofsd-Add-ID-to-the-log-with-FUSE_LOG_DEBUG-leve.patch deleted file mode 100644 index e3aaef9..0000000 --- a/0069-virtiofsd-Add-ID-to-the-log-with-FUSE_LOG_DEBUG-leve.patch +++ /dev/null @@ -1,68 +0,0 @@ -From: Masayoshi Mizuma -Date: Mon, 27 Jan 2020 19:01:38 +0000 -Subject: [PATCH] virtiofsd: Add ID to the log with FUSE_LOG_DEBUG level -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -virtiofsd has some threads, so we see a lot of logs with debug option. -It would be useful for debugging if we can identify the specific thread -from the log. - -Add ID, which is got by gettid(), to the log with FUSE_LOG_DEBUG level -so that we can grep the specific thread. - -The log is like as: - - ]# ./virtiofsd -d -o vhost_user_socket=/tmp/vhostqemu0 -o source=/tmp/share0 -o cache=auto - ... - [ID: 00000097] unique: 12696, success, outsize: 120 - [ID: 00000097] virtio_send_msg: elem 18: with 2 in desc of length 120 - [ID: 00000003] fv_queue_thread: Got queue event on Queue 1 - [ID: 00000003] fv_queue_thread: Queue 1 gave evalue: 1 available: in: 65552 out: 80 - [ID: 00000003] fv_queue_thread: Waiting for Queue 1 event - [ID: 00000071] fv_queue_worker: elem 33: with 2 out desc of length 80 bad_in_num=0 bad_out_num=0 - [ID: 00000071] unique: 12694, opcode: READ (15), nodeid: 2, insize: 80, pid: 2014 - [ID: 00000071] lo_read(ino=2, size=65536, off=131072) - -Signed-off-by: Masayoshi Mizuma - -Signed-off-by: Dr. David Alan Gilbert - added rework as suggested by Daniel P. Berrangé during review -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 36f3846902bd41413f6c0bf797dee509028c29f4) ---- - tools/virtiofsd/passthrough_ll.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index ff6910fd73..f08324f000 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -43,6 +43,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -2268,10 +2269,17 @@ static void setup_nofile_rlimit(void) - - static void log_func(enum fuse_log_level level, const char *fmt, va_list ap) - { -+ g_autofree char *localfmt = NULL; -+ - if (current_log_level < level) { - return; - } - -+ if (current_log_level == FUSE_LOG_DEBUG) { -+ localfmt = g_strdup_printf("[ID: %08ld] %s", syscall(__NR_gettid), fmt); -+ fmt = localfmt; -+ } -+ - if (use_syslog) { - int priority = LOG_ERR; - switch (level) { diff --git a/0070-virtiofsd-Add-timestamp-to-the-log-with-FUSE_LOG_DEB.patch b/0070-virtiofsd-Add-timestamp-to-the-log-with-FUSE_LOG_DEB.patch deleted file mode 100644 index b58a8c2..0000000 --- a/0070-virtiofsd-Add-timestamp-to-the-log-with-FUSE_LOG_DEB.patch +++ /dev/null @@ -1,56 +0,0 @@ -From: Masayoshi Mizuma -Date: Mon, 27 Jan 2020 19:01:39 +0000 -Subject: [PATCH] virtiofsd: Add timestamp to the log with FUSE_LOG_DEBUG level -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -virtiofsd has some threads, so we see a lot of logs with debug option. -It would be useful for debugging if we can see the timestamp. - -Add nano second timestamp, which got by get_clock(), to the log with -FUSE_LOG_DEBUG level if the syslog option isn't set. - -The log is like as: - - # ./virtiofsd -d -o vhost_user_socket=/tmp/vhostqemu0 -o source=/tmp/share0 -o cache=auto - ... - [5365943125463727] [ID: 00000002] fv_queue_thread: Start for queue 0 kick_fd 9 - [5365943125568644] [ID: 00000002] fv_queue_thread: Waiting for Queue 0 event - [5365943125573561] [ID: 00000002] fv_queue_thread: Got queue event on Queue 0 - -Signed-off-by: Masayoshi Mizuma -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 50fb955aa0e6ede929422146936cf68bf1ca876f) ---- - tools/virtiofsd/passthrough_ll.c | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index f08324f000..98114a3f4a 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -36,6 +36,7 @@ - */ - - #include "qemu/osdep.h" -+#include "qemu/timer.h" - #include "fuse_virtio.h" - #include "fuse_log.h" - #include "fuse_lowlevel.h" -@@ -2276,7 +2277,13 @@ static void log_func(enum fuse_log_level level, const char *fmt, va_list ap) - } - - if (current_log_level == FUSE_LOG_DEBUG) { -- localfmt = g_strdup_printf("[ID: %08ld] %s", syscall(__NR_gettid), fmt); -+ if (!use_syslog) { -+ localfmt = g_strdup_printf("[%" PRId64 "] [ID: %08ld] %s", -+ get_clock(), syscall(__NR_gettid), fmt); -+ } else { -+ localfmt = g_strdup_printf("[ID: %08ld] %s", syscall(__NR_gettid), -+ fmt); -+ } - fmt = localfmt; - } - diff --git a/0071-virtiofsd-Handle-reinit.patch b/0071-virtiofsd-Handle-reinit.patch deleted file mode 100644 index 88c91c7..0000000 --- a/0071-virtiofsd-Handle-reinit.patch +++ /dev/null @@ -1,37 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:40 +0000 -Subject: [PATCH] virtiofsd: Handle reinit -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Allow init->destroy->init for mount->umount->mount - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit c806d6435fe95fd54b379920aca2f4e3ea1f3258) ---- - tools/virtiofsd/fuse_lowlevel.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index a7a19685b5..7d742b5a72 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -2028,6 +2028,7 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, - } - - se->got_init = 1; -+ se->got_destroy = 0; - if (se->op.init) { - se->op.init(se->userdata, &se->conn); - } -@@ -2130,6 +2131,7 @@ static void do_destroy(fuse_req_t req, fuse_ino_t nodeid, - (void)iter; - - se->got_destroy = 1; -+ se->got_init = 0; - if (se->op.destroy) { - se->op.destroy(se->userdata); - } diff --git a/0072-virtiofsd-Handle-hard-reboot.patch b/0072-virtiofsd-Handle-hard-reboot.patch deleted file mode 100644 index c5e2dfd..0000000 --- a/0072-virtiofsd-Handle-hard-reboot.patch +++ /dev/null @@ -1,49 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:41 +0000 -Subject: [PATCH] virtiofsd: Handle hard reboot -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Handle a - mount - hard reboot (without unmount) - mount - -we get another 'init' which FUSE doesn't normally expect. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit e8556f49098b5d95634e592d79a97f761b76c96e) ---- - tools/virtiofsd/fuse_lowlevel.c | 16 +++++++++++++++- - 1 file changed, 15 insertions(+), 1 deletion(-) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 7d742b5a72..65f91dabae 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -2433,7 +2433,21 @@ void fuse_session_process_buf_int(struct fuse_session *se, - goto reply_err; - } - } else if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT) { -- goto reply_err; -+ if (fuse_lowlevel_is_virtio(se)) { -+ /* -+ * TODO: This is after a hard reboot typically, we need to do -+ * a destroy, but we can't reply to this request yet so -+ * we can't use do_destroy -+ */ -+ fuse_log(FUSE_LOG_DEBUG, "%s: reinit\n", __func__); -+ se->got_destroy = 1; -+ se->got_init = 0; -+ if (se->op.destroy) { -+ se->op.destroy(se->userdata); -+ } -+ } else { -+ goto reply_err; -+ } - } - - err = EACCES; diff --git a/0073-virtiofsd-Kill-threads-when-queues-are-stopped.patch b/0073-virtiofsd-Kill-threads-when-queues-are-stopped.patch deleted file mode 100644 index 70d3e7a..0000000 --- a/0073-virtiofsd-Kill-threads-when-queues-are-stopped.patch +++ /dev/null @@ -1,126 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:42 +0000 -Subject: [PATCH] virtiofsd: Kill threads when queues are stopped -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Kill the threads we've started when the queues get stopped. - -Signed-off-by: Dr. David Alan Gilbert -With improvements by: -Signed-off-by: Eryu Guan -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 10477ac47fc57d00a84802ff97c15450cd8021c1) ---- - tools/virtiofsd/fuse_virtio.c | 51 ++++++++++++++++++++++++++++++----- - 1 file changed, 44 insertions(+), 7 deletions(-) - -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index 872968f2c8..7a8774a3ee 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -41,6 +41,7 @@ struct fv_QueueInfo { - /* Our queue index, corresponds to array position */ - int qidx; - int kick_fd; -+ int kill_fd; /* For killing the thread */ - - /* The element for the command currently being processed */ - VuVirtqElement *qe; -@@ -412,14 +413,17 @@ static void *fv_queue_thread(void *opaque) - fuse_log(FUSE_LOG_INFO, "%s: Start for queue %d kick_fd %d\n", __func__, - qi->qidx, qi->kick_fd); - while (1) { -- struct pollfd pf[1]; -+ struct pollfd pf[2]; - pf[0].fd = qi->kick_fd; - pf[0].events = POLLIN; - pf[0].revents = 0; -+ pf[1].fd = qi->kill_fd; -+ pf[1].events = POLLIN; -+ pf[1].revents = 0; - - fuse_log(FUSE_LOG_DEBUG, "%s: Waiting for Queue %d event\n", __func__, - qi->qidx); -- int poll_res = ppoll(pf, 1, NULL, NULL); -+ int poll_res = ppoll(pf, 2, NULL, NULL); - - if (poll_res == -1) { - if (errno == EINTR) { -@@ -430,12 +434,23 @@ static void *fv_queue_thread(void *opaque) - fuse_log(FUSE_LOG_ERR, "fv_queue_thread ppoll: %m\n"); - break; - } -- assert(poll_res == 1); -+ assert(poll_res >= 1); - if (pf[0].revents & (POLLERR | POLLHUP | POLLNVAL)) { - fuse_log(FUSE_LOG_ERR, "%s: Unexpected poll revents %x Queue %d\n", - __func__, pf[0].revents, qi->qidx); - break; - } -+ if (pf[1].revents & (POLLERR | POLLHUP | POLLNVAL)) { -+ fuse_log(FUSE_LOG_ERR, -+ "%s: Unexpected poll revents %x Queue %d killfd\n", -+ __func__, pf[1].revents, qi->qidx); -+ break; -+ } -+ if (pf[1].revents) { -+ fuse_log(FUSE_LOG_INFO, "%s: kill event on queue %d - quitting\n", -+ __func__, qi->qidx); -+ break; -+ } - assert(pf[0].revents & POLLIN); - fuse_log(FUSE_LOG_DEBUG, "%s: Got queue event on Queue %d\n", __func__, - qi->qidx); -@@ -589,6 +604,28 @@ out: - return NULL; - } - -+static void fv_queue_cleanup_thread(struct fv_VuDev *vud, int qidx) -+{ -+ int ret; -+ struct fv_QueueInfo *ourqi; -+ -+ assert(qidx < vud->nqueues); -+ ourqi = vud->qi[qidx]; -+ -+ /* Kill the thread */ -+ if (eventfd_write(ourqi->kill_fd, 1)) { -+ fuse_log(FUSE_LOG_ERR, "Eventfd_write for queue %d: %s\n", -+ qidx, strerror(errno)); -+ } -+ ret = pthread_join(ourqi->thread, NULL); -+ if (ret) { -+ fuse_log(FUSE_LOG_ERR, "%s: Failed to join thread idx %d err %d\n", -+ __func__, qidx, ret); -+ } -+ close(ourqi->kill_fd); -+ ourqi->kick_fd = -1; -+} -+ - /* Callback from libvhost-user on start or stop of a queue */ - static void fv_queue_set_started(VuDev *dev, int qidx, bool started) - { -@@ -633,16 +670,16 @@ static void fv_queue_set_started(VuDev *dev, int qidx, bool started) - } - ourqi = vud->qi[qidx]; - ourqi->kick_fd = dev->vq[qidx].kick_fd; -+ -+ ourqi->kill_fd = eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE); -+ assert(ourqi->kill_fd != -1); - if (pthread_create(&ourqi->thread, NULL, fv_queue_thread, ourqi)) { - fuse_log(FUSE_LOG_ERR, "%s: Failed to create thread for queue %d\n", - __func__, qidx); - assert(0); - } - } else { -- /* TODO: Kill the thread */ -- assert(qidx < vud->nqueues); -- ourqi = vud->qi[qidx]; -- ourqi->kick_fd = -1; -+ fv_queue_cleanup_thread(vud, qidx); - } - } - diff --git a/0074-vhost-user-Print-unexpected-slave-message-types.patch b/0074-vhost-user-Print-unexpected-slave-message-types.patch deleted file mode 100644 index cead453..0000000 --- a/0074-vhost-user-Print-unexpected-slave-message-types.patch +++ /dev/null @@ -1,32 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:43 +0000 -Subject: [PATCH] vhost-user: Print unexpected slave message types -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -When we receive an unexpected message type on the slave fd, print -the type. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 0fdc465d7d5aafeae127eba488f247ac6f58df4c) ---- - hw/virtio/vhost-user.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c -index 02a9b25199..e4f46ecf88 100644 ---- a/hw/virtio/vhost-user.c -+++ b/hw/virtio/vhost-user.c -@@ -1055,7 +1055,7 @@ static void slave_read(void *opaque) - fd[0]); - break; - default: -- error_report("Received unexpected msg type."); -+ error_report("Received unexpected msg type: %d.", hdr.request); - ret = -EINVAL; - } - diff --git a/0075-contrib-libvhost-user-Protect-slave-fd-with-mutex.patch b/0075-contrib-libvhost-user-Protect-slave-fd-with-mutex.patch deleted file mode 100644 index 2e05ece..0000000 --- a/0075-contrib-libvhost-user-Protect-slave-fd-with-mutex.patch +++ /dev/null @@ -1,118 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:44 +0000 -Subject: [PATCH] contrib/libvhost-user: Protect slave fd with mutex -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -In future patches we'll be performing commands on the slave-fd driven -by commands on queues, since those queues will be driven by individual -threads we need to make sure they don't attempt to use the slave-fd -for multiple commands in parallel. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit c25c02b9e6a196be87a818f459c426556b24770d) ---- - contrib/libvhost-user/libvhost-user.c | 24 ++++++++++++++++++++---- - contrib/libvhost-user/libvhost-user.h | 3 +++ - 2 files changed, 23 insertions(+), 4 deletions(-) - -diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c -index ec27b78ff1..63e41062a4 100644 ---- a/contrib/libvhost-user/libvhost-user.c -+++ b/contrib/libvhost-user/libvhost-user.c -@@ -392,26 +392,37 @@ vu_send_reply(VuDev *dev, int conn_fd, VhostUserMsg *vmsg) - return vu_message_write(dev, conn_fd, vmsg); - } - -+/* -+ * Processes a reply on the slave channel. -+ * Entered with slave_mutex held and releases it before exit. -+ * Returns true on success. -+ */ - static bool - vu_process_message_reply(VuDev *dev, const VhostUserMsg *vmsg) - { - VhostUserMsg msg_reply; -+ bool result = false; - - if ((vmsg->flags & VHOST_USER_NEED_REPLY_MASK) == 0) { -- return true; -+ result = true; -+ goto out; - } - - if (!vu_message_read(dev, dev->slave_fd, &msg_reply)) { -- return false; -+ goto out; - } - - if (msg_reply.request != vmsg->request) { - DPRINT("Received unexpected msg type. Expected %d received %d", - vmsg->request, msg_reply.request); -- return false; -+ goto out; - } - -- return msg_reply.payload.u64 == 0; -+ result = msg_reply.payload.u64 == 0; -+ -+out: -+ pthread_mutex_unlock(&dev->slave_mutex); -+ return result; - } - - /* Kick the log_call_fd if required. */ -@@ -1105,10 +1116,13 @@ bool vu_set_queue_host_notifier(VuDev *dev, VuVirtq *vq, int fd, - return false; - } - -+ pthread_mutex_lock(&dev->slave_mutex); - if (!vu_message_write(dev, dev->slave_fd, &vmsg)) { -+ pthread_mutex_unlock(&dev->slave_mutex); - return false; - } - -+ /* Also unlocks the slave_mutex */ - return vu_process_message_reply(dev, &vmsg); - } - -@@ -1628,6 +1642,7 @@ vu_deinit(VuDev *dev) - close(dev->slave_fd); - dev->slave_fd = -1; - } -+ pthread_mutex_destroy(&dev->slave_mutex); - - if (dev->sock != -1) { - close(dev->sock); -@@ -1663,6 +1678,7 @@ vu_init(VuDev *dev, - dev->remove_watch = remove_watch; - dev->iface = iface; - dev->log_call_fd = -1; -+ pthread_mutex_init(&dev->slave_mutex, NULL); - dev->slave_fd = -1; - dev->max_queues = max_queues; - -diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h -index 46b600799b..1844b6f8d4 100644 ---- a/contrib/libvhost-user/libvhost-user.h -+++ b/contrib/libvhost-user/libvhost-user.h -@@ -19,6 +19,7 @@ - #include - #include - #include -+#include - #include "standard-headers/linux/virtio_ring.h" - - /* Based on qemu/hw/virtio/vhost-user.c */ -@@ -355,6 +356,8 @@ struct VuDev { - VuVirtq *vq; - VuDevInflightInfo inflight_info; - int log_call_fd; -+ /* Must be held while using slave_fd */ -+ pthread_mutex_t slave_mutex; - int slave_fd; - uint64_t log_size; - uint8_t *log_table; diff --git a/0076-virtiofsd-passthrough_ll-add-renameat2-support.patch b/0076-virtiofsd-passthrough_ll-add-renameat2-support.patch deleted file mode 100644 index e375310..0000000 --- a/0076-virtiofsd-passthrough_ll-add-renameat2-support.patch +++ /dev/null @@ -1,33 +0,0 @@ -From: Miklos Szeredi -Date: Mon, 27 Jan 2020 19:01:45 +0000 -Subject: [PATCH] virtiofsd: passthrough_ll: add renameat2 support - -Signed-off-by: Miklos Szeredi -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit f0ab7d6f78a7d3c1c19fd81a91c9b1199f56c4f6) ---- - tools/virtiofsd/passthrough_ll.c | 10 ++++++++++ - 1 file changed, 10 insertions(+) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 98114a3f4a..18d69abcbc 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1099,7 +1099,17 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, - } - - if (flags) { -+#ifndef SYS_renameat2 - fuse_reply_err(req, EINVAL); -+#else -+ res = syscall(SYS_renameat2, lo_fd(req, parent), name, -+ lo_fd(req, newparent), newname, flags); -+ if (res == -1 && errno == ENOSYS) { -+ fuse_reply_err(req, EINVAL); -+ } else { -+ fuse_reply_err(req, res == -1 ? errno : 0); -+ } -+#endif - return; - } - diff --git a/0077-virtiofsd-passthrough_ll-disable-readdirplus-on-cach.patch b/0077-virtiofsd-passthrough_ll-disable-readdirplus-on-cach.patch deleted file mode 100644 index d4dbb58..0000000 --- a/0077-virtiofsd-passthrough_ll-disable-readdirplus-on-cach.patch +++ /dev/null @@ -1,33 +0,0 @@ -From: Miklos Szeredi -Date: Mon, 27 Jan 2020 19:01:46 +0000 -Subject: [PATCH] virtiofsd: passthrough_ll: disable readdirplus on cache=never -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -...because the attributes sent in the READDIRPLUS reply would be discarded -anyway. - -Signed-off-by: Miklos Szeredi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit ddcbabcb0ea177be3ec3500726b699c7c26ffd93) ---- - tools/virtiofsd/passthrough_ll.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 18d69abcbc..6480c517dc 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -478,6 +478,10 @@ static void lo_init(void *userdata, struct fuse_conn_info *conn) - fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); - conn->want |= FUSE_CAP_FLOCK_LOCKS; - } -+ if (lo->cache == CACHE_NEVER) { -+ fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling readdirplus\n"); -+ conn->want &= ~FUSE_CAP_READDIRPLUS; -+ } - } - - static void lo_getattr(fuse_req_t req, fuse_ino_t ino, diff --git a/0078-virtiofsd-passthrough_ll-control-readdirplus.patch b/0078-virtiofsd-passthrough_ll-control-readdirplus.patch deleted file mode 100644 index d40c6f6..0000000 --- a/0078-virtiofsd-passthrough_ll-control-readdirplus.patch +++ /dev/null @@ -1,60 +0,0 @@ -From: Miklos Szeredi -Date: Mon, 27 Jan 2020 19:01:47 +0000 -Subject: [PATCH] virtiofsd: passthrough_ll: control readdirplus - -Signed-off-by: Miklos Szeredi -Reviewed-by: Misono Tomohiro -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 59aef494be2d8d91055ff3f3a8eb13d9f32873d8) ---- - tools/virtiofsd/helper.c | 4 ++++ - tools/virtiofsd/passthrough_ll.c | 7 ++++++- - 2 files changed, 10 insertions(+), 1 deletion(-) - -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index 6d50a46a7e..14f5d70c10 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -153,6 +153,10 @@ void fuse_cmdline_help(void) - " allowed (default: 10)\n" - " -o norace disable racy fallback\n" - " default: false\n" -+ " -o readdirplus|no_readdirplus\n" -+ " enable/disable readirplus\n" -+ " default: readdirplus except with " -+ "cache=never\n" - ); - } - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 6480c517dc..8b1784ff7b 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -117,6 +117,8 @@ struct lo_data { - double timeout; - int cache; - int timeout_set; -+ int readdirplus_set; -+ int readdirplus_clear; - struct lo_inode root; /* protected by lo->mutex */ - struct lo_map ino_map; /* protected by lo->mutex */ - struct lo_map dirp_map; /* protected by lo->mutex */ -@@ -140,6 +142,8 @@ static const struct fuse_opt lo_opts[] = { - { "cache=auto", offsetof(struct lo_data, cache), CACHE_NORMAL }, - { "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS }, - { "norace", offsetof(struct lo_data, norace), 1 }, -+ { "readdirplus", offsetof(struct lo_data, readdirplus_set), 1 }, -+ { "no_readdirplus", offsetof(struct lo_data, readdirplus_clear), 1 }, - FUSE_OPT_END - }; - static bool use_syslog = false; -@@ -478,7 +482,8 @@ static void lo_init(void *userdata, struct fuse_conn_info *conn) - fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); - conn->want |= FUSE_CAP_FLOCK_LOCKS; - } -- if (lo->cache == CACHE_NEVER) { -+ if ((lo->cache == CACHE_NEVER && !lo->readdirplus_set) || -+ lo->readdirplus_clear) { - fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling readdirplus\n"); - conn->want &= ~FUSE_CAP_READDIRPLUS; - } diff --git a/0079-virtiofsd-rename-unref_inode-to-unref_inode_lolocked.patch b/0079-virtiofsd-rename-unref_inode-to-unref_inode_lolocked.patch deleted file mode 100644 index 90fc806..0000000 --- a/0079-virtiofsd-rename-unref_inode-to-unref_inode_lolocked.patch +++ /dev/null @@ -1,77 +0,0 @@ -From: Miklos Szeredi -Date: Mon, 27 Jan 2020 19:01:48 +0000 -Subject: [PATCH] virtiofsd: rename unref_inode() to unref_inode_lolocked() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Miklos Szeredi -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 95d2715791c60b5dc2d22e4eb7b83217273296fa) ---- - tools/virtiofsd/passthrough_ll.c | 15 ++++++++------- - 1 file changed, 8 insertions(+), 7 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 8b1784ff7b..de12e75a9e 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -148,8 +148,8 @@ static const struct fuse_opt lo_opts[] = { - }; - static bool use_syslog = false; - static int current_log_level; -- --static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n); -+static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, -+ uint64_t n); - - static struct { - pthread_mutex_t mutex; -@@ -586,7 +586,7 @@ retry: - return 0; - - fail_unref: -- unref_inode(lo, p, 1); -+ unref_inode_lolocked(lo, p, 1); - fail: - if (retries) { - retries--; -@@ -624,7 +624,7 @@ fallback: - res = lo_parent_and_name(lo, inode, path, &parent); - if (res != -1) { - res = utimensat(parent->fd, path, tv, AT_SYMLINK_NOFOLLOW); -- unref_inode(lo, parent, 1); -+ unref_inode_lolocked(lo, parent, 1); - } - - return res; -@@ -1027,7 +1027,7 @@ fallback: - res = lo_parent_and_name(lo, inode, path, &parent); - if (res != -1) { - res = linkat(parent->fd, path, dfd, name, 0); -- unref_inode(lo, parent, 1); -+ unref_inode_lolocked(lo, parent, 1); - } - - return res; -@@ -1141,7 +1141,8 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) - fuse_reply_err(req, res == -1 ? errno : 0); - } - --static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) -+static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, -+ uint64_t n) - { - if (!inode) { - return; -@@ -1181,7 +1182,7 @@ static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) - (unsigned long long)ino, (unsigned long long)inode->refcount, - (unsigned long long)nlookup); - -- unref_inode(lo, inode, nlookup); -+ unref_inode_lolocked(lo, inode, nlookup); - } - - static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) diff --git a/0080-virtiofsd-fail-when-parent-inode-isn-t-known-in-lo_d.patch b/0080-virtiofsd-fail-when-parent-inode-isn-t-known-in-lo_d.patch deleted file mode 100644 index cc729ce..0000000 --- a/0080-virtiofsd-fail-when-parent-inode-isn-t-known-in-lo_d.patch +++ /dev/null @@ -1,66 +0,0 @@ -From: Miklos Szeredi -Date: Mon, 27 Jan 2020 19:01:49 +0000 -Subject: [PATCH] virtiofsd: fail when parent inode isn't known in - lo_do_lookup() - -The Linux file handle APIs (struct export_operations) can access inodes -that are not attached to parents because path name traversal is not -performed. Refuse if there is no parent in lo_do_lookup(). - -Also clean up lo_do_lookup() while we're here. - -Signed-off-by: Miklos Szeredi -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Misono Tomohiro -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 9de4fab5995d115f8ebfb41d8d94a866d80a1708) ---- - tools/virtiofsd/passthrough_ll.c | 14 ++++++++++++-- - 1 file changed, 12 insertions(+), 2 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index de12e75a9e..33bfb4d1f4 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -777,6 +777,15 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - struct lo_data *lo = lo_data(req); - struct lo_inode *inode, *dir = lo_inode(req, parent); - -+ /* -+ * name_to_handle_at() and open_by_handle_at() can reach here with fuse -+ * mount point in guest, but we don't have its inode info in the -+ * ino_map. -+ */ -+ if (!dir) { -+ return ENOENT; -+ } -+ - memset(e, 0, sizeof(*e)); - e->attr_timeout = lo->timeout; - e->entry_timeout = lo->timeout; -@@ -786,7 +795,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - name = "."; - } - -- newfd = openat(lo_fd(req, parent), name, O_PATH | O_NOFOLLOW); -+ newfd = openat(dir->fd, name, O_PATH | O_NOFOLLOW); - if (newfd == -1) { - goto out_err; - } -@@ -796,7 +805,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - goto out_err; - } - -- inode = lo_find(lo_data(req), &e->attr); -+ inode = lo_find(lo, &e->attr); - if (inode) { - close(newfd); - newfd = -1; -@@ -812,6 +821,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - inode->is_symlink = S_ISLNK(e->attr.st_mode); - inode->refcount = 1; - inode->fd = newfd; -+ newfd = -1; - inode->ino = e->attr.st_ino; - inode->dev = e->attr.st_dev; - diff --git a/0081-virtiofsd-extract-root-inode-init-into-setup_root.patch b/0081-virtiofsd-extract-root-inode-init-into-setup_root.patch deleted file mode 100644 index f88349d..0000000 --- a/0081-virtiofsd-extract-root-inode-init-into-setup_root.patch +++ /dev/null @@ -1,91 +0,0 @@ -From: Miklos Szeredi -Date: Mon, 27 Jan 2020 19:01:50 +0000 -Subject: [PATCH] virtiofsd: extract root inode init into setup_root() - -Inititialize the root inode in a single place. - -Signed-off-by: Miklos Szeredi -Signed-off-by: Stefan Hajnoczi -dgilbert: -with fix suggested by Misono Tomohiro -Reviewed-by: Misono Tomohiro -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 3ca8a2b1c83eb185c232a4e87abbb65495263756) ---- - tools/virtiofsd/passthrough_ll.c | 35 +++++++++++++++++++++++--------- - 1 file changed, 25 insertions(+), 10 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 33bfb4d1f4..9e7191eb75 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -2351,6 +2351,30 @@ static void log_func(enum fuse_log_level level, const char *fmt, va_list ap) - } - } - -+static void setup_root(struct lo_data *lo, struct lo_inode *root) -+{ -+ int fd, res; -+ struct stat stat; -+ -+ fd = open("/", O_PATH); -+ if (fd == -1) { -+ fuse_log(FUSE_LOG_ERR, "open(%s, O_PATH): %m\n", lo->source); -+ exit(1); -+ } -+ -+ res = fstatat(fd, "", &stat, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -+ if (res == -1) { -+ fuse_log(FUSE_LOG_ERR, "fstatat(%s): %m\n", lo->source); -+ exit(1); -+ } -+ -+ root->is_symlink = false; -+ root->fd = fd; -+ root->ino = stat.st_ino; -+ root->dev = stat.st_dev; -+ root->refcount = 2; -+} -+ - int main(int argc, char *argv[]) - { - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); -@@ -2426,8 +2450,6 @@ int main(int argc, char *argv[]) - if (lo.debug) { - current_log_level = FUSE_LOG_DEBUG; - } -- lo.root.refcount = 2; -- - if (lo.source) { - struct stat stat; - int res; -@@ -2446,7 +2468,6 @@ int main(int argc, char *argv[]) - } else { - lo.source = "/"; - } -- lo.root.is_symlink = false; - if (!lo.timeout_set) { - switch (lo.cache) { - case CACHE_NEVER: -@@ -2466,13 +2487,6 @@ int main(int argc, char *argv[]) - exit(1); - } - -- lo.root.fd = open(lo.source, O_PATH); -- -- if (lo.root.fd == -1) { -- fuse_log(FUSE_LOG_ERR, "open(\"%s\", O_PATH): %m\n", lo.source); -- exit(1); -- } -- - se = fuse_session_new(&args, &lo_oper, sizeof(lo_oper), &lo); - if (se == NULL) { - goto err_out1; -@@ -2495,6 +2509,7 @@ int main(int argc, char *argv[]) - - setup_sandbox(&lo, se, opts.syslog); - -+ setup_root(&lo, &lo.root); - /* Block until ctrl+c or fusermount -u */ - ret = virtio_loop(se); - diff --git a/0082-virtiofsd-passthrough_ll-clean-up-cache-related-opti.patch b/0082-virtiofsd-passthrough_ll-clean-up-cache-related-opti.patch deleted file mode 100644 index 57a2381..0000000 --- a/0082-virtiofsd-passthrough_ll-clean-up-cache-related-opti.patch +++ /dev/null @@ -1,121 +0,0 @@ -From: Miklos Szeredi -Date: Mon, 27 Jan 2020 19:01:51 +0000 -Subject: [PATCH] virtiofsd: passthrough_ll: clean up cache related options -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - - - Rename "cache=never" to "cache=none" to match 9p's similar option. - - - Rename CACHE_NORMAL constant to CACHE_AUTO to match the "cache=auto" - option. - -Signed-off-by: Miklos Szeredi -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 230e777b5e250759ee0480fcc0e9ccfa2b082fba) ---- - tools/virtiofsd/helper.c | 5 ++++- - tools/virtiofsd/passthrough_ll.c | 20 ++++++++++---------- - 2 files changed, 14 insertions(+), 11 deletions(-) - -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index 14f5d70c10..567202444a 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -145,6 +145,9 @@ void fuse_cmdline_help(void) - " --syslog log to syslog (default stderr)\n" - " -f foreground operation\n" - " --daemonize run in background\n" -+ " -o cache= cache mode. could be one of \"auto, " -+ "always, none\"\n" -+ " default: auto\n" - " -o log_level= log level, default to \"info\"\n" - " level could be one of \"debug, " - "info, warn, err\"\n" -@@ -156,7 +159,7 @@ void fuse_cmdline_help(void) - " -o readdirplus|no_readdirplus\n" - " enable/disable readirplus\n" - " default: readdirplus except with " -- "cache=never\n" -+ "cache=none\n" - ); - } - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 9e7191eb75..b40f2874a7 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -101,8 +101,8 @@ struct lo_cred { - }; - - enum { -- CACHE_NEVER, -- CACHE_NORMAL, -+ CACHE_NONE, -+ CACHE_AUTO, - CACHE_ALWAYS, - }; - -@@ -138,8 +138,8 @@ static const struct fuse_opt lo_opts[] = { - { "no_xattr", offsetof(struct lo_data, xattr), 0 }, - { "timeout=%lf", offsetof(struct lo_data, timeout), 0 }, - { "timeout=", offsetof(struct lo_data, timeout_set), 1 }, -- { "cache=never", offsetof(struct lo_data, cache), CACHE_NEVER }, -- { "cache=auto", offsetof(struct lo_data, cache), CACHE_NORMAL }, -+ { "cache=none", offsetof(struct lo_data, cache), CACHE_NONE }, -+ { "cache=auto", offsetof(struct lo_data, cache), CACHE_AUTO }, - { "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS }, - { "norace", offsetof(struct lo_data, norace), 1 }, - { "readdirplus", offsetof(struct lo_data, readdirplus_set), 1 }, -@@ -482,7 +482,7 @@ static void lo_init(void *userdata, struct fuse_conn_info *conn) - fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); - conn->want |= FUSE_CAP_FLOCK_LOCKS; - } -- if ((lo->cache == CACHE_NEVER && !lo->readdirplus_set) || -+ if ((lo->cache == CACHE_NONE && !lo->readdirplus_set) || - lo->readdirplus_clear) { - fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling readdirplus\n"); - conn->want &= ~FUSE_CAP_READDIRPLUS; -@@ -1493,7 +1493,7 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, - fi->fh = fh; - err = lo_do_lookup(req, parent, name, &e); - } -- if (lo->cache == CACHE_NEVER) { -+ if (lo->cache == CACHE_NONE) { - fi->direct_io = 1; - } else if (lo->cache == CACHE_ALWAYS) { - fi->keep_cache = 1; -@@ -1578,7 +1578,7 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) - } - - fi->fh = fh; -- if (lo->cache == CACHE_NEVER) { -+ if (lo->cache == CACHE_NONE) { - fi->direct_io = 1; - } else if (lo->cache == CACHE_ALWAYS) { - fi->keep_cache = 1; -@@ -2395,7 +2395,7 @@ int main(int argc, char *argv[]) - lo.root.next = lo.root.prev = &lo.root; - lo.root.fd = -1; - lo.root.fuse_ino = FUSE_ROOT_ID; -- lo.cache = CACHE_NORMAL; -+ lo.cache = CACHE_AUTO; - - /* - * Set up the ino map like this: -@@ -2470,11 +2470,11 @@ int main(int argc, char *argv[]) - } - if (!lo.timeout_set) { - switch (lo.cache) { -- case CACHE_NEVER: -+ case CACHE_NONE: - lo.timeout = 0.0; - break; - -- case CACHE_NORMAL: -+ case CACHE_AUTO: - lo.timeout = 1.0; - break; - diff --git a/0083-virtiofsd-passthrough_ll-use-hashtable.patch b/0083-virtiofsd-passthrough_ll-use-hashtable.patch deleted file mode 100644 index 0780206..0000000 --- a/0083-virtiofsd-passthrough_ll-use-hashtable.patch +++ /dev/null @@ -1,195 +0,0 @@ -From: Miklos Szeredi -Date: Mon, 27 Jan 2020 19:01:52 +0000 -Subject: [PATCH] virtiofsd: passthrough_ll: use hashtable -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Improve performance of inode lookup by using a hash table. - -Signed-off-by: Miklos Szeredi -Signed-off-by: Dr. David Alan Gilbert -Signed-off-by: Liu Bo -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit bfc50a6e06b10b2f9dbaf6c1a89dd523322e016f) ---- - tools/virtiofsd/passthrough_ll.c | 81 ++++++++++++++++++-------------- - 1 file changed, 45 insertions(+), 36 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index b40f2874a7..b176a314d6 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -84,13 +84,15 @@ struct lo_map { - ssize_t freelist; - }; - -+struct lo_key { -+ ino_t ino; -+ dev_t dev; -+}; -+ - struct lo_inode { -- struct lo_inode *next; /* protected by lo->mutex */ -- struct lo_inode *prev; /* protected by lo->mutex */ - int fd; - bool is_symlink; -- ino_t ino; -- dev_t dev; -+ struct lo_key key; - uint64_t refcount; /* protected by lo->mutex */ - fuse_ino_t fuse_ino; - }; -@@ -119,7 +121,8 @@ struct lo_data { - int timeout_set; - int readdirplus_set; - int readdirplus_clear; -- struct lo_inode root; /* protected by lo->mutex */ -+ struct lo_inode root; -+ GHashTable *inodes; /* protected by lo->mutex */ - struct lo_map ino_map; /* protected by lo->mutex */ - struct lo_map dirp_map; /* protected by lo->mutex */ - struct lo_map fd_map; /* protected by lo->mutex */ -@@ -573,7 +576,7 @@ retry: - } - goto fail_unref; - } -- if (stat.st_dev != inode->dev || stat.st_ino != inode->ino) { -+ if (stat.st_dev != inode->key.dev || stat.st_ino != inode->key.ino) { - if (!retries) { - fuse_log(FUSE_LOG_WARNING, - "%s: failed to match last\n", __func__); -@@ -753,19 +756,20 @@ out_err: - static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st) - { - struct lo_inode *p; -- struct lo_inode *ret = NULL; -+ struct lo_key key = { -+ .ino = st->st_ino, -+ .dev = st->st_dev, -+ }; - - pthread_mutex_lock(&lo->mutex); -- for (p = lo->root.next; p != &lo->root; p = p->next) { -- if (p->ino == st->st_ino && p->dev == st->st_dev) { -- assert(p->refcount > 0); -- ret = p; -- ret->refcount++; -- break; -- } -+ p = g_hash_table_lookup(lo->inodes, &key); -+ if (p) { -+ assert(p->refcount > 0); -+ p->refcount++; - } - pthread_mutex_unlock(&lo->mutex); -- return ret; -+ -+ return p; - } - - static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, -@@ -810,8 +814,6 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - close(newfd); - newfd = -1; - } else { -- struct lo_inode *prev, *next; -- - saverr = ENOMEM; - inode = calloc(1, sizeof(struct lo_inode)); - if (!inode) { -@@ -822,17 +824,12 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - inode->refcount = 1; - inode->fd = newfd; - newfd = -1; -- inode->ino = e->attr.st_ino; -- inode->dev = e->attr.st_dev; -+ inode->key.ino = e->attr.st_ino; -+ inode->key.dev = e->attr.st_dev; - - pthread_mutex_lock(&lo->mutex); - inode->fuse_ino = lo_add_inode_mapping(req, inode); -- prev = &lo->root; -- next = prev->next; -- next->prev = inode; -- inode->next = next; -- inode->prev = prev; -- prev->next = inode; -+ g_hash_table_insert(lo->inodes, &inode->key, inode); - pthread_mutex_unlock(&lo->mutex); - } - e->ino = inode->fuse_ino; -@@ -1162,14 +1159,8 @@ static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, - assert(inode->refcount >= n); - inode->refcount -= n; - if (!inode->refcount) { -- struct lo_inode *prev, *next; -- -- prev = inode->prev; -- next = inode->next; -- next->prev = prev; -- prev->next = next; -- - lo_map_remove(&lo->ino_map, inode->fuse_ino); -+ g_hash_table_remove(lo->inodes, &inode->key); - pthread_mutex_unlock(&lo->mutex); - close(inode->fd); - free(inode); -@@ -1369,7 +1360,7 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, - - /* Hide root's parent directory */ - if (dinode == &lo->root && strcmp(name, "..") == 0) { -- e.attr.st_ino = lo->root.ino; -+ e.attr.st_ino = lo->root.key.ino; - e.attr.st_mode = DT_DIR << 12; - } - -@@ -2370,11 +2361,26 @@ static void setup_root(struct lo_data *lo, struct lo_inode *root) - - root->is_symlink = false; - root->fd = fd; -- root->ino = stat.st_ino; -- root->dev = stat.st_dev; -+ root->key.ino = stat.st_ino; -+ root->key.dev = stat.st_dev; - root->refcount = 2; - } - -+static guint lo_key_hash(gconstpointer key) -+{ -+ const struct lo_key *lkey = key; -+ -+ return (guint)lkey->ino + (guint)lkey->dev; -+} -+ -+static gboolean lo_key_equal(gconstpointer a, gconstpointer b) -+{ -+ const struct lo_key *la = a; -+ const struct lo_key *lb = b; -+ -+ return la->ino == lb->ino && la->dev == lb->dev; -+} -+ - int main(int argc, char *argv[]) - { - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); -@@ -2392,7 +2398,7 @@ int main(int argc, char *argv[]) - umask(0); - - pthread_mutex_init(&lo.mutex, NULL); -- lo.root.next = lo.root.prev = &lo.root; -+ lo.inodes = g_hash_table_new(lo_key_hash, lo_key_equal); - lo.root.fd = -1; - lo.root.fuse_ino = FUSE_ROOT_ID; - lo.cache = CACHE_AUTO; -@@ -2522,6 +2528,9 @@ err_out2: - err_out1: - fuse_opt_free_args(&args); - -+ if (lo.inodes) { -+ g_hash_table_destroy(lo.inodes); -+ } - lo_map_destroy(&lo.fd_map); - lo_map_destroy(&lo.dirp_map); - lo_map_destroy(&lo.ino_map); diff --git a/0084-virtiofsd-Clean-up-inodes-on-destroy.patch b/0084-virtiofsd-Clean-up-inodes-on-destroy.patch deleted file mode 100644 index 48b65d5..0000000 --- a/0084-virtiofsd-Clean-up-inodes-on-destroy.patch +++ /dev/null @@ -1,69 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:01:53 +0000 -Subject: [PATCH] virtiofsd: Clean up inodes on destroy -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Clear out our inodes and fd's on a 'destroy' - so we get rid -of them if we reboot the guest. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 771b01eb76ff480fee984bd1d21727147cc3e702) ---- - tools/virtiofsd/passthrough_ll.c | 26 ++++++++++++++++++++++++++ - 1 file changed, 26 insertions(+) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index b176a314d6..9ed77a17fd 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1169,6 +1169,25 @@ static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, - } - } - -+static int unref_all_inodes_cb(gpointer key, gpointer value, gpointer user_data) -+{ -+ struct lo_inode *inode = value; -+ struct lo_data *lo = user_data; -+ -+ inode->refcount = 0; -+ lo_map_remove(&lo->ino_map, inode->fuse_ino); -+ close(inode->fd); -+ -+ return TRUE; -+} -+ -+static void unref_all_inodes(struct lo_data *lo) -+{ -+ pthread_mutex_lock(&lo->mutex); -+ g_hash_table_foreach_remove(lo->inodes, unref_all_inodes_cb, lo); -+ pthread_mutex_unlock(&lo->mutex); -+} -+ - static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) - { - struct lo_data *lo = lo_data(req); -@@ -2035,6 +2054,12 @@ static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, - } - } - -+static void lo_destroy(void *userdata) -+{ -+ struct lo_data *lo = (struct lo_data *)userdata; -+ unref_all_inodes(lo); -+} -+ - static struct fuse_lowlevel_ops lo_oper = { - .init = lo_init, - .lookup = lo_lookup, -@@ -2073,6 +2098,7 @@ static struct fuse_lowlevel_ops lo_oper = { - .copy_file_range = lo_copy_file_range, - #endif - .lseek = lo_lseek, -+ .destroy = lo_destroy, - }; - - /* Print vhost-user.json backend program capabilities */ diff --git a/0085-virtiofsd-support-nanosecond-resolution-for-file-tim.patch b/0085-virtiofsd-support-nanosecond-resolution-for-file-tim.patch deleted file mode 100644 index 59996ef..0000000 --- a/0085-virtiofsd-support-nanosecond-resolution-for-file-tim.patch +++ /dev/null @@ -1,66 +0,0 @@ -From: Jiufei Xue -Date: Mon, 27 Jan 2020 19:01:54 +0000 -Subject: [PATCH] virtiofsd: support nanosecond resolution for file timestamp -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Define HAVE_STRUCT_STAT_ST_ATIM to 1 if `st_atim' is member of `struct -stat' which means support nanosecond resolution for the file timestamp -fields. - -Signed-off-by: Jiufei Xue -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 8a792b034d4b315251fd842bb4c73a133aa1368f) ---- - configure | 16 ++++++++++++++++ - tools/virtiofsd/fuse_misc.h | 1 + - 2 files changed, 17 insertions(+) - -diff --git a/configure b/configure -index afe9393f04..dd50b03b01 100755 ---- a/configure -+++ b/configure -@@ -5217,6 +5217,19 @@ if compile_prog "" "" ; then - strchrnul=yes - fi - -+######################################### -+# check if we have st_atim -+ -+st_atim=no -+cat > $TMPC << EOF -+#include -+#include -+int main(void) { return offsetof(struct stat, st_atim); } -+EOF -+if compile_prog "" "" ; then -+ st_atim=yes -+fi -+ - ########################################## - # check if trace backend exists - -@@ -6918,6 +6931,9 @@ fi - if test "$strchrnul" = "yes" ; then - echo "HAVE_STRCHRNUL=y" >> $config_host_mak - fi -+if test "$st_atim" = "yes" ; then -+ echo "HAVE_STRUCT_STAT_ST_ATIM=y" >> $config_host_mak -+fi - if test "$byteswap_h" = "yes" ; then - echo "CONFIG_BYTESWAP_H=y" >> $config_host_mak - fi -diff --git a/tools/virtiofsd/fuse_misc.h b/tools/virtiofsd/fuse_misc.h -index f252baa752..5c618ce21f 100644 ---- a/tools/virtiofsd/fuse_misc.h -+++ b/tools/virtiofsd/fuse_misc.h -@@ -7,6 +7,7 @@ - */ - - #include -+#include "config-host.h" - - /* - * Versioned symbols cannot be used in some cases because it diff --git a/0086-virtiofsd-fix-error-handling-in-main.patch b/0086-virtiofsd-fix-error-handling-in-main.patch deleted file mode 100644 index 3828cef..0000000 --- a/0086-virtiofsd-fix-error-handling-in-main.patch +++ /dev/null @@ -1,47 +0,0 @@ -From: Liu Bo -Date: Mon, 27 Jan 2020 19:01:55 +0000 -Subject: [PATCH] virtiofsd: fix error handling in main() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Neither fuse_parse_cmdline() nor fuse_opt_parse() goes to the right place -to do cleanup. - -Signed-off-by: Liu Bo -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit c6de804670f2255ce776263124c37f3370dc5ac1) ---- - tools/virtiofsd/passthrough_ll.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 9ed77a17fd..af050c6d97 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -2443,13 +2443,14 @@ int main(int argc, char *argv[]) - lo_map_init(&lo.fd_map); - - if (fuse_parse_cmdline(&args, &opts) != 0) { -- return 1; -+ goto err_out1; - } - fuse_set_log_func(log_func); - use_syslog = opts.syslog; - if (use_syslog) { - openlog("virtiofsd", LOG_PID, LOG_DAEMON); - } -+ - if (opts.show_help) { - printf("usage: %s [options]\n\n", argv[0]); - fuse_cmdline_help(); -@@ -2468,7 +2469,7 @@ int main(int argc, char *argv[]) - } - - if (fuse_opt_parse(&args, &lo, lo_opts, NULL) == -1) { -- return 1; -+ goto err_out1; - } - - /* diff --git a/0087-virtiofsd-cleanup-allocated-resource-in-se.patch b/0087-virtiofsd-cleanup-allocated-resource-in-se.patch deleted file mode 100644 index cf1ddb1..0000000 --- a/0087-virtiofsd-cleanup-allocated-resource-in-se.patch +++ /dev/null @@ -1,66 +0,0 @@ -From: Liu Bo -Date: Mon, 27 Jan 2020 19:01:56 +0000 -Subject: [PATCH] virtiofsd: cleanup allocated resource in se -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This cleans up unfreed resources in se on quiting, including -se->virtio_dev, se->vu_socket_path, se->vu_socketfd. - -Signed-off-by: Liu Bo -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 61cfc44982e566c33b9d5df17858e4d5ae373873) ---- - tools/virtiofsd/fuse_lowlevel.c | 7 +++++++ - tools/virtiofsd/fuse_virtio.c | 7 +++++++ - tools/virtiofsd/fuse_virtio.h | 2 +- - 3 files changed, 15 insertions(+), 1 deletion(-) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 65f91dabae..440508a6ec 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -2532,6 +2532,13 @@ void fuse_session_destroy(struct fuse_session *se) - if (se->fd != -1) { - close(se->fd); - } -+ -+ if (se->vu_socket_path) { -+ virtio_session_close(se); -+ free(se->vu_socket_path); -+ se->vu_socket_path = NULL; -+ } -+ - free(se); - } - -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index 7a8774a3ee..e7bd772805 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -833,3 +833,10 @@ int virtio_session_mount(struct fuse_session *se) - - return 0; - } -+ -+void virtio_session_close(struct fuse_session *se) -+{ -+ close(se->vu_socketfd); -+ free(se->virtio_dev); -+ se->virtio_dev = NULL; -+} -diff --git a/tools/virtiofsd/fuse_virtio.h b/tools/virtiofsd/fuse_virtio.h -index cc676b9193..111684032c 100644 ---- a/tools/virtiofsd/fuse_virtio.h -+++ b/tools/virtiofsd/fuse_virtio.h -@@ -19,7 +19,7 @@ - struct fuse_session; - - int virtio_session_mount(struct fuse_session *se); -- -+void virtio_session_close(struct fuse_session *se); - int virtio_loop(struct fuse_session *se); - - diff --git a/0088-virtiofsd-fix-memory-leak-on-lo.source.patch b/0088-virtiofsd-fix-memory-leak-on-lo.source.patch deleted file mode 100644 index 3c509a7..0000000 --- a/0088-virtiofsd-fix-memory-leak-on-lo.source.patch +++ /dev/null @@ -1,47 +0,0 @@ -From: Liu Bo -Date: Mon, 27 Jan 2020 19:01:57 +0000 -Subject: [PATCH] virtiofsd: fix memory leak on lo.source - -valgrind reported that lo.source is leaked on quiting, but it was defined -as (const char*) as it may point to a const string "/". - -Signed-off-by: Liu Bo -Reviewed-by: Misono Tomohiro -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit eb68a33b5fc5dde87bd9b99b94e7c33a5d8ea82e) ---- - tools/virtiofsd/passthrough_ll.c | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index af050c6d97..056ebe8556 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -115,7 +115,7 @@ struct lo_data { - int writeback; - int flock; - int xattr; -- const char *source; -+ char *source; - double timeout; - int cache; - int timeout_set; -@@ -2497,9 +2497,8 @@ int main(int argc, char *argv[]) - fuse_log(FUSE_LOG_ERR, "source is not a directory\n"); - exit(1); - } -- - } else { -- lo.source = "/"; -+ lo.source = strdup("/"); - } - if (!lo.timeout_set) { - switch (lo.cache) { -@@ -2570,5 +2569,7 @@ err_out1: - close(lo.root.fd); - } - -+ free(lo.source); -+ - return ret ? 1 : 0; - } diff --git a/0089-virtiofsd-add-helper-for-lo_data-cleanup.patch b/0089-virtiofsd-add-helper-for-lo_data-cleanup.patch deleted file mode 100644 index 2cdf880..0000000 --- a/0089-virtiofsd-add-helper-for-lo_data-cleanup.patch +++ /dev/null @@ -1,72 +0,0 @@ -From: Liu Bo -Date: Mon, 27 Jan 2020 19:01:58 +0000 -Subject: [PATCH] virtiofsd: add helper for lo_data cleanup -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This offers an helper function for lo_data's cleanup. - -Signed-off-by: Liu Bo -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 18a69cbbb6a4caa7c2040c6db4a33b044a32be7e) ---- - tools/virtiofsd/passthrough_ll.c | 37 ++++++++++++++++++-------------- - 1 file changed, 21 insertions(+), 16 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 056ebe8556..e8dc5c7320 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -2407,6 +2407,26 @@ static gboolean lo_key_equal(gconstpointer a, gconstpointer b) - return la->ino == lb->ino && la->dev == lb->dev; - } - -+static void fuse_lo_data_cleanup(struct lo_data *lo) -+{ -+ if (lo->inodes) { -+ g_hash_table_destroy(lo->inodes); -+ } -+ lo_map_destroy(&lo->fd_map); -+ lo_map_destroy(&lo->dirp_map); -+ lo_map_destroy(&lo->ino_map); -+ -+ if (lo->proc_self_fd >= 0) { -+ close(lo->proc_self_fd); -+ } -+ -+ if (lo->root.fd >= 0) { -+ close(lo->root.fd); -+ } -+ -+ free(lo->source); -+} -+ - int main(int argc, char *argv[]) - { - struct fuse_args args = FUSE_ARGS_INIT(argc, argv); -@@ -2554,22 +2574,7 @@ err_out2: - err_out1: - fuse_opt_free_args(&args); - -- if (lo.inodes) { -- g_hash_table_destroy(lo.inodes); -- } -- lo_map_destroy(&lo.fd_map); -- lo_map_destroy(&lo.dirp_map); -- lo_map_destroy(&lo.ino_map); -- -- if (lo.proc_self_fd >= 0) { -- close(lo.proc_self_fd); -- } -- -- if (lo.root.fd >= 0) { -- close(lo.root.fd); -- } -- -- free(lo.source); -+ fuse_lo_data_cleanup(&lo); - - return ret ? 1 : 0; - } diff --git a/0090-virtiofsd-Prevent-multiply-running-with-same-vhost_u.patch b/0090-virtiofsd-Prevent-multiply-running-with-same-vhost_u.patch deleted file mode 100644 index d3f2ebc..0000000 --- a/0090-virtiofsd-Prevent-multiply-running-with-same-vhost_u.patch +++ /dev/null @@ -1,127 +0,0 @@ -From: Masayoshi Mizuma -Date: Mon, 27 Jan 2020 19:01:59 +0000 -Subject: [PATCH] virtiofsd: Prevent multiply running with same - vhost_user_socket -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -virtiofsd can run multiply even if the vhost_user_socket is same path. - - ]# ./virtiofsd -o vhost_user_socket=/tmp/vhostqemu -o source=/tmp/share & - [1] 244965 - virtio_session_mount: Waiting for vhost-user socket connection... - ]# ./virtiofsd -o vhost_user_socket=/tmp/vhostqemu -o source=/tmp/share & - [2] 244966 - virtio_session_mount: Waiting for vhost-user socket connection... - ]# - -The user will get confused about the situation and maybe the cause of the -unexpected problem. So it's better to prevent the multiple running. - -Create a regular file under localstatedir directory to exclude the -vhost_user_socket. To create and lock the file, use qemu_write_pidfile() -because the API has some sanity checks and file lock. - -Signed-off-by: Masayoshi Mizuma -Signed-off-by: Dr. David Alan Gilbert - Applied fixes from Stefan's review and moved osdep include -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 96814800d2b49d18737c36e021c387697ec40c62) ---- - tools/virtiofsd/fuse_lowlevel.c | 1 + - tools/virtiofsd/fuse_virtio.c | 49 ++++++++++++++++++++++++++++++++- - 2 files changed, 49 insertions(+), 1 deletion(-) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 440508a6ec..aac282f278 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -18,6 +18,7 @@ - - #include - #include -+#include - #include - #include - #include -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index e7bd772805..b7948def27 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -13,11 +13,12 @@ - - #include "qemu/osdep.h" - #include "qemu/iov.h" --#include "fuse_virtio.h" -+#include "qapi/error.h" - #include "fuse_i.h" - #include "standard-headers/linux/fuse.h" - #include "fuse_misc.h" - #include "fuse_opt.h" -+#include "fuse_virtio.h" - - #include - #include -@@ -743,6 +744,42 @@ int virtio_loop(struct fuse_session *se) - return 0; - } - -+static void strreplace(char *s, char old, char new) -+{ -+ for (; *s; ++s) { -+ if (*s == old) { -+ *s = new; -+ } -+ } -+} -+ -+static bool fv_socket_lock(struct fuse_session *se) -+{ -+ g_autofree gchar *sk_name = NULL; -+ g_autofree gchar *pidfile = NULL; -+ g_autofree gchar *dir = NULL; -+ Error *local_err = NULL; -+ -+ dir = qemu_get_local_state_pathname("run/virtiofsd"); -+ -+ if (g_mkdir_with_parents(dir, S_IRWXU) < 0) { -+ fuse_log(FUSE_LOG_ERR, "%s: Failed to create directory %s: %s", -+ __func__, dir, strerror(errno)); -+ return false; -+ } -+ -+ sk_name = g_strdup(se->vu_socket_path); -+ strreplace(sk_name, '/', '.'); -+ pidfile = g_strdup_printf("%s/%s.pid", dir, sk_name); -+ -+ if (!qemu_write_pidfile(pidfile, &local_err)) { -+ error_report_err(local_err); -+ return false; -+ } -+ -+ return true; -+} -+ - static int fv_create_listen_socket(struct fuse_session *se) - { - struct sockaddr_un un; -@@ -758,6 +795,16 @@ static int fv_create_listen_socket(struct fuse_session *se) - return -1; - } - -+ if (!strlen(se->vu_socket_path)) { -+ fuse_log(FUSE_LOG_ERR, "Socket path is empty\n"); -+ return -1; -+ } -+ -+ /* Check the vu_socket_path is already used */ -+ if (!fv_socket_lock(se)) { -+ return -1; -+ } -+ - /* - * Create the Unix socket to communicate with qemu - * based on QEMU's vhost-user-bridge diff --git a/0091-virtiofsd-enable-PARALLEL_DIROPS-during-INIT.patch b/0091-virtiofsd-enable-PARALLEL_DIROPS-during-INIT.patch deleted file mode 100644 index dfdd3b3..0000000 --- a/0091-virtiofsd-enable-PARALLEL_DIROPS-during-INIT.patch +++ /dev/null @@ -1,31 +0,0 @@ -From: Liu Bo -Date: Mon, 27 Jan 2020 19:02:00 +0000 -Subject: [PATCH] virtiofsd: enable PARALLEL_DIROPS during INIT -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -lookup is a RO operations, PARALLEL_DIROPS can be enabled. - -Signed-off-by: Liu Bo -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit b7ed733a3841c4d489d3bd6ca7ed23c84db119c2) ---- - tools/virtiofsd/fuse_lowlevel.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index aac282f278..70568d22a4 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -2062,6 +2062,9 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, - if (se->conn.want & FUSE_CAP_ASYNC_READ) { - outarg.flags |= FUSE_ASYNC_READ; - } -+ if (se->conn.want & FUSE_CAP_PARALLEL_DIROPS) { -+ outarg.flags |= FUSE_PARALLEL_DIROPS; -+ } - if (se->conn.want & FUSE_CAP_POSIX_LOCKS) { - outarg.flags |= FUSE_POSIX_LOCKS; - } diff --git a/0092-virtiofsd-fix-incorrect-error-handling-in-lo_do_look.patch b/0092-virtiofsd-fix-incorrect-error-handling-in-lo_do_look.patch deleted file mode 100644 index fa99bf8..0000000 --- a/0092-virtiofsd-fix-incorrect-error-handling-in-lo_do_look.patch +++ /dev/null @@ -1,27 +0,0 @@ -From: Eric Ren -Date: Mon, 27 Jan 2020 19:02:01 +0000 -Subject: [PATCH] virtiofsd: fix incorrect error handling in lo_do_lookup -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Eric Ren -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit fc3f0041b43b6c64aa97b3558a6abe1a10028354) ---- - tools/virtiofsd/passthrough_ll.c | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index e8dc5c7320..05b5f898db 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -814,7 +814,6 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - close(newfd); - newfd = -1; - } else { -- saverr = ENOMEM; - inode = calloc(1, sizeof(struct lo_inode)); - if (!inode) { - goto out_err; diff --git a/0093-Virtiofsd-fix-memory-leak-on-fuse-queueinfo.patch b/0093-Virtiofsd-fix-memory-leak-on-fuse-queueinfo.patch deleted file mode 100644 index bef1ac0..0000000 --- a/0093-Virtiofsd-fix-memory-leak-on-fuse-queueinfo.patch +++ /dev/null @@ -1,44 +0,0 @@ -From: Liu Bo -Date: Mon, 27 Jan 2020 19:02:02 +0000 -Subject: [PATCH] Virtiofsd: fix memory leak on fuse queueinfo - -For fuse's queueinfo, both queueinfo array and queueinfos are allocated in -fv_queue_set_started() but not cleaned up when the daemon process quits. - -This fixes the leak in proper places. - -Signed-off-by: Liu Bo -Signed-off-by: Eric Ren -Reviewed-by: Misono Tomohiro -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 740b0b700a6338a1cf60c26229651ac5f6724944) ---- - tools/virtiofsd/fuse_virtio.c | 8 ++++++++ - 1 file changed, 8 insertions(+) - -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index b7948def27..fb8d6d1379 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -625,6 +625,8 @@ static void fv_queue_cleanup_thread(struct fv_VuDev *vud, int qidx) - } - close(ourqi->kill_fd); - ourqi->kick_fd = -1; -+ free(vud->qi[qidx]); -+ vud->qi[qidx] = NULL; - } - - /* Callback from libvhost-user on start or stop of a queue */ -@@ -884,6 +886,12 @@ int virtio_session_mount(struct fuse_session *se) - void virtio_session_close(struct fuse_session *se) - { - close(se->vu_socketfd); -+ -+ if (!se->virtio_dev) { -+ return; -+ } -+ -+ free(se->virtio_dev->qi); - free(se->virtio_dev); - se->virtio_dev = NULL; - } diff --git a/0094-virtiofsd-Support-remote-posix-locks.patch b/0094-virtiofsd-Support-remote-posix-locks.patch deleted file mode 100644 index 83b237c..0000000 --- a/0094-virtiofsd-Support-remote-posix-locks.patch +++ /dev/null @@ -1,336 +0,0 @@ -From: Vivek Goyal -Date: Mon, 27 Jan 2020 19:02:03 +0000 -Subject: [PATCH] virtiofsd: Support remote posix locks - -Doing posix locks with-in guest kernel are not sufficient if a file/dir -is being shared by multiple guests. So we need the notion of daemon doing -the locks which are visible to rest of the guests. - -Given posix locks are per process, one can not call posix lock API on host, -otherwise bunch of basic posix locks properties are broken. For example, -If two processes (A and B) in guest open the file and take locks on different -sections of file, if one of the processes closes the fd, it will close -fd on virtiofsd and all posix locks on file will go away. This means if -process A closes the fd, then locks of process B will go away too. - -Similar other problems exist too. - -This patch set tries to emulate posix locks while using open file -description locks provided on Linux. - -Daemon provides two options (-o posix_lock, -o no_posix_lock) to enable -or disable posix locking in daemon. By default it is enabled. - -There are few issues though. - -- GETLK() returns pid of process holding lock. As we are emulating locks - using OFD, and these locks are not per process and don't return pid - of process, so GETLK() in guest does not reuturn process pid. - -- As of now only F_SETLK is supported and not F_SETLKW. We can't block - the thread in virtiofsd for arbitrary long duration as there is only - one thread serving the queue. That means unlock request will not make - it to daemon and F_SETLKW will block infinitely and bring virtio-fs - to a halt. This is a solvable problem though and will require significant - changes in virtiofsd and kernel. Left as a TODO item for now. - -Signed-off-by: Vivek Goyal -Reviewed-by: Masayoshi Mizuma -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 0e81414c54161296212f6bc8a1c70526c4a9755a) ---- - tools/virtiofsd/helper.c | 3 + - tools/virtiofsd/passthrough_ll.c | 189 +++++++++++++++++++++++++++++++ - 2 files changed, 192 insertions(+) - -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index 567202444a..33749bfcb7 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -156,6 +156,9 @@ void fuse_cmdline_help(void) - " allowed (default: 10)\n" - " -o norace disable racy fallback\n" - " default: false\n" -+ " -o posix_lock|no_posix_lock\n" -+ " enable/disable remote posix lock\n" -+ " default: posix_lock\n" - " -o readdirplus|no_readdirplus\n" - " enable/disable readirplus\n" - " default: readdirplus except with " -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 05b5f898db..9414935b52 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -67,6 +67,12 @@ - #include "passthrough_helpers.h" - #include "seccomp.h" - -+/* Keep track of inode posix locks for each owner. */ -+struct lo_inode_plock { -+ uint64_t lock_owner; -+ int fd; /* fd for OFD locks */ -+}; -+ - struct lo_map_elem { - union { - struct lo_inode *inode; -@@ -95,6 +101,8 @@ struct lo_inode { - struct lo_key key; - uint64_t refcount; /* protected by lo->mutex */ - fuse_ino_t fuse_ino; -+ pthread_mutex_t plock_mutex; -+ GHashTable *posix_locks; /* protected by lo_inode->plock_mutex */ - }; - - struct lo_cred { -@@ -114,6 +122,7 @@ struct lo_data { - int norace; - int writeback; - int flock; -+ int posix_lock; - int xattr; - char *source; - double timeout; -@@ -137,6 +146,8 @@ static const struct fuse_opt lo_opts[] = { - { "source=%s", offsetof(struct lo_data, source), 0 }, - { "flock", offsetof(struct lo_data, flock), 1 }, - { "no_flock", offsetof(struct lo_data, flock), 0 }, -+ { "posix_lock", offsetof(struct lo_data, posix_lock), 1 }, -+ { "no_posix_lock", offsetof(struct lo_data, posix_lock), 0 }, - { "xattr", offsetof(struct lo_data, xattr), 1 }, - { "no_xattr", offsetof(struct lo_data, xattr), 0 }, - { "timeout=%lf", offsetof(struct lo_data, timeout), 0 }, -@@ -485,6 +496,17 @@ static void lo_init(void *userdata, struct fuse_conn_info *conn) - fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); - conn->want |= FUSE_CAP_FLOCK_LOCKS; - } -+ -+ if (conn->capable & FUSE_CAP_POSIX_LOCKS) { -+ if (lo->posix_lock) { -+ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating posix locks\n"); -+ conn->want |= FUSE_CAP_POSIX_LOCKS; -+ } else { -+ fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling posix locks\n"); -+ conn->want &= ~FUSE_CAP_POSIX_LOCKS; -+ } -+ } -+ - if ((lo->cache == CACHE_NONE && !lo->readdirplus_set) || - lo->readdirplus_clear) { - fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling readdirplus\n"); -@@ -772,6 +794,19 @@ static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st) - return p; - } - -+/* value_destroy_func for posix_locks GHashTable */ -+static void posix_locks_value_destroy(gpointer data) -+{ -+ struct lo_inode_plock *plock = data; -+ -+ /* -+ * We had used open() for locks and had only one fd. So -+ * closing this fd should release all OFD locks. -+ */ -+ close(plock->fd); -+ free(plock); -+} -+ - static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - struct fuse_entry_param *e) - { -@@ -825,6 +860,9 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - newfd = -1; - inode->key.ino = e->attr.st_ino; - inode->key.dev = e->attr.st_dev; -+ pthread_mutex_init(&inode->plock_mutex, NULL); -+ inode->posix_locks = g_hash_table_new_full( -+ g_direct_hash, g_direct_equal, NULL, posix_locks_value_destroy); - - pthread_mutex_lock(&lo->mutex); - inode->fuse_ino = lo_add_inode_mapping(req, inode); -@@ -1160,6 +1198,11 @@ static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, - if (!inode->refcount) { - lo_map_remove(&lo->ino_map, inode->fuse_ino); - g_hash_table_remove(lo->inodes, &inode->key); -+ if (g_hash_table_size(inode->posix_locks)) { -+ fuse_log(FUSE_LOG_WARNING, "Hash table is not empty\n"); -+ } -+ g_hash_table_destroy(inode->posix_locks); -+ pthread_mutex_destroy(&inode->plock_mutex); - pthread_mutex_unlock(&lo->mutex); - close(inode->fd); - free(inode); -@@ -1516,6 +1559,136 @@ out: - } - } - -+/* Should be called with inode->plock_mutex held */ -+static struct lo_inode_plock *lookup_create_plock_ctx(struct lo_data *lo, -+ struct lo_inode *inode, -+ uint64_t lock_owner, -+ pid_t pid, int *err) -+{ -+ struct lo_inode_plock *plock; -+ char procname[64]; -+ int fd; -+ -+ plock = -+ g_hash_table_lookup(inode->posix_locks, GUINT_TO_POINTER(lock_owner)); -+ -+ if (plock) { -+ return plock; -+ } -+ -+ plock = malloc(sizeof(struct lo_inode_plock)); -+ if (!plock) { -+ *err = ENOMEM; -+ return NULL; -+ } -+ -+ /* Open another instance of file which can be used for ofd locks. */ -+ sprintf(procname, "%i", inode->fd); -+ -+ /* TODO: What if file is not writable? */ -+ fd = openat(lo->proc_self_fd, procname, O_RDWR); -+ if (fd == -1) { -+ *err = errno; -+ free(plock); -+ return NULL; -+ } -+ -+ plock->lock_owner = lock_owner; -+ plock->fd = fd; -+ g_hash_table_insert(inode->posix_locks, GUINT_TO_POINTER(plock->lock_owner), -+ plock); -+ return plock; -+} -+ -+static void lo_getlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, -+ struct flock *lock) -+{ -+ struct lo_data *lo = lo_data(req); -+ struct lo_inode *inode; -+ struct lo_inode_plock *plock; -+ int ret, saverr = 0; -+ -+ fuse_log(FUSE_LOG_DEBUG, -+ "lo_getlk(ino=%" PRIu64 ", flags=%d)" -+ " owner=0x%lx, l_type=%d l_start=0x%lx" -+ " l_len=0x%lx\n", -+ ino, fi->flags, fi->lock_owner, lock->l_type, lock->l_start, -+ lock->l_len); -+ -+ inode = lo_inode(req, ino); -+ if (!inode) { -+ fuse_reply_err(req, EBADF); -+ return; -+ } -+ -+ pthread_mutex_lock(&inode->plock_mutex); -+ plock = -+ lookup_create_plock_ctx(lo, inode, fi->lock_owner, lock->l_pid, &ret); -+ if (!plock) { -+ pthread_mutex_unlock(&inode->plock_mutex); -+ fuse_reply_err(req, ret); -+ return; -+ } -+ -+ ret = fcntl(plock->fd, F_OFD_GETLK, lock); -+ if (ret == -1) { -+ saverr = errno; -+ } -+ pthread_mutex_unlock(&inode->plock_mutex); -+ -+ if (saverr) { -+ fuse_reply_err(req, saverr); -+ } else { -+ fuse_reply_lock(req, lock); -+ } -+} -+ -+static void lo_setlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, -+ struct flock *lock, int sleep) -+{ -+ struct lo_data *lo = lo_data(req); -+ struct lo_inode *inode; -+ struct lo_inode_plock *plock; -+ int ret, saverr = 0; -+ -+ fuse_log(FUSE_LOG_DEBUG, -+ "lo_setlk(ino=%" PRIu64 ", flags=%d)" -+ " cmd=%d pid=%d owner=0x%lx sleep=%d l_whence=%d" -+ " l_start=0x%lx l_len=0x%lx\n", -+ ino, fi->flags, lock->l_type, lock->l_pid, fi->lock_owner, sleep, -+ lock->l_whence, lock->l_start, lock->l_len); -+ -+ if (sleep) { -+ fuse_reply_err(req, EOPNOTSUPP); -+ return; -+ } -+ -+ inode = lo_inode(req, ino); -+ if (!inode) { -+ fuse_reply_err(req, EBADF); -+ return; -+ } -+ -+ pthread_mutex_lock(&inode->plock_mutex); -+ plock = -+ lookup_create_plock_ctx(lo, inode, fi->lock_owner, lock->l_pid, &ret); -+ -+ if (!plock) { -+ pthread_mutex_unlock(&inode->plock_mutex); -+ fuse_reply_err(req, ret); -+ return; -+ } -+ -+ /* TODO: Is it alright to modify flock? */ -+ lock->l_pid = 0; -+ ret = fcntl(plock->fd, F_OFD_SETLK, lock); -+ if (ret == -1) { -+ saverr = errno; -+ } -+ pthread_mutex_unlock(&inode->plock_mutex); -+ fuse_reply_err(req, saverr); -+} -+ - static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, - struct fuse_file_info *fi) - { -@@ -1617,6 +1790,19 @@ static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) - { - int res; - (void)ino; -+ struct lo_inode *inode; -+ -+ inode = lo_inode(req, ino); -+ if (!inode) { -+ fuse_reply_err(req, EBADF); -+ return; -+ } -+ -+ /* An fd is going away. Cleanup associated posix locks */ -+ pthread_mutex_lock(&inode->plock_mutex); -+ g_hash_table_remove(inode->posix_locks, GUINT_TO_POINTER(fi->lock_owner)); -+ pthread_mutex_unlock(&inode->plock_mutex); -+ - res = close(dup(lo_fi_fd(req, fi))); - fuse_reply_err(req, res == -1 ? errno : 0); - } -@@ -2080,6 +2266,8 @@ static struct fuse_lowlevel_ops lo_oper = { - .releasedir = lo_releasedir, - .fsyncdir = lo_fsyncdir, - .create = lo_create, -+ .getlk = lo_getlk, -+ .setlk = lo_setlk, - .open = lo_open, - .release = lo_release, - .flush = lo_flush, -@@ -2434,6 +2622,7 @@ int main(int argc, char *argv[]) - struct lo_data lo = { - .debug = 0, - .writeback = 0, -+ .posix_lock = 1, - .proc_self_fd = -1, - }; - struct lo_map_elem *root_elem; diff --git a/0095-virtiofsd-use-fuse_lowlevel_is_virtio-in-fuse_sessio.patch b/0095-virtiofsd-use-fuse_lowlevel_is_virtio-in-fuse_sessio.patch deleted file mode 100644 index 357130e..0000000 --- a/0095-virtiofsd-use-fuse_lowlevel_is_virtio-in-fuse_sessio.patch +++ /dev/null @@ -1,40 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:02:04 +0000 -Subject: [PATCH] virtiofsd: use fuse_lowlevel_is_virtio() in - fuse_session_destroy() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -vu_socket_path is NULL when --fd=FDNUM was used. Use -fuse_lowlevel_is_virtio() instead. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 620e9d8d9cee6df7fe71168dea950dba0cc21a4a) ---- - tools/virtiofsd/fuse_lowlevel.c | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 70568d22a4..dab6a31e08 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -2537,12 +2537,13 @@ void fuse_session_destroy(struct fuse_session *se) - close(se->fd); - } - -- if (se->vu_socket_path) { -+ if (fuse_lowlevel_is_virtio(se)) { - virtio_session_close(se); -- free(se->vu_socket_path); -- se->vu_socket_path = NULL; - } - -+ free(se->vu_socket_path); -+ se->vu_socket_path = NULL; -+ - free(se); - } - diff --git a/0096-virtiofsd-prevent-fv_queue_thread-vs-virtio_loop-rac.patch b/0096-virtiofsd-prevent-fv_queue_thread-vs-virtio_loop-rac.patch deleted file mode 100644 index 0129a03..0000000 --- a/0096-virtiofsd-prevent-fv_queue_thread-vs-virtio_loop-rac.patch +++ /dev/null @@ -1,132 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:02:05 +0000 -Subject: [PATCH] virtiofsd: prevent fv_queue_thread() vs virtio_loop() races -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -We call into libvhost-user from the virtqueue handler thread and the -vhost-user message processing thread without a lock. There is nothing -protecting the virtqueue handler thread if the vhost-user message -processing thread changes the virtqueue or memory table while it is -running. - -This patch introduces a read-write lock. Virtqueue handler threads are -readers. The vhost-user message processing thread is a writer. This -will allow concurrency for multiqueue in the future while protecting -against fv_queue_thread() vs virtio_loop() races. - -Note that the critical sections could be made smaller but it would be -more invasive and require libvhost-user changes. Let's start simple and -improve performance later, if necessary. Another option would be an -RCU-style approach with lighter-weight primitives. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit e7b337326d594b71b07cd6dbb332c49c122c80a4) ---- - tools/virtiofsd/fuse_virtio.c | 34 +++++++++++++++++++++++++++++++++- - 1 file changed, 33 insertions(+), 1 deletion(-) - -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index fb8d6d1379..f6242f9338 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -58,6 +58,18 @@ struct fv_VuDev { - VuDev dev; - struct fuse_session *se; - -+ /* -+ * Either handle virtqueues or vhost-user protocol messages. Don't do -+ * both at the same time since that could lead to race conditions if -+ * virtqueues or memory tables change while another thread is accessing -+ * them. -+ * -+ * The assumptions are: -+ * 1. fv_queue_thread() reads/writes to virtqueues and only reads VuDev. -+ * 2. virtio_loop() reads/writes virtqueues and VuDev. -+ */ -+ pthread_rwlock_t vu_dispatch_rwlock; -+ - /* - * The following pair of fields are only accessed in the main - * virtio_loop -@@ -415,6 +427,8 @@ static void *fv_queue_thread(void *opaque) - qi->qidx, qi->kick_fd); - while (1) { - struct pollfd pf[2]; -+ int ret; -+ - pf[0].fd = qi->kick_fd; - pf[0].events = POLLIN; - pf[0].revents = 0; -@@ -461,6 +475,9 @@ static void *fv_queue_thread(void *opaque) - fuse_log(FUSE_LOG_ERR, "Eventfd_read for queue: %m\n"); - break; - } -+ /* Mutual exclusion with virtio_loop() */ -+ ret = pthread_rwlock_rdlock(&qi->virtio_dev->vu_dispatch_rwlock); -+ assert(ret == 0); /* there is no possible error case */ - /* out is from guest, in is too guest */ - unsigned int in_bytes, out_bytes; - vu_queue_get_avail_bytes(dev, q, &in_bytes, &out_bytes, ~0, ~0); -@@ -469,6 +486,7 @@ static void *fv_queue_thread(void *opaque) - "%s: Queue %d gave evalue: %zx available: in: %u out: %u\n", - __func__, qi->qidx, (size_t)evalue, in_bytes, out_bytes); - -+ - while (1) { - bool allocated_bufv = false; - struct fuse_bufvec bufv; -@@ -597,6 +615,8 @@ static void *fv_queue_thread(void *opaque) - free(elem); - elem = NULL; - } -+ -+ pthread_rwlock_unlock(&qi->virtio_dev->vu_dispatch_rwlock); - } - out: - pthread_mutex_destroy(&ch.lock); -@@ -711,6 +731,8 @@ int virtio_loop(struct fuse_session *se) - - while (!fuse_session_exited(se)) { - struct pollfd pf[1]; -+ bool ok; -+ int ret; - pf[0].fd = se->vu_socketfd; - pf[0].events = POLLIN; - pf[0].revents = 0; -@@ -735,7 +757,15 @@ int virtio_loop(struct fuse_session *se) - } - assert(pf[0].revents & POLLIN); - fuse_log(FUSE_LOG_DEBUG, "%s: Got VU event\n", __func__); -- if (!vu_dispatch(&se->virtio_dev->dev)) { -+ /* Mutual exclusion with fv_queue_thread() */ -+ ret = pthread_rwlock_wrlock(&se->virtio_dev->vu_dispatch_rwlock); -+ assert(ret == 0); /* there is no possible error case */ -+ -+ ok = vu_dispatch(&se->virtio_dev->dev); -+ -+ pthread_rwlock_unlock(&se->virtio_dev->vu_dispatch_rwlock); -+ -+ if (!ok) { - fuse_log(FUSE_LOG_ERR, "%s: vu_dispatch failed\n", __func__); - break; - } -@@ -877,6 +907,7 @@ int virtio_session_mount(struct fuse_session *se) - - se->vu_socketfd = data_sock; - se->virtio_dev->se = se; -+ pthread_rwlock_init(&se->virtio_dev->vu_dispatch_rwlock, NULL); - vu_init(&se->virtio_dev->dev, 2, se->vu_socketfd, fv_panic, fv_set_watch, - fv_remove_watch, &fv_iface); - -@@ -892,6 +923,7 @@ void virtio_session_close(struct fuse_session *se) - } - - free(se->virtio_dev->qi); -+ pthread_rwlock_destroy(&se->virtio_dev->vu_dispatch_rwlock); - free(se->virtio_dev); - se->virtio_dev = NULL; - } diff --git a/0097-virtiofsd-make-lo_release-atomic.patch b/0097-virtiofsd-make-lo_release-atomic.patch deleted file mode 100644 index 8f75b74..0000000 --- a/0097-virtiofsd-make-lo_release-atomic.patch +++ /dev/null @@ -1,46 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:02:06 +0000 -Subject: [PATCH] virtiofsd: make lo_release() atomic -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Hold the lock across both lo_map_get() and lo_map_remove() to prevent -races between two FUSE_RELEASE requests. In this case I don't see a -serious bug but it's safer to do things atomically. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit baed65c060c0e524530bc243eec427fb408bd477) ---- - tools/virtiofsd/passthrough_ll.c | 12 ++++++++---- - 1 file changed, 8 insertions(+), 4 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 9414935b52..690edbc4c5 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1772,14 +1772,18 @@ static void lo_release(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info *fi) - { - struct lo_data *lo = lo_data(req); -- int fd; -+ struct lo_map_elem *elem; -+ int fd = -1; - - (void)ino; - -- fd = lo_fi_fd(req, fi); -- - pthread_mutex_lock(&lo->mutex); -- lo_map_remove(&lo->fd_map, fi->fh); -+ elem = lo_map_get(&lo->fd_map, fi->fh); -+ if (elem) { -+ fd = elem->fd; -+ elem = NULL; -+ lo_map_remove(&lo->fd_map, fi->fh); -+ } - pthread_mutex_unlock(&lo->mutex); - - close(fd); diff --git a/0098-virtiofsd-prevent-races-with-lo_dirp_put.patch b/0098-virtiofsd-prevent-races-with-lo_dirp_put.patch deleted file mode 100644 index a900bc8..0000000 --- a/0098-virtiofsd-prevent-races-with-lo_dirp_put.patch +++ /dev/null @@ -1,131 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:02:07 +0000 -Subject: [PATCH] virtiofsd: prevent races with lo_dirp_put() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Introduce lo_dirp_put() so that FUSE_RELEASEDIR does not cause -use-after-free races with other threads that are accessing lo_dirp. - -Also make lo_releasedir() atomic to prevent FUSE_RELEASEDIR racing with -itself. This prevents double-frees. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit acefdde73b403576a241ebd8dbe8431ddc0d9442) ---- - tools/virtiofsd/passthrough_ll.c | 41 +++++++++++++++++++++++++++----- - 1 file changed, 35 insertions(+), 6 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 690edbc4c5..2d703b57e5 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1284,11 +1284,28 @@ static void lo_readlink(fuse_req_t req, fuse_ino_t ino) - } - - struct lo_dirp { -+ gint refcount; - DIR *dp; - struct dirent *entry; - off_t offset; - }; - -+static void lo_dirp_put(struct lo_dirp **dp) -+{ -+ struct lo_dirp *d = *dp; -+ -+ if (!d) { -+ return; -+ } -+ *dp = NULL; -+ -+ if (g_atomic_int_dec_and_test(&d->refcount)) { -+ closedir(d->dp); -+ free(d); -+ } -+} -+ -+/* Call lo_dirp_put() on the return value when no longer needed */ - static struct lo_dirp *lo_dirp(fuse_req_t req, struct fuse_file_info *fi) - { - struct lo_data *lo = lo_data(req); -@@ -1296,6 +1313,9 @@ static struct lo_dirp *lo_dirp(fuse_req_t req, struct fuse_file_info *fi) - - pthread_mutex_lock(&lo->mutex); - elem = lo_map_get(&lo->dirp_map, fi->fh); -+ if (elem) { -+ g_atomic_int_inc(&elem->dirp->refcount); -+ } - pthread_mutex_unlock(&lo->mutex); - if (!elem) { - return NULL; -@@ -1331,6 +1351,7 @@ static void lo_opendir(fuse_req_t req, fuse_ino_t ino, - d->offset = 0; - d->entry = NULL; - -+ g_atomic_int_set(&d->refcount, 1); /* paired with lo_releasedir() */ - pthread_mutex_lock(&lo->mutex); - fh = lo_add_dirp_mapping(req, d); - pthread_mutex_unlock(&lo->mutex); -@@ -1364,7 +1385,7 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, - off_t offset, struct fuse_file_info *fi, int plus) - { - struct lo_data *lo = lo_data(req); -- struct lo_dirp *d; -+ struct lo_dirp *d = NULL; - struct lo_inode *dinode; - char *buf = NULL; - char *p; -@@ -1454,6 +1475,8 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, - - err = 0; - error: -+ lo_dirp_put(&d); -+ - /* - * If there's an error, we can only signal it if we haven't stored - * any entries yet - otherwise we'd end up with wrong lookup -@@ -1484,22 +1507,25 @@ static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, - struct fuse_file_info *fi) - { - struct lo_data *lo = lo_data(req); -+ struct lo_map_elem *elem; - struct lo_dirp *d; - - (void)ino; - -- d = lo_dirp(req, fi); -- if (!d) { -+ pthread_mutex_lock(&lo->mutex); -+ elem = lo_map_get(&lo->dirp_map, fi->fh); -+ if (!elem) { -+ pthread_mutex_unlock(&lo->mutex); - fuse_reply_err(req, EBADF); - return; - } - -- pthread_mutex_lock(&lo->mutex); -+ d = elem->dirp; - lo_map_remove(&lo->dirp_map, fi->fh); - pthread_mutex_unlock(&lo->mutex); - -- closedir(d->dp); -- free(d); -+ lo_dirp_put(&d); /* paired with lo_opendir() */ -+ - fuse_reply_err(req, 0); - } - -@@ -1710,6 +1736,9 @@ static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, - } else { - res = fsync(fd); - } -+ -+ lo_dirp_put(&d); -+ - fuse_reply_err(req, res == -1 ? errno : 0); - } - diff --git a/0099-virtiofsd-rename-inode-refcount-to-inode-nlookup.patch b/0099-virtiofsd-rename-inode-refcount-to-inode-nlookup.patch deleted file mode 100644 index 2025112..0000000 --- a/0099-virtiofsd-rename-inode-refcount-to-inode-nlookup.patch +++ /dev/null @@ -1,123 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:02:08 +0000 -Subject: [PATCH] virtiofsd: rename inode->refcount to inode->nlookup -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This reference counter plays a specific role in the FUSE protocol. It's -not a generic object reference counter and the FUSE kernel code calls it -"nlookup". - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 1222f015558fc34cea02aa3a5a92de608c82cec8) ---- - tools/virtiofsd/passthrough_ll.c | 37 +++++++++++++++++++++----------- - 1 file changed, 25 insertions(+), 12 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 2d703b57e5..c819b5f782 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -99,7 +99,20 @@ struct lo_inode { - int fd; - bool is_symlink; - struct lo_key key; -- uint64_t refcount; /* protected by lo->mutex */ -+ -+ /* -+ * This counter keeps the inode alive during the FUSE session. -+ * Incremented when the FUSE inode number is sent in a reply -+ * (FUSE_LOOKUP, FUSE_READDIRPLUS, etc). Decremented when an inode is -+ * released by requests like FUSE_FORGET, FUSE_RMDIR, FUSE_RENAME, etc. -+ * -+ * Note that this value is untrusted because the client can manipulate -+ * it arbitrarily using FUSE_FORGET requests. -+ * -+ * Protected by lo->mutex. -+ */ -+ uint64_t nlookup; -+ - fuse_ino_t fuse_ino; - pthread_mutex_t plock_mutex; - GHashTable *posix_locks; /* protected by lo_inode->plock_mutex */ -@@ -568,7 +581,7 @@ retry: - if (last == path) { - p = &lo->root; - pthread_mutex_lock(&lo->mutex); -- p->refcount++; -+ p->nlookup++; - pthread_mutex_unlock(&lo->mutex); - } else { - *last = '\0'; -@@ -786,8 +799,8 @@ static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st) - pthread_mutex_lock(&lo->mutex); - p = g_hash_table_lookup(lo->inodes, &key); - if (p) { -- assert(p->refcount > 0); -- p->refcount++; -+ assert(p->nlookup > 0); -+ p->nlookup++; - } - pthread_mutex_unlock(&lo->mutex); - -@@ -855,7 +868,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - } - - inode->is_symlink = S_ISLNK(e->attr.st_mode); -- inode->refcount = 1; -+ inode->nlookup = 1; - inode->fd = newfd; - newfd = -1; - inode->key.ino = e->attr.st_ino; -@@ -1112,7 +1125,7 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, - } - - pthread_mutex_lock(&lo->mutex); -- inode->refcount++; -+ inode->nlookup++; - pthread_mutex_unlock(&lo->mutex); - e.ino = inode->fuse_ino; - -@@ -1193,9 +1206,9 @@ static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, - } - - pthread_mutex_lock(&lo->mutex); -- assert(inode->refcount >= n); -- inode->refcount -= n; -- if (!inode->refcount) { -+ assert(inode->nlookup >= n); -+ inode->nlookup -= n; -+ if (!inode->nlookup) { - lo_map_remove(&lo->ino_map, inode->fuse_ino); - g_hash_table_remove(lo->inodes, &inode->key); - if (g_hash_table_size(inode->posix_locks)) { -@@ -1216,7 +1229,7 @@ static int unref_all_inodes_cb(gpointer key, gpointer value, gpointer user_data) - struct lo_inode *inode = value; - struct lo_data *lo = user_data; - -- inode->refcount = 0; -+ inode->nlookup = 0; - lo_map_remove(&lo->ino_map, inode->fuse_ino); - close(inode->fd); - -@@ -1241,7 +1254,7 @@ static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) - } - - fuse_log(FUSE_LOG_DEBUG, " forget %lli %lli -%lli\n", -- (unsigned long long)ino, (unsigned long long)inode->refcount, -+ (unsigned long long)ino, (unsigned long long)inode->nlookup, - (unsigned long long)nlookup); - - unref_inode_lolocked(lo, inode, nlookup); -@@ -2609,7 +2622,7 @@ static void setup_root(struct lo_data *lo, struct lo_inode *root) - root->fd = fd; - root->key.ino = stat.st_ino; - root->key.dev = stat.st_dev; -- root->refcount = 2; -+ root->nlookup = 2; - } - - static guint lo_key_hash(gconstpointer key) diff --git a/0100-libvhost-user-Fix-some-memtable-remap-cases.patch b/0100-libvhost-user-Fix-some-memtable-remap-cases.patch deleted file mode 100644 index da82476..0000000 --- a/0100-libvhost-user-Fix-some-memtable-remap-cases.patch +++ /dev/null @@ -1,98 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:02:09 +0000 -Subject: [PATCH] libvhost-user: Fix some memtable remap cases - -If a new setmemtable command comes in once the vhost threads are -running, it will remap the guests address space and the threads -will now be looking in the wrong place. - -Fortunately we're running this command under lock, so we can -update the queue mappings so that threads will look in the new-right -place. - -Note: This doesn't fix things that the threads might be doing -without a lock (e.g. a readv/writev!) That's for another time. - -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 49e9ec749d4db62ae51f76354143cee183912a1d) ---- - contrib/libvhost-user/libvhost-user.c | 33 ++++++++++++++++++++------- - contrib/libvhost-user/libvhost-user.h | 3 +++ - 2 files changed, 28 insertions(+), 8 deletions(-) - -diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c -index 63e41062a4..b89bf18501 100644 ---- a/contrib/libvhost-user/libvhost-user.c -+++ b/contrib/libvhost-user/libvhost-user.c -@@ -564,6 +564,21 @@ vu_reset_device_exec(VuDev *dev, VhostUserMsg *vmsg) - return false; - } - -+static bool -+map_ring(VuDev *dev, VuVirtq *vq) -+{ -+ vq->vring.desc = qva_to_va(dev, vq->vra.desc_user_addr); -+ vq->vring.used = qva_to_va(dev, vq->vra.used_user_addr); -+ vq->vring.avail = qva_to_va(dev, vq->vra.avail_user_addr); -+ -+ DPRINT("Setting virtq addresses:\n"); -+ DPRINT(" vring_desc at %p\n", vq->vring.desc); -+ DPRINT(" vring_used at %p\n", vq->vring.used); -+ DPRINT(" vring_avail at %p\n", vq->vring.avail); -+ -+ return !(vq->vring.desc && vq->vring.used && vq->vring.avail); -+} -+ - static bool - vu_set_mem_table_exec_postcopy(VuDev *dev, VhostUserMsg *vmsg) - { -@@ -767,6 +782,14 @@ vu_set_mem_table_exec(VuDev *dev, VhostUserMsg *vmsg) - close(vmsg->fds[i]); - } - -+ for (i = 0; i < dev->max_queues; i++) { -+ if (dev->vq[i].vring.desc) { -+ if (map_ring(dev, &dev->vq[i])) { -+ vu_panic(dev, "remaping queue %d during setmemtable", i); -+ } -+ } -+ } -+ - return false; - } - -@@ -853,18 +876,12 @@ vu_set_vring_addr_exec(VuDev *dev, VhostUserMsg *vmsg) - DPRINT(" avail_user_addr: 0x%016" PRIx64 "\n", vra->avail_user_addr); - DPRINT(" log_guest_addr: 0x%016" PRIx64 "\n", vra->log_guest_addr); - -+ vq->vra = *vra; - vq->vring.flags = vra->flags; -- vq->vring.desc = qva_to_va(dev, vra->desc_user_addr); -- vq->vring.used = qva_to_va(dev, vra->used_user_addr); -- vq->vring.avail = qva_to_va(dev, vra->avail_user_addr); - vq->vring.log_guest_addr = vra->log_guest_addr; - -- DPRINT("Setting virtq addresses:\n"); -- DPRINT(" vring_desc at %p\n", vq->vring.desc); -- DPRINT(" vring_used at %p\n", vq->vring.used); -- DPRINT(" vring_avail at %p\n", vq->vring.avail); - -- if (!(vq->vring.desc && vq->vring.used && vq->vring.avail)) { -+ if (map_ring(dev, vq)) { - vu_panic(dev, "Invalid vring_addr message"); - return false; - } -diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h -index 1844b6f8d4..5cb7708559 100644 ---- a/contrib/libvhost-user/libvhost-user.h -+++ b/contrib/libvhost-user/libvhost-user.h -@@ -327,6 +327,9 @@ typedef struct VuVirtq { - int err_fd; - unsigned int enable; - bool started; -+ -+ /* Guest addresses of our ring */ -+ struct vhost_vring_addr vra; - } VuVirtq; - - enum VuWatchCondtion { diff --git a/0101-virtiofsd-passthrough_ll-fix-refcounting-on-remove-r.patch b/0101-virtiofsd-passthrough_ll-fix-refcounting-on-remove-r.patch deleted file mode 100644 index e8ba18f..0000000 --- a/0101-virtiofsd-passthrough_ll-fix-refcounting-on-remove-r.patch +++ /dev/null @@ -1,123 +0,0 @@ -From: Miklos Szeredi -Date: Mon, 27 Jan 2020 19:02:10 +0000 -Subject: [PATCH] virtiofsd: passthrough_ll: fix refcounting on remove/rename - -Signed-off-by: Miklos Szeredi -Reviewed-by: Misono Tomohiro -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 9257e514d861afa759c36704e1904d43ca3fec88) ---- - tools/virtiofsd/passthrough_ll.c | 50 +++++++++++++++++++++++++++++++- - 1 file changed, 49 insertions(+), 1 deletion(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index c819b5f782..e3a6d6b611 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1140,17 +1140,42 @@ out_err: - fuse_reply_err(req, saverr); - } - -+static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, -+ const char *name) -+{ -+ int res; -+ struct stat attr; -+ -+ res = fstatat(lo_fd(req, parent), name, &attr, -+ AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW); -+ if (res == -1) { -+ return NULL; -+ } -+ -+ return lo_find(lo_data(req), &attr); -+} -+ - static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) - { - int res; -+ struct lo_inode *inode; -+ struct lo_data *lo = lo_data(req); -+ - if (!is_safe_path_component(name)) { - fuse_reply_err(req, EINVAL); - return; - } - -+ inode = lookup_name(req, parent, name); -+ if (!inode) { -+ fuse_reply_err(req, EIO); -+ return; -+ } -+ - res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR); - - fuse_reply_err(req, res == -1 ? errno : 0); -+ unref_inode_lolocked(lo, inode, 1); - } - - static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, -@@ -1158,12 +1183,23 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, - unsigned int flags) - { - int res; -+ struct lo_inode *oldinode; -+ struct lo_inode *newinode; -+ struct lo_data *lo = lo_data(req); - - if (!is_safe_path_component(name) || !is_safe_path_component(newname)) { - fuse_reply_err(req, EINVAL); - return; - } - -+ oldinode = lookup_name(req, parent, name); -+ newinode = lookup_name(req, newparent, newname); -+ -+ if (!oldinode) { -+ fuse_reply_err(req, EIO); -+ goto out; -+ } -+ - if (flags) { - #ifndef SYS_renameat2 - fuse_reply_err(req, EINVAL); -@@ -1176,26 +1212,38 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, - fuse_reply_err(req, res == -1 ? errno : 0); - } - #endif -- return; -+ goto out; - } - - res = renameat(lo_fd(req, parent), name, lo_fd(req, newparent), newname); - - fuse_reply_err(req, res == -1 ? errno : 0); -+out: -+ unref_inode_lolocked(lo, oldinode, 1); -+ unref_inode_lolocked(lo, newinode, 1); - } - - static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) - { - int res; -+ struct lo_inode *inode; -+ struct lo_data *lo = lo_data(req); - - if (!is_safe_path_component(name)) { - fuse_reply_err(req, EINVAL); - return; - } - -+ inode = lookup_name(req, parent, name); -+ if (!inode) { -+ fuse_reply_err(req, EIO); -+ return; -+ } -+ - res = unlinkat(lo_fd(req, parent), name, 0); - - fuse_reply_err(req, res == -1 ? errno : 0); -+ unref_inode_lolocked(lo, inode, 1); - } - - static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, diff --git a/0102-virtiofsd-introduce-inode-refcount-to-prevent-use-af.patch b/0102-virtiofsd-introduce-inode-refcount-to-prevent-use-af.patch deleted file mode 100644 index 25e6f71..0000000 --- a/0102-virtiofsd-introduce-inode-refcount-to-prevent-use-af.patch +++ /dev/null @@ -1,569 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:02:11 +0000 -Subject: [PATCH] virtiofsd: introduce inode refcount to prevent use-after-free - -If thread A is using an inode it must not be deleted by thread B when -processing a FUSE_FORGET request. - -The FUSE protocol itself already has a counter called nlookup that is -used in FUSE_FORGET messages. We cannot trust this counter since the -untrusted client can manipulate it via FUSE_FORGET messages. - -Introduce a new refcount to keep inodes alive for the required lifespan. -lo_inode_put() must be called to release a reference. FUSE's nlookup -counter holds exactly one reference so that the inode stays alive as -long as the client still wants to remember it. - -Note that the lo_inode->is_symlink field is moved to avoid creating a -hole in the struct due to struct field alignment. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Misono Tomohiro -Reviewed-by: Sergio Lopez -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit c241aa9457d88c6a0d027f48fadfed131646bce3) ---- - tools/virtiofsd/passthrough_ll.c | 169 ++++++++++++++++++++++++++----- - 1 file changed, 146 insertions(+), 23 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index e3a6d6b611..ab1613586e 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -97,7 +97,13 @@ struct lo_key { - - struct lo_inode { - int fd; -- bool is_symlink; -+ -+ /* -+ * Atomic reference count for this object. The nlookup field holds a -+ * reference and release it when nlookup reaches 0. -+ */ -+ gint refcount; -+ - struct lo_key key; - - /* -@@ -116,6 +122,8 @@ struct lo_inode { - fuse_ino_t fuse_ino; - pthread_mutex_t plock_mutex; - GHashTable *posix_locks; /* protected by lo_inode->plock_mutex */ -+ -+ bool is_symlink; - }; - - struct lo_cred { -@@ -471,6 +479,23 @@ static ssize_t lo_add_inode_mapping(fuse_req_t req, struct lo_inode *inode) - return elem - lo_data(req)->ino_map.elems; - } - -+static void lo_inode_put(struct lo_data *lo, struct lo_inode **inodep) -+{ -+ struct lo_inode *inode = *inodep; -+ -+ if (!inode) { -+ return; -+ } -+ -+ *inodep = NULL; -+ -+ if (g_atomic_int_dec_and_test(&inode->refcount)) { -+ close(inode->fd); -+ free(inode); -+ } -+} -+ -+/* Caller must release refcount using lo_inode_put() */ - static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) - { - struct lo_data *lo = lo_data(req); -@@ -478,6 +503,9 @@ static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) - - pthread_mutex_lock(&lo->mutex); - elem = lo_map_get(&lo->ino_map, ino); -+ if (elem) { -+ g_atomic_int_inc(&elem->inode->refcount); -+ } - pthread_mutex_unlock(&lo->mutex); - - if (!elem) { -@@ -487,10 +515,23 @@ static struct lo_inode *lo_inode(fuse_req_t req, fuse_ino_t ino) - return elem->inode; - } - -+/* -+ * TODO Remove this helper and force callers to hold an inode refcount until -+ * they are done with the fd. This will be done in a later patch to make -+ * review easier. -+ */ - static int lo_fd(fuse_req_t req, fuse_ino_t ino) - { - struct lo_inode *inode = lo_inode(req, ino); -- return inode ? inode->fd : -1; -+ int fd; -+ -+ if (!inode) { -+ return -1; -+ } -+ -+ fd = inode->fd; -+ lo_inode_put(lo_data(req), &inode); -+ return fd; - } - - static void lo_init(void *userdata, struct fuse_conn_info *conn) -@@ -545,6 +586,10 @@ static void lo_getattr(fuse_req_t req, fuse_ino_t ino, - fuse_reply_attr(req, &buf, lo->timeout); - } - -+/* -+ * Increments parent->nlookup and caller must release refcount using -+ * lo_inode_put(&parent). -+ */ - static int lo_parent_and_name(struct lo_data *lo, struct lo_inode *inode, - char path[PATH_MAX], struct lo_inode **parent) - { -@@ -582,6 +627,7 @@ retry: - p = &lo->root; - pthread_mutex_lock(&lo->mutex); - p->nlookup++; -+ g_atomic_int_inc(&p->refcount); - pthread_mutex_unlock(&lo->mutex); - } else { - *last = '\0'; -@@ -625,6 +671,7 @@ retry: - - fail_unref: - unref_inode_lolocked(lo, p, 1); -+ lo_inode_put(lo, &p); - fail: - if (retries) { - retries--; -@@ -663,6 +710,7 @@ fallback: - if (res != -1) { - res = utimensat(parent->fd, path, tv, AT_SYMLINK_NOFOLLOW); - unref_inode_lolocked(lo, parent, 1); -+ lo_inode_put(lo, &parent); - } - - return res; -@@ -780,11 +828,13 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, - goto out_err; - } - } -+ lo_inode_put(lo, &inode); - - return lo_getattr(req, ino, fi); - - out_err: - saverr = errno; -+ lo_inode_put(lo, &inode); - fuse_reply_err(req, saverr); - } - -@@ -801,6 +851,7 @@ static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st) - if (p) { - assert(p->nlookup > 0); - p->nlookup++; -+ g_atomic_int_inc(&p->refcount); - } - pthread_mutex_unlock(&lo->mutex); - -@@ -820,6 +871,10 @@ static void posix_locks_value_destroy(gpointer data) - free(plock); - } - -+/* -+ * Increments nlookup and caller must release refcount using -+ * lo_inode_put(&parent). -+ */ - static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - struct fuse_entry_param *e) - { -@@ -827,7 +882,8 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - int res; - int saverr; - struct lo_data *lo = lo_data(req); -- struct lo_inode *inode, *dir = lo_inode(req, parent); -+ struct lo_inode *inode = NULL; -+ struct lo_inode *dir = lo_inode(req, parent); - - /* - * name_to_handle_at() and open_by_handle_at() can reach here with fuse -@@ -868,6 +924,13 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - } - - inode->is_symlink = S_ISLNK(e->attr.st_mode); -+ -+ /* -+ * One for the caller and one for nlookup (released in -+ * unref_inode_lolocked()) -+ */ -+ g_atomic_int_set(&inode->refcount, 2); -+ - inode->nlookup = 1; - inode->fd = newfd; - newfd = -1; -@@ -883,6 +946,8 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, - pthread_mutex_unlock(&lo->mutex); - } - e->ino = inode->fuse_ino; -+ lo_inode_put(lo, &inode); -+ lo_inode_put(lo, &dir); - - fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long)parent, - name, (unsigned long long)e->ino); -@@ -894,6 +959,8 @@ out_err: - if (newfd != -1) { - close(newfd); - } -+ lo_inode_put(lo, &inode); -+ lo_inode_put(lo, &dir); - return saverr; - } - -@@ -991,6 +1058,7 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, - { - int res; - int saverr; -+ struct lo_data *lo = lo_data(req); - struct lo_inode *dir; - struct fuse_entry_param e; - struct lo_cred old = {}; -@@ -1032,9 +1100,11 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, - name, (unsigned long long)e.ino); - - fuse_reply_entry(req, &e); -+ lo_inode_put(lo, &dir); - return; - - out: -+ lo_inode_put(lo, &dir); - fuse_reply_err(req, saverr); - } - -@@ -1085,6 +1155,7 @@ fallback: - if (res != -1) { - res = linkat(parent->fd, path, dfd, name, 0); - unref_inode_lolocked(lo, parent, 1); -+ lo_inode_put(lo, &parent); - } - - return res; -@@ -1095,6 +1166,7 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, - { - int res; - struct lo_data *lo = lo_data(req); -+ struct lo_inode *parent_inode; - struct lo_inode *inode; - struct fuse_entry_param e; - int saverr; -@@ -1104,17 +1176,18 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, - return; - } - -+ parent_inode = lo_inode(req, parent); - inode = lo_inode(req, ino); -- if (!inode) { -- fuse_reply_err(req, EBADF); -- return; -+ if (!parent_inode || !inode) { -+ errno = EBADF; -+ goto out_err; - } - - memset(&e, 0, sizeof(struct fuse_entry_param)); - e.attr_timeout = lo->timeout; - e.entry_timeout = lo->timeout; - -- res = linkat_empty_nofollow(lo, inode, lo_fd(req, parent), name); -+ res = linkat_empty_nofollow(lo, inode, parent_inode->fd, name); - if (res == -1) { - goto out_err; - } -@@ -1133,13 +1206,18 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent, - name, (unsigned long long)e.ino); - - fuse_reply_entry(req, &e); -+ lo_inode_put(lo, &parent_inode); -+ lo_inode_put(lo, &inode); - return; - - out_err: - saverr = errno; -+ lo_inode_put(lo, &parent_inode); -+ lo_inode_put(lo, &inode); - fuse_reply_err(req, saverr); - } - -+/* Increments nlookup and caller must release refcount using lo_inode_put() */ - static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent, - const char *name) - { -@@ -1176,6 +1254,7 @@ static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name) - - fuse_reply_err(req, res == -1 ? errno : 0); - unref_inode_lolocked(lo, inode, 1); -+ lo_inode_put(lo, &inode); - } - - static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, -@@ -1183,8 +1262,10 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, - unsigned int flags) - { - int res; -- struct lo_inode *oldinode; -- struct lo_inode *newinode; -+ struct lo_inode *parent_inode; -+ struct lo_inode *newparent_inode; -+ struct lo_inode *oldinode = NULL; -+ struct lo_inode *newinode = NULL; - struct lo_data *lo = lo_data(req); - - if (!is_safe_path_component(name) || !is_safe_path_component(newname)) { -@@ -1192,6 +1273,13 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, - return; - } - -+ parent_inode = lo_inode(req, parent); -+ newparent_inode = lo_inode(req, newparent); -+ if (!parent_inode || !newparent_inode) { -+ fuse_reply_err(req, EBADF); -+ goto out; -+ } -+ - oldinode = lookup_name(req, parent, name); - newinode = lookup_name(req, newparent, newname); - -@@ -1204,8 +1292,8 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, - #ifndef SYS_renameat2 - fuse_reply_err(req, EINVAL); - #else -- res = syscall(SYS_renameat2, lo_fd(req, parent), name, -- lo_fd(req, newparent), newname, flags); -+ res = syscall(SYS_renameat2, parent_inode->fd, name, -+ newparent_inode->fd, newname, flags); - if (res == -1 && errno == ENOSYS) { - fuse_reply_err(req, EINVAL); - } else { -@@ -1215,12 +1303,16 @@ static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name, - goto out; - } - -- res = renameat(lo_fd(req, parent), name, lo_fd(req, newparent), newname); -+ res = renameat(parent_inode->fd, name, newparent_inode->fd, newname); - - fuse_reply_err(req, res == -1 ? errno : 0); - out: - unref_inode_lolocked(lo, oldinode, 1); - unref_inode_lolocked(lo, newinode, 1); -+ lo_inode_put(lo, &oldinode); -+ lo_inode_put(lo, &newinode); -+ lo_inode_put(lo, &parent_inode); -+ lo_inode_put(lo, &newparent_inode); - } - - static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) -@@ -1244,6 +1336,7 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) - - fuse_reply_err(req, res == -1 ? errno : 0); - unref_inode_lolocked(lo, inode, 1); -+ lo_inode_put(lo, &inode); - } - - static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, -@@ -1265,8 +1358,9 @@ static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, - g_hash_table_destroy(inode->posix_locks); - pthread_mutex_destroy(&inode->plock_mutex); - pthread_mutex_unlock(&lo->mutex); -- close(inode->fd); -- free(inode); -+ -+ /* Drop our refcount from lo_do_lookup() */ -+ lo_inode_put(lo, &inode); - } else { - pthread_mutex_unlock(&lo->mutex); - } -@@ -1280,6 +1374,7 @@ static int unref_all_inodes_cb(gpointer key, gpointer value, gpointer user_data) - inode->nlookup = 0; - lo_map_remove(&lo->ino_map, inode->fuse_ino); - close(inode->fd); -+ lo_inode_put(lo, &inode); /* Drop our refcount from lo_do_lookup() */ - - return TRUE; - } -@@ -1306,6 +1401,7 @@ static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) - (unsigned long long)nlookup); - - unref_inode_lolocked(lo, inode, nlookup); -+ lo_inode_put(lo, &inode); - } - - static void lo_forget(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) -@@ -1537,6 +1633,7 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, - err = 0; - error: - lo_dirp_put(&d); -+ lo_inode_put(lo, &dinode); - - /* - * If there's an error, we can only signal it if we haven't stored -@@ -1595,6 +1692,7 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, - { - int fd; - struct lo_data *lo = lo_data(req); -+ struct lo_inode *parent_inode; - struct fuse_entry_param e; - int err; - struct lo_cred old = {}; -@@ -1607,12 +1705,18 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, - return; - } - -+ parent_inode = lo_inode(req, parent); -+ if (!parent_inode) { -+ fuse_reply_err(req, EBADF); -+ return; -+ } -+ - err = lo_change_cred(req, &old); - if (err) { - goto out; - } - -- fd = openat(lo_fd(req, parent), name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, -+ fd = openat(parent_inode->fd, name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, - mode); - err = fd == -1 ? errno : 0; - lo_restore_cred(&old); -@@ -1625,8 +1729,8 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, - pthread_mutex_unlock(&lo->mutex); - if (fh == -1) { - close(fd); -- fuse_reply_err(req, ENOMEM); -- return; -+ err = ENOMEM; -+ goto out; - } - - fi->fh = fh; -@@ -1639,6 +1743,8 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, - } - - out: -+ lo_inode_put(lo, &parent_inode); -+ - if (err) { - fuse_reply_err(req, err); - } else { -@@ -1712,16 +1818,18 @@ static void lo_getlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, - plock = - lookup_create_plock_ctx(lo, inode, fi->lock_owner, lock->l_pid, &ret); - if (!plock) { -- pthread_mutex_unlock(&inode->plock_mutex); -- fuse_reply_err(req, ret); -- return; -+ saverr = ret; -+ goto out; - } - - ret = fcntl(plock->fd, F_OFD_GETLK, lock); - if (ret == -1) { - saverr = errno; - } -+ -+out: - pthread_mutex_unlock(&inode->plock_mutex); -+ lo_inode_put(lo, &inode); - - if (saverr) { - fuse_reply_err(req, saverr); -@@ -1761,9 +1869,8 @@ static void lo_setlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, - lookup_create_plock_ctx(lo, inode, fi->lock_owner, lock->l_pid, &ret); - - if (!plock) { -- pthread_mutex_unlock(&inode->plock_mutex); -- fuse_reply_err(req, ret); -- return; -+ saverr = ret; -+ goto out; - } - - /* TODO: Is it alright to modify flock? */ -@@ -1772,7 +1879,11 @@ static void lo_setlk(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi, - if (ret == -1) { - saverr = errno; - } -+ -+out: - pthread_mutex_unlock(&inode->plock_mutex); -+ lo_inode_put(lo, &inode); -+ - fuse_reply_err(req, saverr); - } - -@@ -1898,6 +2009,7 @@ static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) - pthread_mutex_unlock(&inode->plock_mutex); - - res = close(dup(lo_fi_fd(req, fi))); -+ lo_inode_put(lo_data(req), &inode); - fuse_reply_err(req, res == -1 ? errno : 0); - } - -@@ -2115,11 +2227,14 @@ out_free: - if (fd >= 0) { - close(fd); - } -+ -+ lo_inode_put(lo, &inode); - return; - - out_err: - saverr = errno; - out: -+ lo_inode_put(lo, &inode); - fuse_reply_err(req, saverr); - goto out_free; - } -@@ -2190,11 +2305,14 @@ out_free: - if (fd >= 0) { - close(fd); - } -+ -+ lo_inode_put(lo, &inode); - return; - - out_err: - saverr = errno; - out: -+ lo_inode_put(lo, &inode); - fuse_reply_err(req, saverr); - goto out_free; - } -@@ -2243,6 +2361,8 @@ out: - if (fd >= 0) { - close(fd); - } -+ -+ lo_inode_put(lo, &inode); - fuse_reply_err(req, saverr); - } - -@@ -2289,6 +2409,8 @@ out: - if (fd >= 0) { - close(fd); - } -+ -+ lo_inode_put(lo, &inode); - fuse_reply_err(req, saverr); - } - -@@ -2671,6 +2793,7 @@ static void setup_root(struct lo_data *lo, struct lo_inode *root) - root->key.ino = stat.st_ino; - root->key.dev = stat.st_dev; - root->nlookup = 2; -+ g_atomic_int_set(&root->refcount, 2); - } - - static guint lo_key_hash(gconstpointer key) diff --git a/0103-virtiofsd-do-not-always-set-FUSE_FLOCK_LOCKS.patch b/0103-virtiofsd-do-not-always-set-FUSE_FLOCK_LOCKS.patch deleted file mode 100644 index ae53c17..0000000 --- a/0103-virtiofsd-do-not-always-set-FUSE_FLOCK_LOCKS.patch +++ /dev/null @@ -1,38 +0,0 @@ -From: Peng Tao -Date: Mon, 27 Jan 2020 19:02:12 +0000 -Subject: [PATCH] virtiofsd: do not always set FUSE_FLOCK_LOCKS - -Right now we always enable it regardless of given commandlines. -Fix it by setting the flag relying on the lo->flock bit. - -Signed-off-by: Peng Tao -Reviewed-by: Misono Tomohiro -Reviewed-by: Sergio Lopez -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit e468d4af5f5192ab33283464a9f6933044ce47f7) ---- - tools/virtiofsd/passthrough_ll.c | 11 ++++++++--- - 1 file changed, 8 insertions(+), 3 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index ab1613586e..ccbbec18b0 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -546,9 +546,14 @@ static void lo_init(void *userdata, struct fuse_conn_info *conn) - fuse_log(FUSE_LOG_DEBUG, "lo_init: activating writeback\n"); - conn->want |= FUSE_CAP_WRITEBACK_CACHE; - } -- if (lo->flock && conn->capable & FUSE_CAP_FLOCK_LOCKS) { -- fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); -- conn->want |= FUSE_CAP_FLOCK_LOCKS; -+ if (conn->capable & FUSE_CAP_FLOCK_LOCKS) { -+ if (lo->flock) { -+ fuse_log(FUSE_LOG_DEBUG, "lo_init: activating flock locks\n"); -+ conn->want |= FUSE_CAP_FLOCK_LOCKS; -+ } else { -+ fuse_log(FUSE_LOG_DEBUG, "lo_init: disabling flock locks\n"); -+ conn->want &= ~FUSE_CAP_FLOCK_LOCKS; -+ } - } - - if (conn->capable & FUSE_CAP_POSIX_LOCKS) { diff --git a/0104-virtiofsd-convert-more-fprintf-and-perror-to-use-fus.patch b/0104-virtiofsd-convert-more-fprintf-and-perror-to-use-fus.patch deleted file mode 100644 index aabec08..0000000 --- a/0104-virtiofsd-convert-more-fprintf-and-perror-to-use-fus.patch +++ /dev/null @@ -1,83 +0,0 @@ -From: Eryu Guan -Date: Mon, 27 Jan 2020 19:02:13 +0000 -Subject: [PATCH] virtiofsd: convert more fprintf and perror to use fuse log - infra -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Eryu Guan -Reviewed-by: Daniel P. Berrangé -Reviewed-by: Misono Tomohiro -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit fc1aed0bf96259d0b46b1cfea7497b7762c4ee3d) ---- - tools/virtiofsd/fuse_signals.c | 7 +++++-- - tools/virtiofsd/helper.c | 9 ++++++--- - 2 files changed, 11 insertions(+), 5 deletions(-) - -diff --git a/tools/virtiofsd/fuse_signals.c b/tools/virtiofsd/fuse_signals.c -index dc7c8ac025..f18625b6e2 100644 ---- a/tools/virtiofsd/fuse_signals.c -+++ b/tools/virtiofsd/fuse_signals.c -@@ -12,6 +12,7 @@ - #include "fuse_i.h" - #include "fuse_lowlevel.h" - -+#include - #include - #include - #include -@@ -47,13 +48,15 @@ static int set_one_signal_handler(int sig, void (*handler)(int), int remove) - sa.sa_flags = 0; - - if (sigaction(sig, NULL, &old_sa) == -1) { -- perror("fuse: cannot get old signal handler"); -+ fuse_log(FUSE_LOG_ERR, "fuse: cannot get old signal handler: %s\n", -+ strerror(errno)); - return -1; - } - - if (old_sa.sa_handler == (remove ? handler : SIG_DFL) && - sigaction(sig, &sa, NULL) == -1) { -- perror("fuse: cannot set signal handler"); -+ fuse_log(FUSE_LOG_ERR, "fuse: cannot set signal handler: %s\n", -+ strerror(errno)); - return -1; - } - return 0; -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index 33749bfcb7..f98d8f2eb2 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -208,7 +208,8 @@ int fuse_daemonize(int foreground) - char completed; - - if (pipe(waiter)) { -- perror("fuse_daemonize: pipe"); -+ fuse_log(FUSE_LOG_ERR, "fuse_daemonize: pipe: %s\n", -+ strerror(errno)); - return -1; - } - -@@ -218,7 +219,8 @@ int fuse_daemonize(int foreground) - */ - switch (fork()) { - case -1: -- perror("fuse_daemonize: fork"); -+ fuse_log(FUSE_LOG_ERR, "fuse_daemonize: fork: %s\n", -+ strerror(errno)); - return -1; - case 0: - break; -@@ -228,7 +230,8 @@ int fuse_daemonize(int foreground) - } - - if (setsid() == -1) { -- perror("fuse_daemonize: setsid"); -+ fuse_log(FUSE_LOG_ERR, "fuse_daemonize: setsid: %s\n", -+ strerror(errno)); - return -1; - } - diff --git a/0105-virtiofsd-Reset-O_DIRECT-flag-during-file-open.patch b/0105-virtiofsd-Reset-O_DIRECT-flag-during-file-open.patch deleted file mode 100644 index bbd90a3..0000000 --- a/0105-virtiofsd-Reset-O_DIRECT-flag-during-file-open.patch +++ /dev/null @@ -1,56 +0,0 @@ -From: Vivek Goyal -Date: Mon, 27 Jan 2020 19:02:14 +0000 -Subject: [PATCH] virtiofsd: Reset O_DIRECT flag during file open -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -If an application wants to do direct IO and opens a file with O_DIRECT -in guest, that does not necessarily mean that we need to bypass page -cache on host as well. So reset this flag on host. - -If somebody needs to bypass page cache on host as well (and it is safe to -do so), we can add a knob in daemon later to control this behavior. - -I check virtio-9p and they do reset O_DIRECT flag. - -Signed-off-by: Vivek Goyal -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 65da4539803373ec4eec97ffc49ee90083e56efd) ---- - tools/virtiofsd/passthrough_ll.c | 14 ++++++++++++++ - 1 file changed, 14 insertions(+) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index ccbbec18b0..948cb19c77 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1721,6 +1721,13 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, - goto out; - } - -+ /* -+ * O_DIRECT in guest should not necessarily mean bypassing page -+ * cache on host as well. If somebody needs that behavior, it -+ * probably should be a configuration knob in daemon. -+ */ -+ fi->flags &= ~O_DIRECT; -+ - fd = openat(parent_inode->fd, name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, - mode); - err = fd == -1 ? errno : 0; -@@ -1950,6 +1957,13 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) - fi->flags &= ~O_APPEND; - } - -+ /* -+ * O_DIRECT in guest should not necessarily mean bypassing page -+ * cache on host as well. If somebody needs that behavior, it -+ * probably should be a configuration knob in daemon. -+ */ -+ fi->flags &= ~O_DIRECT; -+ - sprintf(buf, "%i", lo_fd(req, ino)); - fd = openat(lo->proc_self_fd, buf, fi->flags & ~O_NOFOLLOW); - if (fd == -1) { diff --git a/0106-virtiofsd-Fix-data-corruption-with-O_APPEND-write-in.patch b/0106-virtiofsd-Fix-data-corruption-with-O_APPEND-write-in.patch deleted file mode 100644 index ec53bd4..0000000 --- a/0106-virtiofsd-Fix-data-corruption-with-O_APPEND-write-in.patch +++ /dev/null @@ -1,120 +0,0 @@ -From: Misono Tomohiro -Date: Mon, 27 Jan 2020 19:02:15 +0000 -Subject: [PATCH] virtiofsd: Fix data corruption with O_APPEND write in - writeback mode -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -When writeback mode is enabled (-o writeback), O_APPEND handling is -done in kernel. Therefore virtiofsd clears O_APPEND flag when open. -Otherwise O_APPEND flag takes precedence over pwrite() and write -data may corrupt. - -Currently clearing O_APPEND flag is done in lo_open(), but we also -need the same operation in lo_create(). So, factor out the flag -update operation in lo_open() to update_open_flags() and call it -in both lo_open() and lo_create(). - -This fixes the failure of xfstest generic/069 in writeback mode -(which tests O_APPEND write data integrity). - -Signed-off-by: Misono Tomohiro -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 8e4e41e39eac5ee5f378d66f069a2f70a1734317) ---- - tools/virtiofsd/passthrough_ll.c | 66 ++++++++++++++++---------------- - 1 file changed, 33 insertions(+), 33 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 948cb19c77..4c61ac5065 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1692,6 +1692,37 @@ static void lo_releasedir(fuse_req_t req, fuse_ino_t ino, - fuse_reply_err(req, 0); - } - -+static void update_open_flags(int writeback, struct fuse_file_info *fi) -+{ -+ /* -+ * With writeback cache, kernel may send read requests even -+ * when userspace opened write-only -+ */ -+ if (writeback && (fi->flags & O_ACCMODE) == O_WRONLY) { -+ fi->flags &= ~O_ACCMODE; -+ fi->flags |= O_RDWR; -+ } -+ -+ /* -+ * With writeback cache, O_APPEND is handled by the kernel. -+ * This breaks atomicity (since the file may change in the -+ * underlying filesystem, so that the kernel's idea of the -+ * end of the file isn't accurate anymore). In this example, -+ * we just accept that. A more rigorous filesystem may want -+ * to return an error here -+ */ -+ if (writeback && (fi->flags & O_APPEND)) { -+ fi->flags &= ~O_APPEND; -+ } -+ -+ /* -+ * O_DIRECT in guest should not necessarily mean bypassing page -+ * cache on host as well. If somebody needs that behavior, it -+ * probably should be a configuration knob in daemon. -+ */ -+ fi->flags &= ~O_DIRECT; -+} -+ - static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, - mode_t mode, struct fuse_file_info *fi) - { -@@ -1721,12 +1752,7 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, - goto out; - } - -- /* -- * O_DIRECT in guest should not necessarily mean bypassing page -- * cache on host as well. If somebody needs that behavior, it -- * probably should be a configuration knob in daemon. -- */ -- fi->flags &= ~O_DIRECT; -+ update_open_flags(lo->writeback, fi); - - fd = openat(parent_inode->fd, name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, - mode); -@@ -1936,33 +1962,7 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) - fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino, - fi->flags); - -- /* -- * With writeback cache, kernel may send read requests even -- * when userspace opened write-only -- */ -- if (lo->writeback && (fi->flags & O_ACCMODE) == O_WRONLY) { -- fi->flags &= ~O_ACCMODE; -- fi->flags |= O_RDWR; -- } -- -- /* -- * With writeback cache, O_APPEND is handled by the kernel. -- * This breaks atomicity (since the file may change in the -- * underlying filesystem, so that the kernel's idea of the -- * end of the file isn't accurate anymore). In this example, -- * we just accept that. A more rigorous filesystem may want -- * to return an error here -- */ -- if (lo->writeback && (fi->flags & O_APPEND)) { -- fi->flags &= ~O_APPEND; -- } -- -- /* -- * O_DIRECT in guest should not necessarily mean bypassing page -- * cache on host as well. If somebody needs that behavior, it -- * probably should be a configuration knob in daemon. -- */ -- fi->flags &= ~O_DIRECT; -+ update_open_flags(lo->writeback, fi); - - sprintf(buf, "%i", lo_fd(req, ino)); - fd = openat(lo->proc_self_fd, buf, fi->flags & ~O_NOFOLLOW); diff --git a/0107-virtiofsd-passthrough_ll-Use-cache_readdir-for-direc.patch b/0107-virtiofsd-passthrough_ll-Use-cache_readdir-for-direc.patch deleted file mode 100644 index d78ba2f..0000000 --- a/0107-virtiofsd-passthrough_ll-Use-cache_readdir-for-direc.patch +++ /dev/null @@ -1,29 +0,0 @@ -From: Misono Tomohiro -Date: Mon, 27 Jan 2020 19:02:16 +0000 -Subject: [PATCH] virtiofsd: passthrough_ll: Use cache_readdir for directory - open - -Since keep_cache(FOPEN_KEEP_CACHE) has no effect for directory as -described in fuse_common.h, use cache_readdir(FOPNE_CACHE_DIR) for -diretory open when cache=always mode. - -Signed-off-by: Misono Tomohiro -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 9b610b09b49b1aada256097b338d49da805da6ae) ---- - tools/virtiofsd/passthrough_ll.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 4c61ac5065..79b8b71a4f 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1523,7 +1523,7 @@ static void lo_opendir(fuse_req_t req, fuse_ino_t ino, - - fi->fh = fh; - if (lo->cache == CACHE_ALWAYS) { -- fi->keep_cache = 1; -+ fi->cache_readdir = 1; - } - fuse_reply_open(req, fi); - return; diff --git a/0108-virtiofsd-add-definition-of-fuse_buf_writev.patch b/0108-virtiofsd-add-definition-of-fuse_buf_writev.patch deleted file mode 100644 index 34925c1..0000000 --- a/0108-virtiofsd-add-definition-of-fuse_buf_writev.patch +++ /dev/null @@ -1,77 +0,0 @@ -From: piaojun -Date: Mon, 27 Jan 2020 19:02:17 +0000 -Subject: [PATCH] virtiofsd: add definition of fuse_buf_writev() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Define fuse_buf_writev() which use pwritev and writev to improve io -bandwidth. Especially, the src bufs with 0 size should be skipped as -their mems are not *block_size* aligned which will cause writev failed -in direct io mode. - -Signed-off-by: Jun Piao -Suggested-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 9ceaaa15cf21073c2b23058c374f61c30cd39c31) ---- - tools/virtiofsd/buffer.c | 38 ++++++++++++++++++++++++++++++++++++++ - 1 file changed, 38 insertions(+) - -diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c -index 42a608f6bd..37befebac2 100644 ---- a/tools/virtiofsd/buffer.c -+++ b/tools/virtiofsd/buffer.c -@@ -14,6 +14,7 @@ - #include "fuse_lowlevel.h" - #include - #include -+#include - #include - #include - -@@ -33,6 +34,43 @@ size_t fuse_buf_size(const struct fuse_bufvec *bufv) - return size; - } - -+__attribute__((unused)) -+static ssize_t fuse_buf_writev(struct fuse_buf *out_buf, -+ struct fuse_bufvec *in_buf) -+{ -+ ssize_t res, i, j; -+ size_t iovcnt = in_buf->count; -+ struct iovec *iov; -+ int fd = out_buf->fd; -+ -+ iov = calloc(iovcnt, sizeof(struct iovec)); -+ if (!iov) { -+ return -ENOMEM; -+ } -+ -+ for (i = 0, j = 0; i < iovcnt; i++) { -+ /* Skip the buf with 0 size */ -+ if (in_buf->buf[i].size) { -+ iov[j].iov_base = in_buf->buf[i].mem; -+ iov[j].iov_len = in_buf->buf[i].size; -+ j++; -+ } -+ } -+ -+ if (out_buf->flags & FUSE_BUF_FD_SEEK) { -+ res = pwritev(fd, iov, iovcnt, out_buf->pos); -+ } else { -+ res = writev(fd, iov, iovcnt); -+ } -+ -+ if (res == -1) { -+ res = -errno; -+ } -+ -+ free(iov); -+ return res; -+} -+ - static size_t min_size(size_t s1, size_t s2) - { - return s1 < s2 ? s1 : s2; diff --git a/0109-virtiofsd-use-fuse_buf_writev-to-replace-fuse_buf_wr.patch b/0109-virtiofsd-use-fuse_buf_writev-to-replace-fuse_buf_wr.patch deleted file mode 100644 index 4ed2da4..0000000 --- a/0109-virtiofsd-use-fuse_buf_writev-to-replace-fuse_buf_wr.patch +++ /dev/null @@ -1,66 +0,0 @@ -From: piaojun -Date: Mon, 27 Jan 2020 19:02:18 +0000 -Subject: [PATCH] virtiofsd: use fuse_buf_writev to replace fuse_buf_write for - better performance -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -fuse_buf_writev() only handles the normal write in which src is buffer -and dest is fd. Specially if src buffer represents guest physical -address that can't be mapped by the daemon process, IO must be bounced -back to the VMM to do it by fuse_buf_copy(). - -Signed-off-by: Jun Piao -Suggested-by: Dr. David Alan Gilbert -Suggested-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit c465bba2c90a810f6e71e4f2646b1b4ee4b478de) ---- - tools/virtiofsd/buffer.c | 20 ++++++++++++++++++-- - 1 file changed, 18 insertions(+), 2 deletions(-) - -diff --git a/tools/virtiofsd/buffer.c b/tools/virtiofsd/buffer.c -index 37befebac2..27c1377f22 100644 ---- a/tools/virtiofsd/buffer.c -+++ b/tools/virtiofsd/buffer.c -@@ -34,7 +34,6 @@ size_t fuse_buf_size(const struct fuse_bufvec *bufv) - return size; - } - --__attribute__((unused)) - static ssize_t fuse_buf_writev(struct fuse_buf *out_buf, - struct fuse_bufvec *in_buf) - { -@@ -262,12 +261,29 @@ static int fuse_bufvec_advance(struct fuse_bufvec *bufv, size_t len) - - ssize_t fuse_buf_copy(struct fuse_bufvec *dstv, struct fuse_bufvec *srcv) - { -- size_t copied = 0; -+ size_t copied = 0, i; - - if (dstv == srcv) { - return fuse_buf_size(dstv); - } - -+ /* -+ * use writev to improve bandwidth when all the -+ * src buffers already mapped by the daemon -+ * process -+ */ -+ for (i = 0; i < srcv->count; i++) { -+ if (srcv->buf[i].flags & FUSE_BUF_IS_FD) { -+ break; -+ } -+ } -+ if ((i == srcv->count) && (dstv->count == 1) && -+ (dstv->idx == 0) && -+ (dstv->buf[0].flags & FUSE_BUF_IS_FD)) { -+ dstv->buf[0].pos += dstv->off; -+ return fuse_buf_writev(&dstv->buf[0], srcv); -+ } -+ - for (;;) { - const struct fuse_buf *src = fuse_bufvec_current(srcv); - const struct fuse_buf *dst = fuse_bufvec_current(dstv); diff --git a/0110-virtiofsd-process-requests-in-a-thread-pool.patch b/0110-virtiofsd-process-requests-in-a-thread-pool.patch deleted file mode 100644 index b83ae83..0000000 --- a/0110-virtiofsd-process-requests-in-a-thread-pool.patch +++ /dev/null @@ -1,514 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:02:19 +0000 -Subject: [PATCH] virtiofsd: process requests in a thread pool - -Introduce a thread pool so that fv_queue_thread() just pops -VuVirtqElements and hands them to the thread pool. For the time being -only one worker thread is allowed since passthrough_ll.c is not -thread-safe yet. Future patches will lift this restriction so that -multiple FUSE requests can be processed in parallel. - -The main new concept is struct FVRequest, which contains both -VuVirtqElement and struct fuse_chan. We now have fv_VuDev for a device, -fv_QueueInfo for a virtqueue, and FVRequest for a request. Some of -fv_QueueInfo's fields are moved into FVRequest because they are -per-request. The name FVRequest conforms to QEMU coding style and I -expect the struct fv_* types will be renamed in a future refactoring. - -This patch series is not optimal. fbuf reuse is dropped so each request -does malloc(se->bufsize), but there is no clean and cheap way to keep -this with a thread pool. The vq_lock mutex is held for longer than -necessary, especially during the eventfd_write() syscall. Performance -can be improved in the future. - -prctl(2) had to be added to the seccomp whitelist because glib invokes -it. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Misono Tomohiro -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit a3d756c5aecccc4c0e51060a7e2f1c87bf8f1180) ---- - tools/virtiofsd/fuse_virtio.c | 359 +++++++++++++++++++--------------- - 1 file changed, 201 insertions(+), 158 deletions(-) - -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index f6242f9338..0dcf2ef57a 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -22,6 +22,7 @@ - - #include - #include -+#include - #include - #include - #include -@@ -37,17 +38,28 @@ - struct fv_VuDev; - struct fv_QueueInfo { - pthread_t thread; -+ /* -+ * This lock protects the VuVirtq preventing races between -+ * fv_queue_thread() and fv_queue_worker(). -+ */ -+ pthread_mutex_t vq_lock; -+ - struct fv_VuDev *virtio_dev; - - /* Our queue index, corresponds to array position */ - int qidx; - int kick_fd; - int kill_fd; /* For killing the thread */ -+}; - -- /* The element for the command currently being processed */ -- VuVirtqElement *qe; -+/* A FUSE request */ -+typedef struct { -+ VuVirtqElement elem; -+ struct fuse_chan ch; -+ -+ /* Used to complete requests that involve no reply */ - bool reply_sent; --}; -+} FVRequest; - - /* - * We pass the dev element into libvhost-user -@@ -191,8 +203,11 @@ static void copy_iov(struct iovec *src_iov, int src_count, - int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, - struct iovec *iov, int count) - { -- VuVirtqElement *elem; -- VuVirtq *q; -+ FVRequest *req = container_of(ch, FVRequest, ch); -+ struct fv_QueueInfo *qi = ch->qi; -+ VuDev *dev = &se->virtio_dev->dev; -+ VuVirtq *q = vu_get_queue(dev, qi->qidx); -+ VuVirtqElement *elem = &req->elem; - int ret = 0; - - assert(count >= 1); -@@ -205,11 +220,7 @@ int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, - - /* unique == 0 is notification, which we don't support */ - assert(out->unique); -- /* For virtio we always have ch */ -- assert(ch); -- assert(!ch->qi->reply_sent); -- elem = ch->qi->qe; -- q = &ch->qi->virtio_dev->dev.vq[ch->qi->qidx]; -+ assert(!req->reply_sent); - - /* The 'in' part of the elem is to qemu */ - unsigned int in_num = elem->in_num; -@@ -236,9 +247,15 @@ int virtio_send_msg(struct fuse_session *se, struct fuse_chan *ch, - } - - copy_iov(iov, count, in_sg, in_num, tosend_len); -- vu_queue_push(&se->virtio_dev->dev, q, elem, tosend_len); -- vu_queue_notify(&se->virtio_dev->dev, q); -- ch->qi->reply_sent = true; -+ -+ pthread_rwlock_rdlock(&qi->virtio_dev->vu_dispatch_rwlock); -+ pthread_mutex_lock(&qi->vq_lock); -+ vu_queue_push(dev, q, elem, tosend_len); -+ vu_queue_notify(dev, q); -+ pthread_mutex_unlock(&qi->vq_lock); -+ pthread_rwlock_unlock(&qi->virtio_dev->vu_dispatch_rwlock); -+ -+ req->reply_sent = true; - - err: - return ret; -@@ -254,9 +271,12 @@ int virtio_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, - struct iovec *iov, int count, struct fuse_bufvec *buf, - size_t len) - { -+ FVRequest *req = container_of(ch, FVRequest, ch); -+ struct fv_QueueInfo *qi = ch->qi; -+ VuDev *dev = &se->virtio_dev->dev; -+ VuVirtq *q = vu_get_queue(dev, qi->qidx); -+ VuVirtqElement *elem = &req->elem; - int ret = 0; -- VuVirtqElement *elem; -- VuVirtq *q; - - assert(count >= 1); - assert(iov[0].iov_len >= sizeof(struct fuse_out_header)); -@@ -275,11 +295,7 @@ int virtio_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, - /* unique == 0 is notification which we don't support */ - assert(out->unique); - -- /* For virtio we always have ch */ -- assert(ch); -- assert(!ch->qi->reply_sent); -- elem = ch->qi->qe; -- q = &ch->qi->virtio_dev->dev.vq[ch->qi->qidx]; -+ assert(!req->reply_sent); - - /* The 'in' part of the elem is to qemu */ - unsigned int in_num = elem->in_num; -@@ -395,33 +411,175 @@ int virtio_send_data_iov(struct fuse_session *se, struct fuse_chan *ch, - - ret = 0; - -- vu_queue_push(&se->virtio_dev->dev, q, elem, tosend_len); -- vu_queue_notify(&se->virtio_dev->dev, q); -+ pthread_rwlock_rdlock(&qi->virtio_dev->vu_dispatch_rwlock); -+ pthread_mutex_lock(&qi->vq_lock); -+ vu_queue_push(dev, q, elem, tosend_len); -+ vu_queue_notify(dev, q); -+ pthread_mutex_unlock(&qi->vq_lock); -+ pthread_rwlock_unlock(&qi->virtio_dev->vu_dispatch_rwlock); - - err: - if (ret == 0) { -- ch->qi->reply_sent = true; -+ req->reply_sent = true; - } - - return ret; - } - -+/* Process one FVRequest in a thread pool */ -+static void fv_queue_worker(gpointer data, gpointer user_data) -+{ -+ struct fv_QueueInfo *qi = user_data; -+ struct fuse_session *se = qi->virtio_dev->se; -+ struct VuDev *dev = &qi->virtio_dev->dev; -+ FVRequest *req = data; -+ VuVirtqElement *elem = &req->elem; -+ struct fuse_buf fbuf = {}; -+ bool allocated_bufv = false; -+ struct fuse_bufvec bufv; -+ struct fuse_bufvec *pbufv; -+ -+ assert(se->bufsize > sizeof(struct fuse_in_header)); -+ -+ /* -+ * An element contains one request and the space to send our response -+ * They're spread over multiple descriptors in a scatter/gather set -+ * and we can't trust the guest to keep them still; so copy in/out. -+ */ -+ fbuf.mem = malloc(se->bufsize); -+ assert(fbuf.mem); -+ -+ fuse_mutex_init(&req->ch.lock); -+ req->ch.fd = -1; -+ req->ch.qi = qi; -+ -+ /* The 'out' part of the elem is from qemu */ -+ unsigned int out_num = elem->out_num; -+ struct iovec *out_sg = elem->out_sg; -+ size_t out_len = iov_size(out_sg, out_num); -+ fuse_log(FUSE_LOG_DEBUG, -+ "%s: elem %d: with %d out desc of length %zd\n", -+ __func__, elem->index, out_num, out_len); -+ -+ /* -+ * The elem should contain a 'fuse_in_header' (in to fuse) -+ * plus the data based on the len in the header. -+ */ -+ if (out_len < sizeof(struct fuse_in_header)) { -+ fuse_log(FUSE_LOG_ERR, "%s: elem %d too short for in_header\n", -+ __func__, elem->index); -+ assert(0); /* TODO */ -+ } -+ if (out_len > se->bufsize) { -+ fuse_log(FUSE_LOG_ERR, "%s: elem %d too large for buffer\n", __func__, -+ elem->index); -+ assert(0); /* TODO */ -+ } -+ /* Copy just the first element and look at it */ -+ copy_from_iov(&fbuf, 1, out_sg); -+ -+ pbufv = NULL; /* Compiler thinks an unitialised path */ -+ if (out_num > 2 && -+ out_sg[0].iov_len == sizeof(struct fuse_in_header) && -+ ((struct fuse_in_header *)fbuf.mem)->opcode == FUSE_WRITE && -+ out_sg[1].iov_len == sizeof(struct fuse_write_in)) { -+ /* -+ * For a write we don't actually need to copy the -+ * data, we can just do it straight out of guest memory -+ * but we must still copy the headers in case the guest -+ * was nasty and changed them while we were using them. -+ */ -+ fuse_log(FUSE_LOG_DEBUG, "%s: Write special case\n", __func__); -+ -+ /* copy the fuse_write_in header afte rthe fuse_in_header */ -+ fbuf.mem += out_sg->iov_len; -+ copy_from_iov(&fbuf, 1, out_sg + 1); -+ fbuf.mem -= out_sg->iov_len; -+ fbuf.size = out_sg[0].iov_len + out_sg[1].iov_len; -+ -+ /* Allocate the bufv, with space for the rest of the iov */ -+ pbufv = malloc(sizeof(struct fuse_bufvec) + -+ sizeof(struct fuse_buf) * (out_num - 2)); -+ if (!pbufv) { -+ fuse_log(FUSE_LOG_ERR, "%s: pbufv malloc failed\n", -+ __func__); -+ goto out; -+ } -+ -+ allocated_bufv = true; -+ pbufv->count = 1; -+ pbufv->buf[0] = fbuf; -+ -+ size_t iovindex, pbufvindex; -+ iovindex = 2; /* 2 headers, separate iovs */ -+ pbufvindex = 1; /* 2 headers, 1 fusebuf */ -+ -+ for (; iovindex < out_num; iovindex++, pbufvindex++) { -+ pbufv->count++; -+ pbufv->buf[pbufvindex].pos = ~0; /* Dummy */ -+ pbufv->buf[pbufvindex].flags = 0; -+ pbufv->buf[pbufvindex].mem = out_sg[iovindex].iov_base; -+ pbufv->buf[pbufvindex].size = out_sg[iovindex].iov_len; -+ } -+ } else { -+ /* Normal (non fast write) path */ -+ -+ /* Copy the rest of the buffer */ -+ fbuf.mem += out_sg->iov_len; -+ copy_from_iov(&fbuf, out_num - 1, out_sg + 1); -+ fbuf.mem -= out_sg->iov_len; -+ fbuf.size = out_len; -+ -+ /* TODO! Endianness of header */ -+ -+ /* TODO: Add checks for fuse_session_exited */ -+ bufv.buf[0] = fbuf; -+ bufv.count = 1; -+ pbufv = &bufv; -+ } -+ pbufv->idx = 0; -+ pbufv->off = 0; -+ fuse_session_process_buf_int(se, pbufv, &req->ch); -+ -+out: -+ if (allocated_bufv) { -+ free(pbufv); -+ } -+ -+ /* If the request has no reply, still recycle the virtqueue element */ -+ if (!req->reply_sent) { -+ struct VuVirtq *q = vu_get_queue(dev, qi->qidx); -+ -+ fuse_log(FUSE_LOG_DEBUG, "%s: elem %d no reply sent\n", __func__, -+ elem->index); -+ -+ pthread_rwlock_rdlock(&qi->virtio_dev->vu_dispatch_rwlock); -+ pthread_mutex_lock(&qi->vq_lock); -+ vu_queue_push(dev, q, elem, 0); -+ vu_queue_notify(dev, q); -+ pthread_mutex_unlock(&qi->vq_lock); -+ pthread_rwlock_unlock(&qi->virtio_dev->vu_dispatch_rwlock); -+ } -+ -+ pthread_mutex_destroy(&req->ch.lock); -+ free(fbuf.mem); -+ free(req); -+} -+ - /* Thread function for individual queues, created when a queue is 'started' */ - static void *fv_queue_thread(void *opaque) - { - struct fv_QueueInfo *qi = opaque; - struct VuDev *dev = &qi->virtio_dev->dev; - struct VuVirtq *q = vu_get_queue(dev, qi->qidx); -- struct fuse_session *se = qi->virtio_dev->se; -- struct fuse_chan ch; -- struct fuse_buf fbuf; -+ GThreadPool *pool; - -- fbuf.mem = NULL; -- fbuf.flags = 0; -- -- fuse_mutex_init(&ch.lock); -- ch.fd = (int)0xdaff0d111; -- ch.qi = qi; -+ pool = g_thread_pool_new(fv_queue_worker, qi, 1 /* TODO max_threads */, -+ TRUE, NULL); -+ if (!pool) { -+ fuse_log(FUSE_LOG_ERR, "%s: g_thread_pool_new failed\n", __func__); -+ return NULL; -+ } - - fuse_log(FUSE_LOG_INFO, "%s: Start for queue %d kick_fd %d\n", __func__, - qi->qidx, qi->kick_fd); -@@ -478,6 +636,7 @@ static void *fv_queue_thread(void *opaque) - /* Mutual exclusion with virtio_loop() */ - ret = pthread_rwlock_rdlock(&qi->virtio_dev->vu_dispatch_rwlock); - assert(ret == 0); /* there is no possible error case */ -+ pthread_mutex_lock(&qi->vq_lock); - /* out is from guest, in is too guest */ - unsigned int in_bytes, out_bytes; - vu_queue_get_avail_bytes(dev, q, &in_bytes, &out_bytes, ~0, ~0); -@@ -486,141 +645,22 @@ static void *fv_queue_thread(void *opaque) - "%s: Queue %d gave evalue: %zx available: in: %u out: %u\n", - __func__, qi->qidx, (size_t)evalue, in_bytes, out_bytes); - -- - while (1) { -- bool allocated_bufv = false; -- struct fuse_bufvec bufv; -- struct fuse_bufvec *pbufv; -- -- /* -- * An element contains one request and the space to send our -- * response They're spread over multiple descriptors in a -- * scatter/gather set and we can't trust the guest to keep them -- * still; so copy in/out. -- */ -- VuVirtqElement *elem = vu_queue_pop(dev, q, sizeof(VuVirtqElement)); -- if (!elem) { -+ FVRequest *req = vu_queue_pop(dev, q, sizeof(FVRequest)); -+ if (!req) { - break; - } - -- qi->qe = elem; -- qi->reply_sent = false; -+ req->reply_sent = false; - -- if (!fbuf.mem) { -- fbuf.mem = malloc(se->bufsize); -- assert(fbuf.mem); -- assert(se->bufsize > sizeof(struct fuse_in_header)); -- } -- /* The 'out' part of the elem is from qemu */ -- unsigned int out_num = elem->out_num; -- struct iovec *out_sg = elem->out_sg; -- size_t out_len = iov_size(out_sg, out_num); -- fuse_log(FUSE_LOG_DEBUG, -- "%s: elem %d: with %d out desc of length %zd\n", __func__, -- elem->index, out_num, out_len); -- -- /* -- * The elem should contain a 'fuse_in_header' (in to fuse) -- * plus the data based on the len in the header. -- */ -- if (out_len < sizeof(struct fuse_in_header)) { -- fuse_log(FUSE_LOG_ERR, "%s: elem %d too short for in_header\n", -- __func__, elem->index); -- assert(0); /* TODO */ -- } -- if (out_len > se->bufsize) { -- fuse_log(FUSE_LOG_ERR, "%s: elem %d too large for buffer\n", -- __func__, elem->index); -- assert(0); /* TODO */ -- } -- /* Copy just the first element and look at it */ -- copy_from_iov(&fbuf, 1, out_sg); -- -- if (out_num > 2 && -- out_sg[0].iov_len == sizeof(struct fuse_in_header) && -- ((struct fuse_in_header *)fbuf.mem)->opcode == FUSE_WRITE && -- out_sg[1].iov_len == sizeof(struct fuse_write_in)) { -- /* -- * For a write we don't actually need to copy the -- * data, we can just do it straight out of guest memory -- * but we must still copy the headers in case the guest -- * was nasty and changed them while we were using them. -- */ -- fuse_log(FUSE_LOG_DEBUG, "%s: Write special case\n", __func__); -- -- /* copy the fuse_write_in header after the fuse_in_header */ -- fbuf.mem += out_sg->iov_len; -- copy_from_iov(&fbuf, 1, out_sg + 1); -- fbuf.mem -= out_sg->iov_len; -- fbuf.size = out_sg[0].iov_len + out_sg[1].iov_len; -- -- /* Allocate the bufv, with space for the rest of the iov */ -- allocated_bufv = true; -- pbufv = malloc(sizeof(struct fuse_bufvec) + -- sizeof(struct fuse_buf) * (out_num - 2)); -- if (!pbufv) { -- vu_queue_unpop(dev, q, elem, 0); -- free(elem); -- fuse_log(FUSE_LOG_ERR, "%s: pbufv malloc failed\n", -- __func__); -- goto out; -- } -- -- pbufv->count = 1; -- pbufv->buf[0] = fbuf; -- -- size_t iovindex, pbufvindex; -- iovindex = 2; /* 2 headers, separate iovs */ -- pbufvindex = 1; /* 2 headers, 1 fusebuf */ -- -- for (; iovindex < out_num; iovindex++, pbufvindex++) { -- pbufv->count++; -- pbufv->buf[pbufvindex].pos = ~0; /* Dummy */ -- pbufv->buf[pbufvindex].flags = 0; -- pbufv->buf[pbufvindex].mem = out_sg[iovindex].iov_base; -- pbufv->buf[pbufvindex].size = out_sg[iovindex].iov_len; -- } -- } else { -- /* Normal (non fast write) path */ -- -- /* Copy the rest of the buffer */ -- fbuf.mem += out_sg->iov_len; -- copy_from_iov(&fbuf, out_num - 1, out_sg + 1); -- fbuf.mem -= out_sg->iov_len; -- fbuf.size = out_len; -- -- /* TODO! Endianness of header */ -- -- /* TODO: Add checks for fuse_session_exited */ -- bufv.buf[0] = fbuf; -- bufv.count = 1; -- pbufv = &bufv; -- } -- pbufv->idx = 0; -- pbufv->off = 0; -- fuse_session_process_buf_int(se, pbufv, &ch); -- -- if (allocated_bufv) { -- free(pbufv); -- } -- -- if (!qi->reply_sent) { -- fuse_log(FUSE_LOG_DEBUG, "%s: elem %d no reply sent\n", -- __func__, elem->index); -- /* I think we've still got to recycle the element */ -- vu_queue_push(dev, q, elem, 0); -- vu_queue_notify(dev, q); -- } -- qi->qe = NULL; -- free(elem); -- elem = NULL; -+ g_thread_pool_push(pool, req, NULL); - } - -+ pthread_mutex_unlock(&qi->vq_lock); - pthread_rwlock_unlock(&qi->virtio_dev->vu_dispatch_rwlock); - } --out: -- pthread_mutex_destroy(&ch.lock); -- free(fbuf.mem); -+ -+ g_thread_pool_free(pool, FALSE, TRUE); - - return NULL; - } -@@ -643,6 +683,7 @@ static void fv_queue_cleanup_thread(struct fv_VuDev *vud, int qidx) - fuse_log(FUSE_LOG_ERR, "%s: Failed to join thread idx %d err %d\n", - __func__, qidx, ret); - } -+ pthread_mutex_destroy(&ourqi->vq_lock); - close(ourqi->kill_fd); - ourqi->kick_fd = -1; - free(vud->qi[qidx]); -@@ -696,6 +737,8 @@ static void fv_queue_set_started(VuDev *dev, int qidx, bool started) - - ourqi->kill_fd = eventfd(0, EFD_CLOEXEC | EFD_SEMAPHORE); - assert(ourqi->kill_fd != -1); -+ pthread_mutex_init(&ourqi->vq_lock, NULL); -+ - if (pthread_create(&ourqi->thread, NULL, fv_queue_thread, ourqi)) { - fuse_log(FUSE_LOG_ERR, "%s: Failed to create thread for queue %d\n", - __func__, qidx); diff --git a/0111-virtiofsd-prevent-FUSE_INIT-FUSE_DESTROY-races.patch b/0111-virtiofsd-prevent-FUSE_INIT-FUSE_DESTROY-races.patch deleted file mode 100644 index c2d8e55..0000000 --- a/0111-virtiofsd-prevent-FUSE_INIT-FUSE_DESTROY-races.patch +++ /dev/null @@ -1,84 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:02:20 +0000 -Subject: [PATCH] virtiofsd: prevent FUSE_INIT/FUSE_DESTROY races - -When running with multiple threads it can be tricky to handle -FUSE_INIT/FUSE_DESTROY in parallel with other request types or in -parallel with themselves. Serialize FUSE_INIT and FUSE_DESTROY so that -malicious clients cannot trigger race conditions. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Masayoshi Mizuma -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit cdc497c6925be745bc895355bd4674a17a4b2a8b) ---- - tools/virtiofsd/fuse_i.h | 1 + - tools/virtiofsd/fuse_lowlevel.c | 18 ++++++++++++++++++ - 2 files changed, 19 insertions(+) - -diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h -index a20854f1c4..1447d86866 100644 ---- a/tools/virtiofsd/fuse_i.h -+++ b/tools/virtiofsd/fuse_i.h -@@ -61,6 +61,7 @@ struct fuse_session { - struct fuse_req list; - struct fuse_req interrupts; - pthread_mutex_t lock; -+ pthread_rwlock_t init_rwlock; - int got_destroy; - int broken_splice_nonblock; - uint64_t notify_ctr; -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index dab6a31e08..79a4031266 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -2428,6 +2428,19 @@ void fuse_session_process_buf_int(struct fuse_session *se, - req->ctx.pid = in->pid; - req->ch = ch; - -+ /* -+ * INIT and DESTROY requests are serialized, all other request types -+ * run in parallel. This prevents races between FUSE_INIT and ordinary -+ * requests, FUSE_INIT and FUSE_INIT, FUSE_INIT and FUSE_DESTROY, and -+ * FUSE_DESTROY and FUSE_DESTROY. -+ */ -+ if (in->opcode == FUSE_INIT || in->opcode == CUSE_INIT || -+ in->opcode == FUSE_DESTROY) { -+ pthread_rwlock_wrlock(&se->init_rwlock); -+ } else { -+ pthread_rwlock_rdlock(&se->init_rwlock); -+ } -+ - err = EIO; - if (!se->got_init) { - enum fuse_opcode expected; -@@ -2485,10 +2498,13 @@ void fuse_session_process_buf_int(struct fuse_session *se, - } else { - fuse_ll_ops[in->opcode].func(req, in->nodeid, &iter); - } -+ -+ pthread_rwlock_unlock(&se->init_rwlock); - return; - - reply_err: - fuse_reply_err(req, err); -+ pthread_rwlock_unlock(&se->init_rwlock); - } - - #define LL_OPTION(n, o, v) \ -@@ -2531,6 +2547,7 @@ void fuse_session_destroy(struct fuse_session *se) - se->op.destroy(se->userdata); - } - } -+ pthread_rwlock_destroy(&se->init_rwlock); - pthread_mutex_destroy(&se->lock); - free(se->cuse_data); - if (se->fd != -1) { -@@ -2610,6 +2627,7 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, - list_init_req(&se->list); - list_init_req(&se->interrupts); - fuse_mutex_init(&se->lock); -+ pthread_rwlock_init(&se->init_rwlock, NULL); - - memcpy(&se->op, op, op_size); - se->owner = getuid(); diff --git a/0112-virtiofsd-fix-lo_destroy-resource-leaks.patch b/0112-virtiofsd-fix-lo_destroy-resource-leaks.patch deleted file mode 100644 index 20a6143..0000000 --- a/0112-virtiofsd-fix-lo_destroy-resource-leaks.patch +++ /dev/null @@ -1,78 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:02:21 +0000 -Subject: [PATCH] virtiofsd: fix lo_destroy() resource leaks -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Now that lo_destroy() is serialized we can call unref_inode() so that -all inode resources are freed. - -Signed-off-by: Stefan Hajnoczi -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 28f7a3b026f231bfe8de5fed6a18a8d27b1dfcee) ---- - tools/virtiofsd/passthrough_ll.c | 41 ++++++++++++++++---------------- - 1 file changed, 20 insertions(+), 21 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index 79b8b71a4f..eb001b9d1e 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1371,26 +1371,6 @@ static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, - } - } - --static int unref_all_inodes_cb(gpointer key, gpointer value, gpointer user_data) --{ -- struct lo_inode *inode = value; -- struct lo_data *lo = user_data; -- -- inode->nlookup = 0; -- lo_map_remove(&lo->ino_map, inode->fuse_ino); -- close(inode->fd); -- lo_inode_put(lo, &inode); /* Drop our refcount from lo_do_lookup() */ -- -- return TRUE; --} -- --static void unref_all_inodes(struct lo_data *lo) --{ -- pthread_mutex_lock(&lo->mutex); -- g_hash_table_foreach_remove(lo->inodes, unref_all_inodes_cb, lo); -- pthread_mutex_unlock(&lo->mutex); --} -- - static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) - { - struct lo_data *lo = lo_data(req); -@@ -2477,7 +2457,26 @@ static void lo_lseek(fuse_req_t req, fuse_ino_t ino, off_t off, int whence, - static void lo_destroy(void *userdata) - { - struct lo_data *lo = (struct lo_data *)userdata; -- unref_all_inodes(lo); -+ -+ /* -+ * Normally lo->mutex must be taken when traversing lo->inodes but -+ * lo_destroy() is a serialized request so no races are possible here. -+ * -+ * In addition, we cannot acquire lo->mutex since unref_inode() takes it -+ * too and this would result in a recursive lock. -+ */ -+ while (true) { -+ GHashTableIter iter; -+ gpointer key, value; -+ -+ g_hash_table_iter_init(&iter, lo->inodes); -+ if (!g_hash_table_iter_next(&iter, &key, &value)) { -+ break; -+ } -+ -+ struct lo_inode *inode = value; -+ unref_inode_lolocked(lo, inode, inode->nlookup); -+ } - } - - static struct fuse_lowlevel_ops lo_oper = { diff --git a/0113-virtiofsd-add-thread-pool-size-NUM-option.patch b/0113-virtiofsd-add-thread-pool-size-NUM-option.patch deleted file mode 100644 index 2cb742c..0000000 --- a/0113-virtiofsd-add-thread-pool-size-NUM-option.patch +++ /dev/null @@ -1,90 +0,0 @@ -From: Stefan Hajnoczi -Date: Mon, 27 Jan 2020 19:02:22 +0000 -Subject: [PATCH] virtiofsd: add --thread-pool-size=NUM option -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Add an option to control the size of the thread pool. Requests are now -processed in parallel by default. - -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Daniel P. Berrangé -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 951b3120dbc971f08681e1d860360e4a1e638902) ---- - tools/virtiofsd/fuse_i.h | 1 + - tools/virtiofsd/fuse_lowlevel.c | 7 ++++++- - tools/virtiofsd/fuse_virtio.c | 5 +++-- - 3 files changed, 10 insertions(+), 3 deletions(-) - -diff --git a/tools/virtiofsd/fuse_i.h b/tools/virtiofsd/fuse_i.h -index 1447d86866..4e47e5880d 100644 ---- a/tools/virtiofsd/fuse_i.h -+++ b/tools/virtiofsd/fuse_i.h -@@ -72,6 +72,7 @@ struct fuse_session { - int vu_listen_fd; - int vu_socketfd; - struct fv_VuDev *virtio_dev; -+ int thread_pool_size; - }; - - struct fuse_chan { -diff --git a/tools/virtiofsd/fuse_lowlevel.c b/tools/virtiofsd/fuse_lowlevel.c -index 79a4031266..de2e2e0c65 100644 ---- a/tools/virtiofsd/fuse_lowlevel.c -+++ b/tools/virtiofsd/fuse_lowlevel.c -@@ -28,6 +28,7 @@ - #include - #include - -+#define THREAD_POOL_SIZE 64 - - #define OFFSET_MAX 0x7fffffffffffffffLL - -@@ -2519,6 +2520,7 @@ static const struct fuse_opt fuse_ll_opts[] = { - LL_OPTION("allow_root", deny_others, 1), - LL_OPTION("--socket-path=%s", vu_socket_path, 0), - LL_OPTION("--fd=%d", vu_listen_fd, 0), -+ LL_OPTION("--thread-pool-size=%d", thread_pool_size, 0), - FUSE_OPT_END - }; - -@@ -2537,7 +2539,9 @@ void fuse_lowlevel_help(void) - printf( - " -o allow_root allow access by root\n" - " --socket-path=PATH path for the vhost-user socket\n" -- " --fd=FDNUM fd number of vhost-user socket\n"); -+ " --fd=FDNUM fd number of vhost-user socket\n" -+ " --thread-pool-size=NUM thread pool size limit (default %d)\n", -+ THREAD_POOL_SIZE); - } - - void fuse_session_destroy(struct fuse_session *se) -@@ -2591,6 +2595,7 @@ struct fuse_session *fuse_session_new(struct fuse_args *args, - } - se->fd = -1; - se->vu_listen_fd = -1; -+ se->thread_pool_size = THREAD_POOL_SIZE; - se->conn.max_write = UINT_MAX; - se->conn.max_readahead = UINT_MAX; - -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index 0dcf2ef57a..9f6582343c 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -572,10 +572,11 @@ static void *fv_queue_thread(void *opaque) - struct fv_QueueInfo *qi = opaque; - struct VuDev *dev = &qi->virtio_dev->dev; - struct VuVirtq *q = vu_get_queue(dev, qi->qidx); -+ struct fuse_session *se = qi->virtio_dev->se; - GThreadPool *pool; - -- pool = g_thread_pool_new(fv_queue_worker, qi, 1 /* TODO max_threads */, -- TRUE, NULL); -+ pool = g_thread_pool_new(fv_queue_worker, qi, se->thread_pool_size, TRUE, -+ NULL); - if (!pool) { - fuse_log(FUSE_LOG_ERR, "%s: g_thread_pool_new failed\n", __func__); - return NULL; diff --git a/0114-virtiofsd-Convert-lo_destroy-to-take-the-lo-mutex-lo.patch b/0114-virtiofsd-Convert-lo_destroy-to-take-the-lo-mutex-lo.patch deleted file mode 100644 index 142429b..0000000 --- a/0114-virtiofsd-Convert-lo_destroy-to-take-the-lo-mutex-lo.patch +++ /dev/null @@ -1,96 +0,0 @@ -From: "Dr. David Alan Gilbert" -Date: Mon, 27 Jan 2020 19:02:23 +0000 -Subject: [PATCH] virtiofsd: Convert lo_destroy to take the lo->mutex lock - itself -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -lo_destroy was relying on some implicit knowledge of the locking; -we can avoid this if we create an unref_inode that doesn't take -the lock and then grab it for the whole of the lo_destroy. - -Suggested-by: Vivek Goyal -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Philippe Mathieu-Daudé -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit fe4c15798a48143dd6b1f58d2d3cad12206ce211) ---- - tools/virtiofsd/passthrough_ll.c | 31 +++++++++++++++++-------------- - 1 file changed, 17 insertions(+), 14 deletions(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index eb001b9d1e..fc15d61510 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -1344,14 +1344,13 @@ static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name) - lo_inode_put(lo, &inode); - } - --static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, -- uint64_t n) -+/* To be called with lo->mutex held */ -+static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n) - { - if (!inode) { - return; - } - -- pthread_mutex_lock(&lo->mutex); - assert(inode->nlookup >= n); - inode->nlookup -= n; - if (!inode->nlookup) { -@@ -1362,15 +1361,24 @@ static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, - } - g_hash_table_destroy(inode->posix_locks); - pthread_mutex_destroy(&inode->plock_mutex); -- pthread_mutex_unlock(&lo->mutex); - - /* Drop our refcount from lo_do_lookup() */ - lo_inode_put(lo, &inode); -- } else { -- pthread_mutex_unlock(&lo->mutex); - } - } - -+static void unref_inode_lolocked(struct lo_data *lo, struct lo_inode *inode, -+ uint64_t n) -+{ -+ if (!inode) { -+ return; -+ } -+ -+ pthread_mutex_lock(&lo->mutex); -+ unref_inode(lo, inode, n); -+ pthread_mutex_unlock(&lo->mutex); -+} -+ - static void lo_forget_one(fuse_req_t req, fuse_ino_t ino, uint64_t nlookup) - { - struct lo_data *lo = lo_data(req); -@@ -2458,13 +2466,7 @@ static void lo_destroy(void *userdata) - { - struct lo_data *lo = (struct lo_data *)userdata; - -- /* -- * Normally lo->mutex must be taken when traversing lo->inodes but -- * lo_destroy() is a serialized request so no races are possible here. -- * -- * In addition, we cannot acquire lo->mutex since unref_inode() takes it -- * too and this would result in a recursive lock. -- */ -+ pthread_mutex_lock(&lo->mutex); - while (true) { - GHashTableIter iter; - gpointer key, value; -@@ -2475,8 +2477,9 @@ static void lo_destroy(void *userdata) - } - - struct lo_inode *inode = value; -- unref_inode_lolocked(lo, inode, inode->nlookup); -+ unref_inode(lo, inode, inode->nlookup); - } -+ pthread_mutex_unlock(&lo->mutex); - } - - static struct fuse_lowlevel_ops lo_oper = { diff --git a/0115-virtiofsd-passthrough_ll-Pass-errno-to-fuse_reply_er.patch b/0115-virtiofsd-passthrough_ll-Pass-errno-to-fuse_reply_er.patch deleted file mode 100644 index 91a4062..0000000 --- a/0115-virtiofsd-passthrough_ll-Pass-errno-to-fuse_reply_er.patch +++ /dev/null @@ -1,34 +0,0 @@ -From: Xiao Yang -Date: Mon, 27 Jan 2020 19:02:24 +0000 -Subject: [PATCH] virtiofsd/passthrough_ll: Pass errno to fuse_reply_err() - -lo_copy_file_range() passes -errno to fuse_reply_err() and then fuse_reply_err() -changes it to errno again, so that subsequent fuse_send_reply_iov_nofree() catches -the wrong errno.(i.e. reports "fuse: bad error value: ..."). - -Make fuse_send_reply_iov_nofree() accept the correct -errno by passing errno -directly in lo_copy_file_range(). - -Signed-off-by: Xiao Yang -Reviewed-by: Eryu Guan - -dgilbert: Sent upstream and now Merged as aa1185e153f774f1df65 -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit a931b6861e59c78d861017e9c6a9c161ff49a163) ---- - tools/virtiofsd/passthrough_ll.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c -index fc15d61510..e6f2399efc 100644 ---- a/tools/virtiofsd/passthrough_ll.c -+++ b/tools/virtiofsd/passthrough_ll.c -@@ -2441,7 +2441,7 @@ static void lo_copy_file_range(fuse_req_t req, fuse_ino_t ino_in, off_t off_in, - - res = copy_file_range(in_fd, &off_in, out_fd, &off_out, len, flags); - if (res < 0) { -- fuse_reply_err(req, -errno); -+ fuse_reply_err(req, errno); - } else { - fuse_reply_write(req, res); - } diff --git a/0116-virtiofsd-stop-all-queue-threads-on-exit-in-virtio_l.patch b/0116-virtiofsd-stop-all-queue-threads-on-exit-in-virtio_l.patch deleted file mode 100644 index 6769996..0000000 --- a/0116-virtiofsd-stop-all-queue-threads-on-exit-in-virtio_l.patch +++ /dev/null @@ -1,52 +0,0 @@ -From: Eryu Guan -Date: Mon, 27 Jan 2020 19:02:25 +0000 -Subject: [PATCH] virtiofsd: stop all queue threads on exit in virtio_loop() - -On guest graceful shutdown, virtiofsd receives VHOST_USER_GET_VRING_BASE -request from VMM and shuts down virtqueues by calling fv_set_started(), -which joins fv_queue_thread() threads. So when virtio_loop() returns, -there should be no thread is still accessing data in fuse session and/or -virtio dev. - -But on abnormal exit, e.g. guest got killed for whatever reason, -vhost-user socket is closed and virtio_loop() breaks out the main loop -and returns to main(). But it's possible fv_queue_worker()s are still -working and accessing fuse session and virtio dev, which results in -crash or use-after-free. - -Fix it by stopping fv_queue_thread()s before virtio_loop() returns, -to make sure there's no-one could access fuse session and virtio dev. - -Reported-by: Qingming Su -Signed-off-by: Eryu Guan -Reviewed-by: Stefan Hajnoczi -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 9883df8ccae6d744a0c8d9cbf9d62b1797d70ebd) ---- - tools/virtiofsd/fuse_virtio.c | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - -diff --git a/tools/virtiofsd/fuse_virtio.c b/tools/virtiofsd/fuse_virtio.c -index 9f6582343c..80a6e929df 100644 ---- a/tools/virtiofsd/fuse_virtio.c -+++ b/tools/virtiofsd/fuse_virtio.c -@@ -815,6 +815,19 @@ int virtio_loop(struct fuse_session *se) - } - } - -+ /* -+ * Make sure all fv_queue_thread()s quit on exit, as we're about to -+ * free virtio dev and fuse session, no one should access them anymore. -+ */ -+ for (int i = 0; i < se->virtio_dev->nqueues; i++) { -+ if (!se->virtio_dev->qi[i]) { -+ continue; -+ } -+ -+ fuse_log(FUSE_LOG_INFO, "%s: Stopping queue %d thread\n", __func__, i); -+ fv_queue_cleanup_thread(se->virtio_dev, i); -+ } -+ - fuse_log(FUSE_LOG_INFO, "%s: Exit\n", __func__); - - return 0; diff --git a/0117-virtiofsd-add-some-options-to-the-help-message.patch b/0117-virtiofsd-add-some-options-to-the-help-message.patch deleted file mode 100644 index 8fbb062..0000000 --- a/0117-virtiofsd-add-some-options-to-the-help-message.patch +++ /dev/null @@ -1,55 +0,0 @@ -From: Masayoshi Mizuma -Date: Mon, 27 Jan 2020 19:02:26 +0000 -Subject: [PATCH] virtiofsd: add some options to the help message - -Add following options to the help message: -- cache -- flock|no_flock -- norace -- posix_lock|no_posix_lock -- readdirplus|no_readdirplus -- timeout -- writeback|no_writeback -- xattr|no_xattr - -Signed-off-by: Masayoshi Mizuma - -dgilbert: Split cache, norace, posix_lock, readdirplus off - into our own earlier patches that added the options - -Reviewed-by: Dr. David Alan Gilbert -Reviewed-by: Misono Tomohiro -Signed-off-by: Dr. David Alan Gilbert -(cherry picked from commit 1d59b1b210d7c3b0bdf4b10ebe0bb1fccfcb8b95) ---- - tools/virtiofsd/helper.c | 10 +++++++++- - 1 file changed, 9 insertions(+), 1 deletion(-) - -diff --git a/tools/virtiofsd/helper.c b/tools/virtiofsd/helper.c -index f98d8f2eb2..0801cf752c 100644 ---- a/tools/virtiofsd/helper.c -+++ b/tools/virtiofsd/helper.c -@@ -148,6 +148,8 @@ void fuse_cmdline_help(void) - " -o cache= cache mode. could be one of \"auto, " - "always, none\"\n" - " default: auto\n" -+ " -o flock|no_flock enable/disable flock\n" -+ " default: no_flock\n" - " -o log_level= log level, default to \"info\"\n" - " level could be one of \"debug, " - "info, warn, err\"\n" -@@ -163,7 +165,13 @@ void fuse_cmdline_help(void) - " enable/disable readirplus\n" - " default: readdirplus except with " - "cache=none\n" -- ); -+ " -o timeout= I/O timeout (second)\n" -+ " default: depends on cache= option.\n" -+ " -o writeback|no_writeback enable/disable writeback cache\n" -+ " default: no_writeback\n" -+ " -o xattr|no_xattr enable/disable xattr\n" -+ " default: no_xattr\n" -+ ); - } - - static int fuse_helper_opt_proc(void *data, const char *arg, int key, diff --git a/0118-vfio-pci-Don-t-remove-irqchip-notifier-if-not-regist.patch b/0118-vfio-pci-Don-t-remove-irqchip-notifier-if-not-regist.patch deleted file mode 100644 index a510fc1..0000000 --- a/0118-vfio-pci-Don-t-remove-irqchip-notifier-if-not-regist.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 0446f8121723b134ca1d1ed0b73e96d4a0a8689d Mon Sep 17 00:00:00 2001 -From: Peter Xu -Date: Mon, 6 Jan 2020 13:34:45 -0700 -Subject: [PATCH] vfio/pci: Don't remove irqchip notifier if not registered - -The kvm irqchip notifier is only registered if the device supports -INTx, however it's unconditionally removed. If the assigned device -does not support INTx, this will cause QEMU to crash when unplugging -the device from the system. Change it to conditionally remove the -notifier only if the notify hook is setup. - -CC: Eduardo Habkost -CC: David Gibson -CC: Alex Williamson -Cc: qemu-stable@nongnu.org # v4.2 -Reported-by: yanghliu@redhat.com -Debugged-by: Eduardo Habkost -Fixes: c5478fea27ac ("vfio/pci: Respond to KVM irqchip change notifier") -Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1782678 -Signed-off-by: Peter Xu -Reviewed-by: David Gibson -Reviewed-by: Greg Kurz -Signed-off-by: Alex Williamson ---- - hw/vfio/pci.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c -index 2d40b396f2..337a173ce7 100644 ---- a/hw/vfio/pci.c -+++ b/hw/vfio/pci.c -@@ -3076,7 +3076,9 @@ static void vfio_exitfn(PCIDevice *pdev) - vfio_unregister_req_notifier(vdev); - vfio_unregister_err_notifier(vdev); - pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); -- kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); -+ if (vdev->irqchip_change_notifier.notify) { -+ kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); -+ } - vfio_disable_interrupts(vdev); - if (vdev->intx.mmap_timer) { - timer_free(vdev->intx.mmap_timer); --- -2.25.1 - diff --git a/0201-spapr-Don-t-trigger-a-CAS-reboot-for-XICS-XIVE-mode-.patch b/0201-spapr-Don-t-trigger-a-CAS-reboot-for-XICS-XIVE-mode-.patch deleted file mode 100644 index 812a801..0000000 --- a/0201-spapr-Don-t-trigger-a-CAS-reboot-for-XICS-XIVE-mode-.patch +++ /dev/null @@ -1,90 +0,0 @@ -From: David Gibson -Date: Fri, 18 Oct 2019 15:19:31 +1100 -Subject: [PATCH] spapr: Don't trigger a CAS reboot for XICS/XIVE mode - changeover - -PAPR allows the interrupt controller used on a POWER9 machine (XICS or -XIVE) to be selected by the guest operating system, by using the -ibm,client-architecture-support (CAS) feature negotiation call. - -Currently, if the guest selects an interrupt controller different from the -one selected at initial boot, this causes the system to be reset with the -new model and the boot starts again. This means we run through the SLOF -boot process twice, as well as any other bootloader (e.g. grub) in use -before the OS calls CAS. This can be confusing and/or inconvenient for -users. - -Thanks to two fairly recent changes, we no longer need this reboot. 1) we -now completely regenerate the device tree when CAS is called (meaning we -don't need special case updates for all the device tree changes caused by -the interrupt controller mode change), 2) we now have explicit code paths -to activate and deactivate the different interrupt controllers, rather than -just implicitly calling those at machine reset time. - -We can therefore eliminate the reboot for changing irq mode, simply by -putting a call to spapr_irq_update_active_intc() before we call -spapr_h_cas_compose_response() (which gives the updated device tree to -the guest firmware and OS). - -Signed-off-by: David Gibson -Reviewed-by: Cedric Le Goater -Reviewed-by: Greg Kurz -(cherry picked from commit 8deb8019d696c75e6ecaee7545026b62aba2f1bb) ---- - hw/ppc/spapr_hcall.c | 33 +++++++++++++-------------------- - 1 file changed, 13 insertions(+), 20 deletions(-) - -diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c -index 140f05c1c6..05a7ca275b 100644 ---- a/hw/ppc/spapr_hcall.c -+++ b/hw/ppc/spapr_hcall.c -@@ -1767,21 +1767,10 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, - } - spapr->cas_pre_isa3_guest = !spapr_ovec_test(ov1_guest, OV1_PPC_3_00); - spapr_ovec_cleanup(ov1_guest); -- if (!spapr->cas_reboot) { -- /* If spapr_machine_reset() did not set up a HPT but one is necessary -- * (because the guest isn't going to use radix) then set it up here. */ -- if ((spapr->patb_entry & PATE1_GR) && !guest_radix) { -- /* legacy hash or new hash: */ -- spapr_setup_hpt_and_vrma(spapr); -- } -- spapr->cas_reboot = -- (spapr_h_cas_compose_response(spapr, args[1], args[2], -- ov5_updates) != 0); -- } - - /* -- * Ensure the guest asks for an interrupt mode we support; otherwise -- * terminate the boot. -+ * Ensure the guest asks for an interrupt mode we support; -+ * otherwise terminate the boot. - */ - if (guest_xive) { - if (!spapr->irq->xive) { -@@ -1797,14 +1786,18 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, - } - } - -- /* -- * Generate a machine reset when we have an update of the -- * interrupt mode. Only required when the machine supports both -- * modes. -- */ -+ spapr_irq_update_active_intc(spapr); -+ - if (!spapr->cas_reboot) { -- spapr->cas_reboot = spapr_ovec_test(ov5_updates, OV5_XIVE_EXPLOIT) -- && spapr->irq->xics && spapr->irq->xive; -+ /* If spapr_machine_reset() did not set up a HPT but one is necessary -+ * (because the guest isn't going to use radix) then set it up here. */ -+ if ((spapr->patb_entry & PATE1_GR) && !guest_radix) { -+ /* legacy hash or new hash: */ -+ spapr_setup_hpt_and_vrma(spapr); -+ } -+ spapr->cas_reboot = -+ (spapr_h_cas_compose_response(spapr, args[1], args[2], -+ ov5_updates) != 0); - } - - spapr_ovec_cleanup(ov5_updates); diff --git a/qemu.spec b/qemu.spec index 66c2948..a06407b 100644 --- a/qemu.spec +++ b/qemu.spec @@ -151,7 +151,7 @@ %{obsoletes_block_rbd} # Release candidate version tracking -#%%global rcver rc5 +%global rcver rc0 %if 0%{?rcver:1} %global rcrel .%{rcver} %global rcstr -%{rcver} @@ -160,8 +160,8 @@ Summary: QEMU is a FAST! processor emulator Name: qemu -Version: 4.2.0 -Release: 7%{?rcrel}%{?dist} +Version: 5.0.0 +Release: 0.1%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -185,132 +185,6 @@ Source20: kvm-x86.modprobe.conf # /etc/security/limits.d/95-kvm-ppc64-memlock.conf Source21: 95-kvm-ppc64-memlock.conf -# Fix a test suite error -Patch0001: 0001-tests-fix-modules-test-duplicate-test-case-error.patch -# Miscellaneous fixes for RISC-V -Patch0002: 0002-riscv-sifive_u-fix-a-memory-leak-in-soc_realize.patch -Patch0003: 0003-riscv-Set-xPIE-to-1-after-xRET.patch -Patch0004: 0004-target-riscv-Fix-tb-flags-FS-status.patch -Patch0005: 0005-target-riscv-fsd-fsw-doesn-t-dirty-FP-state.patch -Patch0006: 0006-target-riscv-update-mstatus.SD-when-FS-is-set-dirty.patch -# virtio-fs support -Patch0007: 0007-virtio-fs-fix-MSI-X-nvectors-calculation.patch -Patch0008: 0008-vhost-user-fs-remove-vhostfd-property.patch -Patch0009: 0009-build-rename-CONFIG_LIBCAP-to-CONFIG_LIBCAP_NG.patch -Patch0010: 0010-virtiofsd-Pull-in-upstream-headers.patch -Patch0011: 0011-virtiofsd-Pull-in-kernel-s-fuse.h.patch -Patch0012: 0012-virtiofsd-Add-auxiliary-.c-s.patch -Patch0013: 0013-virtiofsd-Add-fuse_lowlevel.c.patch -Patch0014: 0014-virtiofsd-Add-passthrough_ll.patch -Patch0015: 0015-virtiofsd-Trim-down-imported-files.patch -Patch0016: 0016-virtiofsd-Format-imported-files-to-qemu-style.patch -Patch0017: 0017-virtiofsd-remove-mountpoint-dummy-argument.patch -Patch0018: 0018-virtiofsd-remove-unused-notify-reply-support.patch -Patch0019: 0019-virtiofsd-Remove-unused-enum-fuse_buf_copy_flags.patch -Patch0020: 0020-virtiofsd-Fix-fuse_daemonize-ignored-return-values.patch -Patch0021: 0021-virtiofsd-Fix-common-header-and-define-for-QEMU-buil.patch -Patch0022: 0022-virtiofsd-Trim-out-compatibility-code.patch -Patch0023: 0023-vitriofsd-passthrough_ll-fix-fallocate-ifdefs.patch -Patch0024: 0024-virtiofsd-Make-fsync-work-even-if-only-inode-is-pass.patch -Patch0025: 0025-virtiofsd-Add-options-for-virtio.patch -Patch0026: 0026-virtiofsd-add-o-source-PATH-to-help-output.patch -Patch0027: 0027-virtiofsd-Open-vhost-connection-instead-of-mounting.patch -Patch0028: 0028-virtiofsd-Start-wiring-up-vhost-user.patch -Patch0029: 0029-virtiofsd-Add-main-virtio-loop.patch -Patch0030: 0030-virtiofsd-get-set-features-callbacks.patch -Patch0031: 0031-virtiofsd-Start-queue-threads.patch -Patch0032: 0032-virtiofsd-Poll-kick_fd-for-queue.patch -Patch0033: 0033-virtiofsd-Start-reading-commands-from-queue.patch -Patch0034: 0034-virtiofsd-Send-replies-to-messages.patch -Patch0035: 0035-virtiofsd-Keep-track-of-replies.patch -Patch0036: 0036-virtiofsd-Add-Makefile-wiring-for-virtiofsd-contrib.patch -Patch0037: 0037-virtiofsd-Fast-path-for-virtio-read.patch -Patch0038: 0038-virtiofsd-add-fd-FDNUM-fd-passing-option.patch -Patch0039: 0039-virtiofsd-make-f-foreground-the-default.patch -Patch0040: 0040-virtiofsd-add-vhost-user.json-file.patch -Patch0041: 0041-virtiofsd-add-print-capabilities-option.patch -Patch0042: 0042-virtiofs-Add-maintainers-entry.patch -Patch0043: 0043-virtiofsd-passthrough_ll-create-new-files-in-caller-.patch -Patch0044: 0044-virtiofsd-passthrough_ll-add-lo_map-for-ino-fh-indir.patch -Patch0045: 0045-virtiofsd-passthrough_ll-add-ino_map-to-hide-lo_inod.patch -Patch0046: 0046-virtiofsd-passthrough_ll-add-dirp_map-to-hide-lo_dir.patch -Patch0047: 0047-virtiofsd-passthrough_ll-add-fd_map-to-hide-file-des.patch -Patch0048: 0048-virtiofsd-passthrough_ll-add-fallback-for-racy-ops.patch -Patch0049: 0049-virtiofsd-validate-path-components.patch -Patch0050: 0050-virtiofsd-Plumb-fuse_bufvec-through-to-do_write_buf.patch -Patch0051: 0051-virtiofsd-Pass-write-iov-s-all-the-way-through.patch -Patch0052: 0052-virtiofsd-add-fuse_mbuf_iter-API.patch -Patch0053: 0053-virtiofsd-validate-input-buffer-sizes-in-do_write_bu.patch -Patch0054: 0054-virtiofsd-check-input-buffer-size-in-fuse_lowlevel.c.patch -Patch0055: 0055-virtiofsd-prevent-.-escape-in-lo_do_lookup.patch -Patch0056: 0056-virtiofsd-prevent-.-escape-in-lo_do_readdir.patch -Patch0057: 0057-virtiofsd-use-proc-self-fd-O_PATH-file-descriptor.patch -Patch0058: 0058-virtiofsd-sandbox-mount-namespace.patch -Patch0059: 0059-virtiofsd-move-to-an-empty-network-namespace.patch -Patch0060: 0060-virtiofsd-move-to-a-new-pid-namespace.patch -Patch0061: 0061-virtiofsd-add-seccomp-whitelist.patch -Patch0062: 0062-virtiofsd-Parse-flag-FUSE_WRITE_KILL_PRIV.patch -Patch0063: 0063-virtiofsd-cap-ng-helpers.patch -Patch0064: 0064-virtiofsd-Drop-CAP_FSETID-if-client-asked-for-it.patch -Patch0065: 0065-virtiofsd-set-maximum-RLIMIT_NOFILE-limit.patch -Patch0066: 0066-virtiofsd-fix-libfuse-information-leaks.patch -Patch0067: 0067-virtiofsd-add-syslog-command-line-option.patch -Patch0068: 0068-virtiofsd-print-log-only-when-priority-is-high-enoug.patch -Patch0069: 0069-virtiofsd-Add-ID-to-the-log-with-FUSE_LOG_DEBUG-leve.patch -Patch0070: 0070-virtiofsd-Add-timestamp-to-the-log-with-FUSE_LOG_DEB.patch -Patch0071: 0071-virtiofsd-Handle-reinit.patch -Patch0072: 0072-virtiofsd-Handle-hard-reboot.patch -Patch0073: 0073-virtiofsd-Kill-threads-when-queues-are-stopped.patch -Patch0074: 0074-vhost-user-Print-unexpected-slave-message-types.patch -Patch0075: 0075-contrib-libvhost-user-Protect-slave-fd-with-mutex.patch -Patch0076: 0076-virtiofsd-passthrough_ll-add-renameat2-support.patch -Patch0077: 0077-virtiofsd-passthrough_ll-disable-readdirplus-on-cach.patch -Patch0078: 0078-virtiofsd-passthrough_ll-control-readdirplus.patch -Patch0079: 0079-virtiofsd-rename-unref_inode-to-unref_inode_lolocked.patch -Patch0080: 0080-virtiofsd-fail-when-parent-inode-isn-t-known-in-lo_d.patch -Patch0081: 0081-virtiofsd-extract-root-inode-init-into-setup_root.patch -Patch0082: 0082-virtiofsd-passthrough_ll-clean-up-cache-related-opti.patch -Patch0083: 0083-virtiofsd-passthrough_ll-use-hashtable.patch -Patch0084: 0084-virtiofsd-Clean-up-inodes-on-destroy.patch -Patch0085: 0085-virtiofsd-support-nanosecond-resolution-for-file-tim.patch -Patch0086: 0086-virtiofsd-fix-error-handling-in-main.patch -Patch0087: 0087-virtiofsd-cleanup-allocated-resource-in-se.patch -Patch0088: 0088-virtiofsd-fix-memory-leak-on-lo.source.patch -Patch0089: 0089-virtiofsd-add-helper-for-lo_data-cleanup.patch -Patch0090: 0090-virtiofsd-Prevent-multiply-running-with-same-vhost_u.patch -Patch0091: 0091-virtiofsd-enable-PARALLEL_DIROPS-during-INIT.patch -Patch0092: 0092-virtiofsd-fix-incorrect-error-handling-in-lo_do_look.patch -Patch0093: 0093-Virtiofsd-fix-memory-leak-on-fuse-queueinfo.patch -Patch0094: 0094-virtiofsd-Support-remote-posix-locks.patch -Patch0095: 0095-virtiofsd-use-fuse_lowlevel_is_virtio-in-fuse_sessio.patch -Patch0096: 0096-virtiofsd-prevent-fv_queue_thread-vs-virtio_loop-rac.patch -Patch0097: 0097-virtiofsd-make-lo_release-atomic.patch -Patch0098: 0098-virtiofsd-prevent-races-with-lo_dirp_put.patch -Patch0099: 0099-virtiofsd-rename-inode-refcount-to-inode-nlookup.patch -Patch0100: 0100-libvhost-user-Fix-some-memtable-remap-cases.patch -Patch0101: 0101-virtiofsd-passthrough_ll-fix-refcounting-on-remove-r.patch -Patch0102: 0102-virtiofsd-introduce-inode-refcount-to-prevent-use-af.patch -Patch0103: 0103-virtiofsd-do-not-always-set-FUSE_FLOCK_LOCKS.patch -Patch0104: 0104-virtiofsd-convert-more-fprintf-and-perror-to-use-fus.patch -Patch0105: 0105-virtiofsd-Reset-O_DIRECT-flag-during-file-open.patch -Patch0106: 0106-virtiofsd-Fix-data-corruption-with-O_APPEND-write-in.patch -Patch0107: 0107-virtiofsd-passthrough_ll-Use-cache_readdir-for-direc.patch -Patch0108: 0108-virtiofsd-add-definition-of-fuse_buf_writev.patch -Patch0109: 0109-virtiofsd-use-fuse_buf_writev-to-replace-fuse_buf_wr.patch -Patch0110: 0110-virtiofsd-process-requests-in-a-thread-pool.patch -Patch0111: 0111-virtiofsd-prevent-FUSE_INIT-FUSE_DESTROY-races.patch -Patch0112: 0112-virtiofsd-fix-lo_destroy-resource-leaks.patch -Patch0113: 0113-virtiofsd-add-thread-pool-size-NUM-option.patch -Patch0114: 0114-virtiofsd-Convert-lo_destroy-to-take-the-lo-mutex-lo.patch -Patch0115: 0115-virtiofsd-passthrough_ll-Pass-errno-to-fuse_reply_er.patch -Patch0116: 0116-virtiofsd-stop-all-queue-threads-on-exit-in-virtio_l.patch -Patch0117: 0117-virtiofsd-add-some-options-to-the-help-message.patch -# Fix segfault with SR-IOV hot-{plug,unplug} -Patch0118: 0118-vfio-pci-Don-t-remove-irqchip-notifier-if-not-regist.patch - -# Fix ppc shutdown issue (bz #1784961) -Patch0201: 0201-spapr-Don-t-trigger-a-CAS-reboot-for-XICS-XIVE-mode-.patch - # documentation deps BuildRequires: texinfo @@ -357,7 +231,6 @@ BuildRequires: lzo-devel BuildRequires: ncurses-devel # used by 9pfs BuildRequires: libattr-devel -BuildRequires: libcap-devel # used by qemu-bridge-helper and qemu-pr-helper BuildRequires: libcap-ng-devel # spice usb redirection support @@ -383,8 +256,6 @@ BuildRequires: systemtap-sdt-devel BuildRequires: libjpeg-devel # For VNC PNG support BuildRequires: libpng-devel -# For BlueZ device support -BuildRequires: bluez-libs-devel # For Braille device support BuildRequires: brlapi-devel # For FDT device tree support @@ -456,6 +327,10 @@ BuildRequires: perl-Test-Harness # Required for making python shebangs versioned BuildRequires: /usr/bin/pathfix.py BuildRequires: python3-devel +# qemu 5.0 liburing support +BuildRequires: liburing-devel +# qemu 5.0 zstd compression support +BuildRequires: libzstd-devel BuildRequires: glibc-static pcre-static glib2-static zlib-static @@ -1107,8 +982,8 @@ run_configure_disable_everything() { --disable-attr \ --disable-auth-pam \ --disable-avx2 \ + --disable-avx512f \ --disable-blobs \ - --disable-bluez \ --disable-bochs \ --disable-brlapi \ --disable-bsd-user \ @@ -1145,6 +1020,7 @@ run_configure_disable_everything() { --disable-libusb \ --disable-libxml2 \ --disable-linux-aio \ + --disable-linux-io-uring \ --disable-linux-user \ --disable-live-block-migration \ --disable-lzfse \ @@ -1202,6 +1078,7 @@ run_configure_disable_everything() { --disable-xen \ --disable-xen-pci-passthrough \ --disable-xfsctl \ + --disable-zstd \ --without-default-devices \ "$@" } @@ -1513,8 +1390,6 @@ getent passwd qemu >/dev/null || \ %files common -f %{name}.lang %dir %{qemudocdir} %doc %{qemudocdir}/Changelog -%doc %{qemudocdir}/qemu-doc.html -%doc %{qemudocdir}/qemu-doc.txt %doc %{qemudocdir}/qemu-ga-ref.html %doc %{qemudocdir}/qemu-ga-ref.txt %doc %{qemudocdir}/qemu-qmp-ref.html diff --git a/sources b/sources index 46350e1..68d740e 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (qemu-4.2.0.tar.xz) = 2a79973c2b07c53e8c57a808ea8add7b6b2cbca96488ed5d4b669ead8c9318907dec2b6109f180fc8ca8f04c0f73a56e82b3a527b5626b799d7e849f2474ec56 +SHA512 (qemu-5.0.0-rc0.tar.xz) = d61b11f51647bf70d7bddb4b4e790ab9d634729f8c87b74b51ee5f04a37527e234eab68775ccdc7e59f4ad999b514a1b725d78d63f54233798bdf65cb6493e2f From dd41f1a7ca546285adbf48c4e0ff0dfb0b7a9f15 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 25 Mar 2020 13:25:36 -0400 Subject: [PATCH 29/32] spec: Disable liburing, it's breaking the test suite Signed-off-by: Cole Robinson --- qemu.spec | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qemu.spec b/qemu.spec index a06407b..97883f9 100644 --- a/qemu.spec +++ b/qemu.spec @@ -1124,6 +1124,10 @@ run_configure \ %if 0%{?fedora} > 30 --enable-slirp=system \ %endif + --disable-linux-io-uring + +# uring temporarily disabled, it's breaking the test suite: +# https://lists.gnu.org/archive/html/qemu-block/2020-03/msg01395.html echo "config-host.mak contents:" echo "===" From 17655806bfafbc49d8aec9905b7a498e6fd561db Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 25 Mar 2020 14:14:43 -0400 Subject: [PATCH 30/32] liburing isn't available on %{arm} Signed-off-by: Cole Robinson --- qemu.spec | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qemu.spec b/qemu.spec index 97883f9..d7e5e92 100644 --- a/qemu.spec +++ b/qemu.spec @@ -327,8 +327,10 @@ BuildRequires: perl-Test-Harness # Required for making python shebangs versioned BuildRequires: /usr/bin/pathfix.py BuildRequires: python3-devel -# qemu 5.0 liburing support +%ifnarch %{arm} +# qemu 5.0 liburing support. Library isn't built for arm BuildRequires: liburing-devel +%endif # qemu 5.0 zstd compression support BuildRequires: libzstd-devel From e1b832b513dc9de7686791da5ca2e45e23c17342 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Wed, 25 Mar 2020 16:22:54 -0400 Subject: [PATCH 31/32] Add -rx emulator, install all new files Signed-off-by: Cole Robinson --- qemu.spec | 154 +++++++++++++++++------------------------------------- 1 file changed, 48 insertions(+), 106 deletions(-) diff --git a/qemu.spec b/qemu.spec index d7e5e92..b5d2040 100644 --- a/qemu.spec +++ b/qemu.spec @@ -359,6 +359,7 @@ Requires: %{name}-system-nios2 = %{epoch}:%{version}-%{release} Requires: %{name}-system-or1k = %{epoch}:%{version}-%{release} Requires: %{name}-system-ppc = %{epoch}:%{version}-%{release} Requires: %{name}-system-riscv = %{epoch}:%{version}-%{release} +Requires: %{name}-system-rx = %{epoch}:%{version}-%{release} Requires: %{name}-system-s390x = %{epoch}:%{version}-%{release} Requires: %{name}-system-sh4 = %{epoch}:%{version}-%{release} Requires: %{name}-system-sparc = %{epoch}:%{version}-%{release} @@ -803,6 +804,20 @@ Requires: %{name}-common = %{epoch}:%{version}-%{release} This package provides the QEMU system emulator for RISC-V systems. +%package system-rx +Summary: QEMU system emulator for RX +Requires: %{name}-system-rx-core = %{epoch}:%{version}-%{release} +%{requires_all_modules} +%description system-rx +This package provides the QEMU system emulator for RX systems. + +%package system-rx-core +Summary: QEMU system emulator for RX +Requires: %{name}-common = %{epoch}:%{version}-%{release} +%description system-rx-core +This package provides the QEMU system emulator for RX systems. + + %package system-s390x Summary: QEMU system emulator for S390 Requires: %{name}-system-s390x-core = %{epoch}:%{version}-%{release} @@ -1401,8 +1416,12 @@ getent passwd qemu >/dev/null || \ %doc %{qemudocdir}/qemu-qmp-ref.html %doc %{qemudocdir}/qemu-qmp-ref.txt %doc %{qemudocdir}/README.rst +%doc %{qemudocdir}/index.html %doc %{qemudocdir}/interop %doc %{qemudocdir}/specs +%doc %{qemudocdir}/system +%doc %{qemudocdir}/tools +%doc %{qemudocdir}/user %license %{qemudocdir}/COPYING %license %{qemudocdir}/COPYING.LIB %license %{qemudocdir}/LICENSE @@ -1442,6 +1461,7 @@ getent passwd qemu >/dev/null || \ %{_mandir}/man1/qemu.1* %{_mandir}/man1/qemu-trace-stap.1* %{_mandir}/man1/virtfs-proxy-helper.1* +%{_mandir}/man1/virtiofsd.1* %{_mandir}/man7/qemu-block-drivers.7* %{_mandir}/man7/qemu-cpu-models.7* %{_mandir}/man7/qemu-ga-ref.7* @@ -1450,6 +1470,7 @@ getent passwd qemu >/dev/null || \ %{_bindir}/qemu-edid %{_bindir}/qemu-keymap %{_bindir}/qemu-pr-helper +%{_bindir}/qemu-storage-daemon %{_bindir}/qemu-trace-stap %{_bindir}/virtfs-proxy-helper %{_unitdir}/qemu-pr-helper.service @@ -1574,111 +1595,26 @@ getent passwd qemu >/dev/null || \ %{_bindir}/qemu-xtensa %{_bindir}/qemu-xtensaeb -%{_datadir}/systemtap/tapset/qemu-i386.stp -%{_datadir}/systemtap/tapset/qemu-i386-log.stp -%{_datadir}/systemtap/tapset/qemu-i386-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-x86_64.stp -%{_datadir}/systemtap/tapset/qemu-x86_64-log.stp -%{_datadir}/systemtap/tapset/qemu-x86_64-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-aarch64.stp -%{_datadir}/systemtap/tapset/qemu-aarch64-log.stp -%{_datadir}/systemtap/tapset/qemu-aarch64-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-aarch64_be.stp -%{_datadir}/systemtap/tapset/qemu-aarch64_be-log.stp -%{_datadir}/systemtap/tapset/qemu-aarch64_be-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-alpha.stp -%{_datadir}/systemtap/tapset/qemu-alpha-log.stp -%{_datadir}/systemtap/tapset/qemu-alpha-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-arm.stp -%{_datadir}/systemtap/tapset/qemu-arm-log.stp -%{_datadir}/systemtap/tapset/qemu-arm-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-armeb.stp -%{_datadir}/systemtap/tapset/qemu-armeb-log.stp -%{_datadir}/systemtap/tapset/qemu-armeb-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-cris.stp -%{_datadir}/systemtap/tapset/qemu-cris-log.stp -%{_datadir}/systemtap/tapset/qemu-cris-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-hppa.stp -%{_datadir}/systemtap/tapset/qemu-hppa-log.stp -%{_datadir}/systemtap/tapset/qemu-hppa-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-m68k.stp -%{_datadir}/systemtap/tapset/qemu-m68k-log.stp -%{_datadir}/systemtap/tapset/qemu-m68k-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-microblaze.stp -%{_datadir}/systemtap/tapset/qemu-microblaze-log.stp -%{_datadir}/systemtap/tapset/qemu-microblaze-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-microblazeel.stp -%{_datadir}/systemtap/tapset/qemu-microblazeel-log.stp -%{_datadir}/systemtap/tapset/qemu-microblazeel-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-mips.stp -%{_datadir}/systemtap/tapset/qemu-mips-log.stp -%{_datadir}/systemtap/tapset/qemu-mips-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-mipsel.stp -%{_datadir}/systemtap/tapset/qemu-mipsel-log.stp -%{_datadir}/systemtap/tapset/qemu-mipsel-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-mips64.stp -%{_datadir}/systemtap/tapset/qemu-mips64-log.stp -%{_datadir}/systemtap/tapset/qemu-mips64-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-mips64el.stp -%{_datadir}/systemtap/tapset/qemu-mips64el-log.stp -%{_datadir}/systemtap/tapset/qemu-mips64el-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-mipsn32.stp -%{_datadir}/systemtap/tapset/qemu-mipsn32-log.stp -%{_datadir}/systemtap/tapset/qemu-mipsn32-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-mipsn32el.stp -%{_datadir}/systemtap/tapset/qemu-mipsn32el-log.stp -%{_datadir}/systemtap/tapset/qemu-mipsn32el-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-nios2.stp -%{_datadir}/systemtap/tapset/qemu-nios2-log.stp -%{_datadir}/systemtap/tapset/qemu-nios2-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-or1k.stp -%{_datadir}/systemtap/tapset/qemu-or1k-log.stp -%{_datadir}/systemtap/tapset/qemu-or1k-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-ppc.stp -%{_datadir}/systemtap/tapset/qemu-ppc-log.stp -%{_datadir}/systemtap/tapset/qemu-ppc-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-ppc64.stp -%{_datadir}/systemtap/tapset/qemu-ppc64-log.stp -%{_datadir}/systemtap/tapset/qemu-ppc64-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-ppc64abi32.stp -%{_datadir}/systemtap/tapset/qemu-ppc64abi32-log.stp -%{_datadir}/systemtap/tapset/qemu-ppc64abi32-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-ppc64le.stp -%{_datadir}/systemtap/tapset/qemu-ppc64le-log.stp -%{_datadir}/systemtap/tapset/qemu-ppc64le-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-riscv32.stp -%{_datadir}/systemtap/tapset/qemu-riscv32-log.stp -%{_datadir}/systemtap/tapset/qemu-riscv32-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-riscv64.stp -%{_datadir}/systemtap/tapset/qemu-riscv64-log.stp -%{_datadir}/systemtap/tapset/qemu-riscv64-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-s390x.stp -%{_datadir}/systemtap/tapset/qemu-s390x-log.stp -%{_datadir}/systemtap/tapset/qemu-s390x-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-sh4.stp -%{_datadir}/systemtap/tapset/qemu-sh4-log.stp -%{_datadir}/systemtap/tapset/qemu-sh4-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-sh4eb.stp -%{_datadir}/systemtap/tapset/qemu-sh4eb-log.stp -%{_datadir}/systemtap/tapset/qemu-sh4eb-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-sparc.stp -%{_datadir}/systemtap/tapset/qemu-sparc-log.stp -%{_datadir}/systemtap/tapset/qemu-sparc-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-sparc32plus.stp -%{_datadir}/systemtap/tapset/qemu-sparc32plus-log.stp -%{_datadir}/systemtap/tapset/qemu-sparc32plus-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-sparc64.stp -%{_datadir}/systemtap/tapset/qemu-sparc64-log.stp -%{_datadir}/systemtap/tapset/qemu-sparc64-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-tilegx.stp -%{_datadir}/systemtap/tapset/qemu-tilegx-log.stp -%{_datadir}/systemtap/tapset/qemu-tilegx-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-xtensa.stp -%{_datadir}/systemtap/tapset/qemu-xtensa-log.stp -%{_datadir}/systemtap/tapset/qemu-xtensa-simpletrace.stp -%{_datadir}/systemtap/tapset/qemu-xtensaeb.stp -%{_datadir}/systemtap/tapset/qemu-xtensaeb-log.stp -%{_datadir}/systemtap/tapset/qemu-xtensaeb-simpletrace.stp +%{_datadir}/systemtap/tapset/qemu-i386*.stp +%{_datadir}/systemtap/tapset/qemu-x86_64*.stp +%{_datadir}/systemtap/tapset/qemu-aarch64*.stp +%{_datadir}/systemtap/tapset/qemu-alpha*.stp +%{_datadir}/systemtap/tapset/qemu-arm*.stp +%{_datadir}/systemtap/tapset/qemu-cris*.stp +%{_datadir}/systemtap/tapset/qemu-hppa*.stp +%{_datadir}/systemtap/tapset/qemu-m68k*.stp +%{_datadir}/systemtap/tapset/qemu-microblaze*.stp +%{_datadir}/systemtap/tapset/qemu-mips*.stp +%{_datadir}/systemtap/tapset/qemu-nios2*.stp +%{_datadir}/systemtap/tapset/qemu-or1k*.stp +%{_datadir}/systemtap/tapset/qemu-ppc*.stp +%{_datadir}/systemtap/tapset/qemu-riscv*.stp +%{_datadir}/systemtap/tapset/qemu-s390x*.stp +%{_datadir}/systemtap/tapset/qemu-sh4*.stp +%{_datadir}/systemtap/tapset/qemu-sparc*.stp +%{_datadir}/systemtap/tapset/qemu-tilegx*.stp +%{_datadir}/systemtap/tapset/qemu-xtensa*.stp + %files user-binfmt %{_exec_prefix}/lib/binfmt.d/qemu-*-dynamic.conf @@ -1797,7 +1733,6 @@ getent passwd qemu >/dev/null || \ %{_mandir}/man1/qemu-system-ppc64.1* %{_datadir}/%{name}/bamboo.dtb %{_datadir}/%{name}/canyonlands.dtb -%{_datadir}/%{name}/ppc_rom.bin %{_datadir}/%{name}/qemu_vga.ndrv %{_datadir}/%{name}/skiboot.lid %{_datadir}/%{name}/u-boot.e500 @@ -1816,6 +1751,13 @@ getent passwd qemu >/dev/null || \ %{_mandir}/man1/qemu-system-riscv*.1* +%files system-rx +%files system-rx-core +%{_bindir}/qemu-system-rx +%{_datadir}/systemtap/tapset/qemu-system-rx*.stp +%{_mandir}/man1/qemu-system-rx.1* + + %files system-s390x %files system-s390x-core %{_bindir}/qemu-system-s390x From e2b4e80d3c89b937ab112311b8a37d4a2a1f4e67 Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Tue, 7 Apr 2020 17:30:05 -0700 Subject: [PATCH 32/32] Rebuild for new brltty Also add missing %changelog entry for 5.0.0 bump --- qemu.spec | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/qemu.spec b/qemu.spec index b5d2040..1d1b3c6 100644 --- a/qemu.spec +++ b/qemu.spec @@ -161,7 +161,7 @@ Summary: QEMU is a FAST! processor emulator Name: qemu Version: 5.0.0 -Release: 0.1%{?rcrel}%{?dist} +Release: 0.2%{?rcrel}%{?dist} Epoch: 2 License: GPLv2 and BSD and MIT and CC-BY URL: http://www.qemu.org/ @@ -1835,6 +1835,12 @@ getent passwd qemu >/dev/null || \ %changelog +* Wed Apr 08 2020 Adam Williamson - 2:5.0.0-0.2.rc0 +- Rebuild for new brltty + +* Wed Mar 25 2020 Cole Robinson - 2:5.0.0-0.1.rc0 +- Update to qemu-5.0.0-rc0 + * Tue Mar 17 2020 Fabiano Fidêncio - 2:4.2.0-7 - Fix segfault with SR-IOV hot-{plug,unplug} (bz #1814017)