Commit 02e352287a40bd456eb78df705bf888bc3161d3f

Authored by Tejun Heo
Committed by Jens Axboe
1 parent bf2253a6f0

block: rescan partitions on invalidated devices on -ENOMEDIA too

__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 <tj@kernel.org>
Reported-by: David Zeuthen <zeuthen@gmail.com>
Reported-by: Martin Pitt <martin.pitt@ubuntu.com>
Reported-by: Kay Sievers <kay.sievers@vrfy.org>
Tested-by: Kay Sievers <kay.sievers@vrfy.org>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: stable@kernel.org
Signed-off-by: Jens Axboe <jaxboe@fusionio.com>

Showing 1 changed file with 18 additions and 9 deletions Side-by-side Diff

... ... @@ -1102,6 +1102,7 @@
1102 1102 if (!bdev->bd_part)
1103 1103 goto out_clear;
1104 1104  
  1105 + ret = 0;
1105 1106 if (disk->fops->open) {
1106 1107 ret = disk->fops->open(bdev, mode);
1107 1108 if (ret == -ERESTARTSYS) {
1108 1109  
... ... @@ -1118,9 +1119,18 @@
1118 1119 put_disk(disk);
1119 1120 goto restart;
1120 1121 }
1121   - if (ret)
1122   - goto out_clear;
1123 1122 }
  1123 + /*
  1124 + * If the device is invalidated, rescan partition
  1125 + * if open succeeded or failed with -ENOMEDIUM.
  1126 + * The latter is necessary to prevent ghost
  1127 + * partitions on a removed medium.
  1128 + */
  1129 + if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM))
  1130 + rescan_partitions(disk, bdev);
  1131 + if (ret)
  1132 + goto out_clear;
  1133 +
1124 1134 if (!bdev->bd_openers) {
1125 1135 bd_set_size(bdev,(loff_t)get_capacity(disk)<<9);
1126 1136 bdi = blk_get_backing_dev_info(bdev);
... ... @@ -1128,8 +1138,6 @@
1128 1138 bdi = &default_backing_dev_info;
1129 1139 bdev_inode_switch_bdi(bdev->bd_inode, bdi);
1130 1140 }
1131   - if (bdev->bd_invalidated)
1132   - rescan_partitions(disk, bdev);
1133 1141 } else {
1134 1142 struct block_device *whole;
1135 1143 whole = bdget_disk(disk, 0);
1136 1144  
1137 1145  
... ... @@ -1153,13 +1161,14 @@
1153 1161 }
1154 1162 } else {
1155 1163 if (bdev->bd_contains == bdev) {
1156   - if (bdev->bd_disk->fops->open) {
  1164 + ret = 0;
  1165 + if (bdev->bd_disk->fops->open)
1157 1166 ret = bdev->bd_disk->fops->open(bdev, mode);
1158   - if (ret)
1159   - goto out_unlock_bdev;
1160   - }
1161   - if (bdev->bd_invalidated)
  1167 + /* the same as first opener case, read comment there */
  1168 + if (bdev->bd_invalidated && (!ret || ret == -ENOMEDIUM))
1162 1169 rescan_partitions(bdev->bd_disk, bdev);
  1170 + if (ret)
  1171 + goto out_unlock_bdev;
1163 1172 }
1164 1173 /* only one opener holds refs to the module and disk */
1165 1174 module_put(disk->fops->owner);