@@ -1198,41 +1198,72 @@ static int btrfs_read_disk_super(struct block_device *bdev, u64 bytenr,
int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
struct btrfs_fs_devices **fs_devices_ret)
{
+ struct btrfs_super_block *disk_super_primary;
struct btrfs_super_block *disk_super;
struct btrfs_device *device;
struct block_device *bdev;
struct page *page;
int ret = 0;
- u64 bytenr;
+ int i;
- /*
- * we would like to check all the supers, but that would make
- * a btrfs mount succeed after a mkfs from a different FS.
- * So, we need to add a special mount option to scan for
- * later supers, using BTRFS_SUPER_MIRROR_MAX instead
- */
- bytenr = btrfs_sb_offset(0);
flags |= FMODE_EXCL;
bdev = blkdev_get_by_path(path, flags, holder);
if (IS_ERR(bdev))
return PTR_ERR(bdev);
- if (btrfs_read_disk_super(bdev, bytenr, &page, &disk_super)) {
- ret = -EINVAL;
+ /*
+ * We would like to check all the supers and use one good copy,
+ * but that would make a btrfs mount succeed after a mkfs from
+ * a different FS.
+ * So, we need to add a special mount option to scan for
+ * later supers, using BTRFS_SUPER_MIRROR_MAX instead.
+ * So, just validate if all copies of the superblocks are ok
+ * and have the same fsid.
+ */
+ disk_super_primary = kzalloc(sizeof(*disk_super_primary), GFP_KERNEL);
+ if (!disk_super_primary) {
+ ret = -ENOMEM;
goto error_bdev_put;
}
+ 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;
+ goto error_kfree;
+ }
+ continue;
+ } else if (i == 0) {
+ memcpy(disk_super_primary, disk_super,
+ sizeof(*disk_super_primary));
+ btrfs_release_disk_super(page);
+ continue;
+ }
+
+ if (memcmp(disk_super_primary->fsid, disk_super->fsid,
+ BTRFS_FSID_SIZE)) {
+ pr_err("BTRFS (device %pg): superblock primary:copy#%d fsid missmatch %pU:%pU",
+ bdev, i, disk_super->fsid,
+ disk_super_primary->fsid);
+ ret = -EINVAL;
+ btrfs_release_disk_super(page);
+ goto error_kfree;
+ }
+ btrfs_release_disk_super(page);
+ }
mutex_lock(&uuid_mutex);
- device = device_list_add(path, disk_super);
+ device = device_list_add(path, disk_super_primary);
if (IS_ERR(device))
ret = PTR_ERR(device);
else
*fs_devices_ret = device->fs_devices;
mutex_unlock(&uuid_mutex);
- btrfs_release_disk_super(page);
-
+error_kfree:
+ kfree(disk_super_primary);
error_bdev_put:
blkdev_put(bdev, flags);
During the btrfs dev scan make sure that other copies of superblock contain the same fsid as the primary SB. So that we bring to the user notice if the superblock has been overwritten. mkfs.btrfs -fq /dev/sdc mkfs.btrfs -fq /dev/sdb dd if=/dev/sdb of=/dev/sdc count=4K skip=64K seek=64K obs=1 ibs=1 mount /dev/sdc /btrfs Also reading all copies of the SB is required to verify their csum is correct, so this serves as a preparatory as well. Signed-off-by: Anand Jain <anand.jain@oracle.com> --- v2->v2.1: Fix the page release part. pr_err string changes. add else part for if(btrfs_read_disk_super(..)). v1: Not born yet. fs/btrfs/volumes.c | 57 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 13 deletions(-)