btrfs-progs: convert: properly work with large ext4 filesystems

On large (blockcount > 32bit) filesystems reading directly
super_block->s_blocks_count is not sufficient as the block count is held
in 2 separate 32 bit variables. Instead always use the provided
ext2fs_blocks_count to read the value. This can result in assertion
failure, when the block count is only held in the high 32 bits, in this
case s_block_counts would be zero, which would result in
btrfs_convert_context::block_count/total_bytes to also be 0 and hit an
assertion failure:

    convert/main.c:1162: do_convert: Assertion `cctx.total_bytes != 0` failed, value 0
    btrfs-convert(+0xffb0)[0x557defdabfb0]
    btrfs-convert(main+0x6c5)[0x557defdaa125]
    /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xea)[0x7f66e1f8bd0a]
    btrfs-convert(_start+0x2a)[0x557defdab52a]
    Aborted

What's worse it can also result in btrfs-convert mistakenly thinking
that a filesystem is smaller than it actually is (ignoring the top 32 bits).

Link: https://lore.kernel.org/linux-btrfs/023b5ca9-0610-231b-fc4e-a72fe1377a5a@jansson.tech/
Signed-off-by: Nikolay Borisov <nborisov@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Nikolay Borisov 2022-06-07 13:24:21 +03:00 committed by David Sterba
parent 6f4380a95e
commit 5110ad88cb
2 changed files with 3 additions and 5 deletions

View File

@ -1134,7 +1134,6 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize,
int ret; int ret;
int fd = -1; int fd = -1;
u32 blocksize; u32 blocksize;
u64 total_bytes;
struct btrfs_root *root; struct btrfs_root *root;
struct btrfs_root *image_root; struct btrfs_root *image_root;
struct btrfs_convert_context cctx; struct btrfs_convert_context cctx;
@ -1161,7 +1160,6 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize,
ASSERT(cctx.total_bytes != 0); ASSERT(cctx.total_bytes != 0);
blocksize = cctx.blocksize; blocksize = cctx.blocksize;
total_bytes = (u64)blocksize * (u64)cctx.block_count;
if (blocksize < 4096) { if (blocksize < 4096) {
error("block size is too small: %u < 4096", blocksize); error("block size is too small: %u < 4096", blocksize);
goto fail; goto fail;
@ -1223,7 +1221,7 @@ static int do_convert(const char *devname, u32 convert_flags, u32 nodesize,
mkfs_cfg.csum_type = csum_type; mkfs_cfg.csum_type = csum_type;
mkfs_cfg.label = cctx.label; mkfs_cfg.label = cctx.label;
mkfs_cfg.num_bytes = total_bytes; mkfs_cfg.num_bytes = cctx.total_bytes;
mkfs_cfg.nodesize = nodesize; mkfs_cfg.nodesize = nodesize;
mkfs_cfg.sectorsize = blocksize; mkfs_cfg.sectorsize = blocksize;
mkfs_cfg.stripesize = blocksize; mkfs_cfg.stripesize = blocksize;

View File

@ -92,8 +92,8 @@ static int ext2_open_fs(struct btrfs_convert_context *cctx, const char *name)
cctx->fs_data = ext2_fs; cctx->fs_data = ext2_fs;
cctx->blocksize = ext2_fs->blocksize; cctx->blocksize = ext2_fs->blocksize;
cctx->block_count = ext2_fs->super->s_blocks_count; cctx->block_count = ext2fs_blocks_count(ext2_fs->super);
cctx->total_bytes = (u64)ext2_fs->super->s_blocks_count * ext2_fs->blocksize; cctx->total_bytes = cctx->block_count * cctx->blocksize;
cctx->label = strndup((char *)ext2_fs->super->s_volume_name, 16); cctx->label = strndup((char *)ext2_fs->super->s_volume_name, 16);
cctx->first_data_block = ext2_fs->super->s_first_data_block; cctx->first_data_block = ext2_fs->super->s_first_data_block;
cctx->inodes_count = ext2_fs->super->s_inodes_count; cctx->inodes_count = ext2_fs->super->s_inodes_count;