Skip to content

Commit

Permalink
reiserfs: ignore on disk s_bmap_nr value
Browse files Browse the repository at this point in the history
Implement support for file systems larger than 8 TiB.

The reiserfs superblock contains a 16 bit value for counting the number of
bitmap blocks.  The rest of the disk format supports file systems up to 2^32
blocks, but the bitmap block limitation artificially limits this to 8 TiB with
a 4KiB block size.

Rather than trust the superblock's 16-bit bitmap block count, we calculate it
dynamically based on the number of blocks in the file system.  When an
incorrect value is observed in the superblock, it is zeroed out, ensuring that
older kernels will not be able to mount the file system.

Userspace support has already been implemented and shipped in reiserfsprogs
3.6.20.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Jeff Mahoney authored and Linus Torvalds committed Oct 19, 2007
1 parent 4d20851 commit cb680c1
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 23 deletions.
39 changes: 22 additions & 17 deletions fs/reiserfs/bitmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;

Expand Down
6 changes: 3 additions & 3 deletions fs/reiserfs/journal.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -2734,15 +2734,15 @@ 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);

/* reserved for journal area support */
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);
Expand Down
7 changes: 4 additions & 3 deletions fs/reiserfs/resize.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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) {
Expand Down Expand Up @@ -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));
Expand Down
15 changes: 15 additions & 0 deletions fs/reiserfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
12 changes: 12 additions & 0 deletions include/linux/reiserfs_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) \
Expand Down

0 comments on commit cb680c1

Please sign in to comment.