Update to upstream 6424881cc82a65a833fc6fb79730474caedf6222.

Test: treehugger
Change-Id: Ib11457ec6d166e774c9a053e23db4131c0030321
diff --git a/Android.bp b/Android.bp
index fcaffbc..a447263 100755
--- a/Android.bp
+++ b/Android.bp
@@ -1,5 +1,6 @@
 cc_binary {
     name: "newfs_msdos",
+    c_std: "gnu11",
     cflags: [
         "-Wall",
         "-Werror",
@@ -7,9 +8,7 @@
         "-Wno-unused-parameter",
         "-Wno-unused-variable",
         "-D_FILE_OFFSET_BITS=64",
-        "-D_GNU_SOURCE",
-        "-DSIGINFO=SIGUSR2",
-        "-Dnitems(x)=(sizeof((x))/sizeof((x)[0]))",
+        "-include freebsd-compat.h"
     ],
     srcs: [
         "mkfs_msdos.c",
diff --git a/METADATA b/METADATA
index a0b9090..af1cf5d 100644
--- a/METADATA
+++ b/METADATA
@@ -9,9 +9,9 @@
   }
   url {
     type: ARCHIVE
-    value: "https://github.git.corp.google.com/freebsd/freebsd/+archive/b25a2bc/sbin/newfs_msdos.tar.gz"
+    value: "https://github.git.corp.google.com/freebsd/freebsd/+archive/6424881cc82a65a833fc6fb79730474caedf6222/sbin/newfs_msdos.tar.gz"
   }
-  version: "b25a2bc"
-  last_upgrade_date { year: 2018 month: 4 day: 26 }
+  version: "6424881cc82a65a833fc6fb79730474caedf6222"
+  last_upgrade_date { year: 2021 month: 1 day: 20 }
   license_type: NOTICE
 }
diff --git a/freebsd-compat.h b/freebsd-compat.h
new file mode 100644
index 0000000..5e39c5a
--- /dev/null
+++ b/freebsd-compat.h
@@ -0,0 +1,42 @@
+#pragma once
+
+#define _GNU_SOURCE
+
+#include <stdint.h>
+
+#include <sys/param.h>
+// Ensure we use a BSD powerof2 that works in static_assert (unlike glibc's).
+#undef powerof2
+#define powerof2(x) ((((x)-1)&(x))==0)
+// This is in BSD's <sys/param.h>.
+#define nitems(x) (sizeof((x))/sizeof((x)[0]))
+
+// This is used as the size of the write buffer of sectors.
+#define MAXPHYS (1024 * 1024)
+
+//#define static_assert _Static_assert
+
+// TODO: do we actually want this, or should we live without?
+#define SIGINFO SIGUSR2
+
+
+// On glibc, these headers use `__unused` as an identifier, so drag them in
+// first.
+#include <sys/stat.h>
+#if __has_include(<sys/sysctl.h>)
+#include <sys/sysctl.h>
+#endif
+// Bionic, like the BSDs, has __unused. glibc doesn't.
+#if defined(__GLIBC__)
+#define __unused __attribute__((__unused__))
+#endif
+// Neither macOS nor glibc has __packed.
+#if defined(__APPLE__) || defined(__GLIBC__)
+#define __packed __attribute__((__packed__))
+#endif
+
+// The BSDs (including Android and macOS) have getprogname(), but glibc doesn't.
+#if defined(__GLIBC__)
+#include <errno.h>
+static inline char* getprogname() { return program_invocation_short_name; }
+#endif
diff --git a/mkfs_msdos.c b/mkfs_msdos.c
index 878ff8f..667df47 100644
--- a/mkfs_msdos.c
+++ b/mkfs_msdos.c
@@ -27,11 +27,14 @@
 
 #ifndef lint
 static const char rcsid[] =
-  "$FreeBSD: head/sbin/newfs_msdos/mkfs_msdos.c 335189 2018-06-15 06:03:40Z delphij $";
+  "$FreeBSD$";
 #endif /* not lint */
 
 #include <sys/param.h>
-#if defined(__linux__)
+#ifdef MAKEFS
+/* In the makefs case we only want struct disklabel */
+#include <sys/disk/bsd.h>
+#elif defined(__linux__)
 #include <linux/fs.h>
 #include <linux/hdreg.h>
 #include <sys/ioctl.h>
@@ -44,8 +47,12 @@
 #include <sys/mount.h>
 #endif
 #include <sys/stat.h>
+#if __has_include(<sys/sysctl.h>)
+#include <sys/sysctl.h>
+#endif
 #include <sys/time.h>
 
+#include <assert.h>
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
@@ -61,19 +68,13 @@
 
 #include "mkfs_msdos.h"
 
