]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
libext2fs: don't overflow when punching indirect blocks with large blocks
authorDarrick J. Wong <darrick.wong@oracle.com>
Thu, 12 Dec 2013 17:06:03 +0000 (12:06 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Thu, 12 Dec 2013 17:06:07 +0000 (12:06 -0500)
On a FS with a rather large blockize (> 4K), the old block map
structure can construct a fat enough "tree" (or whatever we call that
lopsided thing) that (at least in theory) one could create mappings
for logical blocks higher than 32 bits.  In practice this doesn't
happen, but the 'max' and 'iter' variables that the punch helpers use
will overflow because the BLOCK_SIZE_BITS shifts are too large to fit
a 32-bit variable.  The current variable declarations also cause punch
to fail on TIND-mapped blocks even if the file is < 16T.  So enlarge
the fields to fit.

Yes, this is an obscure corner case, but it seems a little silly if we
can't punch a file's block 300,000,000 on a 64k-block filesystem.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
lib/ext2fs/punch.c

index 4471f468de78aa7026ee82b7ad3b35817bf02350..790a0ad8588771438e75e16cdca476177d6711d8 100644 (file)
@@ -50,15 +50,16 @@ static errcode_t ind_punch(ext2_filsys fs, struct ext2_inode *inode,
                           blk_t start, blk_t count, int max)
 {
        errcode_t       retval;
-       blk_t           b, offset;
-       int             i, incr;
+       blk_t           b;
+       int             i;
+       blk64_t         offset, incr;
        int             freed = 0;
 
 #ifdef PUNCH_DEBUG
        printf("Entering ind_punch, level %d, start %u, count %u, "
               "max %d\n", level, start, count, max);
 #endif
-       incr = 1 << ((EXT2_BLOCK_SIZE_BITS(fs->super)-2)*level);
+       incr = 1ULL << ((EXT2_BLOCK_SIZE_BITS(fs->super)-2)*level);
        for (i=0, offset=0; i < max; i++, p++, offset += incr) {
                if (offset >= start + count)
                        break;
@@ -87,7 +88,7 @@ static errcode_t ind_punch(ext2_filsys fs, struct ext2_inode *inode,
                                continue;
                }
 #ifdef PUNCH_DEBUG
-               printf("Freeing block %u (offset %d)\n", b, offset);
+               printf("Freeing block %u (offset %llu)\n", b, offset);
 #endif
                ext2fs_block_alloc_stats(fs, b, -1);
                *p = 0;
@@ -108,7 +109,7 @@ static errcode_t ext2fs_punch_ind(ext2_filsys fs, struct ext2_inode *inode,
        int                     num = EXT2_NDIR_BLOCKS;
        blk_t                   *bp = inode->i_block;
        blk_t                   addr_per_block;
-       blk_t                   max = EXT2_NDIR_BLOCKS;
+       blk64_t                 max = EXT2_NDIR_BLOCKS;
 
        if (!block_buf) {
                retval = ext2fs_get_array(3, fs->blocksize, &buf);
@@ -119,10 +120,10 @@ static errcode_t ext2fs_punch_ind(ext2_filsys fs, struct ext2_inode *inode,
 
        addr_per_block = (blk_t) fs->blocksize >> 2;
 
-       for (level=0; level < 4; level++, max *= addr_per_block) {
+       for (level = 0; level < 4; level++, max *= (blk64_t)addr_per_block) {
 #ifdef PUNCH_DEBUG
                printf("Main loop level %d, start %u count %u "
-                      "max %d num %d\n", level, start, count, max, num);
+                      "max %llu num %d\n", level, start, count, max, num);
 #endif
                if (start < max) {
                        retval = ind_punch(fs, inode, block_buf, bp, level,