Merge changes from topic "upstreamz"

* changes:
  Update to latest FreeBSD upstream.
  Convert makefile to blueprint.
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..67858c3
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,25 @@
+cc_binary {
+    name: "fsck_msdos",
+    srcs: [
+        "boot.c",
+        "check.c",
+        "dir.c",
+        "fat.c",
+        "fsutil.c",
+        "main.c",
+    ],
+    include_dirs: ["external/fsck_msdos/"],
+    cflags: [
+        "-O2",
+        "-g",
+        "-Wall",
+        "-Werror",
+        "-D_BSD_SOURCE",
+        "-D_LARGEFILE_SOURCE",
+        "-D_FILE_OFFSET_BITS=64",
+        "-Wno-unused-variable",
+        "-Wno-unused-const-variable",
+        "-Wno-format",
+        "-Wno-sign-compare",
+    ],
+}
diff --git a/Android.mk b/Android.mk
deleted file mode 100644
index 8dec83a..0000000
--- a/Android.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := boot.c check.c dir.c fat.c main.c
-
-LOCAL_C_INCLUDES := external/fsck_msdos/
-
-LOCAL_CFLAGS := -O2 -g \
-    -Wall -Werror \
-    -D_BSD_SOURCE \
-    -D_LARGEFILE_SOURCE \
-    -D_FILE_OFFSET_BITS=64 \
-    -Wno-unused-variable \
-    -Wno-unused-const-variable \
-    -Wno-format \
-    -Wno-sign-compare
-
-LOCAL_MODULE := fsck_msdos
-LOCAL_MODULE_TAGS :=
-LOCAL_SYSTEM_SHARED_LIBRARIES := libc
-
-include $(BUILD_EXECUTABLE)
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..812c788
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,17 @@
+name: "fsck_msdos"
+description:
+    "This is the FreeBSD fsck_msdosfs."
+
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://github.com/freebsd/freebsd/tree/master/sbin/fsck_msdosfs"
+  }
+  url {
+    type: GIT
+    value: "https://github.com/freebsd/freebsd.git"
+  }
+  version: "9131ba637f003fb5894e3f6343a27d6322205f18"
+  last_upgrade_date { year: 2018 month: 9 day: 13 }
+  license_type: NOTICE
+}
diff --git a/boot.c b/boot.c
index 5326a45..9e0958a 100644
--- a/boot.c
+++ b/boot.c
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
  * Copyright (C) 1995, 1997 Wolfgang Solfrank
  * Copyright (c) 1995 Martin Husemann
  *
@@ -10,13 +12,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by Martin Husemann
- *	and Wolfgang Solfrank.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -33,14 +28,13 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: boot.c,v 1.9 2003/07/24 19:25:46 ws Exp $");
+__RCSID("$NetBSD: boot.c,v 1.11 2006/06/05 16:51:18 christos Exp ");
 static const char rcsid[] =
-  "$FreeBSD: src/sbin/fsck_msdosfs/boot.c,v 1.4.28.1 2009/04/15 03:14:26 kensmith Exp $";
+  "$FreeBSD$";
 #endif /* not lint */
 
 #include <stdlib.h>
 #include <string.h>
-#include <ctype.h>
 #include <stdio.h>
 #include <unistd.h>
 
@@ -48,45 +42,56 @@
 #include "fsutil.h"
 
 int
