When programs like resize2fs or e2fsck relocates all of the blocks in
an extent one at a time, the ext2fs_extent_set_bmap() works by
initially adding a new extent and then moving mapping from the old
extent to the new extent. For example:
t=1 EXTENTS: (0-2) 1152-1154
t=2 EXTENTS: (0) 1136, (1-2) 1153-1154
t=3 EXTENTS: (0-1) 1136-1137, (2) 1154
Unfortunately, previously, when the last block is updated, the
resulting extent tree will have two extents instead of one, like this:
t=4 EXTENTS: (0-1) 1136-1137, (2) 1138
With this commit, the resulting extent tree will be more optimally
represented with a single extent:
t=4 EXTENTS: (0-2) 1136-1138
The optimization in this commit solves the prolem reproted at:
https://github.com/tytso/e2fsprogs/issues/146
In that case, the file had a very large, complex (fragmented) extent
tree, and resize2fs needed to relcate all of its blocks as part of a
off-line shrink, the lack of the optimization led to an extent block
overflowing, resulting in the old extent (the one which originally
mapped logical block 2507128 to physical block
389065080) and the new
extent landing in two different leaf blocks:
2/ 2 1/ 1 2507128 - 2507128 640097 - 640097 1
2/ 2 1/135 2507128 - 2507128
389065080 -
389065080 1
This resulted a corrupted extent tree block and data loss.
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
printf("(re/un)mapping only block in extent\n");
#endif
if (physical) {
- retval = ext2fs_extent_replace(handle, 0, &newextent);
+ if (has_prev &&
+ (logical == (prev_extent.e_lblk +
+ prev_extent.e_len)) &&
+ (physical == (prev_extent.e_pblk +
+ prev_extent.e_len)) &&
+ (new_uninit == prev_uninit) &&
+ ((int) prev_extent.e_len < max_len-1)) {
+ retval = ext2fs_extent_get(handle,
+ EXT2_EXTENT_PREV_LEAF, &prev_extent);
+ if (retval)
+ goto done;
+ prev_extent.e_len++;
+ retval = ext2fs_extent_replace(handle, 0,
+ &prev_extent);
+ retval = ext2fs_extent_get(handle,
+ EXT2_EXTENT_NEXT_LEAF,
+ &extent);
+ if (retval)
+ goto done;
+ goto delete_node;
+
+ } else
+ retval = ext2fs_extent_replace(handle, 0, &newextent);
} else {
+ delete_node:
retval = ext2fs_extent_delete(handle, 0);
if (retval)
goto done;
atime: 0x539ff5b2 -- Tue Jun 17 08:00:50 2014
mtime: 0x539ff5b2 -- Tue Jun 17 08:00:50 2014
EXTENTS:
-(0-1):1136-1137, (2):1138
+(0-2):1136-1138
debugfs: stat /b
Inode: 13 Type: regular Mode: 0644 Flags: 0x80000
Generation: 1117152158 Version: 0x00000001
atime: 0x539ff5b2 -- Tue Jun 17 08:00:50 2014
mtime: 0x539ff5b2 -- Tue Jun 17 08:00:50 2014
EXTENTS:
-(0-1):1216-1217, (2):1218
+(0-2):1216-1218
debugfs: stat /d
Inode: 15 Type: regular Mode: 0644 Flags: 0x80000
Generation: 1117152160 Version: 0x00000001
atime: 0x539ff5b2 -- Tue Jun 17 08:00:50 2014
mtime: 0x539ff5b2 -- Tue Jun 17 08:00:50 2014
EXTENTS:
-(0):1232, (1):1233, (2):1234
+(0-2):1232-1234
debugfs: stat /g
Inode: 18 Type: regular Mode: 0644 Flags: 0x80000
Generation: 1117152163 Version: 0x00000001
atime: 0x539ff5b2 -- Tue Jun 17 08:00:50 2014
mtime: 0x539ff5b2 -- Tue Jun 17 08:00:50 2014
EXTENTS:
-(0-2):1680-1682, (3):1683
+(0-3):1680-1683
debugfs: quit