diff --git a/fs/reiserfs/bitmap.c b/fs/reiserfs/bitmap.c index f09a6f6d3ac0619d81180adc36932db1c51671e0..16b331dd9913a93f6575d8ec38dd9b63dcf05251 100644 --- a/fs/reiserfs/bitmap.c +++ b/fs/reiserfs/bitmap.c @@ -61,6 +61,7 @@ static inline void get_bit_address(struct super_block *s, 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, @@ -76,25 +77,26 @@ int is_reusable(struct super_block *s, b_blocknr_t block, int bit_value) 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; } @@ -143,8 +145,8 @@ static int scan_bitmap_block(struct reiserfs_transaction_handle *th, 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 @@ -249,12 +251,12 @@ static int bmap_hash_id(struct super_block *s, u32 id) } 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; } @@ -328,10 +330,10 @@ static int scan_bitmap(struct reiserfs_transaction_handle *th, 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 @@ -397,10 +399,12 @@ static void _reiserfs_free_block(struct reiserfs_transaction_handle *th, 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; } @@ -1269,12 +1273,13 @@ struct buffer_head *reiserfs_read_bitmap_block(struct super_block *sb, 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, 0xff, sizeof(*bitmap) * SB_BMAP_NR(sb)); + memset(bitmap, 0xff, sizeof(*bitmap) * bmap_nr); SB_AP_BITMAP(sb) = bitmap; diff --git a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c index 0e30ba0ac4896e25ed63e144b9b37d53dad987a7..bb05a3e51b93ba709a01135d940e8c3c0fa8f320 100644 --- a/fs/reiserfs/journal.c +++ b/fs/reiserfs/journal.c @@ -240,7 +240,7 @@ static void cleanup_bitmap_list(struct super_block *p_s_sb, 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; @@ -2734,7 +2734,7 @@ int journal_init(struct super_block *p_s_sb, const char *j_dev_name, 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); @@ -2742,7 +2742,7 @@ int journal_init(struct super_block *p_s_sb, const char *j_dev_name, 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); diff --git a/fs/reiserfs/resize.c b/fs/reiserfs/resize.c index 66f1cda83a818ad542788f64b528f7c391d44be0..f71c3948edef8324ac68a71042fbc35209cb5ea3 100644 --- a/fs/reiserfs/resize.c +++ b/fs/reiserfs/resize.c @@ -61,7 +61,8 @@ int reiserfs_resize(struct super_block *s, unsigned long block_count_new) } /* 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 +74,7 @@ int reiserfs_resize(struct super_block *s, unsigned long block_count_new) /* 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) { @@ -200,7 +201,7 @@ int reiserfs_resize(struct super_block *s, unsigned long block_count_new) 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)); diff --git a/fs/reiserfs/super.c b/fs/reiserfs/super.c index b82897ae090bdba89331029216c3ce33893415fd..57adfe90d5aed2e8f64b0674bb9bf104c13b0bce 100644 --- a/fs/reiserfs/super.c +++ b/fs/reiserfs/super.c @@ -1725,6 +1725,21 @@ static int reiserfs_fill_super(struct super_block *s, void *data, int silent) 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 */ diff --git a/include/linux/reiserfs_fs.h b/include/linux/reiserfs_fs.h index 69a3e12cb8c8bee07b03737fc757ee311ef595f9..d8653bf232e3c807f3466bf4049d6e35e1ab44ec 100644 --- a/include/linux/reiserfs_fs.h +++ b/include/linux/reiserfs_fs.h @@ -283,6 +283,18 @@ static inline struct reiserfs_sb_info *REISERFS_SB(const struct super_block *sb) return sb->s_fs_info; } +/* Don't trust REISERFS_SB(sb)->s_bmap_nr, it's a u16 + * which overflows on large file systems. */ +static inline u32 reiserfs_bmap_count(struct super_block *sb) +{ + return (SB_BLOCK_COUNT(sb) - 1) / (sb->s_blocksize * 8) + 1; +} + +static inline int bmap_would_wrap(unsigned bmap_nr) +{ + return bmap_nr > ((1LL << 16) - 1); +} + /** this says about version of key of all items (but stat data) the object consists of */ #define get_inode_item_key_version( inode ) \