-readboot(dosfs, boot)
-	int dosfs;
-	struct bootblock *boot;
+readboot(int dosfs, struct bootblock *boot)
 {
 	u_char block[DOSBOOTBLOCKSIZE];
 	u_char fsinfo[2 * DOSBOOTBLOCKSIZE];
 	u_char backup[DOSBOOTBLOCKSIZE];
 	int ret = FSOK;
+	int i;
 	
-	if (read(dosfs, block, sizeof block) < sizeof block) {
-		perror("could not read boot block");
-                exit(2);
+	if ((size_t)read(dosfs, block, sizeof block) != sizeof block) {
+		perr("could not read boot block");
+		return FSFATAL;
 	}
 
 	if (block[510] != 0x55 || block[511] != 0xaa) {
-		pfatal("Invalid signature in boot block: %02x%02x", block[511], block[510]);
-                exit(2);
+		pfatal("Invalid signature in boot block: %02x%02x",
+		    block[511], block[510]);
+		return FSFATAL;
 	}
 
 	memset(boot, 0, sizeof *boot);
 	boot->ValidFat = -1;
 
 	/* decode bios parameter block */
-	boot->BytesPerSec = block[11] + (block[12] << 8);
-	boot->SecPerClust = block[13];
-	boot->ResSectors = block[14] + (block[15] << 8);
-	boot->FATs = block[16];
-	boot->RootDirEnts = block[17] + (block[18] << 8);
-	boot->Sectors = block[19] + (block[20] << 8);
-	boot->Media = block[21];
-	boot->FATsmall = block[22] + (block[23] << 8);
+	boot->bpbBytesPerSec = block[11] + (block[12] << 8);
+	boot->bpbSecPerClust = block[13];
+	boot->bpbResSectors = block[14] + (block[15] << 8);
+	boot->bpbFATs = block[16];
+	boot->bpbRootDirEnts = block[17] + (block[18] << 8);
+	boot->bpbSectors = block[19] + (block[20] << 8);
+	boot->bpbMedia = block[21];
+	boot->bpbFATsmall = block[22] + (block[23] << 8);
 	boot->SecPerTrack = block[24] + (block[25] << 8);
-	boot->Heads = block[26] + (block[27] << 8);
-	boot->HiddenSecs = block[28] + (block[29] << 8) + (block[30] << 16) + (block[31] << 24);
-	boot->HugeSectors = block[32] + (block[33] << 8) + (block[34] << 16) + (block[35] << 24);
+	boot->bpbHeads = block[26] + (block[27] << 8);
+	boot->bpbHiddenSecs = block[28] + (block[29] << 8) +
+	    (block[30] << 16) + (block[31] << 24);
+	boot->bpbHugeSectors = block[32] + (block[33] << 8) +
+	    (block[34] << 16) + (block[35] << 24);
 
-	boot->FATsecs = boot->FATsmall;
+	boot->FATsecs = boot->bpbFATsmall;
 
-	if (!boot->RootDirEnts)
+	if (boot->bpbBytesPerSec % DOSBOOTBLOCKSIZE_REAL != 0 ||
+	    boot->bpbBytesPerSec / DOSBOOTBLOCKSIZE_REAL == 0) {
+		pfatal("Invalid sector size: %u", boot->bpbBytesPerSec);
+		return FSFATAL;
+	}
+	if (boot->bpbFATs == 0) {
+		pfatal("Invalid number of FATs: %u", boot->bpbFATs);
+		return FSFATAL;
+	}
+	if (!boot->bpbRootDirEnts)
 		boot->flags |= FAT32;
 	if (boot->flags & FAT32) {
 		boot->FATsecs = block[36] + (block[37] << 8)
@@ -99,12 +104,12 @@
 			/* Correct?				XXX */
 			pfatal("Unknown file system version: %x.%x",
 			       block[43], block[42]);
-                        exit(2);
+			return FSFATAL;
 		}
-		boot->RootCl = block[44] + (block[45] << 8)
+		boot->bpbRootClust = block[44] + (block[45] << 8)
 			       + (block[46] << 16) + (block[47] << 24);
-		boot->FSInfo = block[48] + (block[49] << 8);
-		boot->Backup = block[50] + (block[51] << 8);
+		boot->bpbFSInfo = block[48] + (block[49] << 8);
+		boot->bpbBackup = block[50] + (block[51] << 8);
 
 		/* If the OEM Name field is EXFAT, it's not FAT32, so bail */
 		if (!memcmp(&block[3], "EXFAT   ", 8)) {
@@ -113,23 +118,19 @@
 		}
 
 		/* check basic parameters */
-		if ((boot->FSInfo == 0) ||
-		    (boot->BytesPerSec % DOSBOOTBLOCKSIZE != 0) ||
-		    (boot->BytesPerSec / DOSBOOTBLOCKSIZE == 0) ||
-		    (boot->SecPerClust == 0)) {
+		if ((boot->bpbFSInfo == 0) || (boot->bpbSecPerClust == 0)) {
 			/*
 			 * Either the BIOS Parameter Block has been corrupted,
-                         * or this is not a FAT32 filesystem, most likely an
-                         * exFAT filesystem.
+			 * or this is not a FAT32 filesystem, most likely an
+			 * exFAT filesystem.
 			 */
 			pfatal("Invalid FAT32 Extended BIOS Parameter Block");
 			return FSFATAL;
 		}
-		if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
-		    != boot->FSInfo * boot->BytesPerSec
-		    || read(dosfs, fsinfo, sizeof fsinfo)
-		    != sizeof fsinfo) {
-			perror("could not read fsinfo block");
+		if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec,
+		    SEEK_SET) != boot->bpbFSInfo * boot->bpbBytesPerSec
+		    || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) {
+			perr("could not read fsinfo block");
 			return FSFATAL;
 		}
 		if (memcmp(fsinfo, "RRaA", 4)
@@ -143,7 +144,7 @@
 		    || fsinfo[0x3fe] != 0x55
 		    || fsinfo[0x3ff] != 0xaa) {
 			pwarn("Invalid signature in fsinfo block\n");
-			if (ask(1, "fix")) {
+			if (ask(0, "Fix")) {
 				memcpy(fsinfo, "RRaA", 4);
 				memcpy(fsinfo + 0x1e4, "rrAa", 4);
 				fsinfo[0x1fc] = fsinfo[0x1fd] = 0;
@@ -152,18 +153,19 @@
 				fsinfo[0x3fc] = fsinfo[0x3fd] = 0;
 				fsinfo[0x3fe] = 0x55;
 				fsinfo[0x3ff] = 0xaa;
-				if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
-				    != boot->FSInfo * boot->BytesPerSec
+				if (lseek(dosfs, boot->bpbFSInfo *
+				    boot->bpbBytesPerSec, SEEK_SET)
+				    != boot->bpbFSInfo * boot->bpbBytesPerSec
 				    || write(dosfs, fsinfo, sizeof fsinfo)
 				    != sizeof fsinfo) {
-					perror("Unable to write FSInfo");
+					perr("Unable to write bpbFSInfo");
 					return FSFATAL;
 				}
 				ret = FSBOOTMOD;
 			} else
-				boot->FSInfo = 0;
+				boot->bpbFSInfo = 0;
 		}
-		if (boot->FSInfo) {
+		if (boot->bpbFSInfo) {
 			boot->FSFree = fsinfo[0x1e8] + (fsinfo[0x1e9] << 8)
 				       + (fsinfo[0x1ea] << 16)
 				       + (fsinfo[0x1eb] << 24);
@@ -172,75 +174,50 @@
 				       + (fsinfo[0x1ef] << 24);
 		}
 
-		if (lseek(dosfs, boot->Backup * boot->BytesPerSec, SEEK_SET)
-		    != boot->Backup * boot->BytesPerSec
+		if (lseek(dosfs, boot->bpbBackup * boot->bpbBytesPerSec,
+		    SEEK_SET)
+		    != boot->bpbBackup * boot->bpbBytesPerSec
 		    || read(dosfs, backup, sizeof backup) != sizeof  backup) {
-			perror("could not read backup bootblock");
+			perr("could not read backup bootblock");
 			return FSFATAL;
 		}
 		backup[65] = block[65];				/* XXX */
 		if (memcmp(block + 11, backup + 11, 79)) {
-                        char tmp[255];
-                        int i;
-
 			/*
-			 * For now, lets not bail out if they don't match
-			 * It seems a lot of sdcards are formatted with
-			 * the backup either empty or containing garbage.
+			 * XXX We require a reference that explains
+			 * that these bytes need to match, or should
+			 * drop the check.  gdt@NetBSD has observed
+			 * filesystems that work fine under Windows XP
+			 * and NetBSD that do not match, so the
+			 * requirement is suspect.  For now, just
+			 * print out useful information and continue.
 			 */
-
-			pwarn("Primary/Backup bootblock miscompare\n");
-
-                        strcpy(tmp, "");
-                        pwarn("Primary:\n");
-			for (i = 0; i < 79; i++) {
-				char tmp2[16];
-                                snprintf(tmp2, sizeof(tmp2), "%.2x ", block[11 + i]);
-				strcat(tmp, tmp2);
-                        }
-                        pwarn("%s\n", tmp);
-
-			strcpy(tmp, "");
-                        pwarn("Backup:\n");
-			for (i = 0; i < 79; i++) {
-				char tmp2[16];
-                                snprintf(tmp2, sizeof(tmp2), "%.2x ", backup[11 + i]);
-				strcat(tmp, tmp2);
-                        }
-                        pwarn("%s\n", tmp);
+			pwarn("backup (block %d) mismatch with primary bootblock:\n",
+			        boot->bpbBackup);
+			for (i = 11; i < 11 + 90; i++) {
+				if (block[i] != backup[i])
+					pwarn("\ti=%d\tprimary 0x%02x\tbackup 0x%02x\n",
+					       i, block[i], backup[i]);
+			}
 		}
-		/* Check backup FSInfo?					XXX */
+		/* Check backup bpbFSInfo?					XXX */
 	}
 
-	if (boot->BytesPerSec % DOSBOOTBLOCKSIZE != 0) {
-		pfatal("Invalid sector size: %u", boot->BytesPerSec);
+	if (boot->bpbSecPerClust == 0) {
+		pfatal("Invalid cluster size: %u", boot->bpbSecPerClust);
 		return FSFATAL;
 	}
-	if (boot->SecPerClust == 0) {
-		pfatal("Invalid cluster size: %u", boot->SecPerClust);
-		return FSFATAL;
-	}
-	if (boot->BytesPerSec == 0) {
-		pfatal("Invalid sector size: %u", boot->BytesPerSec);
-		return FSFATAL;
-	}
-	if (boot->FATs == 0) {
-		pfatal("Invalid number of FATs: %u", boot->FATs);
-		return FSFATAL;
-	}
-	if (boot->Sectors) {
-		boot->HugeSectors = 0;
-		boot->NumSectors = boot->Sectors;
+	if (boot->bpbSectors) {
+		boot->bpbHugeSectors = 0;
+		boot->NumSectors = boot->bpbSectors;
 	} else
-		boot->NumSectors = boot->HugeSectors;
-
-	boot->ClusterOffset = (boot->RootDirEnts * 32 + boot->BytesPerSec - 1)
-	    / boot->BytesPerSec
-	    + boot->ResSectors
-	    + boot->FATs * boot->FATsecs
-	    - CLUST_FIRST * boot->SecPerClust;
-
-	boot->NumClusters = (boot->NumSectors - boot->ClusterOffset) / boot->SecPerClust;
+		boot->NumSectors = boot->bpbHugeSectors;
+	boot->ClusterOffset = (boot->bpbRootDirEnts * 32 +
+	    boot->bpbBytesPerSec - 1) / boot->bpbBytesPerSec +
+	    boot->bpbResSectors + boot->bpbFATs * boot->FATsecs -
+	    CLUST_FIRST * boot->bpbSecPerClust;
+	boot->NumClusters = (boot->NumSectors - boot->ClusterOffset) /
+	    boot->bpbSecPerClust;
 
 	if (boot->flags&FAT32)
 		boot->ClustMask = CLUST32_MASK;
@@ -256,22 +233,22 @@
 
 	switch (boot->ClustMask) {
 	case CLUST32_MASK:
-		boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec) / 4;
+		boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 4;
 		break;
 	case CLUST16_MASK:
-		boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec) / 2;
+		boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec) / 2;
 		break;
 	default:
-		boot->NumFatEntries = (boot->FATsecs * boot->BytesPerSec * 2) / 3;
+		boot->NumFatEntries = (boot->FATsecs * boot->bpbBytesPerSec * 2) / 3;
 		break;
 	}
 
-	if (boot->NumFatEntries < boot->NumClusters) {
+	if (boot->NumFatEntries < boot->NumClusters - CLUST_FIRST) {
 		pfatal("FAT size too small, %u entries won't fit into %u sectors\n",
 		       boot->NumClusters, boot->FATsecs);
 		return FSFATAL;
 	}
-	boot->ClusterSize = boot->BytesPerSec * boot->SecPerClust;
+	boot->ClusterSize = boot->bpbBytesPerSec * boot->bpbSecPerClust;
 
 	boot->NumFiles = 1;
 	boot->NumFree = 0;
@@ -280,16 +257,14 @@
 }
 
 int
-writefsinfo(dosfs, boot)
-	int dosfs;
-	struct bootblock *boot;
+writefsinfo(int dosfs, struct bootblock *boot)
 {
 	u_char fsinfo[2 * DOSBOOTBLOCKSIZE];
 
-	if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
-	    != boot->FSInfo * boot->BytesPerSec
+	if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET)
+	    != boot->bpbFSInfo * boot->bpbBytesPerSec
 	    || read(dosfs, fsinfo, sizeof fsinfo) != sizeof fsinfo) {
-		perror("could not read fsinfo block");
+		perr("could not read fsinfo block");
 		return FSFATAL;
 	}
 	fsinfo[0x1e8] = (u_char)boot->FSFree;
@@ -300,11 +275,11 @@
 	fsinfo[0x1ed] = (u_char)(boot->FSNext >> 8);
 	fsinfo[0x1ee] = (u_char)(boot->FSNext >> 16);
 	fsinfo[0x1ef] = (u_char)(boot->FSNext >> 24);
-	if (lseek(dosfs, boot->FSInfo * boot->BytesPerSec, SEEK_SET)
-	    != boot->FSInfo * boot->BytesPerSec
+	if (lseek(dosfs, boot->bpbFSInfo * boot->bpbBytesPerSec, SEEK_SET)
+	    != boot->bpbFSInfo * boot->bpbBytesPerSec
 	    || write(dosfs, fsinfo, sizeof fsinfo)
 	    != sizeof fsinfo) {
-		perror("Unable to write FSInfo");
+		perr("Unable to write bpbFSInfo");
 		return FSFATAL;
 	}
 	/*
diff --git a/check.c b/check.c
index fab2d91..2431bd3 100644
--- a/check.c
+++ b/check.c
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
  * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
  * Copyright (c) 1995 Martin Husemann
  *
@@ -10,13 +12,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by Martin Husemann
- *	and Wolfgang Solfrank.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -33,14 +28,13 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: check.c,v 1.10 2000/04/25 23:02:51 jdolecek Exp $");
+__RCSID("$NetBSD: check.c,v 1.14 2006/06/05 16:51:18 christos Exp $");
 static const char rcsid[] =
-  "$FreeBSD: src/sbin/fsck_msdosfs/check.c,v 1.10 2004/02/05 15:47:46 bde Exp $";
+  "$FreeBSD$";
 #endif /* not lint */
 
 #include <stdlib.h>
 #include <string.h>
-#include <ctype.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <fcntl.h>
@@ -48,27 +42,19 @@
 #include "ext.h"
 #include "fsutil.h"
 
-/*
- * If the FAT > this size then skip comparing, lest we risk
- * OOMing the framework. in the future we need to just re-write
- * this whole thing and optimize for less memory
- */
-#define FAT_COMPARE_MAX_KB 4096
-
 int
 checkfilesys(const char *fname)
 {
 	int dosfs;
 	struct bootblock boot;
 	struct fatEntry *fat = NULL;
-	int i, finish_dosdirsection=0;
+	int finish_dosdirsection=0;
+	u_int i;
 	int mod = 0;
 	int ret = 8;
-        int quiet = 0;
-        int skip_fat_compare = 0;
 
 	rdonly = alwaysno;
-	if (!quiet)
+	if (!preen)
 		printf("** %s", fname);
 
 	dosfs = open(fname, rdonly ? O_RDONLY : O_RDWR, 0);
@@ -76,14 +62,15 @@
 		dosfs = open(fname, O_RDONLY, 0);
 		if (dosfs >= 0)
 			pwarn(" (NO WRITE)\n");
-		else if (!quiet)
+		else if (!preen)
 			printf("\n");
 		rdonly = 1;
-	} else if (!quiet)
+	} else if (!preen)
 		printf("\n");
 
 	if (dosfs < 0) {
-		perror("Can't open");
+		perr("Can't open `%s'", fname);
+		printf("\n");
 		return 8;
 	}
 
@@ -100,13 +87,8 @@
 		goto out;
 	}
 
