Import revision ffa396196c4c0fcf8a6bf4d1dd88ffb9ce531ccc from FreeBSD.

Change-Id: Ibbded2a22a581886dd43e0443e786ad867471f1c
diff --git a/Makefile b/Makefile
index b101724..bce4821 100644
--- a/Makefile
+++ b/Makefile
@@ -9,6 +9,7 @@
 MAN=	fsck_msdosfs.8
 SRCS=	main.c check.c boot.c fat.c dir.c fsutil.c
 
-CFLAGS+= -I${FSCK}
+CFLAGS+= -I${FSCK} -DHAVE_LIBUTIL_H
+LIBADD=	util
 
 .include <bsd.prog.mk>
diff --git a/boot.c b/boot.c
index 86528ce..887312e 100644
--- a/boot.c
+++ b/boot.c
@@ -28,7 +28,7 @@
 
 #include <sys/cdefs.h>
 #ifndef lint
-__RCSID("$NetBSD: boot.c,v 1.21 2018/02/08 09:05:17 dholland Exp $");
+__RCSID("$NetBSD: boot.c,v 1.22 2020/01/11 16:29:07 christos Exp $");
 static const char rcsid[] =
   "$FreeBSD$";
 #endif /* not lint */
@@ -267,10 +267,11 @@
 	}
 
 	/*
-	 * The number of clusters is derived from available data sectors, divided
-	 * by sectors per cluster.
+	 * The number of clusters is derived from available data sectors,
+	 * divided by sectors per cluster.
 	 */
-	boot->NumClusters = (boot->NumSectors - boot->FirstCluster) / boot->bpbSecPerClust;
+	boot->NumClusters =
+	    (boot->NumSectors - boot->FirstCluster) / boot->bpbSecPerClust;
 
 	if (boot->flags & FAT32) {
 		if (boot->NumClusters > (CLUST_RSRVD & CLUST32_MASK)) {
@@ -320,8 +321,8 @@
 	}
 
 	/*
-	 * There are two reserved clusters.  To avoid adding CLUST_FIRST every time
-	 * when we perform boundary checks, we increment the NumClusters by 2,
+	 * There are two reserved clusters. To avoid adding CLUST_FIRST every
+	 * time we perform boundary checks, we increment the NumClusters by 2,
 	 * which is CLUST_FIRST to denote the first out-of-range cluster number.
 	 */
 	boot->NumClusters += CLUST_FIRST;
diff --git a/check.c b/check.c
index 2c38662..f2e896b 100644
--- a/check.c
+++ b/check.c
@@ -33,6 +33,9 @@
   "$FreeBSD$";
 #endif /* not lint */
 
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
@@ -126,15 +129,38 @@
 			mod |= FSERROR;
 	}
 
+#ifdef HAVE_LIBUTIL_H
+	char freestr[7], badstr[7];
+
+	int64_t freebytes = boot.NumFree * boot.ClusterSize;
+	humanize_number(freestr, sizeof(freestr), freebytes, "",
+	    HN_AUTOSCALE, HN_DECIMAL | HN_IEC_PREFIXES);
+	if (boot.NumBad) {
+		int64_t badbytes = boot.NumBad * boot.ClusterSize;
+
+		humanize_number(badstr, sizeof(badstr), badbytes, "",
+		    HN_AUTOSCALE, HN_B | HN_DECIMAL | HN_IEC_PREFIXES);
+
+		pwarn("%d files, %sB free (%d clusters), %sB bad (%d clusters)\n",
+		      boot.NumFiles,
+		      freestr, boot.NumFree,
+		      badstr, boot.NumBad);
+	} else {
+		pwarn("%d files, %sB free (%d clusters)\n",
+		      boot.NumFiles,
+		      freestr, boot.NumFree);
+	}
+#else
 	if (boot.NumBad)
-		pwarn("%d files, %d free (%d clusters), %d bad (%d clusters)\n",
+		pwarn("%d files, %d KiB free (%d clusters), %d KiB bad (%d clusters)\n",
 		      boot.NumFiles,
 		      boot.NumFree * boot.ClusterSize / 1024, boot.NumFree,
 		      boot.NumBad * boot.ClusterSize / 1024, boot.NumBad);
 	else
-		pwarn("%d files, %d free (%d clusters)\n",
+		pwarn("%d files, %d KiB free (%d clusters)\n",
 		      boot.NumFiles,
 		      boot.NumFree * boot.ClusterSize / 1024, boot.NumFree);
+#endif
 
 	if (mod && (mod & FSERROR) == 0) {
 		if (mod & FSDIRTY) {
@@ -143,7 +169,7 @@
 
 			if (mod & FSDIRTY) {
 				pwarn("MARKING FILE SYSTEM CLEAN\n");
-				mod |= writefat(fat);
+				mod |= cleardirty(fat);
 			} else {
 				pwarn("\n***** FILE SYSTEM IS LEFT MARKED AS DIRTY *****\n");
 				mod |= FSERROR; /* file system not clean */
diff --git a/ext.h b/ext.h
index 6e9ba7f..532e840 100644
--- a/ext.h
+++ b/ext.h
@@ -90,6 +90,8 @@
 /* Opaque type */
 struct fat_descriptor;
 
+int cleardirty(struct fat_descriptor *);
+
 void fat_clear_cl_head(struct fat_descriptor *, cl_t);
 bool fat_is_cl_head(struct fat_descriptor *, cl_t);
 
diff --git a/fat.c b/fat.c
index cb92010..e35e2f2 100644
--- a/fat.c
+++ b/fat.c
@@ -578,7 +578,6 @@
  * h = hard error flag (1 = ok; 0 = I/O error)
  * x = any value ok
  */
-
 int
 checkdirty(int fs, struct bootblock *boot)
 {
@@ -642,6 +641,53 @@
 	return ret;
 }
 
+int
+cleardirty(struct fat_descriptor *fat)
+{
+	int fd, ret = FSERROR;
+	struct bootblock *boot;
+	u_char *buffer;
+	size_t len;
+	off_t off;
+
+	boot = boot_of_(fat);
+	fd = fd_of_(fat);
+
+	if (boot->ClustMask != CLUST16_MASK && boot->ClustMask != CLUST32_MASK)
+		return 0;
+
+	off = boot->bpbResSectors;
+	off *= boot->bpbBytesPerSec;
+
+	buffer = malloc(len = boot->bpbBytesPerSec);
+	if (buffer == NULL) {
+		perr("No memory for FAT sectors (%zu)", len);
+		return 1;
+	}
+
+	if ((size_t)pread(fd, buffer, len, off) != len) {
+		perr("Unable to read FAT");
+		goto err;
+	}
+
+	if (boot->ClustMask == CLUST16_MASK) {
+		buffer[3] |= 0x80;
+	} else {
+		buffer[7] |= 0x08;
+	}
+
+	if ((size_t)pwrite(fd, buffer, len, off) != len) {
+		perr("Unable to write FAT");
+		goto err;
+	}
+
+	ret = FSOK;
+
+err:
+	free(buffer);
+	return ret;
+}
+
 /*
  * Read a FAT from disk. Returns 1 if successful, 0 otherwise.
  */