-#if !defined(__packed)
-#define __packed __attribute__((__packed__))
-#endif
-#if !defined(__unused)
-#define __unused __attribute__((__unused__))
-#endif
-
 #define	MAXU16	  0xffff	/* maximum unsigned 16-bit quantity */
 #define	BPN	  4		/* bits per nibble */
 #define	NPB	  2		/* nibbles per byte */
 
 #define	DOSMAGIC  0xaa55	/* DOS magic number */
 #define	MINBPS	  512		/* minimum bytes per sector */
+#define	MAXBPS    4096		/* maximum bytes per sector */
 #define	MAXSPC	  128		/* maximum sectors per cluster */
 #define	MAXNFT	  16		/* maximum number of FATs */
 #define	DEFBLK	  4096		/* default block size */
@@ -230,6 +231,7 @@
 static void infohandler(int);
 
 static int check_mounted(const char *, mode_t);
+static ssize_t getchunksize(void);
 static int getstdfmt(const char *, struct bpb *);
 static int getdiskinfo(int, const char *, const char *, int, struct bpb *);
 static void print_bpb(struct bpb *);
@@ -253,6 +255,7 @@
     struct bsx *bsx;
     struct de *de;
     u_int8_t *img;
+    u_int8_t *physbuf, *physbuf_end;
     const char *bname;
     ssize_t n;
     time_t now;
@@ -261,8 +264,9 @@
     bool set_res, set_spf, set_spc;
     int fd, fd1, rv;
     struct msdos_options o = *op;
+    ssize_t chunksize;
 
-    img = NULL;
+    physbuf = NULL;
     rv = -1;
     fd = fd1 = -1;
 
@@ -300,12 +304,18 @@
 	if (!S_ISREG(sb.st_mode))
 	    warnx("warning, %s is not a regular file", fname);
     } else {
+#ifdef MAKEFS
+	errx(1, "o.create_size must be set!");
+#else
 	if (!S_ISCHR(sb.st_mode))
 	    warnx("warning, %s is not a character device", fname);
+#endif
     }
+#ifndef MAKEFS
     if (!o.no_create)
 	if (check_mounted(fname, sb.st_mode) == -1)
 	    goto done;
