| From 203425242df0e5bfa7bd86d934500583390f5fe6 Mon Sep 17 00:00:00 2001 |
| From: Eric Sandeen <sandeen@redhat.com> |
| Date: Wed, 16 Sep 2009 14:45:10 -0400 |
| Subject: [PATCH 36/85] ext4: limit block allocations for indirect-block files to < 2^32 |
| |
| (cherry picked from commit fb0a387dcdcd21aab1b09ee7fd80b7c979bdbbfd) |
| |
| Today, the ext4 allocator will happily allocate blocks past |
| 2^32 for indirect-block files, which results in the block |
| numbers getting truncated, and corruption ensues. |
| |
| This patch limits such allocations to < 2^32, and adds |
| BUG_ONs if we do get blocks larger than that. |
| |
| This should address RH Bug 519471, ext4 bitmap allocator |
| must limit blocks to < 2^32 |
| |
| * ext4_find_goal() is modified to choose a goal < UINT_MAX, |
| so that our starting point is in an acceptable range. |
| |
| * ext4_xattr_block_set() is modified such that the goal block |
| is < UINT_MAX, as above. |
| |
| * ext4_mb_regular_allocator() is modified so that the group |
| search does not continue into groups which are too high |
| |
| * ext4_mb_use_preallocated() has a check that we don't use |
| preallocated space which is too far out |
| |
| * ext4_alloc_blocks() and ext4_xattr_block_set() add some BUG_ONs |
| |
| No attempt has been made to limit inode locations to < 2^32, |
| so we may wind up with blocks far from their inodes. Doing |
| this much already will lead to some odd ENOSPC issues when the |
| "lower 32" gets full, and further restricting inodes could |
| make that even weirder. |
| |
| For high inodes, choosing a goal of the original, % UINT_MAX, |
| may be a bit odd, but then we're in an odd situation anyway, |
| and I don't know of a better heuristic. |
| |
| Signed-off-by: Eric Sandeen <sandeen@redhat.com> |
| Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| --- |
| fs/ext4/ext4.h | 4 ++++ |
| fs/ext4/inode.c | 11 ++++++++++- |
| fs/ext4/mballoc.c | 9 +++++++++ |
| fs/ext4/super.c | 2 ++ |
| fs/ext4/xattr.c | 15 +++++++++++++-- |
| 5 files changed, 38 insertions(+), 3 deletions(-) |
| |
| --- a/fs/ext4/ext4.h |
| +++ b/fs/ext4/ext4.h |
| @@ -388,6 +388,9 @@ struct ext4_mount_options { |
| #endif |
| }; |
| |
| +/* Max physical block we can addres w/o extents */ |
| +#define EXT4_MAX_BLOCK_FILE_PHYS 0xFFFFFFFF |
| + |
| /* |
| * Structure of an inode on the disk |
| */ |
| @@ -843,6 +846,7 @@ struct ext4_sb_info { |
| unsigned long s_gdb_count; /* Number of group descriptor blocks */ |
| unsigned long s_desc_per_block; /* Number of group descriptors per block */ |
| ext4_group_t s_groups_count; /* Number of groups in the fs */ |
| + ext4_group_t s_blockfile_groups;/* Groups acceptable for non-extent files */ |
| unsigned long s_overhead_last; /* Last calculated overhead */ |
| unsigned long s_blocks_last; /* Last seen block count */ |
| loff_t s_bitmap_maxbytes; /* max bytes for bitmap files */ |
| --- a/fs/ext4/inode.c |
| +++ b/fs/ext4/inode.c |
| @@ -564,15 +564,21 @@ static ext4_fsblk_t ext4_find_near(struc |
| * |
| * Normally this function find the preferred place for block allocation, |
| * returns it. |
| + * Because this is only used for non-extent files, we limit the block nr |
| + * to 32 bits. |
| */ |
| static ext4_fsblk_t ext4_find_goal(struct inode *inode, ext4_lblk_t block, |
| Indirect *partial) |
| { |
| + ext4_fsblk_t goal; |
| + |
| /* |
| * XXX need to get goal block from mballoc's data structures |
| */ |
| |
| - return ext4_find_near(inode, partial); |
| + goal = ext4_find_near(inode, partial); |
| + goal = goal & EXT4_MAX_BLOCK_FILE_PHYS; |
| + return goal; |
| } |
| |
| /** |
| @@ -653,6 +659,8 @@ static int ext4_alloc_blocks(handle_t *h |
| if (*err) |
| goto failed_out; |
| |
| + BUG_ON(current_block + count > EXT4_MAX_BLOCK_FILE_PHYS); |
| + |
| target -= count; |
| /* allocate blocks for indirect blocks */ |
| while (index < indirect_blks && count) { |
| @@ -687,6 +695,7 @@ static int ext4_alloc_blocks(handle_t *h |
| ar.flags = EXT4_MB_HINT_DATA; |
| |
| current_block = ext4_mb_new_blocks(handle, &ar, err); |
| + BUG_ON(current_block + ar.len > EXT4_MAX_BLOCK_FILE_PHYS); |
| |
| if (*err && (target == blks)) { |
| /* |
| --- a/fs/ext4/mballoc.c |
| +++ b/fs/ext4/mballoc.c |
| @@ -1960,6 +1960,10 @@ ext4_mb_regular_allocator(struct ext4_al |
| sb = ac->ac_sb; |
| sbi = EXT4_SB(sb); |
| ngroups = ext4_get_groups_count(sb); |
| + /* non-extent files are limited to low blocks/groups */ |
| + if (!(EXT4_I(ac->ac_inode)->i_flags & EXT4_EXTENTS_FL)) |
| + ngroups = sbi->s_blockfile_groups; |
| + |
| BUG_ON(ac->ac_status == AC_STATUS_FOUND); |
| |
| /* first, try the goal */ |
| @@ -3355,6 +3359,11 @@ ext4_mb_use_preallocated(struct ext4_all |
| ac->ac_o_ex.fe_logical >= pa->pa_lstart + pa->pa_len) |
| continue; |
| |
| + /* non-extent files can't have physical blocks past 2^32 */ |
| + if (!(EXT4_I(ac->ac_inode)->i_flags & EXT4_EXTENTS_FL) && |
| + pa->pa_pstart + pa->pa_len > EXT4_MAX_BLOCK_FILE_PHYS) |
| + continue; |
| + |
| /* found preallocated blocks, use them */ |
| spin_lock(&pa->pa_lock); |
| if (pa->pa_deleted == 0 && pa->pa_free) { |
| --- a/fs/ext4/super.c |
| +++ b/fs/ext4/super.c |
| @@ -2618,6 +2618,8 @@ static int ext4_fill_super(struct super_ |
| goto failed_mount; |
| } |
| sbi->s_groups_count = blocks_count; |
| + sbi->s_blockfile_groups = min_t(ext4_group_t, sbi->s_groups_count, |
| + (EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb))); |
| db_count = (sbi->s_groups_count + EXT4_DESC_PER_BLOCK(sb) - 1) / |
| EXT4_DESC_PER_BLOCK(sb); |
| sbi->s_group_desc = kmalloc(db_count * sizeof(struct buffer_head *), |
| --- a/fs/ext4/xattr.c |
| +++ b/fs/ext4/xattr.c |
| @@ -810,12 +810,23 @@ inserted: |
| get_bh(new_bh); |
| } else { |
| /* We need to allocate a new block */ |
| - ext4_fsblk_t goal = ext4_group_first_block_no(sb, |
| + ext4_fsblk_t goal, block; |
| + |
| + goal = ext4_group_first_block_no(sb, |
| EXT4_I(inode)->i_block_group); |
| - ext4_fsblk_t block = ext4_new_meta_blocks(handle, inode, |
| + |
| + /* non-extent files can't have physical blocks past 2^32 */ |
| + if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)) |
| + goal = goal & EXT4_MAX_BLOCK_FILE_PHYS; |
| + |
| + block = ext4_new_meta_blocks(handle, inode, |
| goal, NULL, &error); |
| if (error) |
| goto cleanup; |
| + |
| + if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)) |
| + BUG_ON(block > EXT4_MAX_BLOCK_FILE_PHYS); |
| + |
| ea_idebug(inode, "creating block %d", block); |
| |
| new_bh = sb_getblk(sb, block); |