]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commit
fuse2fs: fix punch-out range calculation in fuse2fs_punch_range
authorDarrick J. Wong <djwong@kernel.org>
Thu, 31 Jul 2025 14:45:50 +0000 (10:45 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 31 Jul 2025 14:45:50 +0000 (10:45 -0400)
commite18b350af2d77f1e063ad9ae765dd161022bb04a
treeb42e78bfe5fada536f43ae9990de5c0eec09f9e4
parent9b44c01c1f9d800a56bb7a01a53e4f318c08d9f2
fuse2fs: fix punch-out range calculation in fuse2fs_punch_range

In non-iomap mode, generic/008 tries to fzero the first byte of a block
and instead zeroes the entire file:

--- a/tests/generic/008.out      2025-07-15 14:45:14.937058680 -0700
+++ b/tests/generic/008.out.bad        2025-07-16 13:31:03.909315508 -0700
@@ -4,10 +4,7 @@
 XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
 wrote 1024/1024 bytes at offset 1024
 XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
-00000000:  00 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41  .AAAAAAAAAAAAAAA
-00000010:  41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41  AAAAAAAAAAAAAAAA
-*
-00000400:  42 42 42 42 42 42 42 42 42 42 42 42 42 42 42 42  BBBBBBBBBBBBBBBB
+00000000:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
 *
 read 2048/2048 bytes at offset 0
 XXX Bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)

Looking at the fuse2fs debugging output, the reason why is obvious:

FUSE2FS (sda): op_fallocate: ino=50 mode=0x10 start=0x0 end=0x1
FUSE2FS (sda): fuse2fs_punch_range: ino=50 mode=0x11 offset=0x0 len=0x1 start=0 end=0

start and end are both zero, so we call ext2fs_punch with those
arguments.  ext2fs_punch interprets [start, end] as a closed interval
and removes block 0, which is not what we asked for!

The computation of end is also too subtle -- the dividend is the
expression (0 + 1 - 4096) which produces a negative number because off_t
is defined to be long long, at least on amd64 Linux.  We rely on the
behavior that dividing a negative dividend by a positive divisor
produces a quotient of zero.

Really what we should do here is round offset up to the next fsblock
and offset+len down to the nearest fsblock.  The first quantity is the
first byte of the range to punch and the second quantity is the next
byte past the range to punch.  Using those as the basis to compute start
and end, the punch should only happen if start < end, and we should pass
[start, end - 1] to ext2fs_punch because it expects a closed interval.

Improve the comments here so that I don't have to work all this out
again the next time I read through here.

Cc: linux-ext4@vger.kernel.org # v1.43
Fixes: 81cbf1ef4f5dab ("misc: add fuse2fs, a FUSE server for e2fsprogs")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
misc/fuse2fs.c