-        if (((boot.FATsecs * boot.BytesPerSec) / 1024) > FAT_COMPARE_MAX_KB)
-            skip_fat_compare = 1;
-
-	if (!quiet)  {
-                if (skip_fat_compare) 
-                        printf("** Phase 1 - Read FAT (compare skipped)\n");
-		else if (boot.ValidFat < 0)
+	if (!preen)  {
+		if (boot.ValidFat < 0)
 			printf("** Phase 1 - Read and Compare FATs\n");
 		else
 			printf("** Phase 1 - Read FAT\n");
@@ -114,56 +96,47 @@
 
 	mod |= readfat(dosfs, &boot, boot.ValidFat >= 0 ? boot.ValidFat : 0, &fat);
 	if (mod & FSFATAL) {
-		printf("Fatal error during readfat()\n");
 		close(dosfs);
 		return 8;
 	}
 
-	if (!skip_fat_compare && boot.ValidFat < 0)
-		for (i = 1; i < (int)boot.FATs; i++) {
+	if (boot.ValidFat < 0)
+		for (i = 1; i < boot.bpbFATs; i++) {
 			struct fatEntry *currentFat;
 
 			mod |= readfat(dosfs, &boot, i, &currentFat);
 
-			if (mod & FSFATAL) {
-				printf("Fatal error during readfat() for comparison\n");
+			if (mod & FSFATAL)
 				goto out;
-			}
 
 			mod |= comparefat(&boot, fat, currentFat, i);
 			free(currentFat);
-			if (mod & FSFATAL) {
-				printf("Fatal error during FAT comparison\n");
+			if (mod & FSFATAL)
 				goto out;
-			}
 		}
 
-	if (!quiet)
+	if (!preen)
 		printf("** Phase 2 - Check Cluster Chains\n");
 
 	mod |= checkfat(&boot, fat);
-	if (mod & FSFATAL) {
-		printf("Fatal error during FAT check\n");
+	if (mod & FSFATAL)
 		goto out;
-	}
 	/* delay writing FATs */
 
-	if (!quiet)
+	if (!preen)
 		printf("** Phase 3 - Checking Directories\n");
 
 	mod |= resetDosDirSection(&boot, fat);
 	finish_dosdirsection = 1;
-	if (mod & FSFATAL) {
-		printf("Fatal error during resetDosDirSection()\n");
+	if (mod & FSFATAL)
 		goto out;
-	}
 	/* delay writing FATs */
 
 	mod |= handleDirTree(dosfs, &boot, fat);
 	if (mod & FSFATAL)
 		goto out;
 
-	if (!quiet)
+	if (!preen)
 		printf("** Phase 4 - Checking for Lost Files\n");
 
 	mod |= checklost(dosfs, &boot, fat);
@@ -171,13 +144,11 @@
 		goto out;
 
 	/* now write the FATs */
-	if (mod & FSFATMOD) {
+	if (mod & (FSFATMOD|FSFIXFAT)) {
 		if (ask(1, "Update FATs")) {
 			mod |= writefat(dosfs, &boot, fat, mod & FSFIXFAT);
-			if (mod & FSFATAL) {
-				printf("Fatal error during writefat()\n");
+			if (mod & FSFATAL)
 				goto out;
-			}
 		} else
 			mod |= FSERROR;
 	}
@@ -218,10 +189,8 @@
 	free(fat);
 	close(dosfs);
 
-	if (mod & (FSFATMOD|FSDIRMOD)) {
+	if (mod & (FSFATMOD|FSDIRMOD))
 		pwarn("\n***** FILE SYSTEM WAS MODIFIED *****\n");
-		return 4; 
-	}
 
 	return ret;
 }
diff --git a/dir.c b/dir.c
index 7e4a567..38c7014 100644
--- a/dir.c
+++ b/dir.c
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
  * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
  * Copyright (c) 1995 Martin Husemann
  * Some structure declaration borrowed from Paul Popelka
@@ -12,13 +14,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by Martin Husemann
- *	and Wolfgang Solfrank.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -35,16 +30,15 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: dir.c,v 1.14 1998/08/25 19:18:15 ross Exp $");
+__RCSID("$NetBSD: dir.c,v 1.20 2006/06/05 16:51:18 christos Exp $");
 static const char rcsid[] =
-  "$FreeBSD: src/sbin/fsck_msdosfs/dir.c,v 1.3 2003/12/26 17:24:37 trhodes Exp $";
+  "$FreeBSD$";
 #endif /* not lint */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
-#include <stdio.h>
 #include <unistd.h>
 #include <time.h>
 
@@ -161,7 +155,7 @@
 /*
  * The stack of unread directories
  */
-struct dirTodoNode *pendingDirectories = NULL;
+static struct dirTodoNode *pendingDirectories = NULL;
 
 /*
  * Return the full pathname for a directory entry.
@@ -214,7 +208,7 @@
 static u_char *buffer = NULL;
 static u_char *delbuf = NULL;
 
-struct dosDirEntry *rootDir;
+static struct dosDirEntry *rootDir;
 static struct dosDirEntry *lostDir;
 
 /*
@@ -226,27 +220,41 @@
 	int b1, b2;
 	cl_t cl;
 	int ret = FSOK;
+	size_t len;
 
-	b1 = boot->RootDirEnts * 32;
-	b2 = boot->SecPerClust * boot->BytesPerSec;
+	b1 = boot->bpbRootDirEnts * 32;
+	b2 = boot->bpbSecPerClust * boot->bpbBytesPerSec;
 
-	if (!(buffer = malloc(b1 > b2 ? b1 : b2))
-	    || !(delbuf = malloc(b2))
-	    || !(rootDir = newDosDirEntry())) {
-		perror("No space for directory");
+	if ((buffer = malloc(len = MAX(b1, b2))) == NULL) {
+		perr("No space for directory buffer (%zu)", len);
 		return FSFATAL;
 	}
+
+	if ((delbuf = malloc(len = b2)) == NULL) {
+		free(buffer);
+		perr("No space for directory delbuf (%zu)", len);
+		return FSFATAL;
+	}
+
+	if ((rootDir = newDosDirEntry()) == NULL) {
+		free(buffer);
+		free(delbuf);
+		perr("No space for directory entry");
+		return FSFATAL;
+	}
+
 	memset(rootDir, 0, sizeof *rootDir);
 	if (boot->flags & FAT32) {
-		if (boot->RootCl < CLUST_FIRST || boot->RootCl >= boot->NumClusters) {
+		if (boot->bpbRootClust < CLUST_FIRST ||
+		    boot->bpbRootClust >= boot->NumClusters) {
 			pfatal("Root directory starts with cluster out of range(%u)",
-			       boot->RootCl);
+			       boot->bpbRootClust);
 			return FSFATAL;
 		}
-		cl = fat[boot->RootCl].next;
+		cl = fat[boot->bpbRootClust].next;
 		if (cl < CLUST_FIRST
 		    || (cl >= CLUST_RSRVD && cl< CLUST_EOFS)
-		    || fat[boot->RootCl].head != boot->RootCl) {
+		    || fat[boot->bpbRootClust].head != boot->bpbRootClust) {
 			if (cl == CLUST_FREE)
 				pwarn("Root directory starts with free cluster\n");
 			else if (cl >= CLUST_RSRVD)
@@ -257,14 +265,14 @@
 				return FSFATAL;
 			}
 			if (ask(1, "Fix")) {
-				fat[boot->RootCl].next = CLUST_FREE;
+				fat[boot->bpbRootClust].next = CLUST_FREE;
 				ret = FSFATMOD;
 			} else
 				ret = FSFATAL;
 		}
 
-		fat[boot->RootCl].flags |= FAT_USED;
-		rootDir->head = boot->RootCl;
+		fat[boot->bpbRootClust].flags |= FAT_USED;
+		rootDir->head = boot->bpbRootClust;
 	}
 
 	return ret;
@@ -283,7 +291,7 @@
 		np = p->next;
 		freeDirTodo(p);
 	}
-	pendingDirectories = 0;
+	pendingDirectories = NULL;
 	for (d = rootDir; d; d = nd) {
 		if ((nd = d->child) != NULL) {
 			d->child = 0;
@@ -308,8 +316,8 @@
     int startoff, cl_t endcl, int endoff, int notlast)
 {
 	u_char *s, *e;
-	loff_t off;
-	int clsz = boot->SecPerClust * boot->BytesPerSec;
+	off_t off;
+	int clsz = boot->bpbSecPerClust * boot->bpbBytesPerSec;
 
 	s = delbuf + startoff;
 	e = delbuf + clsz;
@@ -319,28 +327,20 @@
 				break;
 			e = delbuf + endoff;
 		}
-		off = startcl * boot->SecPerClust + boot->ClusterOffset;
-		off *= boot->BytesPerSec;
-		if (lseek64(f, off, SEEK_SET) != off) {
-			printf("off = %llu\n", off);
-			perror("Unable to lseek64");
-			return FSFATAL;
-		}
-		if (read(f, delbuf, clsz) != clsz) {
-			perror("Unable to read directory");
+		off = startcl * boot->bpbSecPerClust + boot->ClusterOffset;
+		off *= boot->bpbBytesPerSec;
+		if (lseek(f, off, SEEK_SET) != off
+		    || read(f, delbuf, clsz) != clsz) {
+			perr("Unable to read directory");
 			return FSFATAL;
 		}
 		while (s < e) {
 			*s = SLOT_DELETED;
 			s += 32;
 		}
-		if (lseek64(f, off, SEEK_SET) != off) {
-			printf("off = %llu\n", off);
-			perror("Unable to lseek64");
-			return FSFATAL;
-		}
-		if (write(f, delbuf, clsz) != clsz) {
-			perror("Unable to write directory");
+		if (lseek(f, off, SEEK_SET) != off
+		    || write(f, delbuf, clsz) != clsz) {
+			perr("Unable to write directory");
 			return FSFATAL;
 		}
 		if (startcl == endcl)
@@ -360,13 +360,14 @@
 		pwarn("Invalid long filename entry for %s\n", path);
 		break;
 	case 1:
-		pwarn("Invalid long filename entry at end of directory %s\n", path);
+		pwarn("Invalid long filename entry at end of directory %s\n",
+		    path);
 		break;
 	case 2:
 		pwarn("Invalid long filename entry for volume label\n");
 		break;
 	}
-	if (ask(1, "Remove")) {
+	if (ask(0, "Remove")) {
 		if (startcl != curcl) {
 			if (delete(f, boot, fat,
 				   startcl, start - buffer,
@@ -375,7 +376,8 @@
 				return FSFATAL;
 			start = buffer;
 		}
-		if (endcl == curcl)
+		/* startcl is < CLUST_FIRST for !fat32 root */
+		if ((endcl == curcl) || (startcl < CLUST_FIRST))
 			for (; start < end; start += 32)
 				*start = SLOT_DELETED;
 		return FSDIRMOD;
@@ -393,7 +395,7 @@
 	/*
 	 * Check size on ordinary files
 	 */
-	int32_t physicalSize;
+	u_int32_t physicalSize;
 
 	if (dir->head == CLUST_FREE)
 		physicalSize = 0;
@@ -419,12 +421,14 @@
 		      fullpath(dir));
 		if (ask(1, "Drop superfluous clusters")) {
 			cl_t cl;
-			u_int32_t sz = 0;
+			u_int32_t sz, len;
 
-			for (cl = dir->head; (sz += boot->ClusterSize) < dir->size;)
+			for (cl = dir->head, len = sz = 0;
+			    (sz += boot->ClusterSize) < dir->size; len++)
 				cl = fat[cl].next;
 			clearchain(boot, fat, fat[cl].next);
 			fat[cl].next = CLUST_EOF;
+			fat[dir->head].length = len;
 			return FSFATMOD;
 		} else
 			return FSERROR;
@@ -432,89 +436,6 @@
 	return FSOK;
 }
 
-
-static u_char  dot_header[16]={0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00};
-static u_char  dot_dot_header[16]={0x2E, 0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00};
-
-/*
- * Check for missing or broken '.' and '..' entries.
- */
-static int
-check_dot_dot(int f, struct bootblock *boot, struct fatEntry *fat,struct dosDirEntry *dir)
-{
-	u_char *p, *buf;
-	loff_t off;
-	int last;
-	cl_t cl;
-	int rc=0, n_count;
-
-	int dot, dotdot;
-	dot = dotdot = 0;
-	cl = dir->head;
-
-	if (dir->parent && (cl < CLUST_FIRST || cl >= boot->NumClusters)) {
-		return rc;
-	}
-
-	do {
-		if (!(boot->flags & FAT32) && !dir->parent) {
-			last = boot->RootDirEnts * 32;
-			off = boot->ResSectors + boot->FATs * boot->FATsecs;
-		} else {
-			last = boot->SecPerClust * boot->BytesPerSec;
-			off = cl * boot->SecPerClust + boot->ClusterOffset;
-		}
-
-		off *= boot->BytesPerSec;
-		buf = malloc(last);
-		if (!buf) {
-			perror("Unable to malloc");
-			return FSFATAL;
-		}
-		if (lseek64(f, off, SEEK_SET) != off) {
-			printf("off = %llu\n", off);
-			perror("Unable to lseek64");
-			free(buf);
-			return FSFATAL;
-		}
-		if (read(f, buf, last) != last) {
-			perror("Unable to read");
-			free(buf);
-			return FSFATAL;
-		}
-		last /= 32;
-		p = buf;
-		for (n_count=0, rc=0; n_count < 11; n_count++) {
-			if (dot_header[n_count] != p[n_count]) {
-				rc=-1;
-				break;
-			}
-		}
-		 if(!rc)
-			dot=1;
-
-		for (n_count = 0, rc = 0; n_count < 11; n_count++) {
-			if (dot_dot_header[n_count] != p[n_count+32]) {
-				rc=-1;
-				break;
-			}
-		}
-		if(!rc)
-			dotdot=1;
-		free(buf);
-	} while ((cl = fat[cl].next) >= CLUST_FIRST && cl < boot->NumClusters);
-
-	if (!dot || !dotdot) {
-		if (!dot)
-			pwarn("%s: '.' absent for %s.\n",__func__,dir->name);
-
-		if (!dotdot)
-			pwarn("%s: '..' absent for %s. \n",__func__,dir->name);
-		return -1;
-	}
-	return 0;
-}
-
 /*
  * Read a directory and
  *   - resolve long name records
@@ -527,15 +448,13 @@
 {
 	struct dosDirEntry dirent, *d;
 	u_char *p, *vallfn, *invlfn, *empty;
-	loff_t off;
+	off_t off;
 	int i, j, k, last;
 	cl_t cl, valcl = ~0, invcl = ~0, empcl = ~0;
 	char *t;
 	u_int lidx = 0;
 	int shortSum;
 	int mod = FSOK;
-	int n_count=0;
-	int rc=0;
 #define	THISMOD	0x8000			/* Only used within this routine */
 
 	cl = dir->head;
@@ -547,29 +466,26 @@
 	}
 	shortSum = -1;
 	vallfn = invlfn = empty = NULL;
