[Patch] ext2/3 file limits to avoid overflowing i_blocks

From: Stephen C. Tweedie
Date: Thu Mar 17 2005 - 12:26:03 EST


Hi all,

As discussed before, we can overflow i_blocks in ext2/ext3 inodes by
growing a file up to 2TB. That gives us 2^32 sectors of data in the
file; but once you add on the indirect tree and possible EA/ACL
metadata, i_blocks will wrap beyond 2^32. Consensus seemed to be that
the best way to avoid this was simply to stop files getting so large
that this was a problem in the first place; anything else would lead to
complications if a sparse file tried to overflow that 2^32 sector limit
while filling in holes.

I wrote a small program to calculate the total indirect tree overhead
for any given file size, and 0x1ff7fffe000 turned out to be the largest
file we can get without the total i_blocks overflowing 2^32.

But in testing, that *just* wrapped --- we need to limit the file to be
one page smaller than that to deal with the possibility of an EA/ACL
block being accounted against i_blocks.

So this patch has been tested, at least on ext3, by letting a file grow
densely to its maximum size permitted by the kernel; at 0x1ff7fffe000,
stat shows the file to have wrapped back exactly to 0 st_blocks, but
with the limit at 0x1ff7fffd000, du shows it occupying the expected
2TB-blocksize bytes.

Cheers,
Stephen

[Patch] ext2/3 file limits to avoid overflowing i_blocks

ext2/3 can currently overflow i_blocks past the maximum 2^32 sector
(2TB) limit. The ext[23]_max_size functions try to enforce an upper
limit of 2^32-1 blocks by limiting the file size to 2TB-blocksize, but
this fails to account for indirect block metadata and the possibility of
an extended attribute block also being accounted against i_blocks.

The new file size limit of 0x1ff7fffd000 allows a file to grow to
2^32-blocksize sectors, assuming 4k blocksize. (We don't have to worry
about smaller blocksizes as those have indirect tree limits that don't
let their size grow near this upper bound in the first place.)

Signed-off-by: Stephen Tweedie <sct@xxxxxxxxxx>

--- linux-2.6-ext3/fs/ext2/super.c.=K0001=.orig
+++ linux-2.6-ext3/fs/ext2/super.c
@@ -518,12 +518,18 @@ static int ext2_check_descriptors (struc
static loff_t ext2_max_size(int bits)
{
loff_t res = EXT2_NDIR_BLOCKS;
+ /* This constant is calculated to be the largest file size for a
+ * dense, 4k-blocksize file such that the total number of
+ * sectors in the file, including data and all indirect blocks,
+ * does not exceed 2^32. */
+ const loff_t upper_limit = 0x1ff7fffd000LL;
+
res += 1LL << (bits-2);
res += 1LL << (2*(bits-2));
res += 1LL << (3*(bits-2));
res <<= bits;
- if (res > (512LL << 32) - (1 << bits))
- res = (512LL << 32) - (1 << bits);
+ if (res > upper_limit)
+ res = upper_limit;
return res;
}

--- linux-2.6-ext3/fs/ext3/super.c.=K0001=.orig
+++ linux-2.6-ext3/fs/ext3/super.c
@@ -1193,12 +1193,18 @@ static void ext3_orphan_cleanup (struct
static loff_t ext3_max_size(int bits)
{
loff_t res = EXT3_NDIR_BLOCKS;
+ /* This constant is calculated to be the largest file size for a
+ * dense, 4k-blocksize file such that the total number of
+ * sectors in the file, including data and all indirect blocks,
+ * does not exceed 2^32. */
+ const loff_t upper_limit = 0x1ff7fffd000LL;
+
res += 1LL << (bits-2);
res += 1LL << (2*(bits-2));
res += 1LL << (3*(bits-2));
res <<= bits;
- if (res > (512LL << 32) - (1 << bits))
- res = (512LL << 32) - (1 << bits);
+ if (res > upper_limit)
+ res = upper_limit;
return res;
}