]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
iomap: account for unaligned end offsets when truncating read range
authorJoanne Koong <joannelkoong@gmail.com>
Tue, 11 Nov 2025 19:36:51 +0000 (11:36 -0800)
committerChristian Brauner <brauner@kernel.org>
Wed, 12 Nov 2025 09:50:31 +0000 (10:50 +0100)
The end position to start truncating from may be at an offset into a
block, which under the current logic would result in overtruncation.

Adjust the calculation to account for unaligned end offsets.

Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
Link: https://patch.msgid.link/20251111193658.3495942-3-joannelkoong@gmail.com
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/iomap/buffered-io.c

index 7dcb8bbc948483c1cc1293300f644289aa0a6868..0eb439b523b14c6b829cb19db6dfba9bfff7c5b3 100644 (file)
@@ -218,6 +218,22 @@ static void ifs_free(struct folio *folio)
        kfree(ifs);
 }
 
+/*
+ * Calculate how many bytes to truncate based off the number of blocks to
+ * truncate and the end position to start truncating from.
+ */
+static size_t iomap_bytes_to_truncate(loff_t end_pos, unsigned block_bits,
+               unsigned blocks_truncated)
+{
+       unsigned block_size = 1 << block_bits;
+       unsigned block_offset = end_pos & (block_size - 1);
+
+       if (!block_offset)
+               return blocks_truncated << block_bits;
+
+       return ((blocks_truncated - 1) << block_bits) + block_offset;
+}
+
 /*
  * Calculate the range inside the folio that we actually need to read.
  */
@@ -263,7 +279,8 @@ static void iomap_adjust_read_range(struct inode *inode, struct folio *folio,
                /* truncate len if we find any trailing uptodate block(s) */
                while (++i <= last) {
                        if (ifs_block_is_uptodate(ifs, i)) {
-                               plen -= (last - i + 1) * block_size;
+                               plen -= iomap_bytes_to_truncate(*pos + plen,
+                                               block_bits, last - i + 1);
                                last = i - 1;
                                break;
                        }
@@ -279,7 +296,8 @@ static void iomap_adjust_read_range(struct inode *inode, struct folio *folio,
                unsigned end = offset_in_folio(folio, isize - 1) >> block_bits;
 
                if (first <= end && last > end)
-                       plen -= (last - end) * block_size;
+                       plen -= iomap_bytes_to_truncate(*pos + plen, block_bits,
+                                       last - end);
        }
 
        *offp = poff;