-	int dot,dotdot;
-	dot = dotdot = 0;
-
 	do {
 		if (!(boot->flags & FAT32) && !dir->parent) {
-			last = boot->RootDirEnts * 32;
-			off = boot->ResSectors + boot->FATs * boot->FATsecs;
+			last = boot->bpbRootDirEnts * 32;
+			off = boot->bpbResSectors + boot->bpbFATs *
+			    boot->FATsecs;
 		} else {
-			last = boot->SecPerClust * boot->BytesPerSec;
-			off = cl * boot->SecPerClust + boot->ClusterOffset;
+			last = boot->bpbSecPerClust * boot->bpbBytesPerSec;
+			off = cl * boot->bpbSecPerClust + boot->ClusterOffset;
 		}
 
-		off *= boot->BytesPerSec;
-		if (lseek64(f, off, SEEK_SET) != off) {
-                        printf("off = %llu\n", off);
-			perror("Unable to lseek64");
+		off *= boot->bpbBytesPerSec;
+		if (lseek(f, off, SEEK_SET) != off
+		    || read(f, buffer, last) != last) {
+			perr("Unable to read directory");
 			return FSFATAL;
-                }
-                if (read(f, buffer, last) != last) {
-			perror("Unable to read");
-			return FSFATAL;
-                }
+		}
 		last /= 32;
+		/*
+		 * Check `.' and `..' entries here?			XXX
+		 */
 		for (p = buffer, i = 0; i < last; i++, p += 32) {
 			if (dir->fsckflags & DIREMPWARN) {
 				*p = SLOT_EMPTY;
@@ -601,7 +517,7 @@
 						for (; q < p; q += 32)
 							*q = SLOT_DELETED;
 						mod |= THISMOD|FSDIRMOD;
-					} else if (ask(1, "Truncate"))
+					} else if (ask(0, "Truncate"))
 						dir->fsckflags |= DIREMPWARN;
 				}
 				if (dir->fsckflags & DIREMPWARN) {
@@ -639,7 +555,8 @@
 				}
 				lidx = *p & LRNOMASK;
 				t = longName + --lidx * 13;
