---
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