When mkfs.ext2 with '-b 65536' and mount(arm 64KB page size), function mount_fs will trigger WARNING since ext2_max_size will return value less than 0. Also, we cannot write any file in this fs since the sb->maxbytes is less than 0. Fix it by get the exact max file size. First, get the max depth for indirect blocks and check does the max data blocks add indirect blocks will execced upper_limit. If right, bisect to get exact data blocks number which satisfy 'data blocks number + indirect blocks number == upper_limit'. Signed-off-by: yangerkun <yangerkun@xxxxxxxxxx> --- fs/ext2/super.c | 115 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 92 insertions(+), 23 deletions(-) diff --git a/fs/ext2/super.c b/fs/ext2/super.c index 73b2d52..b3eb6e9 100644 --- a/fs/ext2/super.c +++ b/fs/ext2/super.c @@ -753,11 +753,16 @@ static int ext2_check_descriptors(struct super_block *sb) * block limit, and also a limit of (2^32 - 1) 512-byte sectors in i_blocks. * We need to be 1 filesystem block less than the 2^32 sector limit. */ -static loff_t ext2_max_size(int bits) +static loff_t ext2_max_size(struct super_block *sb, int bits) { - loff_t res = EXT2_NDIR_BLOCKS; - int meta_blocks; + loff_t res; + loff_t max_meta_blocks; + loff_t used_data_blocks; + loff_t max_data_blocks; loff_t upper_limit; + int depth; + loff_t high, low; + /* This is calculated to be the largest file size for a * dense, file such that the total number of @@ -771,24 +776,88 @@ static loff_t ext2_max_size(int bits) /* total blocks in file system block size */ upper_limit >>= (bits - 9); + /* Try to get max depth of metadata blocks */ + depth = 0; + max_meta_blocks = 0; + used_data_blocks = 0; + max_data_blocks = EXT2_NDIR_BLOCKS; + if (max_meta_blocks + max_data_blocks > upper_limit) + goto bisect; + + depth++; + max_meta_blocks = 1; + used_data_blocks = max_data_blocks; + max_data_blocks += 1LL << (bits - 2); + if (max_meta_blocks + max_data_blocks > upper_limit) + goto bisect; + + depth++; + max_meta_blocks += 1 + (1LL << (bits - 2)); + used_data_blocks = max_data_blocks; + max_data_blocks += 1LL << (2 * (bits - 2)); + if (max_meta_blocks + max_data_blocks > upper_limit) + goto bisect; + + depth++; + max_meta_blocks += 1 + (1LL << (bits - 2)) + (1LL << (2 * (bits - 2))); + used_data_blocks = max_data_blocks; + max_data_blocks += 1LL << (3 * (bits - 2)); + if (max_meta_blocks + max_data_blocks > upper_limit) + goto bisect; + + goto out; +bisect: + low = 0; + if (depth == 0) + high = EXT2_NDIR_BLOCKS; + else + high = 1 << (depth * (bits - 2)); + while (low <= high) { + int offsets[4]; + loff_t mid = (low + high) >> 1; + depth = ext2_block_to_path(sb, mid + used_data_blocks - 1, offsets, NULL); + if (!depth) + return -EIO; + + max_meta_blocks = 0; + if (depth == 1) + max_meta_blocks = 0; + + if (depth == 2) + max_meta_blocks = 1; + + if (depth == 3) { + /* Indirect blocks */ + max_meta_blocks += 1; + /* Double indirect blocks */ + max_meta_blocks += 1; + max_meta_blocks += offsets[1]; + } - /* indirect blocks */ - meta_blocks = 1; - /* double indirect blocks */ - meta_blocks += 1 + (1LL << (bits-2)); - /* tripple indirect blocks */ - meta_blocks += 1 + (1LL << (bits-2)) + (1LL << (2*(bits-2))); - - upper_limit -= meta_blocks; - upper_limit <<= bits; - - res += 1LL << (bits-2); - res += 1LL << (2*(bits-2)); - res += 1LL << (3*(bits-2)); - res <<= bits; - if (res > upper_limit) - res = upper_limit; - + if (depth == 4) { + /* Indirect blocks */ + max_meta_blocks += 1; + /* Double indirect blocks */ + max_meta_blocks += 1 + (1 << (bits - 2)); + /* Tripple indirect blocks */ + max_meta_blocks += 1; + if (offsets[1]) + max_meta_blocks += (offsets[1] - 1) * (1 << (bits - 2)); + + max_meta_blocks += 1; + max_meta_blocks += offsets[2]; + } + if (max_meta_blocks + mid + used_data_blocks > upper_limit) + high = mid - 1; + if (max_meta_blocks + mid + used_data_blocks < upper_limit) + low = mid + 1; + if (max_meta_blocks + mid + used_data_blocks == upper_limit) { + max_data_blocks = mid + used_data_blocks; + break; + } + } +out: + res = max_data_blocks << bits; if (res > MAX_LFS_FILESIZE) res = MAX_LFS_FILESIZE; @@ -995,7 +1064,9 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) } } - sb->s_maxbytes = ext2_max_size(sb->s_blocksize_bits); + sbi->s_addr_per_block_bits = + ilog2 (EXT2_ADDR_PER_BLOCK(sb)); + sb->s_maxbytes = ext2_max_size(sb, sb->s_blocksize_bits); sb->s_max_links = EXT2_LINK_MAX; if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) { @@ -1035,8 +1106,6 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent) sizeof (struct ext2_group_desc); sbi->s_sbh = bh; sbi->s_mount_state = le16_to_cpu(es->s_state); - sbi->s_addr_per_block_bits = - ilog2 (EXT2_ADDR_PER_BLOCK(sb)); sbi->s_desc_per_block_bits = ilog2 (EXT2_DESC_PER_BLOCK(sb)); -- 2.9.5