From 7e70d6939d848e6c9000444d162ff6e907ae4534 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Tue, 3 Nov 2020 14:11:01 -0800 Subject: [PATCH] Correctly calculate tot_used_blocks on ext4 with uninit_bg The calculated number of blocks to encrypt is too high on ext4 filesystems that have the uninit_bg feature. This is because the calculation assumes that all blocks not counted in bg_free_blocks_count need to encrypted. But actually, uninitialized block groups have inode blocks which vold doesn't encrypt since they are uninitialized, but they are "allocated" and thus reduce bg_free_blocks_count. Therefore, add a helper function num_base_meta_blocks_in_group() which returns the number of blocks to encrypt in an uninitialized block group. Use it both for the encryption and for calculating 'tot_used_blocks'. Also compute 'tot_used_blocks' additively rather than subtractively, as this is easier to understand. Test: see I08fc8465f7962abd698904b5466f3ed080d53953 Change-Id: I4d2cb40291da67dd1bafd61289ccb9e6343bfda3 --- EncryptInplace.cpp | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/EncryptInplace.cpp b/EncryptInplace.cpp index eb7d4c3..471daef 100644 --- a/EncryptInplace.cpp +++ b/EncryptInplace.cpp @@ -155,6 +155,22 @@ static int flush_outstanding_data(struct encryptGroupsData* data) { return 0; } +static uint64_t first_block_in_group(uint32_t group) { + return aux_info.first_data_block + (group * (uint64_t)info.blocks_per_group); +} + +static uint32_t num_blocks_in_group(uint32_t group) { + uint64_t remaining = aux_info.len_blocks - first_block_in_group(group); + return std::min(info.blocks_per_group, remaining); +} + +// In block groups with an uninitialized block bitmap, we only need to encrypt +// the backup superblock and the block group descriptors (if they are present). +static uint32_t num_base_meta_blocks_in_group(uint64_t group) { + if (!ext4_bg_has_super_block(group)) return 0; + return 1 + aux_info.bg_desc_blocks; +} + static int encrypt_groups(struct encryptGroupsData* data) { unsigned int i; u8* block_bitmap = 0; @@ -177,8 +193,7 @@ static int encrypt_groups(struct encryptGroupsData* data) { for (i = 0; i < aux_info.groups; ++i) { LOG(INFO) << "Encrypting group " << i; - u32 first_block = aux_info.first_data_block + i * info.blocks_per_group; - u32 block_count = std::min(info.blocks_per_group, (u32)(aux_info.len_blocks - first_block)); + u32 block_count = num_blocks_in_group(i); off64_t offset = (u64)info.block_size * aux_info.bg_desc[i].bg_block_bitmap; @@ -188,20 +203,17 @@ static int encrypt_groups(struct encryptGroupsData* data) { goto errout; } - offset = (u64)info.block_size * first_block; + offset = (u64)info.block_size * first_block_in_group(i); data->count = 0; for (block = 0; block < block_count; block++) { int used; - if (aux_info.bg_desc[i].bg_flags & EXT4_BG_BLOCK_UNINIT) { - // In block groups with an uninitialized block bitmap, we only - // need to encrypt the backup superblock (if one is present). - used = (ext4_bg_has_super_block(i) && block < 1 + aux_info.bg_desc_blocks); - } else { + if (aux_info.bg_desc[i].bg_flags & EXT4_BG_BLOCK_UNINIT) + used = (block < num_base_meta_blocks_in_group(i)); + else used = bitmap_get_bit(block_bitmap, block); - } update_progress(data, used); if (used) { @@ -282,9 +294,13 @@ static int cryptfs_enable_inplace_ext4(const char* crypto_blkdev, const char* re LOG(INFO) << "Encrypting ext4 filesystem in place..."; - data.tot_used_blocks = size / CRYPT_SECTORS_PER_BUFSIZE; + data.tot_used_blocks = 0; for (i = 0; i < aux_info.groups; ++i) { - data.tot_used_blocks -= aux_info.bg_desc[i].bg_free_blocks_count; + if (aux_info.bg_desc[i].bg_flags & EXT4_BG_BLOCK_UNINIT) + data.tot_used_blocks += num_base_meta_blocks_in_group(i); + else + data.tot_used_blocks += + (num_blocks_in_group(i) - aux_info.bg_desc[i].bg_free_blocks_count); } data.one_pct = data.tot_used_blocks / 100;