+#endif
     if (o.offset && o.offset != lseek(fd, o.offset, SEEK_SET)) {
 	warnx("cannot seek to %jd", (intmax_t)o.offset);
 	goto done;
@@ -331,7 +341,8 @@
 	bpb.bpbHiddenSecs = o.hidden_sectors;
     if (!(o.floppy || (o.drive_heads && o.sectors_per_track &&
 	o.bytes_per_sector && o.size && o.hidden_sectors_set))) {
-	getdiskinfo(fd, fname, dtype, o.hidden_sectors_set, &bpb);
+	if (getdiskinfo(fd, fname, dtype, o.hidden_sectors_set, &bpb) == -1)
+		goto done;
 	bpb.bpbHugeSectors -= (o.offset / bpb.bpbBytesPerSec);
 	if (bpb.bpbSecPerClust == 0) {	/* set defaults */
 	    if (bpb.bpbHugeSectors <= 6000)	/* about 3MB -> 512 bytes */
@@ -346,13 +357,11 @@
 		bpb.bpbSecPerClust = 64;		/* otherwise 32k */
 	}
     }
-    if (!powerof2(bpb.bpbBytesPerSec)) {
-	warnx("bytes/sector (%u) is not a power of 2", bpb.bpbBytesPerSec);
-	goto done;
-    }
-    if (bpb.bpbBytesPerSec < MINBPS) {
-	warnx("bytes/sector (%u) is too small; minimum is %u",
-	     bpb.bpbBytesPerSec, MINBPS);
+    if (bpb.bpbBytesPerSec < MINBPS ||
+        bpb.bpbBytesPerSec > MAXBPS ||
+	!powerof2(bpb.bpbBytesPerSec)) {
+	warnx("Invalid bytes/sector (%u): must be 512, 1024, 2048 or 4096",
+	    bpb.bpbBytesPerSec);
 	goto done;
     }
 
@@ -436,10 +445,7 @@
 	bname = o.bootstrap;
 	if (!strchr(bname, '/')) {
 	    snprintf(buf, sizeof(buf), "/boot/%s", bname);
-	    if (!(bname = strdup(buf))) {
-		warn(NULL);
-		goto done;
-	    }
+	    bname = buf;
 	}
 	if ((fd1 = open(bname, O_RDONLY)) == -1 || fstat(fd1, &sb)) {
 	    warn("%s", bname);
@@ -627,19 +633,25 @@
 	    tm = localtime(&now);
 	}
 
-
-	if (!(img = malloc(bpb.bpbBytesPerSec))) {
+	chunksize = getchunksize();
+	physbuf = malloc(chunksize);
+	if (physbuf == NULL) {
 	    warn(NULL);
 	    goto done;
 	}
+	physbuf_end = physbuf + chunksize;
+	img = physbuf;
+
 	dir = bpb.bpbResSectors + (bpb.bpbFATsecs ? bpb.bpbFATsecs :
 				   bpb.bpbBigFATsecs) * bpb.bpbFATs;
 	memset(&si_sa, 0, sizeof(si_sa));
 	si_sa.sa_handler = infohandler;
+#ifdef SIGINFO
 	if (sigaction(SIGINFO, &si_sa, NULL) == -1) {
 	    warn("sigaction SIGINFO");
 	    goto done;
 	}
+#endif
 
 #if defined(__linux__)
 	if (ioctl(fd, BLKBSZSET, &bpb.bpbBytesPerSec))
@@ -738,7 +750,7 @@
 		mk4(img, 0x41615252);
 		mk4(img + MINBPS - 28, 0x61417272);
 		mk4(img + MINBPS - 24, 0xffffffff);
-		mk4(img + MINBPS - 20, bpb.bpbRootClust);
+		mk4(img + MINBPS - 20, 0xffffffff);
 		mk2(img + MINBPS - 2, DOSMAGIC);
 	    } else if (lsn >= bpb.bpbResSectors && lsn < dir &&
 		       !((lsn - bpb.bpbResSectors) %
@@ -760,19 +772,37 @@
 		    (u_int)tm->tm_mday;
 		mk2(de->deMDate, x);
 	    }
-	    if ((n = write(fd, img, bpb.bpbBytesPerSec)) == -1) {
-		warn("%s", fname);
-		goto done;
+	    /*
+	     * Issue a write of chunksize once we have collected
+	     * enough sectors.
+	     */
+	    img += bpb.bpbBytesPerSec;
+	    if (img >= physbuf_end) {
+		n = write(fd, physbuf, chunksize);
+		if (n != chunksize) {
+		    warnx("%s: can't write sector %u", fname, lsn);
+		    goto done;
+		}
+		img = physbuf;
 	    }
-	    if ((unsigned)n != bpb.bpbBytesPerSec) {
-		warnx("%s: can't write sector %u", fname, lsn);
-		goto done;
-	    }
+	}
+	/*
+	 * Write remaining sectors, if the last write didn't end
+	 * up filling a whole chunk.
+	 */
+	if (img != physbuf) {
+		ssize_t tailsize = img - physbuf;
+
+		n = write(fd, physbuf, tailsize);
+		if (n != tailsize) {
+		    warnx("%s: can't write sector %u", fname, lsn);
+		    goto done;
+		}
 	}
     }
     rv = 0;
 done:
-    free(img);
+    free(physbuf);
     if (fd != -1)
 	    close(fd);
     if (fd1 != -1)
@@ -787,7 +817,11 @@
 static int
 check_mounted(const char *fname, mode_t mode)
 {
-#if 0
+/*
+ * If getmntinfo() is not available (e.g. Linux) don't check. This should
+ * not be a problem since we will only be using makefs to create images.
+ */
+#if 0 && !defined(MAKEFS)
     struct statfs *mp;
     const char *s1, *s2;
     size_t len;
@@ -817,6 +851,47 @@
 }
 
 /*
+ * Get optimal I/O size
+ */
+static ssize_t
+getchunksize(void)
+{
+	static int chunksize;
+
+	if (chunksize != 0)
+		return ((ssize_t)chunksize);
+
+#ifdef	KERN_MAXPHYS
+	int mib[2];
+	size_t len;
+
+	mib[0] = CTL_KERN;
+	mib[1] = KERN_MAXPHYS;
+	len = sizeof(chunksize);
+
+	if (sysctl(mib, 2, &chunksize, &len, NULL, 0) == -1) {
+		warn("sysctl: KERN_MAXPHYS, using %zu", (size_t)MAXPHYS);
+		chunksize = 0;
+	}
+#endif
+	if (chunksize == 0)
+		chunksize = MAXPHYS;
+
+	/*
+	 * For better performance, we want to write larger chunks instead of
+	 * individual sectors (the size can only be 512, 1024, 2048 or 4096
+	 * bytes). Assert that chunksize can always hold an integer number of
+	 * sectors by asserting that both are power of two numbers and the
+	 * chunksize is greater than MAXBPS.
+	 */
+	static_assert(powerof2(MAXBPS), "MAXBPS is not power of 2");
+	assert(powerof2(chunksize));
+	assert(chunksize > MAXBPS);
+
+	return ((ssize_t)chunksize);
+}
+
+/*
  * Get a standard format.
  */
 static int
@@ -834,6 +909,25 @@
     return 0;
 }
 
+#if 0
+static void
+compute_geometry_from_file(int fd, const char *fname, struct disklabel *lp)
+{
+	struct stat st;
+	off_t ms;
+
+	if (fstat(fd, &st))
+		err(1, "cannot get disk size");
+	if (!S_ISREG(st.st_mode))
+		errx(1, "%s is not a regular file", fname);
+	ms = st.st_size;
+	lp->d_secsize = 512;
+	lp->d_nsectors = 63;
+	lp->d_ntracks = 255;
+	lp->d_secperunit = ms / lp->d_secsize;
+}
+#endif
+
 /*
  * Get disk slice, partition, and geometry information.
  */
@@ -879,8 +973,10 @@
 	    struct bpb *bpb)
 {
     struct disklabel *lp, dlp;
+    off_t hs = 0;
+#ifndef MAKEFS
+    off_t ms;
     struct fd_type type;
-    off_t ms, hs = 0;
 
     lp = NULL;
 
@@ -892,16 +988,8 @@
     /* Maybe it's a floppy drive */
     if (lp == NULL) {
 	if (ioctl(fd, DIOCGMEDIASIZE, &ms) == -1) {
-	    struct stat st;
-
-	    if (fstat(fd, &st))
-		err(1, "cannot get disk size");
 	    /* create a fake geometry for a file image */
-	    ms = st.st_size;
-	    dlp.d_secsize = 512;
-	    dlp.d_nsectors = 63;
-	    dlp.d_ntracks = 255;
-	    dlp.d_secperunit = ms / dlp.d_secsize;
+	    compute_geometry_from_file(fd, fname, &dlp);
 	    lp = &dlp;
 	} else if (ioctl(fd, FD_GTYPE, &type) != -1) {
 	    dlp.d_secsize = 128 << type.secsize;
@@ -941,6 +1029,11 @@
 	hs = (ms / dlp.d_secsize) - dlp.d_secperunit;
 	lp = &dlp;
     }
+#else
+    /* In the makefs case we only support image files: */
+    compute_geometry_from_file(fd, fname, &dlp);
+    lp = &dlp;
+#endif
 
     if (bpb->bpbBytesPerSec == 0) {
 	if (ckgeom(fname, lp->d_secsize, "bytes/sector") == -1)
diff --git a/mkfs_msdos.h b/mkfs_msdos.h
index b49a577..7e0c662 100644
--- a/mkfs_msdos.h
+++ b/mkfs_msdos.h
@@ -1,4 +1,4 @@
-/*	$FreeBSD: head/sbin/newfs_msdos/mkfs_msdos.h 335189 2018-06-15 06:03:40Z delphij $	*/
+/*	$FreeBSD$	*/
 /*	$NetBSD: mkfs_msdos.h,v 1.3 2015/10/16 17:38:17 christos Exp $	*/
 
 /*-
@@ -32,7 +32,6 @@
 
 #include <sys/types.h>
 #include <stdbool.h>
-#include <stdint.h>
 #define ALLOPTS \
 AOPT('@', off_t, offset, 0, "Offset in device") \
 AOPT('A', bool, align, -2, "Attempt to cluster align root directory") \
@@ -71,7 +70,3 @@
 };
 
 int mkfs_msdos(const char *, const char *, const struct msdos_options *);
-
-#if defined(__GLIBC__)
-static inline char* getprogname() { return program_invocation_short_name; }
-#endif
diff --git a/newfs_msdos.c b/newfs_msdos.c
index cd03164..bac250e 100644
--- a/newfs_msdos.c
+++ b/newfs_msdos.c
@@ -29,7 +29,7 @@
 
 #ifndef lint
 static const char rcsid[] =
-  "$FreeBSD: head/sbin/newfs_msdos/newfs_msdos.c 335189 2018-06-15 06:03:40Z delphij $";
+  "$FreeBSD$";
 #endif /* not lint */
 
 #include <sys/param.h>
@@ -178,18 +178,17 @@
     argv += optind;
     if (argc < 1 || argc > 2)
 	usage();
-	if (o.align) {
-		if (o.hidden_sectors_set)
-		    errx(1, "align (-A) is incompatible with -r");
-	}
+    if (o.align) {
+	if (o.reserved_sectors)
+	    errx(1, "align (-A) is incompatible with -r");
+    }
     fname = *argv++;
     if (!o.create_size && !strchr(fname, '/')) {
 	snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
-	if (!(fname = strdup(buf)))
-	    err(1, NULL);
+	fname = buf;
     }
     dtype = *argv;
-    return !!mkfs_msdos(fname, dtype, &o);
+    exit(!!mkfs_msdos(fname, dtype, &o));
 }
 
 /*