From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 89B9960A2601 for ; Thu, 29 Oct 2026 16:07:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20210309 header.b=J06v10kr; dkim=fail reason="signature verification failed" (2048-bit key, unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256 header.s=k20260515 header.b=hUtWbXhW DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:Cc:To:In-Reply-To:References:Message-Id :MIME-Version:Subject:Date:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Unsubscribe-Post:List-Owner; bh=LGlhwsihLiO/+IAITyN0t8tyn1GGskU3QC62qCXNO8E=; b=J06v10kr0sa0CvFS6xyrwftdc1 pr6q3POZ2YfFKaETXcuWxjLT5J0Uz2zOHKBDHslWYuCNHLuNUY8JAku697vALELQnvu5iGmyXZfX4 w0UHQ5lE+RweLtlZdroMI6v2HOAV/6XRFdCEu1bTpMKNSCV2O9Giu2SACdbZpZxJZMrrHJPHB6h8H HRH+czRG5HiVDQZsV+reRo5bDSGCvFLT1XrpReKEnkWhToSxh1uUpvZWZ032FAtLLTxB/5fc0p130 7BsFAUpm3ZV8v9HJON7R0he/cJu8CvK52OxOh8/l4ht0ZxoFW5+jPJ9k8Kgie4ljTDUlQIf+0HOrF eSXYDfeQ==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.100.1 #2 (Red Hat Linux)) id 1xMSfR-00000009k66-0uHm; Thu, 29 Oct 2026 16:07:53 +0000 Received: from sea.source.kernel.org ([172.234.252.31]) by bombadil.infradead.org with esmtps (Exim 4.100.1 #2 (Red Hat Linux)) id 1xMSfP-00000009k4p-3X7b for linux-mtd@lists.infradead.org; Thu, 29 Oct 2026 16:07:51 +0000 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) by sea.source.kernel.org (Postfix) with ESMTP id 9891B40A75; Thu, 29 Oct 2026 16:07:51 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id CFADB1F031E1; Thu, 29 Oct 2026 16:07:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1793290071; bh=vWXoC3DGBRzxnPUyhv6W7lJ/GIRbUyR3KNIt8HGMfr4=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=hUtWbXhW3fJajno1Nrb7Ec4J0dHYN6h6leJoiQclqb/1dXpC0rODkG3fKujqVJr7g 34NGOPd1Z8IWVuqab9cP4r7S6etN9kh/tBXDV04IsaZ7rc2d4TTMfJjVR6z3LsJoWX 0mS6H/DFYxU2xnNZuLCWw9zQFOsRo2dvw4vxruXIcM2HLQ3+08QrcQNbaK/VZR48kh YE0d2V6FVDCPczojym4K41diZWjEoXTRQZEnMtQK4MjJ5FoKWk6P5rJJAGCIuXKDmD DOfd75tdwmpnHOd2KQUSHKuR9w7mDMj+YlbQ5eIr8OX70MQbHtWSH4qBw84eEdmYXu MtDsALtrfKoSw== From: Christian Brauner Date: Thu, 29 Oct 2026 17:06:58 +0100 Subject: [PATCH v2 07/34] fat: don't set SB_RDONLY after filesystem errors X-Archived-By: MIME-Version: 1.0 Message-Id: <20261029-work-fs-sb_rdonly-v2-7-2f44f64f0b66@kernel.org> References: <20261029-work-fs-sb_rdonly-v2-0-2f44f64f0b66@kernel.org> In-Reply-To: <20261029-work-fs-sb_rdonly-v2-0-2f44f64f0b66@kernel.org> To: linux-fsdevel@vger.kernel.org, Jan Kara Cc: Al Viro , ecryptfs@vger.kernel.org, linux-ext4@vger.kernel.org, jfs-discussion@lists.sourceforge.net, linux-nilfs@vger.kernel.org, exfat@lists.linux.dev, linux-mm@kvack.org, ntfs@lists.linux.dev, ocfs2-devel@lists.linux.dev, linux-btrfs@vger.kernel.org, gfs2@lists.linux.dev, linux-efi@vger.kernel.org, linux-mtd@lists.infradead.org, linux-xfs@vger.kernel.org, "Christian Brauner (Amutable)" X-Mailer: b4 0.17-dev-ce4c1 X-Developer-Signature: v=1; a=openpgp-sha256; l=7436; i=brauner@kernel.org; h=from:subject:message-id; bh=8raaRLMglHB1XzqC3kqIXTQ2e6NIuFME1v48GfRyzxs=; b=owGbwMvMwCU28Zj0gdSKO4sYT6slMWQ9ztfvYHB9vsnz5x3XmYyHfxs2f/r63Dfm+ZOFbRuEH 52bm8O1pqOUhUGMi0FWTJHFod0kXG45T8Vmo0wNmDmsTCBDGLg4BWAixzcx/BVea9y9bfOv8ijd w1cCtgem+tSvUBc8JL28WUXJ6fkhj0UM/x1Lc8MKtk/YanDo+RP+95vm84afb1uT/P97oXVzw79 bYVwA X-Developer-Key: i=brauner@kernel.org; a=openpgp; fpr=4880B8C9BD0E5106FC070F4F7B3C391EFEA93624 X-BeenThere: linux-mtd@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Linux MTD discussion mailing list List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "linux-mtd" X-Spam-Status: No, score=-16.9 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_EF,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_NONE, SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=4.0.1 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on aws-us-west-2-korg-listserv-alma10-1 With errors=remount-ro, the default, __fat_fs_error() sets SB_RDONLY from whatever context hit the error and without holding s_umount. If that happens while the filesystem is frozen through bdev_freeze(), e.g. by a device mapper suspend, thaw_super() sees a read-only superblock and leaves the s_writers rwsems locked so every writer hangs. So call sb_set_emergency_ro() instead and let the VFS refuse modifications. fat still refuses FAT writes, cluster allocation, write_begin, O_DIRECT writes, FITRIM and fat_write_inode() itself, leaves the volume marked dirty and doesn't free fallocated clusters on evict. With the flush mount option msdos treats EROFS from fat_flush_inodes() as success in that state because the directory entries were already changed. Errors while mounting still set SB_RDONLY, nothing can freeze the superblock before SB_BORN. A remount to read-write fails until umount and a remount to read-only doesn't mark the volume clean. Writes through file descriptors opened before the error fail with EROFS. Signed-off-by: Christian Brauner (Amutable) --- fs/fat/dir.c | 3 +++ fs/fat/fatent.c | 11 ++++++++++- fs/fat/file.c | 4 ++++ fs/fat/inode.c | 19 +++++++++++++++++-- fs/fat/misc.c | 19 +++++++++++++------ 5 files changed, 47 insertions(+), 9 deletions(-) diff --git a/fs/fat/dir.c b/fs/fat/dir.c index 35bdb62944a2..553034bcbed4 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c @@ -1329,6 +1329,9 @@ int fat_add_entries(struct inode *dir, void *slots, int nr_slots, loff_t pos; bool saw_eod; + if (unlikely(sb_emergency_ro(sb))) + return -EROFS; + sinfo->nr_slots = nr_slots; /* First stage: search free directory entries */ diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index 2d10a815a579..158ce7056dcf 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c @@ -314,7 +314,7 @@ static void mark_fsinfo_dirty(struct super_block *sb) { struct msdos_sb_info *sbi = MSDOS_SB(sb); - if (sb_rdonly(sb) || !is_fat32(sbi)) + if (sb_rdonly(sb) || sb_emergency_ro(sb) || !is_fat32(sbi)) return; __mark_inode_dirty(sbi->fsinfo_inode, I_DIRTY_SYNC); @@ -419,6 +419,9 @@ int fat_ent_write(struct inode *inode, struct fat_entry *fatent, const struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops; int err; + if (unlikely(sb_emergency_ro(sb))) + return -EROFS; + ops->ent_put(fatent, new); if (wait) { err = fat_sync_bhs(fatent->bhs, fatent->nr_bhs); @@ -513,6 +516,9 @@ static int __fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluste struct buffer_head *bhs[MAX_BUF_PER_PAGE]; int i, count, err, nr_bhs, idx_clus; + if (unlikely(sb_emergency_ro(sb))) + return -EROFS; + lock_fat(sbi); if (sbi->free_clusters != -1 && sbi->free_clus_valid && sbi->free_clusters < nr_cluster) { @@ -620,6 +626,9 @@ int fat_free_clusters(struct inode *inode, int cluster) int i, err, nr_bhs; int first_cl = cluster, dirty_fsinfo = 0; + if (unlikely(sb_emergency_ro(sb))) + return -EROFS; + nr_bhs = 0; fatent_init(&fatent); lock_fat(sbi); diff --git a/fs/fat/file.c b/fs/fat/file.c index d4f1b360b974..3d0a81994fb7 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c @@ -135,6 +135,10 @@ static int fat_ioctl_fitrim(struct inode *inode, unsigned long arg) if (!capable(CAP_SYS_ADMIN)) return -EPERM; + /* Don't trust the FAT after an error. */ + if (unlikely(sb_emergency_ro(sb))) + return -EROFS; + if (!bdev_max_discard_sectors(sb->s_bdev)) return -EOPNOTSUPP; diff --git a/fs/fat/inode.c b/fs/fat/inode.c index d85c8fbdcca4..afd73dc4ba9a 100644 --- a/fs/fat/inode.c +++ b/fs/fat/inode.c @@ -228,6 +228,9 @@ static int fat_write_begin(const struct kiocb *iocb, { int err; + if (unlikely(sb_emergency_ro(mapping->host->i_sb))) + return -EROFS; + err = cont_write_begin(iocb, mapping, pos, len, foliop, fsdata, fat_get_block, &MSDOS_I(mapping->host)->mmu_private); @@ -264,6 +267,8 @@ static ssize_t fat_direct_IO(struct kiocb *iocb, struct iov_iter *iter) ssize_t ret; if (iov_iter_rw(iter) == WRITE) { + if (unlikely(sb_emergency_ro(inode->i_sb))) + return -EROFS; /* * FIXME: blockdev_direct_IO() doesn't use ->write_begin(), * so we need to update the ->mmu_private to block boundary. @@ -662,6 +667,10 @@ static int fat_sync_inode_metadata(struct inode *inode, static void fat_free_eofblocks(struct inode *inode) { + /* Nothing can be freed after an error. */ + if (sb_emergency_ro(inode->i_sb)) + return; + /* Release unwritten fallocated blocks on inode eviction. */ if ((inode->i_blocks << 9) > round_up(MSDOS_I(inode)->mmu_private, @@ -710,8 +719,8 @@ static void fat_set_state(struct super_block *sb, struct fat_boot_sector *b; struct msdos_sb_info *sbi = MSDOS_SB(sb); - /* do not change any thing if mounted read only */ - if (sb_rdonly(sb) && !force) + /* do not change any thing if mounted read only or after an error */ + if ((sb_rdonly(sb) && !force) || sb_emergency_ro(sb)) return; /* do not change state if fs was dirty */ @@ -954,6 +963,9 @@ static int fat_write_inode(struct inode *inode, struct writeback_control *wbc) { int err; + if (unlikely(sb_emergency_ro(inode->i_sb))) + return -EROFS; + if (inode->i_ino == MSDOS_FSINFO_INO) { struct super_block *sb = inode->i_sb; @@ -1949,6 +1961,9 @@ int fat_flush_inodes(struct super_block *sb, struct inode *i1, struct inode *i2) ret = writeback_inode(i2); if (!ret) ret = sync_blockdev_nowait(sb->s_bdev); + /* The change is done, an error made us read-only in the meantime. */ + if (ret == -EROFS && sb_emergency_ro(sb)) + ret = 0; return ret; } EXPORT_SYMBOL_GPL(fat_flush_inodes); diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 65d02f2edec3..05d061b594a6 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c @@ -14,9 +14,9 @@ * fat_fs_error reports a file system problem that might indicate fa data * corruption/inconsistency. Depending on 'errors' mount option the * panic() is called, or error message is printed FAT and nothing is done, - * or filesystem is remounted read-only (default behavior). - * In case the file system is remounted read-only, it can be made writable - * again by remounting it. + * or the filesystem is made read-only (default behavior): a mount in + * progress comes up read-only, a mounted filesystem refuses all further + * modifications until it is unmounted. */ void __fat_fs_error(struct super_block *sb, int report, const char *fmt, ...) { @@ -32,10 +32,17 @@ void __fat_fs_error(struct super_block *sb, int report, const char *fmt, ...) va_end(args); } - if (opts->errors == FAT_ERRORS_PANIC) + if (opts->errors == FAT_ERRORS_PANIC) { panic("FAT-fs (%s): fs panic from previous error\n", sb->s_id); - else if (opts->errors == FAT_ERRORS_RO && !sb_rdonly(sb)) { - sb->s_flags |= SB_RDONLY; + } else if (opts->errors == FAT_ERRORS_RO && !sb_rdonly(sb)) { + /* + * Before SB_BORN the mounter holds s_umount so SB_RDONLY is + * fine. Later it needs a remount, see sb_set_emergency_ro(). + */ + if (!(sb->s_flags & SB_BORN)) + sb->s_flags |= SB_RDONLY; + else if (!sb_set_emergency_ro(sb)) + return; fat_msg(sb, KERN_ERR, "Filesystem has been set read-only"); } } -- 2.53.0 ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/