From: Jan Kara <jack@suse.cz>
Date: Wed, 30 Nov 2022 18:19:46 +0100
Subject: [PATCH] block: Do not reread partition table on exclusively open
device
References: bsc#1190969
Patch-mainline: v6.2-rc1
Git-commit: 36369f46e91785688a5f39d7a5590e3f07981316
Since commit 10c70d95c0f2 ("block: remove the bd_openers checks in
blk_drop_partitions") we allow rereading of partition table although
there are users of the block device. This has an undesirable consequence
that e.g. if sda and sdb are assembled to a RAID1 device md0 with
partitions, BLKRRPART ioctl on sda will rescan partition table and
create sda1 device. This partition device under a raid device confuses
some programs (such as libstorage-ng used for initial partitioning for
distribution installation) leading to failures.
Fix the problem refusing to rescan partitions if there is another user
that has the block device exclusively open.
Link: https://lore.kernel.org/all/20221130135344.2ul4cyfstfs3znxg@quack3
Fixes: 10c70d95c0f2 ("block: remove the bd_openers checks in blk_drop_partitions")
Signed-off-by: Jan Kara <jack@suse.cz>
---
block/ioctl.c | 32 +++++++++++++++++++++++++-------
fs/block_dev.c | 3 +--
include/linux/genhd.h | 1 +
3 files changed, 27 insertions(+), 9 deletions(-)
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -81,7 +81,8 @@ static int compat_blkpg_ioctl(struct blo
}
#endif
-static int blkdev_reread_part(struct block_device *bdev, fmode_t mode)
+static int blkdev_reread_part(struct block_device *bdev, fmode_t mode,
+ void *owner)
{
struct block_device *tmp;
@@ -91,6 +92,8 @@ static int blkdev_reread_part(struct blo
return -EACCES;
if (bdev->bd_disk->open_partitions)
return -EBUSY;
+ if (bdev->bd_holder && bdev->bd_holder != owner)
+ return -EBUSY;
/*
* Reopen the device to revalidate the driver state and force a
@@ -465,7 +468,8 @@ static int blkdev_bszset(struct block_de
* to deal with the compat_ptr() conversion.
*/
static int blkdev_common_ioctl(struct block_device *bdev, fmode_t mode,
- unsigned cmd, unsigned long arg, void __user *argp)
+ unsigned cmd, unsigned long arg,
+ void __user *argp, void *owner)
{
unsigned int max_sectors;
@@ -519,7 +523,7 @@ static int blkdev_common_ioctl(struct bl
bdev->bd_bdi->ra_pages = (arg * 512) / PAGE_SIZE;
return 0;
case BLKRRPART:
- return blkdev_reread_part(bdev, mode);
+ return blkdev_reread_part(bdev, mode, owner);
case BLKTRACESTART:
case BLKTRACESTOP:
case BLKTRACETEARDOWN:
@@ -547,8 +551,8 @@ static int blkdev_common_ioctl(struct bl
*
* New commands must be compatible and go into blkdev_common_ioctl
*/
-int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
- unsigned long arg)
+static int blkdev_do_ioctl(struct block_device *bdev, fmode_t mode,
+ unsigned cmd, unsigned long arg, void *owner)
{
int ret;
loff_t size;
@@ -588,7 +592,7 @@ int blkdev_ioctl(struct block_device *bd
break;
}
- ret = blkdev_common_ioctl(bdev, mode, cmd, arg, argp);
+ ret = blkdev_common_ioctl(bdev, mode, cmd, arg, argp, owner);
if (ret != -ENOIOCTLCMD)
return ret;
@@ -596,6 +600,20 @@ int blkdev_ioctl(struct block_device *bd
return -ENOTTY;
return bdev->bd_disk->fops->ioctl(bdev, mode, cmd, arg);
}
+
+int blkdev_file_ioctl(struct file *file, fmode_t mode, unsigned cmd,
+ unsigned long arg)
+{
+ struct block_device *bdev = I_BDEV(file->f_mapping->host);
+
+ return blkdev_do_ioctl(bdev, mode, cmd, arg, file);
+}
+
+int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
+ unsigned long arg)
+{
+ return blkdev_do_ioctl(bdev, mode, cmd, arg, NULL);
+}
EXPORT_SYMBOL_GPL(blkdev_ioctl); /* for /dev/raw */
#ifdef CONFIG_COMPAT
@@ -660,7 +678,7 @@ long compat_blkdev_ioctl(struct file *fi
break;
}
- ret = blkdev_common_ioctl(bdev, mode, cmd, arg, argp);
+ ret = blkdev_common_ioctl(bdev, mode, cmd, arg, argp, file);
if (ret == -ENOIOCTLCMD && disk->fops->compat_ioctl)
ret = disk->fops->compat_ioctl(bdev, mode, cmd, arg);
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -1589,7 +1589,6 @@ static int blkdev_close(struct inode * i
static long block_ioctl(struct file *file, unsigned cmd, unsigned long arg)
{
- struct block_device *bdev = I_BDEV(bdev_file_inode(file));
fmode_t mode = file->f_mode;
/*
@@ -1601,7 +1600,7 @@ static long block_ioctl(struct file *fil
else
mode &= ~FMODE_NDELAY;
- return blkdev_ioctl(bdev, mode, cmd, arg);
+ return blkdev_file_ioctl(file, mode, cmd, arg);
}
/*
--- a/include/linux/genhd.h
+++ b/include/linux/genhd.h
@@ -304,6 +304,7 @@ void set_capacity(struct gendisk *disk,
/* for drivers/char/raw.c: */
int blkdev_ioctl(struct block_device *, fmode_t, unsigned, unsigned long);
+int blkdev_file_ioctl(struct file *, fmode_t, unsigned, unsigned long);
long compat_blkdev_ioctl(struct file *, unsigned, unsigned long);
#ifdef CONFIG_SYSFS