在 2024/1/9 11:02, Yu Kuai 写道:
Hi,
在 2024/01/09 10:46, Yu Kuai 写道:
Hi,
在 2024/01/09 10:19, Li Lingfeng 写道:
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8S3GW CVE: NA
When writing to a mounted block device is allowed, we need to identify which processes may write to the block device to prevent the file system from being damaged in silence.
Signed-off-by: Li Lingfeng lilingfeng3@huawei.com
block/Kconfig | 9 +++++++++ block/bdev.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 2 deletions(-)
diff --git a/block/Kconfig b/block/Kconfig index 05fdd81a708f..29065fb2860b 100644 --- a/block/Kconfig +++ b/block/Kconfig @@ -249,6 +249,15 @@ config BLK_DEV_DETECT_WRITING_PART0 As a supplement to BLK_DEV_WRITE_MOUNTED(_QUIET), enabling this to detect the scenario above. +config BLK_DEV_WRITE_MOUNTED_QUIET + bool "Keep quiet when detecting conflict of opening block device" + default y + depends on BLK_DEV_WRITE_MOUNTED + help + While writing to mounted block devices is allowed, disabling this + to dump info when opening mounted block devices for write or + trying to mount write opened block devices.
source "block/partitions/Kconfig" config BLK_MQ_PCI diff --git a/block/bdev.c b/block/bdev.c index 4b0e6c49a9e7..720b01b4cb45 100644 --- a/block/bdev.c +++ b/block/bdev.c @@ -34,10 +34,13 @@ #define BLKDEV_ALLOW_WRITE_MOUNTED 0 /* Should we detect writing to part0 when partitions mounted */ #define BLKDEV_DETECT_WRITING_PART0 1 +/* Should we keep quiet when opening mounted block devices for write? */ +#define BLKDEV_WRITE_MOUNTED_QUIET 2 static u8 bdev_allow_write_mounted = IS_ENABLED(CONFIG_BLK_DEV_WRITE_MOUNTED) << BLKDEV_ALLOW_WRITE_MOUNTED || - IS_ENABLED(CONFIG_BLK_DEV_DETECT_WRITING_PART0) << BLKDEV_DETECT_WRITING_PART0; + IS_ENABLED(CONFIG_BLK_DEV_DETECT_WRITING_PART0) << BLKDEV_DETECT_WRITING_PART0 || + IS_ENABLED(CONFIG_BLK_DEV_WRITE_MOUNTED_QUIET) << BLKDEV_WRITE_MOUNTED_QUIET; struct bdev_inode { struct block_device bdev; @@ -739,6 +742,27 @@ void blkdev_put_no_open(struct block_device *bdev) put_device(&bdev->bd_device); } +static void blkdev_dump_conflict_opener(struct block_device *bdev, char *msg) +{ + char name[BDEVNAME_SIZE]; + struct task_struct *p = NULL; + char comm_buf[TASK_COMM_LEN]; + pid_t p_pid;
+ rcu_read_lock(); + p = rcu_dereference(current->real_parent); + task_lock(p); + strncpy(comm_buf, p->comm, TASK_COMM_LEN); + p_pid = p->pid; + task_unlock(p); + rcu_read_unlock();
+ snprintf(name, sizeof(name), "%pg", bdev); + pr_info_ratelimited("%s [%s]. current [%d %s]. parent [%d %s]\n", + msg, name, + current->pid, current->comm, p_pid, comm_buf); +}
static bool bdev_writes_blocked(struct block_device *bdev) { return !!bdev->bd_mounters; @@ -767,10 +791,25 @@ static bool bdev_mount_blocked(struct block_device *bdev) return bdev->bd_writers > 0; } +static void bdev_dump_info(struct block_device *bdev, blk_mode_t mode) +{ + if (bdev_allow_write_mounted & (1 << BLKDEV_WRITE_MOUNTED_QUIET)) + return;
+ if (mode & BLK_OPEN_WRITE && bdev_writes_blocked(bdev)) + blkdev_dump_conflict_opener(bdev, "VFS: Open an exclusive opened " + "block device for write"); + else if (mode & BLK_OPEN_RESTRICT_WRITES && bdev_mount_blocked(bdev)) + blkdev_dump_conflict_opener(bdev, "VFS: Open a write opened " + "block device exclusively"); +}
bdev_dump_info() looks like the same as bdev_may_open(), how about:
bdev_may_open() { bool blocked = false;
if (bdev_allow_write_mounted & (1 << BLKDEV_WRITE_MOUNTED_QUIET)) return true;
if (mode & BLK_OPEN_WRITE && bdev_writes_blocked(bdev)) { blocked = true; bdev_dump... } else is (else if (mode & BLK_OPEN_RESTRICT_WRITES && bdev_mount_blocked(bdev))) { blocked = true; bdev_dump... }
return !blocked || (bdev_allow_write_mounted & (1 << BLKDEV_ALLOW_WRITE_MOUNTED); }
Or this is better:
bdev_may_conflict_open() { if (bdev_allow_write_mounted & (1 << BLKDEV_ALLOW_WRITE_MOUNTED)) return true;
// kernel log return false; }
bdev_may_open() { if (bdev_allow_write_mounted & (1 << BLKDEV_WRITE_MOUNTED_QUIET)) return true; if (xxx) return bdev_may_conflict_open(); else if (xxx) return bdev_may_conflict_open(); return true; }
How about just extract the logic of detection as a function, like this:
u8 bdev_open_conficted() { if (mode & BLK_OPEN_WRITE && bdev_writes_blocked(bdev)) return 1; if (mode & BLK_OPEN_RESTRICT_WRITES && bdev_mount_blocked(bdev)) return 2; return 0; }
bdev_may_open() { bool blocked; u8 open_conficted;
if (bdev_allow_write_mounted & (1 << BLKDEV_WRITE_MOUNTED_QUIET)) return true;
open_conficted = bdev_open_conficted(); sitch (open_conficted) { case 0: blocked = false; case 1: blkdev_dump_conflict_opener(); blocked = true; case 2: blkdev_dump_conflict_opener(); blocked = true; };
return (bdev_allow_write_mounted & (1 << BLKDEV_ALLOW_WRITE_MOUNTED)) || !blocked; }
static bool bdev_may_open(struct block_device *bdev, blk_mode_t mode) { - if (bdev_allow_write_mounted & (1 << BLKDEV_ALLOW_WRITE_MOUNTED)) + if (bdev_allow_write_mounted & (1 << BLKDEV_ALLOW_WRITE_MOUNTED)) { + bdev_dump_info(bdev, mode); return true; + } /* Writes blocked? */ if (mode & BLK_OPEN_WRITE && bdev_writes_blocked(bdev)) return false; @@ -1151,6 +1190,19 @@ static int __init setup_bdev_allow_write_mounted(char *str) if (kstrtou8(str, 0, &bdev_allow_write_mounted)) pr_warn("Invalid option string for bdev_allow_write_mounted:" " '%s'\n", str); + /* + * It's meaningless to set BLKDEV_WRITE_MOUNTED_QUIET if + * BLKDEV_ALLOW_WRITE_MOUNTED is not set. + */ + if (!(bdev_allow_write_mounted & (1 << BLKDEV_ALLOW_WRITE_MOUNTED))) { + if (bdev_allow_write_mounted & (1 << BLKDEV_WRITE_MOUNTED_QUIET)) { + pr_warn("Invalid value for bdev_allow_write_mounted:" + " '%d', it should be set as 0/1/2/3/5/7\n", + bdev_allow_write_mounted); + bdev_allow_write_mounted &= ~(1 << BLKDEV_WRITE_MOUNTED_QUIET); + } + }
return 1; } __setup("bdev_allow_write_mounted=", setup_bdev_allow_write_mounted);