[Concept,18/26] ext4l: Add support for read-only devices

Message ID 20251231223008.3251711-19-sjg@u-boot.org
State New
Headers
Series ext4l: Add write support (part L) |

Commit Message

Simon Glass Dec. 31, 2025, 10:29 p.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

Some block devices, such as LUKS-encrypted volumes mounted via
blkmap_crypt, only support read access. When ext4l attempts to write
the superblock during mount, the write fails and causes mount to fail.

Add a way to detect this read-only device detection:

- Test writes during mount by writing back the superblock data; if the
  write fails, mark the device as read-only
- Update bdev_read_only() to return the actual read_only status
- Update sb_rdonly() to check the SB_RDONLY flag

This allows ext4l to successfully mount read-only devices like LUKS
volumes for read access.

We could perhaps have a read-only flag in the block device, but that is
left for another day.

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 fs/ext4l/ext4_uboot.h |  8 ++++----
 fs/ext4l/interface.c  | 14 ++++++++++++--
 include/linux/fs.h    |  1 +
 3 files changed, 17 insertions(+), 6 deletions(-)
  

Patch

diff --git a/fs/ext4l/ext4_uboot.h b/fs/ext4l/ext4_uboot.h
index 00419fe834e..da59956eee8 100644
--- a/fs/ext4l/ext4_uboot.h
+++ b/fs/ext4l/ext4_uboot.h
@@ -315,8 +315,8 @@  extern struct user_namespace init_user_ns;
 /* might_sleep - stub */
 #define might_sleep()	do { } while (0)
 
-/* sb_rdonly - U-Boot mounts filesystems read-write */
-#define sb_rdonly(sb)	0
+/* sb_rdonly - check if filesystem is mounted read-only */
+#define sb_rdonly(sb)	((sb)->s_flags & SB_RDONLY)
 
 /* Trace stubs */
 #define trace_ext4_journal_start_inode(...)	do { } while (0)
@@ -687,10 +687,10 @@  struct super_block {
 	struct list_head s_inodes;
 };
 
-/* Block device read-only check - stub */
+/* Block device read-only check */
 static inline int bdev_read_only(struct block_device *bdev)
 {
-	return 0;
+	return bdev ? bdev->read_only : 0;
 }
 
 /* kuid_t and kgid_t - from linux/cred.h */
diff --git a/fs/ext4l/interface.c b/fs/ext4l/interface.c
index 301e28af3b8..c60ea7db684 100644
--- a/fs/ext4l/interface.c
+++ b/fs/ext4l/interface.c
@@ -401,8 +401,6 @@  int ext4l_probe(struct blk_desc *fs_dev_desc,
 		goto err_free_buf;
 	}
 
-	free(buf);
-
 	/* Save device info for later operations */
 	ext4l_dev_desc = fs_dev_desc;
 	if (fs_partition)
@@ -411,6 +409,18 @@  int ext4l_probe(struct blk_desc *fs_dev_desc,
 	/* Set block device for buffer I/O */
 	ext4l_set_blk_dev(fs_dev_desc, fs_partition);
 
+	/*
+	 * Test if device supports writes by writing back the same data.
+	 * If write returns 0, the device is read-only (e.g. LUKS/blkmap_crypt)
+	 */
+	if (blk_dwrite(fs_dev_desc,
+		       (part_offset + BLOCK_SIZE) / fs_dev_desc->blksz,
+		       2, buf) != 2) {
+		sb->s_bdev->read_only = true;
+		sb->s_flags |= SB_RDONLY;
+	}
+	free(buf);
+
 	/* Mount the filesystem */
 	ret = ext4_fill_super(sb, fc);
 	if (ret) {
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 0bf0d3b0379..54c0148ee72 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -76,6 +76,7 @@  struct block_device {
 	void *bd_disk;
 	struct super_block *bd_super;
 	dev_t bd_dev;
+	bool read_only;
 };
 
 /* errseq functions - stubs */