Btrfs: make sure we update latest_bdev
authorChris Mason <chris.mason@oracle.com>
Tue, 21 Feb 2012 01:53:43 +0000 (20:53 -0500)
committerChris Mason <chris.mason@oracle.com>
Thu, 23 Feb 2012 15:43:45 +0000 (10:43 -0500)
When we are setting up the mount, we close all the
devices that were not actually part of the metadata we found.

But, we don't make sure that one of those devices wasn't
fs_devices->latest_bdev, which means we can do a use after free
on the one we closed.

This updates latest_bdev as it goes.

Signed-off-by: Chris Mason <chris.mason@oracle.com>
fs/btrfs/disk-io.c
fs/btrfs/volumes.c

index 58d0678dfcba78ddbd41efb4f56539977914d315..b801d29f3f10ca6f21234ae8ece69765dcbb7438 100644 (file)
@@ -2305,6 +2305,12 @@ struct btrfs_root *open_ctree(struct super_block *sb,
 
        btrfs_close_extra_devices(fs_devices);
 
+       if (!fs_devices->latest_bdev) {
+               printk(KERN_CRIT "btrfs: failed to read devices on %s\n",
+                      sb->s_id);
+               goto fail_tree_roots;
+       }
+
 retry_root_backup:
        blocksize = btrfs_level_size(tree_root,
                                     btrfs_super_root_level(disk_super));
index cd040bf3fd87134bd36c76e40b29d7d0eab46b1d..7eefdef8a14c76b1d62e64fb18b36fcbba44e6d4 100644 (file)
@@ -459,12 +459,23 @@ int btrfs_close_extra_devices(struct btrfs_fs_devices *fs_devices)
 {
        struct btrfs_device *device, *next;
 
+       struct block_device *latest_bdev = NULL;
+       u64 latest_devid = 0;
+       u64 latest_transid = 0;
+
        mutex_lock(&uuid_mutex);
 again:
        /* This is the initialized path, it is safe to release the devices. */
        list_for_each_entry_safe(device, next, &fs_devices->devices, dev_list) {
-               if (device->in_fs_metadata)
+               if (device->in_fs_metadata) {
+                       if (!latest_transid ||
+                           device->generation > latest_transid) {
+                               latest_devid = device->devid;
+                               latest_transid = device->generation;
+                               latest_bdev = device->bdev;
+                       }
                        continue;
+               }
 
                if (device->bdev) {
                        blkdev_put(device->bdev, device->mode);
@@ -487,6 +498,10 @@ again:
                goto again;
        }
 
+       fs_devices->latest_bdev = latest_bdev;
+       fs_devices->latest_devid = latest_devid;
+       fs_devices->latest_trans = latest_transid;
+
        mutex_unlock(&uuid_mutex);
        return 0;
 }