@@ -395,7 +395,7 @@ static int verify_parent_transid(struct extent_io_tree *io_tree,
* Otherwise: -EINVAL if csum type is not found
* -EUCLEAN if csum does not match
*/
-static int btrfs_check_super_csum(char *raw_disk_sb)
+int btrfs_check_super_csum(char *raw_disk_sb)
{
struct btrfs_super_block *disk_sb =
(struct btrfs_super_block *)raw_disk_sb;
@@ -69,6 +69,7 @@ int write_all_supers(struct btrfs_fs_info *fs_info, int max_mirrors);
struct buffer_head *btrfs_read_dev_super(struct block_device *bdev);
int btrfs_read_dev_one_super(struct block_device *bdev, int copy_num,
struct buffer_head **bh_ret);
+int btrfs_check_super_csum(char *raw_disk_sb);
int btrfs_commit_super(struct btrfs_fs_info *fs_info);
struct btrfs_root *btrfs_read_fs_root(struct btrfs_root *tree_root,
struct btrfs_key *location);
@@ -1149,38 +1149,53 @@ static int btrfs_read_disk_super(struct block_device *bdev, u64 bytenr,
struct page **page,
struct btrfs_super_block **disk_super)
{
+ int err;
void *p;
pgoff_t index;
/* make sure our super fits in the device */
if (bytenr + PAGE_SIZE >= i_size_read(bdev->bd_inode))
- return 1;
+ return -EINVAL;
/* make sure our super fits in the page */
if (sizeof(**disk_super) > PAGE_SIZE)
- return 1;
+ return -EINVAL;
/* make sure our super doesn't straddle pages on disk */
index = bytenr >> PAGE_SHIFT;
if ((bytenr + sizeof(**disk_super) - 1) >> PAGE_SHIFT != index)
- return 1;
+ return -EINVAL;
/* pull in the page with our super */
*page = read_cache_page_gfp(bdev->bd_inode->i_mapping,
index, GFP_KERNEL);
if (IS_ERR_OR_NULL(*page))
- return 1;
+ return -EINVAL;
p = kmap(*page);
/* align our pointer to the offset of the super block */
*disk_super = p + (bytenr & ~PAGE_MASK);
+ err = btrfs_check_super_csum((char *) *disk_super);
+ if (err) {
+ if (err == -EINVAL)
+ pr_err("BTRFS error (device %pg): "\
+ "unsupported checksum type, bytenr=%llu",
+ bdev, bytenr);
+ else
+ pr_err("BTRFS error (device %pg): "\
+ "superblock checksum failed, bytenr=%llu",
+ bdev, bytenr);
+ btrfs_release_disk_super(*page);
+ return err;
+ }
+
if (btrfs_super_bytenr(*disk_super) != bytenr ||
btrfs_super_magic(*disk_super) != BTRFS_MAGIC) {
btrfs_release_disk_super(*page);
- return 1;
+ return -EINVAL;
}
if ((*disk_super)->label[0] &&
@@ -1229,11 +1244,10 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
u64 bytenr = btrfs_sb_offset(i);
- if (btrfs_read_disk_super(bdev, bytenr, &page, &disk_super)) {
- if (i == 0) {
- ret = -EINVAL;
+ ret = btrfs_read_disk_super(bdev, bytenr, &page, &disk_super);
+ if (ret) {
+ if (i == 0)
goto error_kfree;
- }
continue;
} else if (i == 0) {
memcpy(disk_super_primary, disk_super,
During the scan context, we aren't verifying if the superblock- checksum is correct for the primary and its copies. This patch fixes it by adding the checksum verification function btrfs_check_super_csum() in the function btrfs_read_disk_super(). It would fail the scan only if the primary SB csum is wrong, whereas if the copy-SB csum is wrong it would just warn and still makes the scan successful. Since because the mount reads only primary SB. And further, all SB gets overwritten. Thus fixing the bad SB. The context in which this will be called are, scan, ready, for the device in the mount argument, for the devices in the mount -o device option. Test script: mkfs.btrfs -fq /dev/sdc dd if=/dev/urandom of=/dev/sdc ibs=1 obs=1 count=1 seek=64K btrfs dev scan Signed-off-by: Anand Jain <anand.jain@oracle.com> --- fs/btrfs/disk-io.c | 2 +- fs/btrfs/disk-io.h | 1 + fs/btrfs/volumes.c | 32 +++++++++++++++++++++++--------- 3 files changed, 25 insertions(+), 10 deletions(-)