btrfs: add more superblock checks
Previous reply seems failed to be delivered.
Resend.
-------- 转发的消息 --------
主题: Re: [PATCH] btrfs: add more superblock checks
日期: Tue, 07 Oct 2014 16:44:07 +0800
发件人: Qu Wenruo <[email protected]>
收件人: David Sterba <[email protected]>, [email protected]
Thanks a lot for the patch to deal with the broken image.
Also this seems more extendable.
Small comment inlined below.
-------- Original Message --------
Subject: [PATCH] btrfs: add more superblock checks
From: David Sterba <[email protected]>
To: <[email protected]>
Date: 2014年10月01日 01:16
Quoted 46 lines
Populate
btrfs_check_super_valid()with checks that try to verify consistency of superblock by additional conditions that may arise from corrupted devices or bitflips. Some of tests are only hints and issue warnings instead of failing the mount, basically when the checks are derived from the data found in the superblock.Tested on a broken image provided by Qu.
Reported-by Qu Wenruo<[email protected]>Signed-off-by David Sterba<[email protected]>--- fs/btrfs/disk-io.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-)fs/btrfs/disk-io.c +21
-1@@ -3814,10 +3814,73 @@ int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid)static int btrfs_check_super_valid (struct btrfs_fs_info *fs_info,int read_only) { +struct btrfs_super_block *sb = fs_info->super_copy; +int ret =0 ; + +if (sb->root_level >BTRFS_MAX_LEVEL ) { +printk (KERN_ERR "BTRFS: tree_root level too big: %d > %d\n" , + sb->root_level,BTRFS_MAX_LEVEL ); + ret = -EINVAL ; + } +if (sb->chunk_root_level >BTRFS_MAX_LEVEL ) { +printk (KERN_ERR "BTRFS: chunk_root level too big: %d > %d\n" , + sb->chunk_root_level,BTRFS_MAX_LEVEL ); + ret = -EINVAL ; + } +if (sb->log_root_level >BTRFS_MAX_LEVEL ) { +printk (KERN_ERR "BTRFS: log_root level too big: %d > %d\n" , + sb->log_root_level,BTRFS_MAX_LEVEL ); + ret = -EINVAL ; + } +/* -* Placeholder for checks +* The common minimum, we don't know if we can trust the nodesize/sectorsize +* items yet, they'll be verified later. Issue just a warning. */
fs/btrfs/disk-io.c int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid) · line 3848
Quoted 10 lines
fs/btrfs/disk-io.c +9
-1-return 0 ; +if (!IS_ALIGNED (sb->root,4096 )) +printk (KERN_WARNING "BTRFS: tree_root block unaligned: %llu\n" , + sb->root); +if (!IS_ALIGNED (sb->chunk_root,4096 )) +printk (KERN_WARNING "BTRFS: tree_root block unaligned: %llu\n" , + sb->chunk_root); +if (!IS_ALIGNED (sb->log_root,4096 )) +printk (KERN_WARNING "BTRFS: tree_root block unaligned: %llu\n" , + sb->log_root);
1) it is better not to call IS_ALIGNED to immediate value//.
Although current btrfs implement ensures that all sectorsize is larger equal than page_size, but Chandan Rajendra is trying to support subpage-sized blocksize,
which may cause false alert later.
It would be much better using btrfs_super_sectorsize() instead to improve extendability.
2) missing endian convert.
On big endian system it would be a disaster....
btrfs_super_* marco should be used.
Quoted 4 lines
fs/btrfs/disk-io.c +4
-0+ +if (memcmp (fs_info->fsid, sb->dev_item.fsid,BTRFS_UUID_SIZE ) !=0 ) { +printk (KERN_ERR "BTRFS: dev_item UUID does not match fsid: %pU != %pU\n" , + fs_info->fsid, sb->dev_item.fsid);
fs/btrfs/disk-io.c int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid) · line 3862
Quoted 10 lines
fs/btrfs/disk-io.c +10
-0+ ret = -EINVAL ; + } + +/* +* Hint to catch really bogus numbers, bitflips or so, more exact checks are +* done later +*/ +if (sb->num_devices > (1UL <<31 )) +printk (KERN_WARNING "BTRFS: suspicious number of devices: %llu\n" , + sb->num_devices);
What about also check the devid with sb->num_devices too? Every valid devid should be less equal than sb->num_devices if I am right. Although iterate dev_item here may be overkilled...
Quoted 13 lines
fs/btrfs/disk-io.c +13
-0+ +if (sb->bytenr !=BTRFS_SUPER_INFO_OFFSET ) { +printk (KERN_ERR "BTRFS: super offset mismatch %llu != %u\n" , + sb->bytenr,BTRFS_SUPER_INFO_OFFSET ); + ret = -EINVAL ; + } + +/* +* The generation is a global counter, we'll trust it more than the others +* but it's still possible that it's the one that's wrong. +*/ +if (sb->generation < sb->chunk_root_generation) + printk(KERN_WARNING
fs/btrfs/disk-io.c int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid) · line 3885
Quoted 10 lines
fs/btrfs/disk-io.c +8
-0+"BTRFS: suspicious: generation < chunk_root_generation: %llu < %llu\n" , + sb->generation, sb->chunk_root_generation); +if (sb->generation < sb->cache_generation && sb->cache_generation != (u64)-1 ) +printk (KERN_WARNING +"BTRFS: suspicious: generation < cache_generation: %llu < %llu\n" , + sb->generation, sb->cache_generation); + +return ret; }
Still the endian problem.
Thanks,
Qu
Quoted 1 line
fs/btrfs/disk-io.c +0
-0static void btrfs_error_commit_super (struct btrfs_root *root)
Quoted 31 lines · Qu Wenruo
Quoted 31 lines
(quoted diff) +27
-2+ struct btrfs_super_block *sb = fs_info->super_copy; + int ret = 0; + + if (sb->root_level > BTRFS_MAX_LEVEL) { + printk(KERN_ERR "BTRFS: tree_root level too big: %d > %d\n", + sb->root_level, BTRFS_MAX_LEVEL); + ret = -EINVAL; + } + if (sb->chunk_root_level > BTRFS_MAX_LEVEL) { + printk(KERN_ERR "BTRFS: chunk_root level too big: %d > %d\n", + sb->chunk_root_level, BTRFS_MAX_LEVEL); + ret = -EINVAL; + } + if (sb->log_root_level > BTRFS_MAX_LEVEL) { + printk(KERN_ERR "BTRFS: log_root level too big: %d > %d\n", + sb->log_root_level, BTRFS_MAX_LEVEL); + ret = -EINVAL; + } + /* - * Placeholder for checks + * The common minimum, we don't know if we can trust the nodesize/sectorsize + * items yet, they'll be verified later. Issue just a warning. */ - return 0; + if (!IS_ALIGNED(sb->root, 4096)) + printk(KERN_WARNING "BTRFS: tree_root block unaligned: %llu\n", + sb->root); + if (!IS_ALIGNED(sb->chunk_root, 4096)) + printk(KERN_WARNING "BTRFS: tree_root block unaligned: %llu\n", + sb->chunk_root);
Quoted 8 lines
Review
Quoted 3 lines
(quoted diff) +3
-0+ if (!IS_ALIGNED(sb->log_root, 4096)) + printk(KERN_WARNING "BTRFS: tree_root block unaligned: %llu\n", + sb->log_root);1) it is better not to call
IS_ALIGNEDto immediate value//. Although current btrfs implement ensures that all sectorsize is larger equal thanpage_size,
but Chandan Rajendra is trying to support subpage-sized blocksize, which may cause false alert later.
The patch reflects current state, so when the subpage blocksize patches are merged, this will have to be changed accordingly.
Quoted 2 lines
It would be much better using
btrfs_super_sectorsize()instead to improve extendability.
See the comment above, we don't trust the superblock yet and cannot use the sectorsize reliably.
Quoted 3 lines
2) missing endian convert.
On big endian system it would be a disaster....
btrfs_super_* marco should be used.
Thanks, will fix it.
Quoted 9 lines
Quoted 9 lines
(quoted diff) +9
-0+ if (memcmp(fs_info->fsid, sb->dev_item.fsid, BTRFS_UUID_SIZE) != 0) { + printk(KERN_ERR "BTRFS: dev_item UUID does not match fsid: %pU != %pU\n", + fs_info->fsid, sb->dev_item.fsid); + ret = -EINVAL; + } + + /* + * Hint to catch really bogus numbers, bitflips or so, more exact checks are + * done later
Quoted 8 lines
Review
Quoted 4 lines
(quoted diff) +4
-0+ */ + if (sb->num_devices > (1UL << 31)) + printk(KERN_WARNING "BTRFS: suspicious number of devices: %llu\n", + sb->num_devices);What about also check the devid with
sb->num_devicestoo? Every valid devid should be less equal thansb->num_devicesif I am right. Although iteratedev_itemhere may be overkilled...
This could be done of course, I've tried to keep the checks very small and using only directly accessible information. More is possible of course.
Quoted 16 lines
Quoted 16 lines
(quoted diff) +16
-0+ + if (sb->bytenr != BTRFS_SUPER_INFO_OFFSET) { + printk(KERN_ERR "BTRFS: super offset mismatch %llu != %u\n", + sb->bytenr, BTRFS_SUPER_INFO_OFFSET); + ret = -EINVAL; + } + + /* + * The generation is a global counter, we'll trust it more than the others + * but it's still possible that it's the one that's wrong. + */ + if (sb->generation < sb->chunk_root_generation) + printk(KERN_WARNING + "BTRFS: suspicious: generation < chunk_root_generation: %llu < %llu\n", + sb->generation, sb->chunk_root_generation); + if (sb->generation < sb->cache_generation && sb->cache_generation != (u64)-1)
Quoted 8 lines
Review
Quoted 7 lines
(quoted diff) +5
-0+ printk(KERN_WARNING + "BTRFS: suspicious: generation < cache_generation: %llu < %llu\n", + sb->generation, sb->cache_generation); + + return ret; }Still the endian problem.
Will fix, thanks.