ext4: fix hole length detection in ext4_ind_map_blocks()
authorJan Kara <jack@suse.cz>
Sat, 12 May 2018 23:55:00 +0000 (19:55 -0400)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 26 Jun 2018 00:08:07 +0000 (08:08 +0800)
commit 2ee3ee06a8fd792765fa3267ddf928997797eec5 upstream.

When ext4_ind_map_blocks() computes a length of a hole, it doesn't count
with the fact that mapped offset may be somewhere in the middle of the
completely empty subtree. In such case it will return too large length
of the hole which then results in lseek(SEEK_DATA) to end up returning
an incorrect offset beyond the end of the hole.

Fix the problem by correctly taking offset within a subtree into account
when computing a length of a hole.

Fixes: facab4d9711e7aa3532cb82643803e8f1b9518e8
CC: stable@vger.kernel.org
Reported-by: Jeff Mahoney <jeffm@suse.com>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/ext4/indirect.c

index bc15c2c17633079a54de855baf1272b0124f19eb..58229c1b4a3d752c26139621c9538db9ef9613d8 100644 (file)
@@ -560,10 +560,16 @@ int ext4_ind_map_blocks(handle_t *handle, struct inode *inode,
                unsigned epb = inode->i_sb->s_blocksize / sizeof(u32);
                int i;
 
-               /* Count number blocks in a subtree under 'partial' */
-               count = 1;
-               for (i = 0; partial + i != chain + depth - 1; i++)
-                       count *= epb;
+               /*
+                * Count number blocks in a subtree under 'partial'. At each
+                * level we count number of complete empty subtrees beyond
+                * current offset and then descend into the subtree only
+                * partially beyond current offset.
+                */
+               count = 0;
+               for (i = partial - chain + 1; i < depth; i++)
+                       count = count * epb + (epb - offsets[i] - 1);
+               count++;
                /* Fill in size of a hole we found */
                map->m_pblk = 0;
                map->m_len = min_t(unsigned int, map->m_len, count);