I think I know what could be going wrong here.
So the problem happens when we have overlayfs mounted on top of ext4.
Now overlayfs might be supporting max logical filesize which is more
than what ext4 could support (i.e. sb->s_maxbytes for overlayfs must
be greater than compared to ext4). So that's why the check in func
ioctl_fiemap -> fiemap_check_ranges() couldn't truncate to logical
filesize which the actual underlying filesystem supports.
@All,
Do you think we should make overlayfs also check for
fiemap_check_ranges()? Not as part of this fix, but as a later
addition to overlayfs? Please let me know, I could also make that patch.
Now coming back to ext4. I guess since the min_t() is returning
EXT4_MAX_LOGICAL_BLOCK as the min value among the two. That then
followed by +1 is resulting into a overflow of unsigned int and it is
becoming 0. Hence the warning in iomap_apply of iomap.length == 0.
Note (there are 2 points mentioned below). Please check both.
1. I think below diff should fix this reported problem. Do you agree?
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index e416096fc081..d630ec7a9c8e 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3424,6 +3424,7 @@ static int ext4_iomap_begin(struct inode *inode,
loff_t offset, loff_t length,
int ret;
struct ext4_map_blocks map;
u8 blkbits = inode->i_blkbits;
+ loff_t len;
if ((offset >> blkbits) > EXT4_MAX_LOGICAL_BLOCK)
return -EINVAL;
@@ -3435,8 +3436,11 @@ static int ext4_iomap_begin(struct inode *inode,
loff_t offset, loff_t length,
* Calculate the first and last logical blocks respectively.
*/
map.m_lblk = offset >> blkbits;
- map.m_len = min_t(loff_t, (offset + length - 1) >> blkbits,
+ len = min_t(loff_t, (offset + length - 1) >> blkbits,
EXT4_MAX_LOGICAL_BLOCK) - map.m_lblk + 1;
+ if (len > EXT4_MAX_LOGICAL_BLOCK)
+ len = EXT4_MAX_LOGICAL_BLOCK;
+ map.m_len = len;
if (flags & IOMAP_WRITE)
ret = ext4_iomap_alloc(inode, &map, flags);
@@ -3524,6 +3528,7 @@ static int ext4_iomap_begin_report(struct inode
*inode, loff_t offset,
bool delalloc = false;
struct ext4_map_blocks map;
u8 blkbits = inode->i_blkbits;
+ loff_t len
if ((offset >> blkbits) > EXT4_MAX_LOGICAL_BLOCK)
return -EINVAL;
@@ -3541,8 +3546,11 @@ static int ext4_iomap_begin_report(struct inode
*inode, loff_t offset,
* Calculate the first and last logical block respectively.
*/
map.m_lblk = offset >> blkbits;
- map.m_len = min_t(loff_t, (offset + length - 1) >> blkbits,
+ len = min_t(loff_t, (offset + length - 1) >> blkbits,
EXT4_MAX_LOGICAL_BLOCK) - map.m_lblk + 1;
+ if (len > EXT4_MAX_LOGICAL_BLOCK)
+ len = EXT4_MAX_LOGICAL_BLOCK;
+ map.m_len = len;
/*
* Fiemap callers may call for offset beyond s_bitmap_maxbytes.
2. One other discrepancy which I noted is, in function
ext4_iomap_begin_** v/s ext4_map_blocks().
In ext4_iomap_begin_** we check, if offset(in terms of blocksize units)
is greater than EXT4_MAX_LOGICAL_BLOCK, if yes, then return -EINVAL.
Whereas in function ext4_map_blocks() we check, if offset is greater
then equal to EXT_MAX_BLOCKS, if yes, then return -EFSCORRUPTED.
Now both EXT_MAX_BLOCKS and EXT4_MAX_LOGICAL_BLOCK are same.
So if actually offset == EXT4_MAX_LOGICAL_BLOCK then we end up
returning -EFSCORRUPTED. Which do you also think is wrong?
The request may come to map just the last logical block of file
which is EXT4_MAX_LOGICAL_BLOCK, no?
The history of the change in ext4_map_blocks for checking EXT_MAX_BLOCKS
goes back to this patch.
https://lore.kernel.org/patchwork/patch/461641/
I will have to read more about it and see all the references
of EXT_MAX_BLOCKS to tell why the discrepancy. But if someone
already knows about this, please let me know.
But the diff mentioned in point 1 above should fix the problem
reported at hand. I can address this 2nd point once I go and look
at all references of EXT_MAX_BLOCKS. But nevertheless,
I wanted to make sure I this is logged in this mail.
-ritesh