From: Tejun Heo Date: Fri, 29 Apr 2011 08:15:20 +0000 (+0200) Subject: block: rescan partitions on invalidated devices on -ENOMEDIA too X-Git-Tag: v2.6.33.15~79 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=6f31747bfeb8c74e6d0a10ecef0abe2a04c5a6cb;p=karo-tx-linux.git block: rescan partitions on invalidated devices on -ENOMEDIA too commit 02e352287a40bd456eb78df705bf888bc3161d3f upstream. __blkdev_get() doesn't rescan partitions if disk->fops->open() fails, which leads to ghost partition devices lingering after medimum removal is known to both the kernel and userland. The behavior also creates a subtle inconsistency where O_NONBLOCK open, which doesn't fail even if there's no medium, clears the ghots partitions, which is exploited to work around the problem from userland. Fix it by updating __blkdev_get() to issue partition rescan after -ENOMEDIA too. This was reported in the following bz. https://bugzilla.kernel.org/show_bug.cgi?id=13029 Stable: 2.6.38 Signed-off-by: Tejun Heo Reported-by: David Zeuthen Reported-by: Martin Pitt Reported-by: Kay Sievers Tested-by: Kay Sievers Cc: Alan Cox Signed-off-by: Jens Axboe Signed-off-by: Greg Kroah-Hartman --- diff --git a/fs/block_dev.c b/fs/block_dev.c index 8db62b2b6df8..5d8d311af9f4 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -1211,6 +1211,7 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) if (!bdev->bd_part) goto out_clear; + ret = 0; if (disk->fops->open) { ret = disk->fops->open(bdev, mode); if (ret == -ERESTARTSYS) { @@ -1226,9 +1227,18 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) mutex_unlock(&bdev->bd_mutex); goto restart; } - if (ret) - goto out_clear; } + /* + * If the device is invalidated, rescan partition + * if open succeeded or failed with -ENOMEDIUM. + * The latter is necessary to prevent ghost + * partitions on a removed medium. + */ + if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM)) + rescan_partitions(disk, bdev); + if (ret) + goto out_clear; + if (!bdev->bd_openers) { bd_set_size(bdev,(loff_t)get_capacity(disk)<<9); bdi = blk_get_backing_dev_info(bdev); @@ -1236,8 +1246,6 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) bdi = &default_backing_dev_info; bdev->bd_inode->i_data.backing_dev_info = bdi; } - if (bdev->bd_invalidated) - rescan_partitions(disk, bdev); } else { struct block_device *whole; whole = bdget_disk(disk, 0); @@ -1264,13 +1272,14 @@ static int __blkdev_get(struct block_device *bdev, fmode_t mode, int for_part) put_disk(disk); disk = NULL; if (bdev->bd_contains == bdev) { - if (bdev->bd_disk->fops->open) { + ret = 0; + if (bdev->bd_disk->fops->open) ret = bdev->bd_disk->fops->open(bdev, mode); - if (ret) - goto out_unlock_bdev; - } - if (bdev->bd_invalidated) + /* the same as first opener case, read comment there */ + if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM)) rescan_partitions(bdev->bd_disk, bdev); + if (ret) + goto out_unlock_bdev; } } bdev->bd_openers++;