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
