[PATCH 07/10] ufs: revalidate filesystem state before remounting read-write

From: Ali Ahmet Memis

Date: Sat Aug 01 2026 - 18:57:09 EST


ufs_fill_super() inspects fs_clean and, for the Solaris flavours, the
state timestamp, and forces SB_RDONLY when the filesystem was not shut
down cleanly. The remount path never repeats that work. Going from
read-only to read-write only checks that the flavour has write support
at all and that the cylinder groups can be read:

if (!ufs_read_cylinder_structures(sb)) {
...
}
sb->s_flags &= ~SB_RDONLY;

So the decision taken at mount time can simply be undone:

mount -t ufs -o ufstype=ufs2,rw bad.img /mnt # forced read-only
mount -o remount,rw /mnt # writable again
touch /mnt/x

A filesystem that is active, bad or in need of fsck becomes writable
after all, which makes the check at mount time close to meaningless.

Move the state test into ufs_state_allows_write() and call it from both
places, returning -EROFS from the remount when it fails. Linux never
writes fs_clean itself outside of the error paths, so the value the
helper sees on remount is still the one that came off the disk.

No change in which filesystems are accepted at mount time.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Ali Ahmet Memis <ali@xxxxxxxxxxxxxx>
---
fs/ufs/super.c | 95 +++++++++++++++++++++++++++++++-------------------
1 file changed, 59 insertions(+), 36 deletions(-)

diff --git a/fs/ufs/super.c b/fs/ufs/super.c
index c4831a8b9b3f..4b0e9196fa41 100644
--- a/fs/ufs/super.c
+++ b/fs/ufs/super.c
@@ -713,6 +713,60 @@ static u64 ufs_max_bytes(struct super_block *sb)
return res << uspi->s_bshift;
}

+/*
+ * Does the recorded state of the filesystem allow us to write to it?
+ * Called both when mounting and when remounting read-write.
+ */
+static bool ufs_state_allows_write(struct super_block *sb)
+{
+ struct ufs_sb_private_info *uspi = UFS_SB(sb)->s_uspi;
+ struct ufs_super_block_first *usb1 = ubh_get_usb_first(uspi);
+ struct ufs_super_block_third *usb3 = ubh_get_usb_third(uspi);
+
+ switch (UFS_SB(sb)->s_flags & UFS_ST_MASK) {
+ case UFS_ST_44BSD:
+ case UFS_ST_OLD:
+ break;
+ case UFS_ST_SUN:
+ case UFS_ST_SUNOS:
+ case UFS_ST_SUNx86:
+ if (ufs_get_fs_state(sb, usb1, usb3) !=
+ UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time)) {
+ pr_err("%s(): fs needs fsck\n", __func__);
+ return false;
+ }
+ break;
+ default:
+ pr_err("%s(): fs needs fsck\n", __func__);
+ return false;
+ }
+
+ switch (usb1->fs_clean) {
+ case UFS_FSCLEAN:
+ UFSD("fs is clean\n");
+ return true;
+ case UFS_FSSTABLE:
+ UFSD("fs is stable\n");
+ return true;
+ case UFS_FSLOG:
+ UFSD("fs is logging fs\n");
+ return true;
+ case UFS_FSOSF1:
+ UFSD("fs is DEC OSF/1\n");
+ return true;
+ case UFS_FSACTIVE:
+ pr_err("%s(): fs is active\n", __func__);
+ return false;
+ case UFS_FSBAD:
+ pr_err("%s(): fs is bad\n", __func__);
+ return false;
+ default:
+ pr_err("%s(): can't grok fs_clean 0x%x\n",
+ __func__, usb1->fs_clean);
+ return false;
+ }
+}
+
static int ufs_fill_super(struct super_block *sb, struct fs_context *fc)
{
struct ufs_fs_context *ctx = fc->fs_private;
@@ -1049,43 +1103,8 @@ static int ufs_fill_super(struct super_block *sb, struct fs_context *fc)
* Check, if file system was correctly unmounted.
* If not, make it read only.
*/
- if (((flags & UFS_ST_MASK) == UFS_ST_44BSD) ||
- ((flags & UFS_ST_MASK) == UFS_ST_OLD) ||
- (((flags & UFS_ST_MASK) == UFS_ST_SUN ||
- (flags & UFS_ST_MASK) == UFS_ST_SUNOS ||
- (flags & UFS_ST_MASK) == UFS_ST_SUNx86) &&
- (ufs_get_fs_state(sb, usb1, usb3) == (UFS_FSOK - fs32_to_cpu(sb, usb1->fs_time))))) {
- switch(usb1->fs_clean) {
- case UFS_FSCLEAN:
- UFSD("fs is clean\n");
- break;
- case UFS_FSSTABLE:
- UFSD("fs is stable\n");
- break;
- case UFS_FSLOG:
- UFSD("fs is logging fs\n");
- break;
- case UFS_FSOSF1:
- UFSD("fs is DEC OSF/1\n");
- break;
- case UFS_FSACTIVE:
- pr_err("%s(): fs is active\n", __func__);
- sb->s_flags |= SB_RDONLY;
- break;
- case UFS_FSBAD:
- pr_err("%s(): fs is bad\n", __func__);
- sb->s_flags |= SB_RDONLY;
- break;
- default:
- pr_err("%s(): can't grok fs_clean 0x%x\n",
- __func__, usb1->fs_clean);
- sb->s_flags |= SB_RDONLY;
- break;
- }
- } else {
- pr_err("%s(): fs needs fsck\n", __func__);
+ if (!ufs_state_allows_write(sb))
sb->s_flags |= SB_RDONLY;
- }

/*
* Read ufs_super_block into internal data structures
@@ -1291,6 +1310,10 @@ static int ufs_reconfigure(struct fs_context *fc)
mutex_unlock(&UFS_SB(sb)->s_lock);
return -EINVAL;
}
+ if (!ufs_state_allows_write(sb)) {
+ mutex_unlock(&UFS_SB(sb)->s_lock);
+ return -EROFS;
+ }
if (!ufs_read_cylinder_structures(sb)) {
pr_err("failed during remounting\n");
mutex_unlock(&UFS_SB(sb)->s_lock);
--
2.55.0