The reiserfs disk format is designed to handle file systems up to 2^32-1 blocks, which at 4KiB blocks means ~ 16 TiB - 4KiB. Unfortunately, the superblock's s_bmap_nr value, which contains a count of the number of bitmap blocks in the file system is a 16 bit value. This limits the usable size of the file system to 8 TiB (4096^2 * 8 * 65536). This patch implements the changes introduced with reiserfsprogs 3.6.20, where s_bmap_nr is zeroed out if the value would overflow. Older implementations will see the zero value of s_bmap_nr and abort the mount due to a vmalloc(0) before reading the bitmaps. This is safer than the current behavior which can result in corruption or BUG's. Signed-off-by: Jeff Mahoney <jeffm@xxxxxxxx> --- fs/reiserfs/bitmap.c | 39 ++++++++++++++++++++++----------------- fs/reiserfs/journal.c | 6 +++--- fs/reiserfs/resize.c | 6 +++--- fs/reiserfs/super.c | 15 +++++++++++++++ include/linux/reiserfs_fs.h | 5 +++++ include/linux/reiserfs_fs_sb.h | 1 + 6 files changed, 49 insertions(+), 23 deletions(-) --- a/fs/reiserfs/bitmap.c 2007-08-08 15:59:01.000000000 -0400 +++ b/fs/reiserfs/bitmap.c 2007-08-08 16:09:59.000000000 -0400 @@ -62,6 +62,7 @@ static inline void get_bit_address(struc int is_reusable(struct super_block *s, b_blocknr_t block, int bit_value) { unsigned int bmap, offset; + unsigned int bmap_count = reiserfs_bmap_count(s); if (block == 0 || block >= SB_BLOCK_COUNT(s)) { reiserfs_warning(s, @@ -77,25 +78,26 @@ int is_reusable(struct super_block *s, b if (unlikely(test_bit(REISERFS_OLD_FORMAT, &(REISERFS_SB(s)->s_properties)))) { b_blocknr_t bmap1 = REISERFS_SB(s)->s_sbh->b_blocknr + 1; - if (block >= bmap1 && block <= bmap1 + SB_BMAP_NR(s)) { + if (block >= bmap1 && + block <= bmap1 + bmap_count) { reiserfs_warning(s, "vs: 4019: is_reusable: " "bitmap block %lu(%u) can't be freed or reused", - block, SB_BMAP_NR(s)); + block, bmap_count); return 0; } } else { if (offset == 0) { reiserfs_warning(s, "vs: 4020: is_reusable: " "bitmap block %lu(%u) can't be freed or reused", - block, SB_BMAP_NR(s)); + block, bmap_count); return 0; } } - if (bmap >= SB_BMAP_NR(s)) { + if (bmap >= bmap_count) { reiserfs_warning(s, "vs-4030: is_reusable: there is no so many bitmap blocks: " - "block=%lu, bitmap_nr=%d", block, bmap); + "block=%lu, bitmap_nr=%u", block, bmap); return 0; } @@ -145,8 +147,8 @@ static int scan_bitmap_block(struct reis BUG_ON(!th->t_trans_id); - RFALSE(bmap_n >= SB_BMAP_NR(s), "Bitmap %d is out of range (0..%d)", - bmap_n, SB_BMAP_NR(s) - 1); + RFALSE(bmap_n >= reiserfs_bmap_count(s), "Bitmap %u is out of " + "range (0..%u)", bmap_n, reiserfs_bmap_count(s) - 1); PROC_INFO_INC(s, scan_bitmap.bmap); /* this is unclear and lacks comments, explain how journal bitmaps work here for the reader. Convey a sense of the design here. What @@ -251,12 +253,12 @@ static int bmap_hash_id(struct super_blo } else { hash_in = (char *)(&id); hash = keyed_hash(hash_in, 4); - bm = hash % SB_BMAP_NR(s); + bm = hash % reiserfs_bmap_count(s); if (!bm) bm = 1; } /* this can only be true when SB_BMAP_NR = 1 */ - if (bm >= SB_BMAP_NR(s)) + if (bm >= reiserfs_bmap_count(s)) bm = 0; return bm; } @@ -330,10 +332,10 @@ static int scan_bitmap(struct reiserfs_t get_bit_address(s, *start, &bm, &off); get_bit_address(s, finish, &end_bm, &end_off); - if (bm > SB_BMAP_NR(s)) + if (bm > reiserfs_bmap_count(s)) return 0; - if (end_bm > SB_BMAP_NR(s)) - end_bm = SB_BMAP_NR(s); + if (end_bm > reiserfs_bmap_count(s)) + end_bm = reiserfs_bmap_count(s); /* When the bitmap is more than 10% free, anyone can allocate. * When it's less than 10% free, only files that already use the @@ -399,10 +401,12 @@ static void _reiserfs_free_block(struct get_bit_address(s, block, &nr, &offset); - if (nr >= sb_bmap_nr(rs)) { + if (nr >= reiserfs_bmap_count(s)) { reiserfs_warning(s, "vs-4075: reiserfs_free_block: " - "block %lu is out of range on %s", - block, reiserfs_bdevname(s)); + "block %lu is out of range on %s " + "(nr=%u,max=%u)", block, + reiserfs_bdevname(s), nr, + reiserfs_bmap_count(s)); return; } @@ -1326,12 +1330,13 @@ struct buffer_head *reiserfs_read_bitmap int reiserfs_init_bitmap_cache(struct super_block *sb) { struct reiserfs_bitmap_info *bitmap; + unsigned int bmap_nr = reiserfs_bmap_count(sb); - bitmap = vmalloc(sizeof (*bitmap) * SB_BMAP_NR(sb)); + bitmap = vmalloc(sizeof (*bitmap) * bmap_nr); if (bitmap == NULL) return -ENOMEM; - memset(bitmap, 0, sizeof (*bitmap) * SB_BMAP_NR(sb)); + memset(bitmap, 0, sizeof (*bitmap) * bmap_nr); SB_AP_BITMAP(sb) = bitmap; --- a/fs/reiserfs/journal.c 2007-08-08 15:59:01.000000000 -0400 +++ b/fs/reiserfs/journal.c 2007-08-08 16:03:29.000000000 -0400 @@ -240,7 +240,7 @@ static void cleanup_bitmap_list(struct s if (jb->bitmaps == NULL) return; - for (i = 0; i < SB_BMAP_NR(p_s_sb); i++) { + for (i = 0; i < reiserfs_bmap_count(p_s_sb); i++) { if (jb->bitmaps[i]) { free_bitmap_node(p_s_sb, jb->bitmaps[i]); jb->bitmaps[i] = NULL; @@ -2651,7 +2651,7 @@ int journal_init(struct super_block *p_s journal->j_persistent_trans = 0; if (reiserfs_allocate_list_bitmaps(p_s_sb, journal->j_list_bitmap, - SB_BMAP_NR(p_s_sb))) + reiserfs_bmap_count(p_s_sb))) goto free_and_return; allocate_bitmap_nodes(p_s_sb); @@ -2659,7 +2659,7 @@ int journal_init(struct super_block *p_s SB_JOURNAL_1st_RESERVED_BLOCK(p_s_sb) = (old_format ? REISERFS_OLD_DISK_OFFSET_IN_BYTES / p_s_sb->s_blocksize + - SB_BMAP_NR(p_s_sb) + + reiserfs_bmap_count(p_s_sb) + 1 : REISERFS_DISK_OFFSET_IN_BYTES / p_s_sb->s_blocksize + 2); --- a/fs/reiserfs/resize.c 2007-08-08 15:59:01.000000000 -0400 +++ b/fs/reiserfs/resize.c 2007-08-08 16:04:14.000000000 -0400 @@ -61,7 +61,7 @@ int reiserfs_resize(struct super_block * } /* count used bits in last bitmap block */ - block_r = SB_BLOCK_COUNT(s) - (SB_BMAP_NR(s) - 1) * s->s_blocksize * 8; + block_r = SB_BLOCK_COUNT(s) - (reiserfs_bmap_count(s) - 1) * s->s_blocksize * 8; /* count bitmap blocks in new fs */ bmap_nr_new = block_count_new / (s->s_blocksize * 8); @@ -73,7 +73,7 @@ int reiserfs_resize(struct super_block * /* save old values */ block_count = SB_BLOCK_COUNT(s); - bmap_nr = SB_BMAP_NR(s); + bmap_nr = reiserfs_bmap_count(s); /* resizing of reiserfs bitmaps (journal and real), if needed */ if (bmap_nr_new > bmap_nr) { @@ -206,7 +206,7 @@ int reiserfs_resize(struct super_block * free_blocks + (block_count_new - block_count - (bmap_nr_new - bmap_nr))); PUT_SB_BLOCK_COUNT(s, block_count_new); - PUT_SB_BMAP_NR(s, bmap_nr_new); + PUT_SB_BMAP_NR(s, bmap_would_wrap(bmap_nr_new) ? : bmap_nr_new ); s->s_dirt = 1; journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB(s)); --- a/fs/reiserfs/super.c 2007-08-08 15:59:01.000000000 -0400 +++ b/fs/reiserfs/super.c 2007-08-08 16:04:51.000000000 -0400 @@ -1713,6 +1713,21 @@ static int reiserfs_fill_super(struct su set_sb_umount_state(rs, REISERFS_ERROR_FS); set_sb_fs_state(rs, 0); + /* Clear out s_bmap_nr if it would wrap. We can handle this + * case, but older revisions can't. This will cause the + * file system to fail mount on those older implementations, + * avoiding corruption. -jeffm */ + if (bmap_would_wrap(reiserfs_bmap_count(s)) && + sb_bmap_nr(rs) != 0) { + reiserfs_warning(s, "super-2030: This file system " + "claims to use %u bitmap blocks in " + "its super block, but requires %u. " + "Clearing to zero.", sb_bmap_nr(rs), + reiserfs_bmap_count(s)); + + set_sb_bmap_nr(rs, 0); + } + if (old_format_only(s)) { /* filesystem of format 3.5 either with standard or non-standard journal */ --- a/include/linux/reiserfs_fs.h 2007-08-08 15:59:01.000000000 -0400 +++ b/include/linux/reiserfs_fs.h 2007-08-08 16:10:59.000000000 -0400 @@ -229,6 +229,11 @@ struct reiserfs_super_block { ((!is_reiserfs_jr(SB_DISK_SUPER_BLOCK(s)) ? \ SB_ONDISK_JOURNAL_SIZE(s) + 1 : SB_ONDISK_RESERVED_FOR_JOURNAL(s))) +/* s_bmap_nr is a u16 */ +#define reiserfs_bmap_count(sb) reiserfs_bmap_nr(SB_BLOCK_COUNT(sb), sb->s_blocksize) +#define reiserfs_bmap_nr(count, blk_size) ((count - 1) / (blk_size * 8) + 1) +#define bmap_would_wrap(n) (n > ((1LL << 16) - 1)) + int is_reiserfs_3_5(struct reiserfs_super_block *rs); int is_reiserfs_3_6(struct reiserfs_super_block *rs); int is_reiserfs_jr(struct reiserfs_super_block *rs); --- a/include/linux/reiserfs_fs_sb.h 2007-08-08 15:59:01.000000000 -0400 +++ b/include/linux/reiserfs_fs_sb.h 2007-08-08 15:59:03.000000000 -0400 @@ -410,6 +410,7 @@ struct reiserfs_sb_info { char *s_qf_names[MAXQUOTAS]; int s_jquota_fmt; #endif + unsigned int s_bmap_nr; }; /* Definitions of reiserfs on-disk properties: */ -- Jeff Mahoney SUSE Labs - To unsubscribe from this list: send the line "unsubscribe reiserfs-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html