|
@@ -988,6 +988,56 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+void btrfs_release_disk_super(struct page *page)
|
|
|
+{
|
|
|
+ kunmap(page);
|
|
|
+ put_page(page);
|
|
|
+}
|
|
|
+
|
|
|
+int btrfs_read_disk_super(struct block_device *bdev, u64 bytenr,
|
|
|
+ struct page **page, struct btrfs_super_block **disk_super)
|
|
|
+{
|
|
|
+ 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;
|
|
|
+
|
|
|
+ /* make sure our super fits in the page */
|
|
|
+ if (sizeof(**disk_super) > PAGE_SIZE)
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ /* 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;
|
|
|
+
|
|
|
+ /* 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;
|
|
|
+
|
|
|
+ p = kmap(*page);
|
|
|
+
|
|
|
+ /* align our pointer to the offset of the super block */
|
|
|
+ *disk_super = p + (bytenr & ~PAGE_MASK);
|
|
|
+
|
|
|
+ if (btrfs_super_bytenr(*disk_super) != bytenr ||
|
|
|
+ btrfs_super_magic(*disk_super) != BTRFS_MAGIC) {
|
|
|
+ btrfs_release_disk_super(*page);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((*disk_super)->label[0] &&
|
|
|
+ (*disk_super)->label[BTRFS_LABEL_SIZE - 1])
|
|
|
+ (*disk_super)->label[BTRFS_LABEL_SIZE - 1] = '\0';
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Look for a btrfs signature on a device. This may be called out of the mount path
|
|
|
* and we are not allowed to call set_blocksize during the scan. The superblock
|
|
@@ -999,13 +1049,11 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
|
|
|
struct btrfs_super_block *disk_super;
|
|
|
struct block_device *bdev;
|
|
|
struct page *page;
|
|
|
- void *p;
|
|
|
int ret = -EINVAL;
|
|
|
u64 devid;
|
|
|
u64 transid;
|
|
|
u64 total_devices;
|
|
|
u64 bytenr;
|
|
|
- pgoff_t index;
|
|
|
|
|
|
/*
|
|
|
* we would like to check all the supers, but that would make
|
|
@@ -1018,41 +1066,14 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
|
|
|
mutex_lock(&uuid_mutex);
|
|
|
|
|
|
bdev = blkdev_get_by_path(path, flags, holder);
|
|
|
-
|
|
|
if (IS_ERR(bdev)) {
|
|
|
ret = PTR_ERR(bdev);
|
|
|
goto error;
|
|
|
}
|
|
|
|
|
|
- /* make sure our super fits in the device */
|
|
|
- if (bytenr + PAGE_SIZE >= i_size_read(bdev->bd_inode))
|
|
|
- goto error_bdev_put;
|
|
|
-
|
|
|
- /* make sure our super fits in the page */
|
|
|
- if (sizeof(*disk_super) > PAGE_SIZE)
|
|
|
- goto error_bdev_put;
|
|
|
-
|
|
|
- /* make sure our super doesn't straddle pages on disk */
|
|
|
- index = bytenr >> PAGE_SHIFT;
|
|
|
- if ((bytenr + sizeof(*disk_super) - 1) >> PAGE_SHIFT != index)
|
|
|
- goto error_bdev_put;
|
|
|
-
|
|
|
- /* pull in the page with our super */
|
|
|
- page = read_cache_page_gfp(bdev->bd_inode->i_mapping,
|
|
|
- index, GFP_NOFS);
|
|
|
-
|
|
|
- if (IS_ERR_OR_NULL(page))
|
|
|
+ if (btrfs_read_disk_super(bdev, bytenr, &page, &disk_super))
|
|
|
goto error_bdev_put;
|
|
|
|
|
|
- p = kmap(page);
|
|
|
-
|
|
|
- /* align our pointer to the offset of the super block */
|
|
|
- disk_super = p + (bytenr & ~PAGE_MASK);
|
|
|
-
|
|
|
- if (btrfs_super_bytenr(disk_super) != bytenr ||
|
|
|
- btrfs_super_magic(disk_super) != BTRFS_MAGIC)
|
|
|
- goto error_unmap;
|
|
|
-
|
|
|
devid = btrfs_stack_device_id(&disk_super->dev_item);
|
|
|
transid = btrfs_super_generation(disk_super);
|
|
|
total_devices = btrfs_super_num_devices(disk_super);
|
|
@@ -1060,8 +1081,6 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
|
|
|
ret = device_list_add(path, disk_super, devid, fs_devices_ret);
|
|
|
if (ret > 0) {
|
|
|
if (disk_super->label[0]) {
|
|
|
- if (disk_super->label[BTRFS_LABEL_SIZE - 1])
|
|
|
- disk_super->label[BTRFS_LABEL_SIZE - 1] = '\0';
|
|
|
printk(KERN_INFO "BTRFS: device label %s ", disk_super->label);
|
|
|
} else {
|
|
|
printk(KERN_INFO "BTRFS: device fsid %pU ", disk_super->fsid);
|
|
@@ -1073,9 +1092,7 @@ int btrfs_scan_one_device(const char *path, fmode_t flags, void *holder,
|
|
|
if (!ret && fs_devices_ret)
|
|
|
(*fs_devices_ret)->total_devices = total_devices;
|
|
|
|
|
|
-error_unmap:
|
|
|
- kunmap(page);
|
|
|
- put_page(page);
|
|
|
+ btrfs_release_disk_super(page);
|
|
|
|
|
|
error_bdev_put:
|
|
|
blkdev_put(bdev, flags);
|