[Devel] [PATCH hci-8.1 v2 1/3] drivers/md/dm-qcow2: treat images of a read-only table as read-only

Alexander Ivanov alexander.ivanov at virtuozzo.com
Thu Oct 29 17:43:56 MSK 2026


Whether an image may be modified is decided by the mode of its file
only. Userspace may pass an O_RDWR top image to a read-only table, and
then dm-qcow2 pads the unaligned image tail on .ctr and .preresume and
considers the image writable on the discard and merge paths.

merge_backward is also allowed on a read-only table if the lower image
is opened for write. qcow2_set_image_file_features() checks the table
mode and skips the lower image, so the merge writes to it without the
dirty bit and without clearing the bitmaps autoclear bit. Enabled
persistent bitmaps of the lower image are left marked consistent
although the image data changed.

Consider an image writable only if both its file and the table are
writable, and use this check for the header update, the tail padding
and the short read near EOF as well. merge_backward on a read-only
table now fails with -EACCES.

https://virtuozzo.atlassian.net/browse/VSTOR-138887
Feature: dm-qcow2: block device over QCOW2 files driver

Signed-off-by: Alexander Ivanov <alexander.ivanov at virtuozzo.com>
---
 drivers/md/dm-qcow2-map.c    | 6 ++----
 drivers/md/dm-qcow2-target.c | 6 ++----
 drivers/md/dm-qcow2.h        | 3 ++-
 3 files changed, 6 insertions(+), 9 deletions(-)

diff --git a/drivers/md/dm-qcow2-map.c b/drivers/md/dm-qcow2-map.c
index 78ee7cf5e881..bc317c8db3a6 100644
--- a/drivers/md/dm-qcow2-map.c
+++ b/drivers/md/dm-qcow2-map.c
@@ -1470,16 +1470,14 @@ static void md_page_read_complete(struct qio *qio)
 	struct md_page *md = qio->ext->md;
 	struct qcow2 *qcow2 = qio->qcow2;
 	int ret = qio->ret;
-	mode_t mode;
 
 	BUG_ON(qvec->bvec[0].bv_page != md->page);
 
 	if (unlikely(ret != PAGE_SIZE && ret > 0)) {
-		/* Read near EOF? See qcow2_attach_file() */
+		/* Read near EOF? See qcow2_parse_header() */
 		loff_t pos = (md->id << PAGE_SHIFT) + ret;
 
-		mode = qcow2->file->f_mode;
-		if (pos == qcow2->file_size && !(mode & FMODE_WRITE)) {
+		if (pos == qcow2->file_size && !qcow2_file_is_writable(qcow2)) {
 			qcow2_zero_fill_page_from(md->page, ret);
 			ret = PAGE_SIZE;
 		}
diff --git a/drivers/md/dm-qcow2-target.c b/drivers/md/dm-qcow2-target.c
index be09615eb8e2..2d1e5d4e1b3a 100644
--- a/drivers/md/dm-qcow2-target.c
+++ b/drivers/md/dm-qcow2-target.c
@@ -623,13 +623,11 @@ int qcow2_set_image_file_features(struct qcow2 *qcow2, bool dirty)
 	u64 dirty_mask = cpu_to_be64(INCOMPATIBLE_FEATURES_DIRTY_BIT);
 	struct QCowHeader *raw_hdr;
 	struct md_page *md;
-	bool is_ro;
 
 	if (qcow2->hdr.version ==  2)
 		return 0;
 
-	is_ro = !(dm_table_get_mode(qcow2->tgt->ti->table) & FMODE_WRITE);
-	if (is_ro)
+	if (!qcow2_file_is_writable(qcow2))
 		return 0;
 
 	md = qcow2_md_page_find(qcow2, 0);
@@ -840,7 +838,7 @@ static int qcow2_parse_header(struct dm_target *ti, struct qcow2 *qcow2,
 	int ret;
 
 	qcow2->file_size = i_size_read(file_inode(file));
-	if ((file->f_mode & FMODE_WRITE) && (qcow2->file_size & ~PAGE_MASK)) {
+	if (qcow2_file_is_writable(qcow2) && (qcow2->file_size & ~PAGE_MASK)) {
 		new_size = PAGE_ALIGN(qcow2->file_size);
 		ret = qcow2_truncate_safe(file, new_size);
 		if (ret) {
diff --git a/drivers/md/dm-qcow2.h b/drivers/md/dm-qcow2.h
index d9b8c38e093f..8f10c5812a1b 100644
--- a/drivers/md/dm-qcow2.h
+++ b/drivers/md/dm-qcow2.h
@@ -462,7 +462,8 @@ static inline bool qcow2_wants_check(struct qcow2_target *tgt)
 
 static inline bool qcow2_file_is_writable(struct qcow2 *qcow2)
 {
-	return qcow2->file->f_mode & FMODE_WRITE;
+	return (qcow2->file->f_mode & FMODE_WRITE) &&
+	       (dm_table_get_mode(qcow2->tgt->ti->table) & BLK_OPEN_WRITE);
 }
 
 static inline void remap_to_clu(struct qcow2 *qcow2, struct qio *qio, loff_t clu_pos)
-- 
2.53.0



More information about the Devel mailing list