git: b2e4b6358440 - main - msdosfs: add msdosfs_integrity_error()

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Sat, 08 Jan 2022 04:29:37 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=b2e4b635844091de0601f5b7157d87115386b2a9

commit b2e4b635844091de0601f5b7157d87115386b2a9
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2021-12-25 21:20:56 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2022-01-08 04:20:48 +0000

    msdosfs: add msdosfs_integrity_error()
    
    A function to remount the filesystem from rw to ro on integrity error.
    The work is performed in taskqueue to allow the call to be done from
    almost arbitrary context where erronous state was detected.
    
    Tested by:      pho
    Reviewed by:    markj, mckusick
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D33721
---
 sys/fs/msdosfs/msdosfs_vfsops.c | 58 +++++++++++++++++++++++++++++++++++++++++
 sys/fs/msdosfs/msdosfsmount.h   |  7 +++++
 2 files changed, 65 insertions(+)

diff --git a/sys/fs/msdosfs/msdosfs_vfsops.c b/sys/fs/msdosfs/msdosfs_vfsops.c
index 8ef46e063420..3c2606ab5a91 100644
--- a/sys/fs/msdosfs/msdosfs_vfsops.c
+++ b/sys/fs/msdosfs/msdosfs_vfsops.c
@@ -65,6 +65,7 @@
 #include <sys/priv.h>
 #include <sys/proc.h>
 #include <sys/stat.h>
+#include <sys/taskqueue.h>
 #include <sys/vnode.h>
 
 #include <geom/geom.h>
@@ -112,6 +113,7 @@ struct iconv_functions *msdosfs_iconv;
 
 static int	update_mp(struct mount *mp, struct thread *td);
 static int	mountmsdosfs(struct vnode *devvp, struct mount *mp);
+static void	msdosfs_remount_ro(void *arg, int pending);
 static vfs_fhtovp_t	msdosfs_fhtovp;
 static vfs_mount_t	msdosfs_mount;
 static vfs_root_t	msdosfs_root;
@@ -337,6 +339,13 @@ msdosfs_mount(struct mount *mp)
 			mp->mnt_flag &= ~MNT_RDONLY;
 			MNT_IUNLOCK(mp);
 		}
+
+		/*
+		 * Avoid namei() below.  The "from" option is not set.
+		 * Update of the devvp is pointless for this case.
+		 */
+		if ((pmp->pm_flags & MSDOSFS_ERR_RO) != 0)
+			return (0);
 	}
 	/*
 	 * Not an update, or updating the name: look up the name
@@ -471,6 +480,8 @@ mountmsdosfs(struct vnode *devvp, struct mount *mp)
 	lockinit(&pmp->pm_fatlock, 0, msdosfs_lock_msg, 0, 0);
 	lockinit(&pmp->pm_checkpath_lock, 0, "msdoscp", 0, 0);
 
+	TASK_INIT(&pmp->pm_rw2ro_task, 0, msdosfs_remount_ro, pmp);
+
 	/*
 	 * Initialize ownerships and permissions, since nothing else will
 	 * initialize them iff we are mounting root.
@@ -847,6 +858,53 @@ msdosfs_unmount(struct mount *mp, int mntflags)
 	return (error);
 }
 
+static void
+msdosfs_remount_ro(void *arg, int pending)
+{
+	struct msdosfsmount *pmp;
+	int error;
+
+	pmp = arg;
+
+	MSDOSFS_LOCK_MP(pmp);
+	if ((pmp->pm_flags & MSDOSFS_ERR_RO) != 0) {
+		while ((pmp->pm_flags & MSDOSFS_ERR_RO) != 0)
+			msleep(&pmp->pm_flags, &pmp->pm_fatlock, PVFS,
+			    "msdoserrro", hz);
+	} else if ((pmp->pm_mountp->mnt_flag & MNT_RDONLY) == 0) {
+		pmp->pm_flags |= MSDOSFS_ERR_RO;
+		MSDOSFS_UNLOCK_MP(pmp);
+		printf("%s: remounting read-only due to corruption\n",
+		    pmp->pm_mountp->mnt_stat.f_mntfromname);
+		error = vfs_remount_ro(pmp->pm_mountp);
+		if (error != 0)
+			printf("%s: remounting read-only failed: error %d\n",
+			    pmp->pm_mountp->mnt_stat.f_mntfromname, error);
+		else
+			printf("remounted %s read-only\n",
+			    pmp->pm_mountp->mnt_stat.f_mntfromname);
+		MSDOSFS_LOCK_MP(pmp);
+		pmp->pm_flags &= ~MSDOSFS_ERR_RO;
+		wakeup(&pmp->pm_flags);
+	}
+	MSDOSFS_UNLOCK_MP(pmp);
+
+	vfs_unbusy(pmp->pm_mountp);
+}
+
+void
+msdosfs_integrity_error(struct msdosfsmount *pmp)
+{
+	int error;
+
+	error = vfs_busy(pmp->pm_mountp, MBF_NOWAIT);
+	if (error == 0)
+		taskqueue_enqueue(taskqueue_thread, &pmp->pm_rw2ro_task);
+	else
+		printf("%s: integrity error busying failed, error %d\n",
+		    pmp->pm_mountp->mnt_stat.f_mntfromname, error);
+}
+
 static int
 msdosfs_root(struct mount *mp, int flags, struct vnode **vpp)
 {
diff --git a/sys/fs/msdosfs/msdosfsmount.h b/sys/fs/msdosfs/msdosfsmount.h
index 46a02e611cf5..6a0ba896dff5 100644
--- a/sys/fs/msdosfs/msdosfsmount.h
+++ b/sys/fs/msdosfs/msdosfsmount.h
@@ -59,6 +59,7 @@
 #ifndef MAKEFS
 #include <sys/lock.h>
 #include <sys/lockmgr.h>
+#include <sys/_task.h>
 #endif
 #include <sys/tree.h>
 
@@ -115,6 +116,7 @@ struct msdosfsmount {
 #ifndef MAKEFS
 	struct lock pm_fatlock;	/* lockmgr protecting allocations */
 	struct lock pm_checkpath_lock; /* protects doscheckpath result */
+	struct task pm_rw2ro_task; /* context for emergency remount ro */
 #endif
 };
 
@@ -263,5 +265,10 @@ struct msdosfs_args {
 #define	MSDOSFSMNT_WAITONFAT	0x40000000	/* mounted synchronous	*/
 #define	MSDOSFS_FATMIRROR	0x20000000	/* FAT is mirrored */
 #define	MSDOSFS_FSIMOD		0x01000000
+#define	MSDOSFS_ERR_RO		0x00800000	/* remouning ro due to error */
+
+#ifdef _KERNEL
+void msdosfs_integrity_error(struct msdosfsmount *pmp);
+#endif
 
 #endif /* !_MSDOSFS_MSDOSFSMOUNT_H_ */