-				for (k = 1; k < 11 && t < longName + sizeof(longName); k += 2) {
+				for (k = 1; k < 11 && t < longName +
+				    sizeof(longName); k += 2) {
 					if (!p[k] && !p[k + 1])
 						break;
 					*t++ = p[k];
@@ -704,7 +621,7 @@
 			dirent.name[8] = '\0';
 			for (k = 7; k >= 0 && dirent.name[k] == ' '; k--)
 				dirent.name[k] = '\0';
-			if (dirent.name[k] != '\0')
+			if (k < 0 || dirent.name[k] != '\0')
 				k++;
 			if (dirent.name[0] == SLOT_E5)
 				dirent.name[0] = 0xe5;
@@ -741,7 +658,8 @@
 				dirent.head |= (p[20] << 16) | (p[21] << 24);
 			dirent.size = p[28] | (p[29] << 8) | (p[30] << 16) | (p[31] << 24);
 			if (vallfn) {
-				strcpy(dirent.lname, longName);
+				strlcpy(dirent.lname, longName,
+				    sizeof(dirent.lname));
 				longName[0] = '\0';
 				shortSum = -1;
 			}
@@ -812,7 +730,7 @@
 					pwarn("%s doesn't start a cluster chain\n",
 					      fullpath(&dirent));
 				if (dirent.flags & ATTR_DIRECTORY) {
-					if (ask(1, "Remove")) {
+					if (ask(0, "Remove")) {
 						*p = SLOT_DELETED;
 						mod |= THISMOD|FSDIRMOD;
 					} else
@@ -851,11 +769,11 @@
 						mod |= FSERROR;
 				}
 				/*
-				 * handle '.' and '..' specially
+				 * handle `.' and `..' specially
 				 */
 				if (strcmp(dirent.name, ".") == 0) {
 					if (dirent.head != dir->head) {
-						pwarn("'.' entry in %s has incorrect start cluster\n",
+						pwarn("`.' entry in %s has incorrect start cluster\n",
 						      fullpath(dir));
 						if (ask(1, "Correct")) {
 							dirent.head = dir->head;
@@ -870,11 +788,12 @@
 							mod |= FSERROR;
 					}
 					continue;
-                } else if (strcmp(dirent.name, "..") == 0) {
+				}
+				if (strcmp(dirent.name, "..") == 0) {
 					if (dir->parent) {		/* XXX */
 						if (!dir->parent->parent) {
 							if (dirent.head) {
-								pwarn("'..' entry in %s has non-zero start cluster\n",
+								pwarn("`..' entry in %s has non-zero start cluster\n",
 								      fullpath(dir));
 								if (ask(1, "Correct")) {
 									dirent.head = 0;
@@ -886,7 +805,7 @@
 									mod |= FSERROR;
 							}
 						} else if (dirent.head != dir->parent->head) {
-							pwarn("'..' entry in %s has incorrect start cluster\n",
+							pwarn("`..' entry in %s has incorrect start cluster\n",
 							      fullpath(dir));
 							if (ask(1, "Correct")) {
 								dirent.head = dir->parent->head;
@@ -902,48 +821,20 @@
 						}
 					}
 					continue;
-				} else { //only one directory entry can point to dir->head, it's  '.'
-					if (dirent.head == dir->head) {
-						pwarn("%s entry in %s has incorrect start cluster.remove\n",
-								dirent.name, fullpath(dir));
-						//we have to remove this directory entry rigth now rigth here
-						if (ask(1, "Remove")) {
-							*p = SLOT_DELETED;
-							mod |= THISMOD|FSDIRMOD;
-						} else
-							mod |= FSERROR;
-						continue;
-					}
-					/* Consistency checking. a directory must have at least two entries:
-					   a dot (.) entry that points to itself, and a dot-dot (..)
-					   entry that points to its parent.
-					 */
-					if (check_dot_dot(f,boot,fat,&dirent)) {
-						//mark directory entry as deleted.
-						if (ask(1, "Remove")) {
-							*p = SLOT_DELETED;
-							mod |= THISMOD|FSDIRMOD;
-						} else
-							mod |= FSERROR;
-						continue;
-                    }
 				}
 
 				/* create directory tree node */
 				if (!(d = newDosDirEntry())) {
-					perror("No space for directory");
+					perr("No space for directory");
 					return FSFATAL;
 				}
 				memcpy(d, &dirent, sizeof(struct dosDirEntry));
 				/* link it into the tree */
 				dir->child = d;
-#if 0
-				printf("%s: %s : 0x%02x:head %d, next 0x%0x parent 0x%0x child 0x%0x\n",
-						__func__,d->name,d->flags,d->head,d->next,d->parent,d->child);
-#endif
+
 				/* Enter this directory into the todo list */
 				if (!(n = newDirTodo())) {
-					perror("No space for todo list");
+					perr("No space for todo list");
 					return FSFATAL;
 				}
 				n->next = pendingDirectories;
@@ -956,11 +847,15 @@
 			}
 			boot->NumFiles++;
 		}
+
+		if (!(boot->flags & FAT32) && !dir->parent)
+			break;
+
 		if (mod & THISMOD) {
 			last *= 32;
-			if (lseek64(f, off, SEEK_SET) != off
+			if (lseek(f, off, SEEK_SET) != off
 			    || write(f, buffer, last) != last) {
-				perror("Unable to write directory");
+				perr("Unable to write directory");
 				return FSFATAL;
 			}
 			mod &= ~THISMOD;
@@ -971,6 +866,19 @@
 				invlfn ? invlfn : vallfn, p,
 				invlfn ? invcl : valcl, -1, 0,
 				fullpath(dir), 1);
+
+	/* The root directory of non fat32 filesystems is in a special
+	 * area and may have been modified above without being written out.
+	 */
+	if ((mod & FSDIRMOD) && !(boot->flags & FAT32) && !dir->parent) {
+		last *= 32;
+		if (lseek(f, off, SEEK_SET) != off
+		    || write(f, buffer, last) != last) {
+			perr("Unable to write directory");
+			return FSFATAL;
+		}
+		mod &= ~THISMOD;
+	}
 	return mod & ~THISMOD;
 }
 
@@ -1013,12 +921,13 @@
  */
 static u_char *lfbuf;
 static cl_t lfcl;
-static loff_t lfoff;
+static off_t lfoff;
 
 int
 reconnect(int dosfs, struct bootblock *boot, struct fatEntry *fat, cl_t head)
 {
 	struct dosDirEntry d;
+	int len;
 	u_char *p;
 
 	if (!ask(1, "Reconnect"))
@@ -1037,7 +946,7 @@
 	if (!lfbuf) {
 		lfbuf = malloc(boot->ClusterSize);
 		if (!lfbuf) {
-			perror("No space for buffer");
+			perr("No space for buffer");
 			return FSFATAL;
 		}
 		p = NULL;
@@ -1055,14 +964,13 @@
 		if (lfcl < CLUST_FIRST || lfcl >= boot->NumClusters) {
 			/* Extend LOSTDIR?				XXX */
 			pwarn("No space in %s\n", LOSTDIR);
-			lfcl = (lostDir->head < boot->NumClusters) ? lostDir->head : 0;
 			return FSERROR;
 		}
 		lfoff = lfcl * boot->ClusterSize
-		    + boot->ClusterOffset * boot->BytesPerSec;
-		if (lseek64(dosfs, lfoff, SEEK_SET) != lfoff
-		    || read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
-			perror("could not read LOST.DIR");
+		    + boot->ClusterOffset * boot->bpbBytesPerSec;
+		if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
+		    || (size_t)read(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
+			perr("could not read LOST.DIR");
 			return FSFATAL;
 		}
 		p = lfbuf;
@@ -1071,14 +979,15 @@
 	boot->NumFiles++;
 	/* Ensure uniqueness of entry here!				XXX */
 	memset(&d, 0, sizeof d);
-	(void)snprintf(d.name, sizeof(d.name), "%u", head);
+	/* worst case -1 = 4294967295, 10 digits */
+	len = snprintf(d.name, sizeof(d.name), "%u", head);
 	d.flags = 0;
 	d.head = head;
 	d.size = fat[head].length * boot->ClusterSize;
 
-	memset(p, 0, 32);
-	memset(p, ' ', 11);
-	memcpy(p, d.name, strlen(d.name));
+	memcpy(p, d.name, len);
+	memset(p + len, ' ', 11 - len);
+	memset(p + 11, 0, 32 - 11);
 	p[26] = (u_char)d.head;
 	p[27] = (u_char)(d.head >> 8);
 	if (boot->ClustMask == CLUST32_MASK) {
@@ -1090,9 +999,9 @@
 	p[30] = (u_char)(d.size >> 16);
 	p[31] = (u_char)(d.size >> 24);
 	fat[head].flags |= FAT_USED;
-	if (lseek64(dosfs, lfoff, SEEK_SET) != lfoff
-	    || write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
-		perror("could not write LOST.DIR");
+	if (lseek(dosfs, lfoff, SEEK_SET) != lfoff
+	    || (size_t)write(dosfs, lfbuf, boot->ClusterSize) != boot->ClusterSize) {
+		perr("could not write LOST.DIR");
 		return FSFATAL;
 	}
 	return FSDIRMOD;
diff --git a/dosfs.h b/dosfs.h
index 5420e25..3d84ea0 100644
--- a/dosfs.h
+++ b/dosfs.h
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
  * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
  * Copyright (c) 1995 Martin Husemann
  * Some structure declaration borrowed from Paul Popelka
@@ -12,13 +14,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by Martin Husemann
- *	and Wolfgang Solfrank.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -31,13 +26,15 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *	$NetBSD: dosfs.h,v 1.4 1997/01/03 14:32:48 ws Exp $
- * $FreeBSD: src/sbin/fsck_msdosfs/dosfs.h,v 1.3 2003/12/26 17:24:37 trhodes Exp $
+ * $FreeBSD$
  */
 
 #ifndef DOSFS_H
 #define DOSFS_H
 
-#define DOSBOOTBLOCKSIZE 512
+/* support 4Kn disk reads */
+#define DOSBOOTBLOCKSIZE_REAL 512
+#define DOSBOOTBLOCKSIZE 4096
 
 typedef	u_int32_t	cl_t;	/* type holding a cluster number */
 
@@ -46,21 +43,21 @@
  * FAT boot block.
  */
 struct bootblock {
-	u_int	BytesPerSec;		/* bytes per sector */
-	u_int	SecPerClust;		/* sectors per cluster */
-	u_int	ResSectors;		/* number of reserved sectors */
-	u_int	FATs;			/* number of FATs */
-	u_int	RootDirEnts;		/* number of root directory entries */
-	u_int	Media;			/* media descriptor */
-	u_int	FATsmall;		/* number of sectors per FAT */
+	u_int	bpbBytesPerSec;		/* bytes per sector */
+	u_int	bpbSecPerClust;		/* sectors per cluster */
+	u_int	bpbResSectors;		/* number of reserved sectors */
+	u_int	bpbFATs;		/* number of bpbFATs */
+	u_int	bpbRootDirEnts;		/* number of root directory entries */
+	u_int32_t bpbSectors;		/* total number of sectors */
+	u_int	bpbMedia;		/* media descriptor */
+	u_int	bpbFATsmall;		/* number of sectors per FAT */
 	u_int	SecPerTrack;		/* sectors per track */
-	u_int	Heads;			/* number of heads */
-	u_int32_t Sectors;		/* total number of sectors */
-	u_int32_t HiddenSecs;		/* # of hidden sectors */
-	u_int32_t HugeSectors;		/* # of sectors if bpbSectors == 0 */
-	u_int	FSInfo;			/* FSInfo sector */
-	u_int	Backup;			/* Backup of Bootblocks */
-	cl_t	RootCl;			/* Start of Root Directory */
+	u_int	bpbHeads;		/* number of heads */
+	u_int32_t bpbHiddenSecs;	/* # of hidden sectors */
+	u_int32_t bpbHugeSectors;	/* # of sectors if bpbbpbSectors == 0 */
+	cl_t	bpbRootClust;		/* Start of Root Directory */
+	u_int	bpbFSInfo;		/* FSInfo sector */
+	u_int	bpbBackup;		/* Backup of Bootblocks */
 	cl_t	FSFree;			/* Number of free clusters acc. FSInfo */
 	cl_t	FSNext;			/* Next free cluster acc. FSInfo */
 
diff --git a/ext.h b/ext.h
index 6d183e9..ebc9467 100644
--- a/ext.h
+++ b/ext.h
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
  * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
  * Copyright (c) 1995 Martin Husemann
  *
@@ -10,13 +12,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by Martin Husemann
- *	and Wolfgang Solfrank.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -29,11 +24,11 @@
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *	$NetBSD: ext.h,v 1.6 2000/04/25 23:02:51 jdolecek Exp $
- * $FreeBSD: src/sbin/fsck_msdosfs/ext.h,v 1.10.20.1 2009/04/15 03:14:26 kensmith Exp $
+ * $FreeBSD$
  */
 
 #ifndef EXT_H
-#define EXT_H
+#define	EXT_H
 
 #include <sys/types.h>
 
@@ -50,12 +45,10 @@
 extern int rdonly;	/* device is opened read only (supersedes above) */
 extern int skipclean;	/* skip clean file systems if preening */
 
-extern struct dosDirEntry *rootDir;
-
 /*
  * function declarations
  */
-int ask(int, const char *, ...);
+int ask(int, const char *, ...) __printflike(2, 3);
 
 /*
  * Check the dirty flag.  If the file system is clean, then return 1.
@@ -77,12 +70,12 @@
 #define	FSDIRMOD	2		/* Some directory was modified */
 #define	FSFATMOD	4		/* The FAT was modified */
 #define	FSERROR		8		/* Some unrecovered error remains */
-#define	FSFATAL		16		/* Some unrecoverable error occured */
-#define FSDIRTY		32		/* File system is dirty */
-#define FSFIXFAT	64		/* Fix file system FAT */
+#define	FSFATAL		16		/* Some unrecoverable error occurred */
+#define	FSDIRTY		32		/* File system is dirty */
+#define	FSFIXFAT	64		/* Fix file system FAT */
 
 /*
- * read a boot block in a machine independend fashion and translate
+ * read a boot block in a machine independent fashion and translate
  * it into our struct bootblock.
  */
 int readboot(int, struct bootblock *);
@@ -96,13 +89,13 @@
  * Read one of the FAT copies and return a pointer to the new
  * allocated array holding our description of it.
  */
-int readfat(int, struct bootblock *, int, struct fatEntry **);
+int readfat(int, struct bootblock *, u_int, struct fatEntry **);
 
 /*
  * Check two FAT copies for consistency and merge changes into the
- * first if neccessary.
+ * first if necessary.
  */
-int comparefat(struct bootblock *, struct fatEntry *, struct fatEntry *, int);
+int comparefat(struct bootblock *, struct fatEntry *, struct fatEntry *, u_int);
 
 /*
  * Check a FAT
@@ -140,7 +133,7 @@
 /*
  * Return the type of a reserved cluster as text
  */
-char *rsrvdcltype(cl_t);
+const char *rsrvdcltype(cl_t);
 
 /*
  * Clear a cluster chain in a FAT
diff --git a/fat.c b/fat.c
index 9b58ffb..9e4f5c8 100644
--- a/fat.c
+++ b/fat.c
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 1995, 1996, 1997 Wolfgang Solfrank
  * Copyright (c) 1995 Martin Husemann
  *
@@ -10,13 +12,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by Martin Husemann
- *	and Wolfgang Solfrank.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -33,9 +28,9 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: fat.c,v 1.12 2000/10/10 20:24:52 is Exp $");
+__RCSID("$NetBSD: fat.c,v 1.18 2006/06/05 16:51:18 christos Exp $");
 static const char rcsid[] =
-  "$FreeBSD: src/sbin/fsck_msdosfs/fat.c,v 1.9 2008/01/31 13:22:13 yar Exp $";
+  "$FreeBSD$";
 #endif /* not lint */
 
 #include <stdlib.h>
@@ -47,10 +42,10 @@
 #include "ext.h"
 #include "fsutil.h"
 
-static int checkclnum(struct bootblock *, int, cl_t, cl_t *);
-static int clustdiffer(cl_t, cl_t *, cl_t *, int);
+static int checkclnum(struct bootblock *, u_int, cl_t, cl_t *);
+static int clustdiffer(cl_t, cl_t *, cl_t *, u_int);
 static int tryclear(struct bootblock *, struct fatEntry *, cl_t, cl_t *);
-static int _readfat(int, struct bootblock *, int, u_char **);
+static int _readfat(int, struct bootblock *, u_int, u_char **);
 
 /*-
  * The first 2 FAT entries contain pseudo-cluster numbers with the following
@@ -76,26 +71,28 @@
 	off_t off;
 	u_char *buffer;
 	int ret = 0;
+	size_t len;
 
 	if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK)
 		return 0;
 
-	off = boot->ResSectors;
-	off *= boot->BytesPerSec;
+	off = boot->bpbResSectors;
+	off *= boot->bpbBytesPerSec;
 
-	buffer = malloc(boot->BytesPerSec);
+	buffer = malloc(len = boot->bpbBytesPerSec);
 	if (buffer == NULL) {
-		perror("No space for FAT");
+		perr("No space for FAT sectors (%zu)", len);
 		return 1;
 	}
 
 	if (lseek(fs, off, SEEK_SET) != off) {
-		perror("Unable to read FAT");
+		perr("Unable to read FAT");
 		goto err;
 	}
 
-	if (read(fs, buffer, boot->BytesPerSec) != boot->BytesPerSec) {
-		perror("Unable to read FAT");
+	if ((size_t)read(fs, buffer, boot->bpbBytesPerSec) !=
+	    boot->bpbBytesPerSec) {
+		perr("Unable to read FAT");
 		goto err;
 	}
 
@@ -103,7 +100,7 @@
 	 * If we don't understand the FAT, then the file system must be
 	 * assumed to be unclean.
 	 */
-	if (buffer[0] != boot->Media || buffer[1] != 0xff)
+	if (buffer[0] != boot->bpbMedia || buffer[1] != 0xff)
 		goto err;
 	if (boot->ClustMask == CLUST16_MASK) {
 		if ((buffer[2] & 0xf8) != 0xf8 || (buffer[3] & 0x3f) != 0x3f)
@@ -135,7 +132,7 @@
  * Check a cluster number for valid value
  */
 static int
-checkclnum(struct bootblock *boot, int fat, cl_t cl, cl_t *next)
+checkclnum(struct bootblock *boot, u_int fat, cl_t cl, cl_t *next)
 {
 	if (*next >= (CLUST_RSRVD&boot->ClustMask))
 		*next |= ~boot->ClustMask;
@@ -153,7 +150,7 @@
 		      cl, fat,
 		      *next < CLUST_RSRVD ? "out of range" : "reserved",
 		      *next&boot->ClustMask);
-		if (ask(1, "Truncate")) {
+		if (ask(0, "Truncate")) {
 			*next = CLUST_EOF;
 			return FSFATMOD;
 		}
@@ -166,30 +163,28 @@
  * Read a FAT from disk. Returns 1 if successful, 0 otherwise.
  */
 static int
-_readfat(int fs, struct bootblock *boot, int no, u_char **buffer)
+_readfat(int fs, struct bootblock *boot, u_int no, u_char **buffer)
 {
 	off_t off;
+	size_t len;
 
-        printf("Attempting to allocate %u KB for FAT\n",
-                (boot->FATsecs * boot->BytesPerSec) / 1024);
-
-	*buffer = malloc(boot->FATsecs * boot->BytesPerSec);
+	*buffer = malloc(len = boot->FATsecs * boot->bpbBytesPerSec);
 	if (*buffer == NULL) {
-		perror("No space for FAT");
+		perr("No space for FAT sectors (%zu)", len);
 		return 0;
 	}
 
-	off = boot->ResSectors + no * boot->FATsecs;
-	off *= boot->BytesPerSec;
+	off = boot->bpbResSectors + no * boot->FATsecs;
+	off *= boot->bpbBytesPerSec;
 
 	if (lseek(fs, off, SEEK_SET) != off) {
-		perror("Unable to read FAT");
+		perr("Unable to read FAT");
 		goto err;
 	}
 
-	if (read(fs, *buffer, boot->FATsecs * boot->BytesPerSec)
-	    != boot->FATsecs * boot->BytesPerSec) {
-		perror("Unable to read FAT");
+	if ((size_t)read(fs, *buffer, boot->FATsecs * boot->bpbBytesPerSec)
+	    != boot->FATsecs * boot->bpbBytesPerSec) {
+		perr("Unable to read FAT");
 		goto err;
 	}
 
@@ -204,26 +199,28 @@
  * Read a FAT and decode it into internal format
  */
 int
-readfat(int fs, struct bootblock *boot, int no, struct fatEntry **fp)
+readfat(int fs, struct bootblock *boot, u_int no, struct fatEntry **fp)
 {
 	struct fatEntry *fat;
 	u_char *buffer, *p;
 	cl_t cl;
 	int ret = FSOK;
+	size_t len;
 
 	boot->NumFree = boot->NumBad = 0;
 
 	if (!_readfat(fs, boot, no, &buffer))
 		return FSFATAL;
-		
-	fat = calloc(boot->NumClusters, sizeof(struct fatEntry));
+
+	fat = malloc(len = boot->NumClusters * sizeof(struct fatEntry));
 	if (fat == NULL) {
-		perror("No space for FAT");
+		perr("No space for FAT clusters (%zu)", len);
 		free(buffer);
 		return FSFATAL;
 	}
+	(void)memset(fat, 0, len);
 
-	if (buffer[0] != boot->Media
+	if (buffer[0] != boot->bpbMedia
 	    || buffer[1] != 0xff || buffer[2] != 0xff
 	    || (boot->ClustMask == CLUST16_MASK && buffer[3] != 0xff)
 	    || (boot->ClustMask == CLUST32_MASK
@@ -237,7 +234,7 @@
 		 * file system is dirty if it doesn't reboot cleanly.
 		 * Check this special condition before errorring out.
 		 */
-		if (buffer[0] == boot->Media && buffer[1] == 0xff
+		if (buffer[0] == boot->bpbMedia && buffer[1] == 0xff
 		    && buffer[2] == 0xff
 		    && ((boot->ClustMask == CLUST16_MASK && buffer[3] == 0x7f)
 			|| (boot->ClustMask == CLUST32_MASK
@@ -247,7 +244,7 @@
 			ret |= FSDIRTY;
 		else {
 			/* just some odd byte sequence in FAT */
-				
+
 			switch (boot->ClustMask) {
 			case CLUST32_MASK:
 				pwarn("%s (%02x%02x%02x%02x%02x%02x%02x%02x)\n",
@@ -267,7 +264,7 @@
 				break;
 			}
 
-	
+
 			if (ask(1, "Correct"))
 				ret |= FSFIXFAT;
 		}
@@ -314,14 +311,18 @@
 	}
 
 	free(buffer);
-	*fp = fat;
+	if (ret & FSFATAL) {
+		free(fat);
+		*fp = NULL;
+	} else
+		*fp = fat;
 	return ret;
 }
 
 /*
  * Get type of reserved cluster
  */
-char *
+const char *
 rsrvdcltype(cl_t cl)
 {
 	if (cl == CLUST_FREE)
@@ -334,7 +335,7 @@
 }
 
 static int
-clustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, int fatnum)
+clustdiffer(cl_t cl, cl_t *cp1, cl_t *cp2, u_int fatnum)
 {
 	if (*cp1 == CLUST_FREE || *cp1 >= CLUST_RSRVD) {
 		if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
@@ -349,13 +350,13 @@
 				}
 				return FSFATAL;
 			}
-			pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %d\n",
+			pwarn("Cluster %u is marked %s in FAT 0, %s in FAT %u\n",
 			      cl, rsrvdcltype(*cp1), rsrvdcltype(*cp2), fatnum);
-			if (ask(1, "Use FAT 0's entry")) {
+			if (ask(0, "Use FAT 0's entry")) {
 				*cp2 = *cp1;
 				return FSFATMOD;
 			}
-			if (ask(1, "Use FAT %d's entry", fatnum)) {
+			if (ask(0, "Use FAT %u's entry", fatnum)) {
 				*cp1 = *cp2;
 				return FSFATMOD;
 			}
@@ -363,36 +364,36 @@
 		}
 		pwarn("Cluster %u is marked %s in FAT 0, but continues with cluster %u in FAT %d\n",
 		      cl, rsrvdcltype(*cp1), *cp2, fatnum);
-		if (ask(1, "Use continuation from FAT %d", fatnum)) {
+		if (ask(0, "Use continuation from FAT %u", fatnum)) {
 			*cp1 = *cp2;
 			return FSFATMOD;
 		}
-		if (ask(1, "Use mark from FAT 0")) {
+		if (ask(0, "Use mark from FAT 0")) {
 			*cp2 = *cp1;
 			return FSFATMOD;
 		}
 		return FSFATAL;
 	}
 	if (*cp2 == CLUST_FREE || *cp2 >= CLUST_RSRVD) {
-		pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %d\n",
+		pwarn("Cluster %u continues with cluster %u in FAT 0, but is marked %s in FAT %u\n",
 		      cl, *cp1, rsrvdcltype(*cp2), fatnum);
-		if (ask(1, "Use continuation from FAT 0")) {
+		if (ask(0, "Use continuation from FAT 0")) {
 			*cp2 = *cp1;
 			return FSFATMOD;
 		}
-		if (ask(1, "Use mark from FAT %d", fatnum)) {
+		if (ask(0, "Use mark from FAT %d", fatnum)) {
 			*cp1 = *cp2;
 			return FSFATMOD;
 		}
 		return FSERROR;
 	}
-	pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %d\n",
+	pwarn("Cluster %u continues with cluster %u in FAT 0, but with cluster %u in FAT %u\n",
 	      cl, *cp1, *cp2, fatnum);
-	if (ask(1, "Use continuation from FAT 0")) {
+	if (ask(0, "Use continuation from FAT 0")) {
 		*cp2 = *cp1;
 		return FSFATMOD;
 	}
-	if (ask(1, "Use continuation from FAT %d", fatnum)) {
+	if (ask(0, "Use continuation from FAT %u", fatnum)) {
 		*cp1 = *cp2;
 		return FSFATMOD;
 	}
@@ -404,8 +405,8 @@
  * into the first one.
  */
 int
-comparefat(struct bootblock *boot, struct fatEntry *first, 
-    struct fatEntry *second, int fatnum)
+comparefat(struct bootblock *boot, struct fatEntry *first,
+    struct fatEntry *second, u_int fatnum)
 {
 	cl_t cl;
 	int ret = FSOK;
@@ -431,13 +432,21 @@
 }
 
 int
-tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *trunc)
+tryclear(struct bootblock *boot, struct fatEntry *fat, cl_t head, cl_t *truncp)
 {
-	if (ask(1, "Clear chain starting at %u", head)) {
+	if (ask(0, "Clear chain starting at %u", head)) {
 		clearchain(boot, fat, head);
 		return FSFATMOD;
-	} else if (ask(1, "Truncate")) {
-		*trunc = CLUST_EOF;
+	} else if (ask(0, "Truncate")) {
+		uint32_t len;
+		cl_t p;
+
+		for (p = head, len = 0;
+		    p >= CLUST_FIRST && p < boot->NumClusters;
+		    p = fat[p].next, len++)
+			continue;
+		*truncp = CLUST_EOF;
+		fat[head].length = len;
 		return FSFATMOD;
 	} else
 		return FSERROR;
@@ -449,7 +458,7 @@
 int
 checkfat(struct bootblock *boot, struct fatEntry *fat)
 {
-	cl_t head, p, h, n, wdk;
+	cl_t head, p, h, n;
 	u_int len;
 	int ret = 0;
 	int conf;
@@ -466,14 +475,9 @@
 
 		/* follow the chain and mark all clusters on the way */
 		for (len = 0, p = head;
-			 p >= CLUST_FIRST && p < boot->NumClusters;
-			 p = fat[p].next) {
-				/* we have to check the len, to avoid infinite loop */
-				if (len > boot->NumClusters) {
-					printf("detect cluster chain loop: head %u for p %u\n", head, p);
-					break;
-			}
-
+		     p >= CLUST_FIRST && p < boot->NumClusters &&
+		     fat[p].head != head;
+		     p = fat[p].next) {
 			fat[p].head = head;
 			len++;
 		}
@@ -493,33 +497,36 @@
 			continue;
 
 		/* follow the chain to its end (hopefully) */
-		/* also possible infinite loop, that's why I insert wdk counter */
-		for (p = head,wdk=boot->NumClusters;
-		     (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters && wdk;
-				 p = n,wdk--) {
-			if (fat[n].head != head)
+		for (len = fat[head].length, p = head;
+		     (n = fat[p].next) >= CLUST_FIRST && n < boot->NumClusters;
+		     p = n)
+			if (fat[n].head != head || len-- < 2)
 				break;
-		}
-
 		if (n >= CLUST_EOFS)
 			continue;
 
 		if (n == CLUST_FREE || n >= CLUST_RSRVD) {
 			pwarn("Cluster chain starting at %u ends with cluster marked %s\n",
 			      head, rsrvdcltype(n));
+clear:
 			ret |= tryclear(boot, fat, head, &fat[p].next);
 			continue;
 		}
 		if (n < CLUST_FIRST || n >= boot->NumClusters) {
 			pwarn("Cluster chain starting at %u ends with cluster out of range (%u)\n",
-			      head, n);
-			ret |= tryclear(boot, fat, head, &fat[p].next);
-			continue;
+			    head, n);
+			goto clear;
+		}
+		if (head == fat[n].head) {
+			pwarn("Cluster chain starting at %u loops at cluster %u\n",
+
+			    head, p);
+			goto clear;
 		}
 		pwarn("Cluster chains starting at %u and %u are linked at cluster %u\n",
 		      head, fat[n].head, n);
 		conf = tryclear(boot, fat, head, &fat[p].next);
-		if (ask(1, "Clear chain starting at %u", h = fat[n].head)) {
+		if (ask(0, "Clear chain starting at %u", h = fat[n].head)) {
 			if (conf == FSERROR) {
 				/*
 				 * Transfer the common chain to the one not cleared above.
@@ -554,21 +561,21 @@
 {
 	u_char *buffer, *p;
 	cl_t cl;
-	int i;
-	u_int32_t fatsz;
+	u_int i;
+	size_t fatsz;
 	off_t off;
 	int ret = FSOK;
 
-	buffer = malloc(fatsz = boot->FATsecs * boot->BytesPerSec);
+	buffer = malloc(fatsz = boot->FATsecs * boot->bpbBytesPerSec);
 	if (buffer == NULL) {
-		perror("No space for FAT");
+		perr("No space for FAT sectors (%zu)", fatsz);
 		return FSFATAL;
 	}
 	memset(buffer, 0, fatsz);
 	boot->NumFree = 0;
 	p = buffer;
 	if (correct_fat) {
-		*p++ = (u_char)boot->Media;
+		*p++ = (u_char)boot->bpbMedia;
 		*p++ = 0xff;
 		*p++ = 0xff;
 		switch (boot->ClustMask) {
@@ -610,7 +617,7 @@
 		free(old_fat);
 		p += count;
 	}
-			
+
 	for (cl = CLUST_FIRST; cl < boot->NumClusters; cl++) {
 		switch (boot->ClustMask) {
 		case CLUST32_MASK:
@@ -631,22 +638,24 @@
 		default:
 			if (fat[cl].next == CLUST_FREE)
 				boot->NumFree++;
-			if (cl + 1 < boot->NumClusters
-			    && fat[cl + 1].next == CLUST_FREE)
-				boot->NumFree++;
 			*p++ = (u_char)fat[cl].next;
-			*p++ = (u_char)((fat[cl].next >> 8) & 0xf)
-			       |(u_char)(fat[cl+1].next << 4);
-			*p++ = (u_char)(fat[++cl].next >> 4);
+			*p = (u_char)((fat[cl].next >> 8) & 0xf);
+			cl++;
+			if (cl >= boot->NumClusters)
+				break;
+			if (fat[cl].next == CLUST_FREE)
+				boot->NumFree++;
+			*p++ |= (u_char)(fat[cl + 1].next << 4);
+			*p++ = (u_char)(fat[cl + 1].next >> 4);
 			break;
 		}
 	}
-	for (i = 0; i < boot->FATs; i++) {
-		off = boot->ResSectors + i * boot->FATsecs;
-		off *= boot->BytesPerSec;
+	for (i = 0; i < boot->bpbFATs; i++) {
+		off = boot->bpbResSectors + i * boot->FATsecs;
+		off *= boot->bpbBytesPerSec;
 		if (lseek(fs, off, SEEK_SET) != off
-		    || write(fs, buffer, fatsz) != fatsz) {
-			perror("Unable to write FAT");
+		    || (size_t)write(fs, buffer, fatsz) != fatsz) {
+			perr("Unable to write FAT");
 			ret = FSFATAL; /* Return immediately?		XXX */
 		}
 	}
@@ -663,7 +672,7 @@
 	cl_t head;
 	int mod = FSOK;
 	int ret;
-	
+
 	for (head = CLUST_FIRST; head < boot->NumClusters; head++) {
 		/* find next untravelled chain */
 		if (fat[head].head != head
@@ -676,64 +685,26 @@
 		pwarn("Lost cluster chain at cluster %u\n%d Cluster(s) lost\n",
 		      head, fat[head].length);
 		mod |= ret = reconnect(dosfs, boot, fat, head);
-		if (mod & FSFATAL) {
-			/* If the reconnect failed, then just clear the chain */
-			pwarn("Error reconnecting chain - clearing\n");
-			mod &= ~FSFATAL;
-			clearchain(boot, fat, head);
-			mod |= FSFATMOD;
-			continue;
-		}
-		if (ret == FSERROR && ask(1, "Clear")) {
+		if (mod & FSFATAL)
+			break;
+		if (ret == FSERROR && ask(0, "Clear")) {
 			clearchain(boot, fat, head);
 			mod |= FSFATMOD;
 		}
 	}
 	finishlf();
 
-	if (boot->FSInfo) {
+	if (boot->bpbFSInfo) {
 		ret = 0;
-		if (boot->FSFree != boot->NumFree) {
-			pwarn("Free space in FSInfo block (%d) not correct (%d)\n",
+		if (boot->FSFree != 0xffffffffU &&
+		    boot->FSFree != boot->NumFree) {
+			pwarn("Free space in FSInfo block (%u) not correct (%u)\n",
 			      boot->FSFree, boot->NumFree);
 			if (ask(1, "Fix")) {
 				boot->FSFree = boot->NumFree;
 				ret = 1;
 			}
 		}
-
-		if (boot->NumFree) {
-			if ((boot->FSNext >= boot->NumClusters) || (fat[boot->FSNext].next != CLUST_FREE)) {
-				pwarn("Next free cluster in FSInfo block (%u) not free\n",
-				      boot->FSNext);
-				if (ask(1, "Fix"))
-					for (head = CLUST_FIRST; head < boot->NumClusters; head++)
-						if (fat[head].next == CLUST_FREE) {
-							boot->FSNext = head;
-							ret = 1;
-							break;
-						}
-			}
-        }
-
-		if (boot->FSNext > boot->NumClusters  ) {
-			pwarn("FSNext block (%d) not correct NumClusters (%d)\n",
-					boot->FSNext, boot->NumClusters);
-			boot->FSNext=CLUST_FIRST; // boot->FSNext can have -1 value.
-	    }
-
-		if (boot->NumFree && fat[boot->FSNext].next != CLUST_FREE) {
-			pwarn("Next free cluster in FSInfo block (%u) not free\n",
-					boot->FSNext);
-			if (ask(1, "Fix"))
-				for (head = CLUST_FIRST; head < boot->NumClusters; head++)
-					if (fat[head].next == CLUST_FREE) {
-						boot->FSNext = head;
-						ret = 1;
-						break;
-					}
-	    }
-
 		if (ret)
 			mod |= writefsinfo(dosfs, boot);
 	}
diff --git a/fsutil.c b/fsutil.c
new file mode 100644
index 0000000..0fb0613
--- /dev/null
+++ b/fsutil.c
@@ -0,0 +1,234 @@
+/*	$NetBSD: fsutil.c,v 1.15 2006/06/05 16:52:05 christos Exp $	*/
+
+/*-
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: fsutil.c,v 1.15 2006/06/05 16:52:05 christos Exp $");
+#endif /* not lint */
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <errno.h>
+#ifndef __ANDROID__
+#include <fstab.h>
+#endif
+#include <paths.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "fsutil.h"
+
+static const char *dev = NULL;
+static int preen = 0;
+
+static void vmsg(int, const char *, va_list) __printflike(2, 0);
+
+void
+setcdevname(const char *cd, int pr)
+{
+	dev = cd;
+	preen = pr;
+}
+
+const char *
+cdevname(void)
+{
+	return dev;
+}
+
+static void
+vmsg(int fatal, const char *fmt, va_list ap)
+{
+	if (!fatal && preen)
+		(void) printf("%s: ", dev);
+
+	(void) vprintf(fmt, ap);
+
+	if (fatal && preen)
+		(void) printf("\n");
+
+	if (fatal && preen) {
+		(void) printf(
+		    "%s: UNEXPECTED INCONSISTENCY; RUN %s MANUALLY.\n",
+		    dev, getprogname());
+		exit(8);
+	}
+}
+
+/*VARARGS*/
+void
+pfatal(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vmsg(1, fmt, ap);
+	va_end(ap);
+}
+
+/*VARARGS*/
+void
+pwarn(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vmsg(0, fmt, ap);
+	va_end(ap);
+}
+
+void
+perr(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vmsg(1, fmt, ap);
+	va_end(ap);
+}
+
+void
+panic(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vmsg(1, fmt, ap);
+	va_end(ap);
+	exit(8);
+}
+
+const char *
+devcheck(const char *origname)
+{
+	struct stat stslash, stchar;
+
+	if (stat("/", &stslash) < 0) {
+		perr("Can't stat `/'");
+		return (origname);
+	}
+	if (stat(origname, &stchar) < 0) {
+		perr("Can't stat %s\n", origname);
+		return (origname);
+	}
+	if (!S_ISCHR(stchar.st_mode)) {
+		perr("%s is not a char device\n", origname);
+	}
+	return (origname);
+}
+
+#ifndef __ANDROID__
+/*
+ * Get the mount point information for name.
+ */
+struct statfs *
+getmntpt(const char *name)
+{
+	struct stat devstat, mntdevstat;
+	char device[sizeof(_PATH_DEV) - 1 + MNAMELEN];
+	char *dev_name;
+	struct statfs *mntbuf, *statfsp;
+	int i, mntsize, isdev;
+
+	if (stat(name, &devstat) != 0)
+		return (NULL);
+	if (S_ISCHR(devstat.st_mode) || S_ISBLK(devstat.st_mode))
+		isdev = 1;
+	else
+		isdev = 0;
+	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
+	for (i = 0; i < mntsize; i++) {
+		statfsp = &mntbuf[i];
+		dev_name = statfsp->f_mntfromname;
+		if (*dev_name != '/') {
+			if (strlen(_PATH_DEV) + strlen(dev_name) + 1 >
+			    sizeof(statfsp->f_mntfromname))
+				continue;
+			strcpy(device, _PATH_DEV);
+			strcat(device, dev_name);
+			strcpy(statfsp->f_mntfromname, device);
+		}
+		if (isdev == 0) {
+			if (strcmp(name, statfsp->f_mntonname))
+				continue;
+			return (statfsp);
+		}
+		if (stat(dev_name, &mntdevstat) == 0 &&
+		    mntdevstat.st_rdev == devstat.st_rdev)
+			return (statfsp);
+	}
+	statfsp = NULL;
+	return (statfsp);
+}
+#endif
+
+void *
+emalloc(size_t s)
+{
+	void *p;
+
+	p = malloc(s);
+	if (p == NULL)
+		err(1, "malloc failed");
+	return (p);
+}
+
+
+void *
+erealloc(void *p, size_t s)
+{
+	void *q;
+
+	q = realloc(p, s);
+	if (q == NULL)
+		err(1, "realloc failed");
+	return (q);
+}
+
+
+char *
+estrdup(const char *s)
+{
+	char *p;
+
+	p = strdup(s);
+	if (p == NULL)
+		err(1, "strdup failed");
+	return (p);
+}
diff --git a/fsutil.h b/fsutil.h
index 7acfdd6..21e3649 100644
--- a/fsutil.h
+++ b/fsutil.h
@@ -1,7 +1,58 @@
-#ifndef _FS_UTIL_H
-#define _FS_UTIL_H
+/*	$NetBSD: fsutil.h,v 1.114 2009/10/21 01:07:46 snj Exp $	*/
 
-#define pwarn printf
-#define pfatal printf
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 1996 Christos Zoulas.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
 
+#ifdef __ANDROID__
+#define    __dead2     __attribute__((__noreturn__))
+#endif
+
+void pfatal(const char *, ...) __printflike(1, 2);
+void pwarn(const char *, ...) __printflike(1, 2);
+void perr(const char *, ...) __printflike(1, 2);
+void panic(const char *, ...) __dead2 __printflike(1, 2);
+const char *devcheck(const char *);
+const char *cdevname(void);
+void setcdevname(const char *, int);
+struct statfs *getmntpt(const char *);
+void *emalloc(size_t);
+void *erealloc(void *, size_t);
+char *estrdup(const char *);
+
+#ifndef __ANDROID__
+#define	CHECK_PREEN	0x0001
+#define	CHECK_VERBOSE	0x0002
+#define	CHECK_DEBUG	0x0004
+#define	CHECK_BACKGRD	0x0008
+#define	DO_BACKGRD	0x0010
+#define	CHECK_CLEAN	0x0020
+
+struct fstab;
+int checkfstab(int, int (*)(struct fstab *),
+    int (*) (const char *, const char *, const char *, const char *, pid_t *));
 #endif
diff --git a/main.c b/main.c
index 4938227..8425d8a 100644
--- a/main.c
+++ b/main.c
@@ -1,4 +1,6 @@
-/*
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (C) 1995 Wolfgang Solfrank
  * Copyright (c) 1995 Martin Husemann
  *
@@ -10,13 +12,6 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *	This product includes software developed by Martin Husemann
- *	and Wolfgang Solfrank.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
@@ -35,12 +30,11 @@
 #ifndef lint
 __RCSID("$NetBSD: main.c,v 1.10 1997/10/01 02:18:14 enami Exp $");
 static const char rcsid[] =
-  "$FreeBSD: src/sbin/fsck_msdosfs/main.c,v 1.16 2009/06/10 19:02:54 avg Exp $";
+  "$FreeBSD$";
 #endif /* not lint */
 
 #include <stdlib.h>
 #include <string.h>
-#include <ctype.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <errno.h>
@@ -55,7 +49,7 @@
 int rdonly;		/* device is opened read only (supersedes above) */
 int skipclean;		/* skip clean file systems if preening */
 
-static void usage(void);
+static void usage(void) __dead2;
 
 static void
 usage(void)
@@ -117,7 +111,7 @@
 		usage();
 
 	while (--argc >= 0) {
-//		setcdevname(*argv, preen);
+		setcdevname(*argv, preen);
 		erg = checkfilesys(*argv++);
 		if (erg > ret)
 			ret = erg;
@@ -146,6 +140,7 @@
 
 	va_start(ap, fmt);
 	vsnprintf(prompt, sizeof(prompt), fmt, ap);
+	va_end(ap);
 	if (alwaysyes || rdonly) {
 		printf("%s? %s\n", prompt, rdonly ? "no" : "yes");
 		return !rdonly;