git: 6830340cfad6 - main - fstyp: search for file system headers with the largest offset first

From: Robert Wing <rew_at_FreeBSD.org>
Date: Wed, 04 Dec 2024 08:51:22 UTC
The branch main has been updated by rew:

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

commit 6830340cfad6a69715d03f5062f76b65e988e6e9
Author:     Robert Wing <rew@FreeBSD.org>
AuthorDate: 2024-12-04 07:19:23 +0000
Commit:     Robert Wing <rew@FreeBSD.org>
CommitDate: 2024-12-04 08:45:09 +0000

    fstyp: search for file system headers with the largest offset first
    
    fstyp can misidentify a UFS file system as MS-DOS if the device was
    repurposed from MS-DOS to UFS via newfs.
    
    This happens for the following reasons:
        - the header for MS-DOS begins at offset 0
        - the superblock for UFS begins at offset 64k, 8k, 0k, or 256k
        - newfs does not clear the area in front of UFS's superblock,
          leaving the MS-DOS header intact.
        - fstyp searches for file system headers alphabetically
    
    To avoid this misidentification, have fstyp search for file system
    headers with the largest offset first instead of alphabetically.
    
    The implemented fix was suggested by reporter, Richard M. Kreuter.
    
    PR:             252787
    Reviewed by:    imp, emaste
    Differential Revision:  https://reviews.freebsd.org/D47855
---
 usr.sbin/fstyp/fstyp.c | 42 ++++++++++++++++++++++++++++++++++++------
 1 file changed, 36 insertions(+), 6 deletions(-)

diff --git a/usr.sbin/fstyp/fstyp.c b/usr.sbin/fstyp/fstyp.c
index 578210116ae5..dd06df33bbe2 100644
--- a/usr.sbin/fstyp/fstyp.c
+++ b/usr.sbin/fstyp/fstyp.c
@@ -54,25 +54,55 @@ bool show_label = false;
 
 typedef int (*fstyp_function)(FILE *, char *, size_t);
 
+/*
+ * The ordering of fstypes[] is not arbitrary.
+ *
+ * fstyp checks the existence of a file system header to determine the
+ * type of file system on a given device. For different file systems,
+ * these headers reside at different offsets within the device.
+ *
+ * For example, the header for an MS-DOS file system begins at offset 0,
+ * whereas a header for UFS *normally* begins at offset 64k. If a device
+ * was constructed as MS-DOS and then repurposed as UFS (via newfs), it
+ * is possible the MS-DOS header will still be intact. To prevent
+ * misidentifying the file system, it is desirable to check the header
+ * with the largest offset first (i.e., UFS before MS-DOS).
+ */
 static struct {
 	const char	*name;
 	fstyp_function	function;
 	bool		unmountable;
 	const char	*precache_encoding;
 } fstypes[] = {
-	{ "apfs", &fstyp_apfs, true, NULL },
-	{ "befs", &fstyp_befs, false, NULL },
+	/* last sector of geli device */
+	{ "geli", &fstyp_geli, true, NULL },
+	/*
+	 * ufs headers have four different areas, searched in this order:
+	 * offsets: 64k, 8k, 0k, 256k + 8192 bytes
+	 */
+	{ "ufs", &fstyp_ufs, false, NULL },
+	/* offset 32768 + 512 bytes */
 	{ "cd9660", &fstyp_cd9660, false, NULL },
-	{ "exfat", &fstyp_exfat, false, EXFAT_ENC },
+	/* offset 1024 + 512 bytes */
+	{ "hfs+", &fstyp_hfsp, false, NULL },
+	/* offset 1024 + 512 bytes */
 	{ "ext2fs", &fstyp_ext2fs, false, NULL },
-	{ "geli", &fstyp_geli, true, NULL },
+	/* offset 512 + 36 bytes */
+	{ "befs", &fstyp_befs, false, NULL },
+	/* offset 0 + 40 bytes */
+	{ "apfs", &fstyp_apfs, true, NULL },
+	/* offset 0 + 512 bytes (for initial signature check) */
+	{ "exfat", &fstyp_exfat, false, EXFAT_ENC },
+	/* offset 0 + 1928 bytes */
 	{ "hammer", &fstyp_hammer, true, NULL },
+	/* offset 0 + 65536 bytes (for initial signature check) */
 	{ "hammer2", &fstyp_hammer2, true, NULL },
-	{ "hfs+", &fstyp_hfsp, false, NULL },
+	/* offset 0 + 512 bytes (for initial signature check) */
 	{ "msdosfs", &fstyp_msdosfs, false, NULL },
+	/* offset 0 + 512 bytes (for initial signature check) */
 	{ "ntfs", &fstyp_ntfs, false, NTFS_ENC },
-	{ "ufs", &fstyp_ufs, false, NULL },
 #ifdef HAVE_ZFS
+	/* offset 0 + 256k */
 	{ "zfs", &fstyp_zfs, true, NULL },
 #endif
 	{ NULL, NULL, NULL, NULL }