From 01361bd7913c2690b999ca26274de94be48f5564 Mon Sep 17 00:00:00 2001
From: Sascha Wildner <saw@online.de>
Date: Tue, 27 Mar 2018 08:05:32 +0200
Subject: [PATCH] WIP makefs, libnetbsd, contrib/mtree, contrib/mknod

---
 contrib/mknod/pack_dev.c                     |  290 ++++
 contrib/mknod/pack_dev.h                     |   52 +
 contrib/mtree/Makefile                       |   20 +
 contrib/mtree/compare.c                      |  576 +++++++
 contrib/mtree/crc.c                          |  163 ++
 contrib/mtree/create.c                       |  476 ++++++
 contrib/mtree/excludes.c                     |  121 ++
 contrib/mtree/extern.h                       |   90 ++
 contrib/mtree/getid.c                        |  446 ++++++
 contrib/mtree/misc.c                         |  312 ++++
 contrib/mtree/mtree.8                        |  808 ++++++++++
 contrib/mtree/mtree.c                        |  332 ++++
 contrib/mtree/mtree.h                        |  158 ++
 contrib/mtree/only.c                         |  152 ++
 contrib/mtree/spec.c                         |  852 ++++++++++
 contrib/mtree/specspec.c                     |  273 ++++
 contrib/mtree/verify.c                       |  307 ++++
 lib/Makefile                                 |    1 +
 lib/libnetbsd/Makefile                       |   13 +
 lib/libnetbsd/Makefile.depend                |   14 +
 lib/libnetbsd/README                         |    7 +
 lib/libnetbsd/netinet/in.h                   |   72 +
 lib/libnetbsd/rmd160.h                       |   44 +
 lib/libnetbsd/sha1.h                         |   43 +
 lib/libnetbsd/sha2.h                         |   39 +
 lib/libnetbsd/stdlib.h                       |   70 +
 lib/libnetbsd/strsuftoll.c                   |  222 +++
 lib/libnetbsd/sys/cdefs.h                    |   74 +
 lib/libnetbsd/sys/time.h                     |   65 +
 lib/libnetbsd/util.c                         |   60 +
 lib/libnetbsd/util.h                         |   41 +
 usr.sbin/Makefile                            |    1 +
 usr.sbin/makefs/Makefile                     |   46 +
 usr.sbin/makefs/Makefile.depend              |   21 +
 usr.sbin/makefs/cd9660.c                     | 2136 ++++++++++++++++++++++++++
 usr.sbin/makefs/cd9660.h                     |  364 +++++
 usr.sbin/makefs/cd9660/Makefile.inc          |    9 +
 usr.sbin/makefs/cd9660/cd9660_archimedes.c   |  128 ++
 usr.sbin/makefs/cd9660/cd9660_archimedes.h   |   50 +
 usr.sbin/makefs/cd9660/cd9660_conversion.c   |  200 +++
 usr.sbin/makefs/cd9660/cd9660_debug.c        |  488 ++++++
 usr.sbin/makefs/cd9660/cd9660_eltorito.c     |  711 +++++++++
 usr.sbin/makefs/cd9660/cd9660_eltorito.h     |  164 ++
 usr.sbin/makefs/cd9660/cd9660_strings.c      |  120 ++
 usr.sbin/makefs/cd9660/cd9660_write.c        |  524 +++++++
 usr.sbin/makefs/cd9660/iso9660_rrip.c        |  840 ++++++++++
 usr.sbin/makefs/cd9660/iso9660_rrip.h        |  290 ++++
 usr.sbin/makefs/ffs.c                        | 1072 +++++++++++++
 usr.sbin/makefs/ffs.h                        |   69 +
 usr.sbin/makefs/ffs/Makefile.inc             |    9 +
 usr.sbin/makefs/ffs/buf.c                    |  222 +++
 usr.sbin/makefs/ffs/buf.h                    |   67 +
 usr.sbin/makefs/ffs/ffs_alloc.c              |  678 ++++++++
 usr.sbin/makefs/ffs/ffs_balloc.c             |  323 ++++
 usr.sbin/makefs/ffs/ffs_bswap.c              |  221 +++
 usr.sbin/makefs/ffs/ffs_extern.h             |   75 +
 usr.sbin/makefs/ffs/ffs_subr.c               |  194 +++
 usr.sbin/makefs/ffs/mkfs.c                   |  764 +++++++++
 usr.sbin/makefs/ffs/newfs_extern.h           |   36 +
 usr.sbin/makefs/ffs/ufs_bmap.c               |  140 ++
 usr.sbin/makefs/ffs/ufs_bswap.h              |   90 ++
 usr.sbin/makefs/ffs/ufs_inode.h              |   73 +
 usr.sbin/makefs/makefs.8                     |  411 +++++
 usr.sbin/makefs/makefs.c                     |  414 +++++
 usr.sbin/makefs/makefs.h                     |  285 ++++
 usr.sbin/makefs/mtree.c                      | 1137 ++++++++++++++
 usr.sbin/makefs/tests/Makefile               |   18 +
 usr.sbin/makefs/tests/Makefile.depend        |   11 +
 usr.sbin/makefs/tests/makefs_cd9660_tests.sh |  373 +++++
 usr.sbin/makefs/tests/makefs_ffs_tests.sh    |  237 +++
 usr.sbin/makefs/tests/makefs_tests_common.sh |  152 ++
 usr.sbin/makefs/walk.c                       |  696 +++++++++
 72 files changed, 20052 insertions(+)
 create mode 100644 contrib/mknod/pack_dev.c
 create mode 100644 contrib/mknod/pack_dev.h
 create mode 100644 contrib/mtree/Makefile
 create mode 100644 contrib/mtree/compare.c
 create mode 100644 contrib/mtree/crc.c
 create mode 100644 contrib/mtree/create.c
 create mode 100644 contrib/mtree/excludes.c
 create mode 100644 contrib/mtree/extern.h
 create mode 100644 contrib/mtree/getid.c
 create mode 100644 contrib/mtree/misc.c
 create mode 100644 contrib/mtree/mtree.8
 create mode 100644 contrib/mtree/mtree.c
 create mode 100644 contrib/mtree/mtree.h
 create mode 100644 contrib/mtree/only.c
 create mode 100644 contrib/mtree/spec.c
 create mode 100644 contrib/mtree/specspec.c
 create mode 100644 contrib/mtree/verify.c
 create mode 100644 lib/libnetbsd/Makefile
 create mode 100644 lib/libnetbsd/Makefile.depend
 create mode 100644 lib/libnetbsd/README
 create mode 100644 lib/libnetbsd/netinet/in.h
 create mode 100644 lib/libnetbsd/rmd160.h
 create mode 100644 lib/libnetbsd/sha1.h
 create mode 100644 lib/libnetbsd/sha2.h
 create mode 100644 lib/libnetbsd/stdlib.h
 create mode 100644 lib/libnetbsd/strsuftoll.c
 create mode 100644 lib/libnetbsd/sys/cdefs.h
 create mode 100644 lib/libnetbsd/sys/time.h
 create mode 100644 lib/libnetbsd/util.c
 create mode 100644 lib/libnetbsd/util.h
 create mode 100644 usr.sbin/makefs/Makefile
 create mode 100644 usr.sbin/makefs/Makefile.depend
 create mode 100644 usr.sbin/makefs/cd9660.c
 create mode 100644 usr.sbin/makefs/cd9660.h
 create mode 100644 usr.sbin/makefs/cd9660/Makefile.inc
 create mode 100644 usr.sbin/makefs/cd9660/cd9660_archimedes.c
 create mode 100644 usr.sbin/makefs/cd9660/cd9660_archimedes.h
 create mode 100644 usr.sbin/makefs/cd9660/cd9660_conversion.c
 create mode 100644 usr.sbin/makefs/cd9660/cd9660_debug.c
 create mode 100644 usr.sbin/makefs/cd9660/cd9660_eltorito.c
 create mode 100644 usr.sbin/makefs/cd9660/cd9660_eltorito.h
 create mode 100644 usr.sbin/makefs/cd9660/cd9660_strings.c
 create mode 100644 usr.sbin/makefs/cd9660/cd9660_write.c
 create mode 100644 usr.sbin/makefs/cd9660/iso9660_rrip.c
 create mode 100644 usr.sbin/makefs/cd9660/iso9660_rrip.h
 create mode 100644 usr.sbin/makefs/ffs.c
 create mode 100644 usr.sbin/makefs/ffs.h
 create mode 100644 usr.sbin/makefs/ffs/Makefile.inc
 create mode 100644 usr.sbin/makefs/ffs/buf.c
 create mode 100644 usr.sbin/makefs/ffs/buf.h
 create mode 100644 usr.sbin/makefs/ffs/ffs_alloc.c
 create mode 100644 usr.sbin/makefs/ffs/ffs_balloc.c
 create mode 100644 usr.sbin/makefs/ffs/ffs_bswap.c
 create mode 100644 usr.sbin/makefs/ffs/ffs_extern.h
 create mode 100644 usr.sbin/makefs/ffs/ffs_subr.c
 create mode 100644 usr.sbin/makefs/ffs/mkfs.c
 create mode 100644 usr.sbin/makefs/ffs/newfs_extern.h
 create mode 100644 usr.sbin/makefs/ffs/ufs_bmap.c
 create mode 100644 usr.sbin/makefs/ffs/ufs_bswap.h
 create mode 100644 usr.sbin/makefs/ffs/ufs_inode.h
 create mode 100644 usr.sbin/makefs/makefs.8
 create mode 100644 usr.sbin/makefs/makefs.c
 create mode 100644 usr.sbin/makefs/makefs.h
 create mode 100644 usr.sbin/makefs/mtree.c
 create mode 100644 usr.sbin/makefs/tests/Makefile
 create mode 100644 usr.sbin/makefs/tests/Makefile.depend
 create mode 100755 usr.sbin/makefs/tests/makefs_cd9660_tests.sh
 create mode 100755 usr.sbin/makefs/tests/makefs_ffs_tests.sh
 create mode 100755 usr.sbin/makefs/tests/makefs_tests_common.sh
 create mode 100644 usr.sbin/makefs/walk.c

diff --git a/contrib/mknod/pack_dev.c b/contrib/mknod/pack_dev.c
new file mode 100644
index 0000000000..800a56c6e6
--- /dev/null
+++ b/contrib/mknod/pack_dev.c
@@ -0,0 +1,290 @@
+/*	$NetBSD: pack_dev.c,v 1.11 2011/08/27 18:37:41 joerg Exp $	*/
+
+/*-
+ * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Charles M. Hannum.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if !defined(lint)
+__RCSID("$NetBSD: pack_dev.c,v 1.11 2011/08/27 18:37:41 joerg Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pack_dev.h"
+
+static	pack_t	pack_netbsd;
+static	pack_t	pack_freebsd;
+static	pack_t	pack_8_8;
+static	pack_t	pack_12_20;
+static	pack_t	pack_14_18;
+static	pack_t	pack_8_24;
+static	pack_t	pack_bsdos;
+static	int	compare_format(const void *, const void *);
+
+static const char iMajorError[] = "invalid major number";
+static const char iMinorError[] = "invalid minor number";
+static const char tooManyFields[] = "too many fields for format";
+
+	/* exported */
+portdev_t
+pack_native(int n, u_long numbers[], const char **error)
+{
+	portdev_t dev = 0;
+
+	if (n == 2) {
+		dev = makedev(numbers[0], numbers[1]);
+		if ((u_long)major(dev) != numbers[0])
+			*error = iMajorError;
+		else if ((u_long)minor(dev) != numbers[1])
+			*error = iMinorError;
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+static portdev_t
+pack_netbsd(int n, u_long numbers[], const char **error)
+{
+	portdev_t dev = 0;
+
+	if (n == 2) {
+		dev = makedev_netbsd(numbers[0], numbers[1]);
+		if ((u_long)major_netbsd(dev) != numbers[0])
+			*error = iMajorError;
+		else if ((u_long)minor_netbsd(dev) != numbers[1])
+			*error = iMinorError;
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+#define	major_freebsd(x)	((int32_t)(((x) & 0x0000ff00) >> 8))
+#define	minor_freebsd(x)	((int32_t)(((x) & 0xffff00ff) >> 0))
+#define	makedev_freebsd(x,y)	((portdev_t)((((x) << 8) & 0x0000ff00) | \
+					 (((y) << 0) & 0xffff00ff)))
+
+static portdev_t
+pack_freebsd(int n, u_long numbers[], const char **error)
+{
+	portdev_t dev = 0;
+
+	if (n == 2) {
+		dev = makedev_freebsd(numbers[0], numbers[1]);
+		if ((u_long)major_freebsd(dev) != numbers[0])
+			*error = iMajorError;
+		if ((u_long)minor_freebsd(dev) != numbers[1])
+			*error = iMinorError;
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+#define	major_8_8(x)		((int32_t)(((x) & 0x0000ff00) >> 8))
+#define	minor_8_8(x)		((int32_t)(((x) & 0x000000ff) >> 0))
+#define	makedev_8_8(x,y)	((portdev_t)((((x) << 8) & 0x0000ff00) | \
+					 (((y) << 0) & 0x000000ff)))
+
+static portdev_t
+pack_8_8(int n, u_long numbers[], const char **error)
+{
+	portdev_t dev = 0;
+
+	if (n == 2) {
+		dev = makedev_8_8(numbers[0], numbers[1]);
+		if ((u_long)major_8_8(dev) != numbers[0])
+			*error = iMajorError;
+		if ((u_long)minor_8_8(dev) != numbers[1])
+			*error = iMinorError;
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+#define	major_12_20(x)		((int32_t)(((x) & 0xfff00000) >> 20))
+#define	minor_12_20(x)		((int32_t)(((x) & 0x000fffff) >>  0))
+#define	makedev_12_20(x,y)	((portdev_t)((((x) << 20) & 0xfff00000) | \
+					 (((y) <<  0) & 0x000fffff)))
+
+static portdev_t
+pack_12_20(int n, u_long numbers[], const char **error)
+{
+	portdev_t dev = 0;
+
+	if (n == 2) {
+		dev = makedev_12_20(numbers[0], numbers[1]);
+		if ((u_long)major_12_20(dev) != numbers[0])
+			*error = iMajorError;
+		if ((u_long)minor_12_20(dev) != numbers[1])
+			*error = iMinorError;
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+#define	major_14_18(x)		((int32_t)(((x) & 0xfffc0000) >> 18))
+#define	minor_14_18(x)		((int32_t)(((x) & 0x0003ffff) >>  0))
+#define	makedev_14_18(x,y)	((portdev_t)((((x) << 18) & 0xfffc0000) | \
+					 (((y) <<  0) & 0x0003ffff)))
+
+static portdev_t
+pack_14_18(int n, u_long numbers[], const char **error)
+{
+	portdev_t dev = 0;
+
+	if (n == 2) {
+		dev = makedev_14_18(numbers[0], numbers[1]);
+		if ((u_long)major_14_18(dev) != numbers[0])
+			*error = iMajorError;
+		if ((u_long)minor_14_18(dev) != numbers[1])
+			*error = iMinorError;
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+#define	major_8_24(x)		((int32_t)(((x) & 0xff000000) >> 24))
+#define	minor_8_24(x)		((int32_t)(((x) & 0x00ffffff) >>  0))
+#define	makedev_8_24(x,y)	((portdev_t)((((x) << 24) & 0xff000000) | \
+					 (((y) <<  0) & 0x00ffffff)))
+
+static portdev_t
+pack_8_24(int n, u_long numbers[], const char **error)
+{
+	portdev_t dev = 0;
+
+	if (n == 2) {
+		dev = makedev_8_24(numbers[0], numbers[1]);
+		if ((u_long)major_8_24(dev) != numbers[0])
+			*error = iMajorError;
+		if ((u_long)minor_8_24(dev) != numbers[1])
+			*error = iMinorError;
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+#define	major_12_12_8(x)	((int32_t)(((x) & 0xfff00000) >> 20))
+#define	unit_12_12_8(x)		((int32_t)(((x) & 0x000fff00) >>  8))
+#define	subunit_12_12_8(x)	((int32_t)(((x) & 0x000000ff) >>  0))
+#define	makedev_12_12_8(x,y,z)	((portdev_t)((((x) << 20) & 0xfff00000) | \
+					 (((y) <<  8) & 0x000fff00) | \
+					 (((z) <<  0) & 0x000000ff)))
+
+static portdev_t
+pack_bsdos(int n, u_long numbers[], const char **error)
+{
+	portdev_t dev = 0;
+
+	if (n == 2) {
+		dev = makedev_12_20(numbers[0], numbers[1]);
+		if ((u_long)major_12_20(dev) != numbers[0])
+			*error = iMajorError;
+		if ((u_long)minor_12_20(dev) != numbers[1])
+			*error = iMinorError;
+	} else if (n == 3) {
+		dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]);
+		if ((u_long)major_12_12_8(dev) != numbers[0])
+			*error = iMajorError;
+		if ((u_long)unit_12_12_8(dev) != numbers[1])
+			*error = "invalid unit number";
+		if ((u_long)subunit_12_12_8(dev) != numbers[2])
+			*error = "invalid subunit number";
+	} else
+		*error = tooManyFields;
+	return (dev);
+}
+
+
+		/* list of formats and pack functions */
+		/* this list must be sorted lexically */
+static struct format {
+	const char	*name;
+	pack_t		*pack;
+} formats[] = {
+	{"386bsd",  pack_8_8},
+	{"4bsd",    pack_8_8},
+	{"bsdos",   pack_bsdos},
+	{"freebsd", pack_freebsd},
+	{"hpux",    pack_8_24},
+	{"isc",     pack_8_8},
+	{"linux",   pack_8_8},
+	{"native",  pack_native},
+	{"netbsd",  pack_netbsd},
+	{"osf1",    pack_12_20},
+	{"sco",     pack_8_8},
+	{"solaris", pack_14_18},
+	{"sunos",   pack_8_8},
+	{"svr3",    pack_8_8},
+	{"svr4",    pack_14_18},
+	{"ultrix",  pack_8_8},
+};
+
+static int
+compare_format(const void *key, const void *element)
+{
+	const char		*name;
+	const struct format	*format;
+
+	name = key;
+	format = element;
+
+	return (strcmp(name, format->name));
+}
+
+
+pack_t *
+pack_find(const char *name)
+{
+	struct format	*format;
+
+	format = bsearch(name, formats,
+	    sizeof(formats)/sizeof(formats[0]),
+	    sizeof(formats[0]), compare_format);
+	if (format == 0)
+		return (NULL);
+	return (format->pack);
+}
diff --git a/contrib/mknod/pack_dev.h b/contrib/mknod/pack_dev.h
new file mode 100644
index 0000000000..d909df4280
--- /dev/null
+++ b/contrib/mknod/pack_dev.h
@@ -0,0 +1,52 @@
+/*	$NetBSD: pack_dev.h,v 1.7 2008/04/28 20:23:09 martin Exp $	*/
+
+/*-
+ * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Charles M. Hannum.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef	_PACK_DEV_H
+#define	_PACK_DEV_H
+
+#ifdef __CYGWIN__
+typedef	__dev32_t	portdev_t;
+#else
+typedef	dev_t		portdev_t;
+#endif
+typedef	portdev_t pack_t(int, u_long [], const char **);
+
+pack_t	*pack_find(const char *);
+pack_t	 pack_native;
+
+#define	major_netbsd(x)		((int32_t)((((x) & 0x000fff00) >>  8)))
+#define	minor_netbsd(x)		((int32_t)((((x) & 0xfff00000) >> 12) | \
+					   (((x) & 0x000000ff) >>  0)))
+#define	makedev_netbsd(x,y)	((dev_t)((((x) <<  8) & 0x000fff00) | \
+					 (((y) << 12) & 0xfff00000) | \
+					 (((y) <<  0) & 0x000000ff)))
+
+#endif	/* _PACK_DEV_H */
diff --git a/contrib/mtree/Makefile b/contrib/mtree/Makefile
new file mode 100644
index 0000000000..3613e141a9
--- /dev/null
+++ b/contrib/mtree/Makefile
@@ -0,0 +1,20 @@
+#	$NetBSD: Makefile,v 1.34 2013/02/03 19:15:16 christos Exp $
+#	from: @(#)Makefile	8.2 (Berkeley) 4/27/95
+
+.include <bsd.own.mk>
+
+PROG=	mtree
+#CPPFLAGS+=-DDEBUG
+CPPFLAGS+= -DMTREE
+MAN=	mtree.8
+SRCS=	compare.c crc.c create.c excludes.c misc.c mtree.c spec.c specspec.c \
+	verify.c getid.c pack_dev.c only.c
+.if (${HOSTPROG:U} == "")
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+.endif
+
+CPPFLAGS+=	-I${NETBSDSRCDIR}/sbin/mknod
+.PATH:		${NETBSDSRCDIR}/sbin/mknod
+
+.include <bsd.prog.mk>
diff --git a/contrib/mtree/compare.c b/contrib/mtree/compare.c
new file mode 100644
index 0000000000..e3639ceea0
--- /dev/null
+++ b/contrib/mtree/compare.c
@@ -0,0 +1,576 @@
+/*	$NetBSD: compare.c,v 1.58 2013/11/21 18:39:50 christos Exp $	*/
+
+/*-
+ * Copyright (c) 1989, 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)compare.c	8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: compare.c,v 1.58 2013/11/21 18:39:50 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef NO_MD5
+#include <md5.h>
+#endif
+#ifndef NO_RMD160
+#include <rmd160.h>
+#endif
+#ifndef NO_SHA1
+#include <sha1.h>
+#endif
+#ifndef NO_SHA2
+#include <sha2.h>
+#endif
+
+#include "extern.h"
+
+#define	INDENTNAMELEN	8
+#define MARK								\
+do {									\
+	if (flavor == F_FREEBSD9) {					\
+		len = printf("%s changed\n", RP(p));			\
+		tab = "\t";						\
+	} else {							\
+		len = printf("%s: ", RP(p));				\
+		if (len > INDENTNAMELEN) {				\
+			tab = "\t";					\
+			printf("\n");					\
+		} else {						\
+			tab = "";					\
+			printf("%*s", INDENTNAMELEN - (int)len, "");	\
+		}							\
+	}								\
+} while (0)
+#define	LABEL if (!label++) MARK
+
+#if HAVE_STRUCT_STAT_ST_FLAGS
+
+
+#define CHANGEFLAGS							\
+	if (flags != p->fts_statp->st_flags) {				\
+		char *sf;						\
+		if (!label) {						\
+			MARK;						\
+			sf = flags_to_string(p->fts_statp->st_flags, "none"); \
+			printf("%sflags (\"%s\"", tab, sf);		\
+			free(sf);					\
+		}							\
+		if (lchflags(p->fts_accpath, flags)) {			\
+			label++;					\
+			printf(", not modified: %s)\n",			\
+			    strerror(errno));				\
+		} else {						\
+			sf = flags_to_string(flags, "none");		\
+			printf(", modified to \"%s\")\n", sf);		\
+			free(sf);					\
+		}							\
+	}
+
+/* SETFLAGS:
+ * given pflags, additionally set those flags specified in s->st_flags and
+ * selected by mask (the other flags are left unchanged).
+ */
+#define SETFLAGS(pflags, mask)						\
+do {									\
+	flags = (s->st_flags & (mask)) | (pflags);			\
+	CHANGEFLAGS;							\
+} while (0)
+
+/* CLEARFLAGS:
+ * given pflags, reset the flags specified in s->st_flags and selected by mask
+ * (the other flags are left unchanged).
+ */
+#define CLEARFLAGS(pflags, mask)					\
+do {									\
+	flags = (~(s->st_flags & (mask)) & CH_MASK) & (pflags);		\
+	CHANGEFLAGS;							\
+} while (0)
+#endif	/* HAVE_STRUCT_STAT_ST_FLAGS */
+
+int
+compare(NODE *s, FTSENT *p)
+{
+	u_int32_t len, val, flags;
+	int fd, label;
+	const char *cp, *tab;
+#if !defined(NO_MD5) || !defined(NO_RMD160) || !defined(NO_SHA1) || !defined(NO_SHA2)
+	char *digestbuf;
+#endif
+
+	tab = NULL;
+	label = 0;
+	switch(s->type) {
+	case F_BLOCK:
+		if (!S_ISBLK(p->fts_statp->st_mode))
+			goto typeerr;
+		break;
+	case F_CHAR:
+		if (!S_ISCHR(p->fts_statp->st_mode))
+			goto typeerr;
+		break;
+	case F_DIR:
+		if (!S_ISDIR(p->fts_statp->st_mode))
+			goto typeerr;
+		break;
+	case F_FIFO:
+		if (!S_ISFIFO(p->fts_statp->st_mode))
+			goto typeerr;
+		break;
+	case F_FILE:
+		if (!S_ISREG(p->fts_statp->st_mode))
+			goto typeerr;
+		break;
+	case F_LINK:
+		if (!S_ISLNK(p->fts_statp->st_mode))
+			goto typeerr;
+		break;
+#ifdef S_ISSOCK
+	case F_SOCK:
+		if (!S_ISSOCK(p->fts_statp->st_mode))
+			goto typeerr;
+		break;
+#endif
+typeerr:		LABEL;
+		printf(flavor == F_FREEBSD9 ?
+		    "\ttype expected %s found %s\n" : "\ttype (%s, %s)\n",
+		    nodetype(s->type), inotype(p->fts_statp->st_mode));
+		return (label);
+	}
+	if (mtree_Wflag)
+		goto afterpermwhack;
+#if HAVE_STRUCT_STAT_ST_FLAGS
+	if (iflag && !uflag) {
+		if (s->flags & F_FLAGS)
+		    SETFLAGS(p->fts_statp->st_flags, SP_FLGS);
+		return (label);
+        }
+	if (mflag && !uflag) {
+		if (s->flags & F_FLAGS)
+		    CLEARFLAGS(p->fts_statp->st_flags, SP_FLGS);
+		return (label);
+        }
+#endif
+	if (s->flags & F_DEV &&
+	    (s->type == F_BLOCK || s->type == F_CHAR) &&
+	    s->st_rdev != p->fts_statp->st_rdev) {
+		LABEL;
+		printf(flavor == F_FREEBSD9 ?
+		    "%sdevice expected %#jx found %#jx" :
+		    "%sdevice (%#jx, %#jx",
+		    tab, (uintmax_t)s->st_rdev,
+		    (uintmax_t)p->fts_statp->st_rdev);
+		if (uflag) {
+			if ((unlink(p->fts_accpath) == -1) ||
+			    (mknod(p->fts_accpath,
+			      s->st_mode | nodetoino(s->type),
+			      s->st_rdev) == -1) ||
+			    (lchown(p->fts_accpath, p->fts_statp->st_uid,
+			      p->fts_statp->st_gid) == -1) )
+				printf(", not modified: %s%s\n",
+				    strerror(errno),
+				    flavor == F_FREEBSD9 ? "" : ")");
+			 else
+				printf(", modified%s\n",
+				    flavor == F_FREEBSD9 ? "" : ")");
+		} else
+			printf(")\n");
+		tab = "\t";
+	}
+	/* Set the uid/gid first, then set the mode. */
+	if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
+		LABEL;
+		printf(flavor == F_FREEBSD9 ?
+		    "%suser expected %lu found %lu" : "%suser (%lu, %lu",
+		    tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
+		if (uflag) {
+			if (lchown(p->fts_accpath, s->st_uid, -1))
+				printf(", not modified: %s%s\n",
+				    strerror(errno),
+				    flavor == F_FREEBSD9 ? "" : ")");
+			else
+				printf(", modified%s\n",
+				    flavor == F_FREEBSD9 ? "" : ")");
+		} else
+			printf(")\n");
+		tab = "\t";
+	}
+	if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
+		LABEL;
+		printf(flavor == F_FREEBSD9 ?
+		    "%sgid expected %lu found %lu" : "%sgid (%lu, %lu",
+		    tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
+		if (uflag) {
+			if (lchown(p->fts_accpath, -1, s->st_gid))
+				printf(", not modified: %s%s\n",
+				    strerror(errno),
+				    flavor == F_FREEBSD9 ? "" : ")");
+			else
+				printf(", modified%s\n",
+				    flavor == F_FREEBSD9 ? "" : ")");
+		}
+		else
+			printf(")\n");
+		tab = "\t";
+	}
+	if (s->flags & F_MODE &&
+	    s->st_mode != (p->fts_statp->st_mode & MBITS)) {
+		if (lflag) {
+			mode_t tmode, mode;
+
+			tmode = s->st_mode;
+			mode = p->fts_statp->st_mode & MBITS;
+			/*
+			 * if none of the suid/sgid/etc bits are set,
+			 * then if the mode is a subset of the target,
+			 * skip.
+			 */
+			if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) ||
+			    (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO))))
+				if ((mode | tmode) == tmode)
+					goto skip;
+		}
+
+		LABEL;
+		printf(flavor == F_FREEBSD9 ?
+		    "%spermissions expcted %#lo found %#lo" :
+		    "%spermissions (%#lo, %#lo",
+		    tab, (u_long)s->st_mode,
+		    (u_long)p->fts_statp->st_mode & MBITS);
+		if (uflag) {
+			if (lchmod(p->fts_accpath, s->st_mode))
+				printf(", not modified: %s%s\n",
+				    strerror(errno),
+				    flavor == F_FREEBSD9 ? "" : ")");
+			else
+				printf(", modified%s\n",
+				    flavor == F_FREEBSD9 ? "" : ")");
+		}
+		else
+			printf(")\n");
+		tab = "\t";
+	skip:	;
+	}
+	if (s->flags & F_NLINK && s->type != F_DIR &&
+	    s->st_nlink != p->fts_statp->st_nlink) {
+		LABEL;
+		printf(flavor == F_FREEBSD9 ?
+		    "%slink count expected %lu found %lu\n" :
+		    "%slink count (%lu, %lu)\n",
+		    tab, (u_long)s->st_nlink, (u_long)p->fts_statp->st_nlink);
+		tab = "\t";
+	}
+	if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) {
+		LABEL;
+		printf(flavor == F_FREEBSD9 ?
+		    "%ssize expected %ju found %ju\n" : "%ssize (%ju, %ju)\n",
+		    tab, (uintmax_t)s->st_size,
+		    (uintmax_t)p->fts_statp->st_size);
+		tab = "\t";
+	}
+	/*
+	 * XXX
+	 * Since utimes(2) only takes a timeval, there's no point in
+	 * comparing the low bits of the timespec nanosecond field.  This
+	 * will only result in mismatches that we can never fix.
+	 *
+	 * Doesn't display microsecond differences.
+	 */
+	if (s->flags & F_TIME) {
+		struct timeval tv[2];
+		struct stat *ps = p->fts_statp;
+		time_t smtime = s->st_mtimespec.tv_sec;
+
+#if defined(BSD4_4) && !defined(HAVE_NBTOOL_CONFIG_H)
+		time_t pmtime = ps->st_mtimespec.tv_sec;
+
+		TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec);
+		TIMESPEC_TO_TIMEVAL(&tv[1], &ps->st_mtimespec);
+#else
+		time_t pmtime = (time_t)ps->st_mtime;
+
+		tv[0].tv_sec = smtime;
+		tv[0].tv_usec = 0;
+		tv[1].tv_sec = pmtime;
+		tv[1].tv_usec = 0;
+#endif
+
+		if (tv[0].tv_sec != tv[1].tv_sec ||
+		    tv[0].tv_usec != tv[1].tv_usec) {
+			LABEL;
+			printf(flavor == F_FREEBSD9 ?
+			    "%smodification time expected %.24s found " :
+			    "%smodification time (%.24s, ",
+			    tab, ctime(&smtime));
+			printf("%.24s", ctime(&pmtime));
+			if (tflag) {
+				tv[1] = tv[0];
+				if (utimes(p->fts_accpath, tv))
+					printf(", not modified: %s%s\n",
+					    strerror(errno),
+					    flavor == F_FREEBSD9 ? "" : ")");
+				else
+					printf(", modified%s\n",
+					    flavor == F_FREEBSD9 ? "" : ")");
+			} else
+				printf("%s\n", flavor == F_FREEBSD9 ? "" : ")");
+			tab = "\t";
+		}
+	}
+#if HAVE_STRUCT_STAT_ST_FLAGS
+	/*
+	 * XXX
+	 * since lchflags(2) will reset file times, the utimes() above
+	 * may have been useless!  oh well, we'd rather have correct
+	 * flags, rather than times?
+	 */
+        if ((s->flags & F_FLAGS) && ((s->st_flags != p->fts_statp->st_flags)
+	    || mflag || iflag)) {
+		if (s->st_flags != p->fts_statp->st_flags) {
+			char *f_s;
+			LABEL;
+			f_s = flags_to_string(s->st_flags, "none");
+			printf(flavor == F_FREEBSD9 ?
+			    "%sflags expected \"%s\" found " :
+			    "%sflags (\"%s\" is not ", tab, f_s);
+			free(f_s);
+			f_s = flags_to_string(p->fts_statp->st_flags, "none");
+			printf("\"%s\"", f_s);
+			free(f_s);
+		}
+		if (uflag) {
+			if (iflag)
+				SETFLAGS(0, CH_MASK);
+			else if (mflag)
+				CLEARFLAGS(0, SP_FLGS);
+			else
+				SETFLAGS(0, (~SP_FLGS & CH_MASK));
+		} else
+			printf("%s\n", flavor == F_FREEBSD9 ? "" : ")");
+		tab = "\t";
+	}
+#endif	/* HAVE_STRUCT_STAT_ST_FLAGS */
+
+	/*
+	 * from this point, no more permission checking or whacking
+	 * occurs, only checking of stuff like checksums and symlinks.
+	 */
+ afterpermwhack:
+	if (s->flags & F_CKSUM) {
+		if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
+			LABEL;
+			printf("%scksum: %s: %s\n",
+			    tab, p->fts_accpath, strerror(errno));
+			tab = "\t";
+		} else if (crc(fd, &val, &len)) {
+			close(fd);
+			LABEL;
+			printf("%scksum: %s: %s\n",
+			    tab, p->fts_accpath, strerror(errno));
+			tab = "\t";
+		} else {
+			close(fd);
+			if (s->cksum != val) {
+				LABEL;
+				printf(flavor == F_FREEBSD9 ?
+				    "%scksum expected %lu found %lu\n" :
+				    "%scksum (%lu, %lu)\n",
+				    tab, s->cksum, (unsigned long)val);
+			}
+			tab = "\t";
+		}
+	}
+#ifndef NO_MD5
+	if (s->flags & F_MD5) {
+		if ((digestbuf = MD5File(p->fts_accpath, NULL)) == NULL) {
+			LABEL;
+			printf("%s%s: %s: %s\n",
+			    tab, MD5KEY, p->fts_accpath, strerror(errno));
+			tab = "\t";
+		} else {
+			if (strcmp(s->md5digest, digestbuf)) {
+				LABEL;
+				printf(flavor == F_FREEBSD9 ?
+				    "%s%s expected %s found %s\n" :
+				    "%s%s (0x%s, 0x%s)\n",
+				    tab, MD5KEY, s->md5digest, digestbuf);
+			}
+			tab = "\t";
+			free(digestbuf);
+		}
+	}
+#endif	/* ! NO_MD5 */
+#ifndef NO_RMD160
+	if (s->flags & F_RMD160) {
+		if ((digestbuf = RMD160File(p->fts_accpath, NULL)) == NULL) {
+			LABEL;
+			printf("%s%s: %s: %s\n",
+			    tab, RMD160KEY, p->fts_accpath, strerror(errno));
+			tab = "\t";
+		} else {
+			if (strcmp(s->rmd160digest, digestbuf)) {
+				LABEL;
+				printf(flavor == F_FREEBSD9 ?
+				    "%s%s expected %s found %s\n" :
+				    "%s%s (0x%s, 0x%s)\n",
+				    tab, RMD160KEY, s->rmd160digest, digestbuf);
+			}
+			tab = "\t";
+			free(digestbuf);
+		}
+	}
+#endif	/* ! NO_RMD160 */
+#ifndef NO_SHA1
+	if (s->flags & F_SHA1) {
+		if ((digestbuf = SHA1File(p->fts_accpath, NULL)) == NULL) {
+			LABEL;
+			printf("%s%s: %s: %s\n",
+			    tab, SHA1KEY, p->fts_accpath, strerror(errno));
+			tab = "\t";
+		} else {
+			if (strcmp(s->sha1digest, digestbuf)) {
+				LABEL;
+				printf(flavor == F_FREEBSD9 ? 
+				    "%s%s expected %s found %s\n" :
+				    "%s%s (0x%s, 0x%s)\n",
+				    tab, SHA1KEY, s->sha1digest, digestbuf);
+			}
+			tab = "\t";
+			free(digestbuf);
+		}
+	}
+#endif	/* ! NO_SHA1 */
+#ifndef NO_SHA2
+	if (s->flags & F_SHA256) {
+		if ((digestbuf = SHA256_File(p->fts_accpath, NULL)) == NULL) {
+			LABEL;
+			printf("%s%s: %s: %s\n",
+			    tab, SHA256KEY, p->fts_accpath, strerror(errno));
+			tab = "\t";
+		} else {
+			if (strcmp(s->sha256digest, digestbuf)) {
+				LABEL;
+				printf(flavor == F_FREEBSD9 ? 
+				    "%s%s expected %s found %s\n" :
+				    "%s%s (0x%s, 0x%s)\n",
+				    tab, SHA256KEY, s->sha256digest, digestbuf);
+			}
+			tab = "\t";
+			free(digestbuf);
+		}
+	}
+#ifdef SHA384_BLOCK_LENGTH
+	if (s->flags & F_SHA384) {
+		if ((digestbuf = SHA384_File(p->fts_accpath, NULL)) == NULL) {
+			LABEL;
+			printf("%s%s: %s: %s\n",
+			    tab, SHA384KEY, p->fts_accpath, strerror(errno));
+			tab = "\t";
+		} else {
+			if (strcmp(s->sha384digest, digestbuf)) {
+				LABEL;
+				printf(flavor == F_FREEBSD9 ? 
+				    "%s%s expected %s found %s\n" :
+				    "%s%s (0x%s, 0x%s)\n",
+				    tab, SHA384KEY, s->sha384digest, digestbuf);
+			}
+			tab = "\t";
+			free(digestbuf);
+		}
+	}
+#endif
+	if (s->flags & F_SHA512) {
+		if ((digestbuf = SHA512_File(p->fts_accpath, NULL)) == NULL) {
+			LABEL;
+			printf("%s%s: %s: %s\n",
+			    tab, SHA512KEY, p->fts_accpath, strerror(errno));
+			tab = "\t";
+		} else {
+			if (strcmp(s->sha512digest, digestbuf)) {
+				LABEL;
+				printf(flavor == F_FREEBSD9 ? 
+				    "%s%s expected %s found %s\n" :
+				    "%s%s (0x%s, 0x%s)\n",
+				    tab, SHA512KEY, s->sha512digest, digestbuf);
+			}
+			tab = "\t";
+			free(digestbuf);
+		}
+	}
+#endif	/* ! NO_SHA2 */
+	if (s->flags & F_SLINK &&
+	    strcmp(cp = rlink(p->fts_accpath), s->slink)) {
+		LABEL;
+		printf(flavor == F_FREEBSD9 ? 
+		    "%slink ref expected %s found %s" :
+		    "%slink ref (%s, %s", tab, cp, s->slink);
+		if (uflag) {
+			if ((unlink(p->fts_accpath) == -1) ||
+			    (symlink(s->slink, p->fts_accpath) == -1) )
+				printf(", not modified: %s%s\n",
+				    strerror(errno),
+				    flavor == F_FREEBSD9 ? "" : ")");
+			else
+				printf(", modified%s\n",
+				    flavor == F_FREEBSD9 ? "" : ")");
+		} else
+			printf("%s\n", flavor == F_FREEBSD9 ? "" : ")");
+	}
+	return (label);
+}
+
+const char *
+rlink(const char *name)
+{
+	static char lbuf[MAXPATHLEN];
+	int len;
+
+	if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1)
+		mtree_err("%s: %s", name, strerror(errno));
+	lbuf[len] = '\0';
+	return (lbuf);
+}
diff --git a/contrib/mtree/crc.c b/contrib/mtree/crc.c
new file mode 100644
index 0000000000..4eac66e5b9
--- /dev/null
+++ b/contrib/mtree/crc.c
@@ -0,0 +1,163 @@
+/*	$NetBSD: crc.c,v 1.9 2012/10/05 00:40:51 christos Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James W. Williams of NASA Goddard Space Flight Center.
+ *
+ * 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)crc.c	8.1 (Berkeley) 6/17/93";
+#else
+__RCSID("$NetBSD: crc.c,v 1.9 2012/10/05 00:40:51 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static const u_int32_t crctab[] = {
+	0x0,
+	0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+	0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
+	0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+	0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
+	0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
+	0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
+	0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+	0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
+	0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
+	0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
+	0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+	0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
+	0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+	0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
+	0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+	0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
+	0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
+	0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
+	0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+	0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
+	0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
+	0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
+	0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+	0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
+	0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+	0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
+	0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+	0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
+	0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
+	0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
+	0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+	0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
+	0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
+	0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
+	0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+	0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
+	0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+	0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
+	0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+	0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
+	0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
+	0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
+	0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+	0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
+	0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
+	0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
+	0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+	0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
+	0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+	0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
+	0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+};
+
+/*
+ * Compute a POSIX 1003.2 checksum.  This routine has been broken out so that
+ * other programs can use it.  It takes a file descriptor to read from and
+ * locations to store the crc and the number of bytes read.  It returns 0 on
+ * success and 1 on failure.  Errno is set on failure.
+ */
+u_int32_t crc_total = ~0;		/* The crc over a number of files. */
+
+int
+crc(int fd, u_int32_t *cval, u_int32_t *clen)
+{
+	u_char *p;
+	int nr;
+	u_int32_t thecrc, len;
+	u_int32_t crctot;
+	u_char buf[16 * 1024];
+
+#define	COMPUTE(var, ch)	(var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
+
+	thecrc = len = crctot = 0;
+	if (sflag)
+		crctot = ~crc_total;
+	while ((nr = read(fd, buf, sizeof(buf))) > 0)
+		if (sflag) {
+			for (len += nr, p = buf; nr--; ++p) {
+				COMPUTE(thecrc, *p);
+				COMPUTE(crctot, *p);
+			}
+		} else {
+			for (len += nr, p = buf; nr--; ++p)
+				COMPUTE(thecrc, *p);
+		}
+	if (nr < 0)
+		return 1;
+
+	*clen = len;
+
+	/* Include the length of the file. */
+	if (sflag) {
+		for (; len != 0; len >>= 8) {
+			COMPUTE(thecrc, len & 0xff);
+			COMPUTE(crctot, len & 0xff);
+		}
+	} else {
+		for (; len != 0; len >>= 8)
+			COMPUTE(thecrc, len & 0xff);
+	}
+
+	*cval = ~thecrc;
+	if (sflag)
+		crc_total = ~crctot;
+	return 0;
+}
diff --git a/contrib/mtree/create.c b/contrib/mtree/create.c
new file mode 100644
index 0000000000..8601cfa537
--- /dev/null
+++ b/contrib/mtree/create.c
@@ -0,0 +1,476 @@
+/*	$NetBSD: create.c,v 1.73 2014/04/24 17:22:41 christos Exp $	*/
+
+/*-
+ * Copyright (c) 1989, 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)create.c	8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: create.c,v 1.73 2014/04/24 17:22:41 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#if ! HAVE_NBTOOL_CONFIG_H
+#include <dirent.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef NO_MD5
+#include <md5.h>
+#endif
+#ifndef NO_RMD160
+#include <rmd160.h>
+#endif
+#ifndef NO_SHA1
+#include <sha1.h>
+#endif
+#ifndef NO_SHA2
+#include <sha2.h>
+#endif
+
+#include "extern.h"
+
+#define	INDENTNAMELEN	15
+#define	MAXLINELEN	80
+
+static gid_t gid;
+static uid_t uid;
+static mode_t mode;
+static u_long flags;
+
+#ifdef __FreeBSD__
+#define	FTS_CONST const
+#else
+#define	FTS_CONST
+#endif
+
+static int	dcmp(const FTSENT *FTS_CONST *, const FTSENT *FTS_CONST *);
+static void	output(FILE *, int, int *, const char *, ...)
+    __printflike(4, 5);
+static int	statd(FILE *, FTS *, FTSENT *, uid_t *, gid_t *, mode_t *,
+    u_long *);
+static void	statf(FILE *, int, FTSENT *);
+
+void
+cwalk(FILE *fp)
+{
+	FTS *t;
+	FTSENT *p;
+	time_t clocktime;
+	char host[MAXHOSTNAMELEN + 1];
+	const char *user;
+	char *argv[2];
+	char  dot[] = ".";
+	int indent = 0;
+
+	argv[0] = dot;
+	argv[1] = NULL;
+
+	time(&clocktime);
+	gethostname(host, sizeof(host));
+	host[sizeof(host) - 1] = '\0';
+	if ((user = getlogin()) == NULL) {
+		struct passwd *pw;
+		user = (pw = getpwuid(getuid())) == NULL ? pw->pw_name :
+		    "<unknown>";
+	}
+
+	if (!nflag)
+		fprintf(fp,
+	    	    "#\t   user: %s\n#\tmachine: %s\n#\t   tree: %s\n"
+		    "#\t   date: %s",
+		    user, host, fullpath, ctime(&clocktime));
+
+	if ((t = fts_open(argv, ftsoptions, dcmp)) == NULL)
+		mtree_err("fts_open: %s", strerror(errno));
+	while ((p = fts_read(t)) != NULL) {
+		if (jflag)
+			indent = p->fts_level * 4;
+		if (check_excludes(p->fts_name, p->fts_path)) {
+			fts_set(t, p, FTS_SKIP);
+			continue;
+		}
+		if (!find_only(p->fts_path)) {
+			fts_set(t, p, FTS_SKIP);
+			continue;
+		}
+		switch(p->fts_info) {
+		case FTS_D:
+			if (!bflag)
+				fprintf(fp, "\n");
+			if (!nflag)
+				fprintf(fp, "# %s\n", p->fts_path);
+			statd(fp, t, p, &uid, &gid, &mode, &flags);
+			statf(fp, indent, p);
+			break;
+		case FTS_DP:
+			if (p->fts_level > 0)
+				if (!nflag)
+					fprintf(fp, "%*s# %s\n", indent, "",
+					    p->fts_path);
+			if (p->fts_level > 0 || flavor == F_FREEBSD9) {
+				fprintf(fp, "%*s..\n", indent, "");
+				if (!bflag)
+					fprintf(fp, "\n");
+			}
+			break;
+		case FTS_DNR:
+		case FTS_ERR:
+		case FTS_NS:
+			mtree_err("%s: %s",
+			    p->fts_path, strerror(p->fts_errno));
+			break;
+		default:
+			if (!dflag)
+				statf(fp, indent, p);
+			break;
+
+		}
+	}
+	fts_close(t);
+	if (sflag && keys & F_CKSUM)
+		mtree_err("%s checksum: %u", fullpath, crc_total);
+}
+
+static void
+statf(FILE *fp, int indent, FTSENT *p)
+{
+	u_int32_t len, val;
+	int fd, offset;
+	const char *name = NULL;
+#if !defined(NO_MD5) || !defined(NO_RMD160) || !defined(NO_SHA1) || !defined(NO_SHA2)
+	char *digestbuf;
+#endif
+
+	offset = fprintf(fp, "%*s%s%s", indent, "",
+	    S_ISDIR(p->fts_statp->st_mode) ? "" : "    ", vispath(p->fts_name));
+
+	if (offset > (INDENTNAMELEN + indent))
+		offset = MAXLINELEN;
+	else
+		offset += fprintf(fp, "%*s",
+		    (INDENTNAMELEN + indent) - offset, "");
+
+	if (!S_ISREG(p->fts_statp->st_mode) && (flavor == F_NETBSD6 || !dflag))
+		output(fp, indent, &offset, "type=%s",
+		    inotype(p->fts_statp->st_mode));
+	if (keys & (F_UID | F_UNAME) && p->fts_statp->st_uid != uid) {
+		if (keys & F_UNAME &&
+		    (name = user_from_uid(p->fts_statp->st_uid, 1)) != NULL)
+			output(fp, indent, &offset, "uname=%s", name);
+		if (keys & F_UID || (keys & F_UNAME && name == NULL))
+			output(fp, indent, &offset, "uid=%u",
+			    p->fts_statp->st_uid);
+	}
+	if (keys & (F_GID | F_GNAME) && p->fts_statp->st_gid != gid) {
+		if (keys & F_GNAME &&
+		    (name = group_from_gid(p->fts_statp->st_gid, 1)) != NULL)
+			output(fp, indent, &offset, "gname=%s", name);
+		if (keys & F_GID || (keys & F_GNAME && name == NULL))
+			output(fp, indent, &offset, "gid=%u",
+			    p->fts_statp->st_gid);
+	}
+	if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode)
+		output(fp, indent, &offset, "mode=%#o",
+		    p->fts_statp->st_mode & MBITS);
+	if (keys & F_DEV &&
+	    (S_ISBLK(p->fts_statp->st_mode) || S_ISCHR(p->fts_statp->st_mode)))
+		output(fp, indent, &offset, "device=%#jx",
+		    (uintmax_t)p->fts_statp->st_rdev);
+	if (keys & F_NLINK && p->fts_statp->st_nlink != 1)
+		output(fp, indent, &offset, "nlink=%u", p->fts_statp->st_nlink);
+	if (keys & F_SIZE &&
+	    (flavor == F_FREEBSD9 || S_ISREG(p->fts_statp->st_mode)))
+		output(fp, indent, &offset, "size=%ju",
+		    (uintmax_t)p->fts_statp->st_size);
+	if (keys & F_TIME)
+#if defined(BSD4_4) && !defined(HAVE_NBTOOL_CONFIG_H)
+		output(fp, indent, &offset, "time=%jd.%09ld",
+		    (intmax_t)p->fts_statp->st_mtimespec.tv_sec,
+		    p->fts_statp->st_mtimespec.tv_nsec);
+#else
+		output(fp, indent, &offset, "time=%jd.%09ld",
+		    (intmax_t)p->fts_statp->st_mtime, (long)0);
+#endif
+	if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) {
+		if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 ||
+		    crc(fd, &val, &len))
+			mtree_err("%s: %s", p->fts_accpath, strerror(errno));
+		close(fd);
+		output(fp, indent, &offset, "cksum=%lu", (long)val);
+	}
+#ifndef NO_MD5
+	if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) {
+		if ((digestbuf = MD5File(p->fts_accpath, NULL)) == NULL)
+			mtree_err("%s: MD5File failed: %s", p->fts_accpath,
+			    strerror(errno));
+		output(fp, indent, &offset, "%s=%s", MD5KEY, digestbuf);
+		free(digestbuf);
+	}
+#endif	/* ! NO_MD5 */
+#ifndef NO_RMD160
+	if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) {
+		if ((digestbuf = RMD160File(p->fts_accpath, NULL)) == NULL)
+			mtree_err("%s: RMD160File failed: %s", p->fts_accpath,
+			    strerror(errno));
+		output(fp, indent, &offset, "%s=%s", RMD160KEY, digestbuf);
+		free(digestbuf);
+	}
+#endif	/* ! NO_RMD160 */
+#ifndef NO_SHA1
+	if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) {
+		if ((digestbuf = SHA1File(p->fts_accpath, NULL)) == NULL)
+			mtree_err("%s: SHA1File failed: %s", p->fts_accpath,
+			    strerror(errno));
+		output(fp, indent, &offset, "%s=%s", SHA1KEY, digestbuf);
+		free(digestbuf);
+	}
+#endif	/* ! NO_SHA1 */
+#ifndef NO_SHA2
+	if (keys & F_SHA256 && S_ISREG(p->fts_statp->st_mode)) {
+		if ((digestbuf = SHA256_File(p->fts_accpath, NULL)) == NULL)
+			mtree_err("%s: SHA256_File failed: %s", p->fts_accpath,
+			    strerror(errno));
+		output(fp, indent, &offset, "%s=%s", SHA256KEY, digestbuf);
+		free(digestbuf);
+	}
+#ifdef SHA384_BLOCK_LENGTH
+	if (keys & F_SHA384 && S_ISREG(p->fts_statp->st_mode)) {
+		if ((digestbuf = SHA384_File(p->fts_accpath, NULL)) == NULL)
+			mtree_err("%s: SHA384_File failed: %s", p->fts_accpath,
+			    strerror(errno));
+		output(fp, indent, &offset, "%s=%s", SHA384KEY, digestbuf);
+		free(digestbuf);
+	}
+#endif
+	if (keys & F_SHA512 && S_ISREG(p->fts_statp->st_mode)) {
+		if ((digestbuf = SHA512_File(p->fts_accpath, NULL)) == NULL)
+			mtree_err("%s: SHA512_File failed: %s", p->fts_accpath,
+			    strerror(errno));
+		output(fp, indent, &offset, "%s=%s", SHA512KEY, digestbuf);
+		free(digestbuf);
+	}
+#endif	/* ! NO_SHA2 */
+	if (keys & F_SLINK &&
+	    (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE))
+		output(fp, indent, &offset, "link=%s",
+		    vispath(rlink(p->fts_accpath)));
+#if HAVE_STRUCT_STAT_ST_FLAGS
+	if (keys & F_FLAGS && p->fts_statp->st_flags != flags) {
+		char *str = flags_to_string(p->fts_statp->st_flags, "none");
+		output(fp, indent, &offset, "flags=%s", str);
+		free(str);
+	}
+#endif
+	putchar('\n');
+}
+
+/* XXX
+ * FLAGS2INDEX will fail once the user and system settable bits need more
+ * than one byte, respectively.
+ */
+#define FLAGS2INDEX(x)  (((x >> 8) & 0x0000ff00) | (x & 0x000000ff))
+
+#define	MTREE_MAXGID	5000
+#define	MTREE_MAXUID	5000
+#define	MTREE_MAXMODE	(MBITS + 1)
+#if HAVE_STRUCT_STAT_ST_FLAGS
+#define	MTREE_MAXFLAGS  (FLAGS2INDEX(CH_MASK) + 1)   /* 1808 */
+#else
+#define MTREE_MAXFLAGS	1
+#endif
+#define	MTREE_MAXS 16
+
+static int
+statd(FILE *fp, FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode,
+    u_long *pflags)
+{
+	FTSENT *p;
+	gid_t sgid;
+	uid_t suid;
+	mode_t smode;
+	u_long sflags = 0;
+	const char *name = NULL;
+	gid_t savegid;
+	uid_t saveuid;
+	mode_t savemode;
+	u_long saveflags;
+	u_short maxgid, maxuid, maxmode, maxflags;
+	u_short g[MTREE_MAXGID], u[MTREE_MAXUID],
+		m[MTREE_MAXMODE], f[MTREE_MAXFLAGS];
+	static int first = 1;
+
+	savegid = *pgid;
+	saveuid = *puid;
+	savemode = *pmode;
+	saveflags = *pflags;
+	if ((p = fts_children(t, 0)) == NULL) {
+		if (errno)
+			mtree_err("%s: %s", RP(parent), strerror(errno));
+		return (1);
+	}
+
+	memset(g, 0, sizeof(g));
+	memset(u, 0, sizeof(u));
+	memset(m, 0, sizeof(m));
+	memset(f, 0, sizeof(f));
+
+	maxuid = maxgid = maxmode = maxflags = 0;
+	for (; p; p = p->fts_link) {
+		if (flavor == F_NETBSD6 || !dflag ||
+		    (dflag && S_ISDIR(p->fts_statp->st_mode))) {
+			smode = p->fts_statp->st_mode & MBITS;
+			if (smode < MTREE_MAXMODE && ++m[smode] > maxmode) {
+				savemode = smode;
+				maxmode = m[smode];
+			}
+			sgid = p->fts_statp->st_gid;
+			if (sgid < MTREE_MAXGID && ++g[sgid] > maxgid) {
+				savegid = sgid;
+				maxgid = g[sgid];
+			}
+			suid = p->fts_statp->st_uid;
+			if (suid < MTREE_MAXUID && ++u[suid] > maxuid) {
+				saveuid = suid;
+				maxuid = u[suid];
+			}
+
+#if HAVE_STRUCT_STAT_ST_FLAGS
+			sflags = FLAGS2INDEX(p->fts_statp->st_flags);
+			if (sflags < MTREE_MAXFLAGS && ++f[sflags] > maxflags) {
+				saveflags = p->fts_statp->st_flags;
+				maxflags = f[sflags];
+			}
+#endif
+		}
+	}
+	/*
+	 * If the /set record is the same as the last one we do not need to
+	 * output a new one.  So first we check to see if anything changed.
+	 * Note that we always output a /set record for the first directory.
+	 */
+	if (((keys & (F_UNAME | F_UID)) && (*puid != saveuid)) ||
+	    ((keys & (F_GNAME | F_GID)) && (*pgid != savegid)) ||
+	    ((keys & F_MODE) && (*pmode != savemode)) || 
+	    ((keys & F_FLAGS) && (*pflags != saveflags)) ||
+	    first) {
+		first = 0;
+		if (flavor != F_NETBSD6 && dflag)
+			fprintf(fp, "/set type=dir");
+		else
+			fprintf(fp, "/set type=file");
+		if (keys & (F_UID | F_UNAME)) {
+			if (keys & F_UNAME &&
+			    (name = user_from_uid(saveuid, 1)) != NULL)
+				fprintf(fp, " uname=%s", name);
+			if (keys & F_UID || (keys & F_UNAME && name == NULL))
+				fprintf(fp, " uid=%lu", (u_long)saveuid);
+		}
+		if (keys & (F_GID | F_GNAME)) {
+			if (keys & F_GNAME &&
+			    (name = group_from_gid(savegid, 1)) != NULL)
+				fprintf(fp, " gname=%s", name);
+			if (keys & F_GID || (keys & F_GNAME && name == NULL))
+				fprintf(fp, " gid=%lu", (u_long)savegid);
+		}
+		if (keys & F_MODE)
+			fprintf(fp, " mode=%#lo", (u_long)savemode);
+		if (keys & F_NLINK)
+			fprintf(fp, " nlink=1");
+		if (keys & F_FLAGS) {
+			char *str = flags_to_string(saveflags, "none");
+			fprintf(fp, " flags=%s", str);
+			free(str);
+		}
+		fprintf(fp, "\n");
+		*puid = saveuid;
+		*pgid = savegid;
+		*pmode = savemode;
+		*pflags = saveflags;
+	}
+	return (0);
+}
+
+/*
+ * dcmp --
+ *	used as a comparison function passed to fts_open() to control
+ *	the order in which fts_read() returns results.	We make
+ *	directories sort after non-directories, but otherwise sort in
+ *	strcmp() order.
+ *
+ * Keep this in sync with nodecmp() in spec.c.
+ */
+static int
+dcmp(const FTSENT *FTS_CONST *a, const FTSENT *FTS_CONST *b)
+{
+
+	if (S_ISDIR((*a)->fts_statp->st_mode)) {
+		if (!S_ISDIR((*b)->fts_statp->st_mode))
+			return (1);
+	} else if (S_ISDIR((*b)->fts_statp->st_mode))
+		return (-1);
+	return (strcmp((*a)->fts_name, (*b)->fts_name));
+}
+
+void
+output(FILE *fp, int indent, int *offset, const char *fmt, ...)
+{
+	va_list ap;
+	char buf[1024];
+
+	va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	va_end(ap);
+
+	if (*offset + strlen(buf) > MAXLINELEN - 3) {
+		fprintf(fp, " \\\n%*s", INDENTNAMELEN + indent, "");
+		*offset = INDENTNAMELEN + indent;
+	}
+	*offset += fprintf(fp, " %s", buf) + 1;
+}
diff --git a/contrib/mtree/excludes.c b/contrib/mtree/excludes.c
new file mode 100644
index 0000000000..4b46b89092
--- /dev/null
+++ b/contrib/mtree/excludes.c
@@ -0,0 +1,121 @@
+/*	$NetBSD: excludes.c,v 1.13 2004/06/20 22:20:18 jmc Exp $	*/
+
+/*
+ * Copyright 2000 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.  M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+
+#if defined(__RCSID) && !defined(lint)
+__RCSID("$NetBSD: excludes.c,v 1.13 2004/06/20 22:20:18 jmc Exp $");
+#endif
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <fnmatch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <util.h>
+
+#include "extern.h"
+
+
+/*
+ * We're assuming that there won't be a whole lot of excludes,
+ * so it's OK to use a stupid algorithm.
+ */
+struct exclude {
+	LIST_ENTRY(exclude) link;
+	const char *glob;
+	int pathname;
+};
+static LIST_HEAD(, exclude) excludes;
+
+
+void
+init_excludes(void)
+{
+
+	LIST_INIT(&excludes);
+}
+
+void
+read_excludes_file(const char *name)
+{
+	FILE *fp;
+	char *line;
+	struct exclude *e;
+
+	fp = fopen(name, "r");
+	if (fp == 0)
+		err(1, "%s", name);
+
+	while ((line = fparseln(fp, NULL, NULL, NULL,
+	    FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT | FPARSELN_UNESCESC))
+	    != NULL) {
+		if (line[0] == '\0')
+			continue;
+
+		if ((e = malloc(sizeof *e)) == NULL)
+			mtree_err("memory allocation error");
+
+		e->glob = line;
+		if (strchr(e->glob, '/') != NULL)
+			e->pathname = 1;
+		else
+			e->pathname = 0;
+		LIST_INSERT_HEAD(&excludes, e, link);
+	}
+	fclose(fp);
+}
+
+int
+check_excludes(const char *fname, const char *path)
+{
+	struct exclude *e;
+
+	/* fnmatch(3) has a funny return value convention... */
+#define MATCH(g, n) (fnmatch((g), (n), FNM_PATHNAME) == 0)
+
+	e = LIST_FIRST(&excludes);
+	while (e) {
+		if ((e->pathname && MATCH(e->glob, path))
+		    || MATCH(e->glob, fname)) {
+			return (1);
+		}
+		e = LIST_NEXT(e, link);
+	}
+	return (0);
+}
diff --git a/contrib/mtree/extern.h b/contrib/mtree/extern.h
new file mode 100644
index 0000000000..091cf8f844
--- /dev/null
+++ b/contrib/mtree/extern.h
@@ -0,0 +1,90 @@
+/*	$NetBSD: extern.h,v 1.39 2014/04/24 17:22:41 christos Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 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.
+ *
+ *	@(#)extern.h	8.1 (Berkeley) 6/6/93
+ */
+
+#include "mtree.h"
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#else 
+#define HAVE_STRUCT_STAT_ST_FLAGS 1
+#endif
+ 
+#include <err.h> 
+#include <fts.h>
+#include <util.h>
+#include <stdbool.h>
+
+#if HAVE_NETDB_H
+/* For MAXHOSTNAMELEN on some platforms. */
+#include <netdb.h>
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+enum flavor {
+	F_MTREE,
+	F_FREEBSD9,
+	F_NETBSD6
+};
+
+void	 addtag(slist_t *, char *);
+int	 check_excludes(const char *, const char *);
+int	 compare(NODE *, FTSENT *);
+int	 crc(int, u_int32_t *, u_int32_t *);
+void	 cwalk(FILE *);
+void	 dump_nodes(FILE *, const char *, NODE *, int);
+void	 init_excludes(void);
+int	 matchtags(NODE *);
+__dead __printflike(1,2) void	 mtree_err(const char *, ...);
+const char *nodetype(u_int);
+u_int	 parsekey(const char *, int *);
+void	 parsetags(slist_t *, char *);
+u_int	 parsetype(const char *);
+void	 read_excludes_file(const char *);
+const char *rlink(const char *);
+int	 verify(FILE *);
+void	 load_only(const char *fname);
+bool	 find_only(const char *path);
+
+extern int	bflag, dflag, eflag, iflag, jflag, lflag, mflag,
+		nflag, qflag, rflag, sflag, tflag, uflag;
+extern int	mtree_Mflag, mtree_Sflag, mtree_Wflag;
+extern size_t	mtree_lineno;
+extern enum flavor	flavor;
+extern u_int32_t crc_total;
+extern int	ftsoptions, keys;
+extern char	fullpath[];
+extern slist_t	includetags, excludetags;
+
diff --git a/contrib/mtree/getid.c b/contrib/mtree/getid.c
new file mode 100644
index 0000000000..2c9e641500
--- /dev/null
+++ b/contrib/mtree/getid.c
@@ -0,0 +1,446 @@
+/*	$NetBSD: getid.c,v 1.10 2014/10/27 21:46:45 christos Exp $	*/
+/*	from: NetBSD: getpwent.c,v 1.48 2000/10/03 03:22:26 enami Exp */
+/*	from: NetBSD: getgrent.c,v 1.41 2002/01/12 23:51:30 lukem Exp */
+
+/*
+ * Copyright (c) 1987, 1988, 1989, 1993, 1994, 1995
+ *	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.
+ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn of Wasabi Systems.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: getid.c,v 1.10 2014/10/27 21:46:45 christos Exp $");
+
+#include <sys/param.h>
+
+#include <grp.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static	struct group *	gi_getgrnam(const char *);
+static	struct group *	gi_getgrgid(gid_t);
+static	int		gi_setgroupent(int);
+static	void		gi_endgrent(void);
+static	int		grstart(void);
+static	int		grscan(int, gid_t, const char *);
+static	int		grmatchline(int, gid_t, const char *);
+
+static	struct passwd *	gi_getpwnam(const char *);
+static	struct passwd *	gi_getpwuid(uid_t);
+static	int		gi_setpassent(int);
+static	void		gi_endpwent(void);
+static	int		pwstart(void);
+static	int		pwscan(int, uid_t, const char *);
+static	int		pwmatchline(int, uid_t, const char *);
+
+#define	MAXGRP		200
+#define	MAXLINELENGTH	1024
+
+static	FILE		*_gr_fp;
+static	struct group	_gr_group;
+static	int		_gr_stayopen;
+static	int		_gr_filesdone;
+static	FILE		*_pw_fp;
+static	struct passwd	_pw_passwd;	/* password structure */
+static	int		_pw_stayopen;	/* keep fd's open */
+static	int		_pw_filesdone;
+
+static	char		grfile[MAXPATHLEN];
+static	char		pwfile[MAXPATHLEN];
+
+static	char		*members[MAXGRP];
+static	char		grline[MAXLINELENGTH];
+static	char		pwline[MAXLINELENGTH];
+
+int
+setup_getid(const char *dir)
+{
+	if (dir == NULL)
+		return (0);
+
+				/* close existing databases */
+	gi_endgrent();
+	gi_endpwent();
+
+				/* build paths to new databases */
+	snprintf(grfile, sizeof(grfile), "%s/group", dir);
+	snprintf(pwfile, sizeof(pwfile), "%s/master.passwd", dir);
+
+				/* try to open new databases */
+	if (!grstart() || !pwstart())
+		return (0);
+
+				/* switch pwcache(3) lookup functions */
+	if (pwcache_groupdb(gi_setgroupent, gi_endgrent,
+			    gi_getgrnam, gi_getgrgid) == -1
+	    || pwcache_userdb(gi_setpassent, gi_endpwent,
+			    gi_getpwnam, gi_getpwuid) == -1)
+		return (0);
+
+	return (1);
+}
+
+
+/*
+ * group lookup functions
+ */
+
+static struct group *
+gi_getgrnam(const char *name)
+{
+	int rval;
+
+	if (!grstart())
+		return NULL;
+	rval = grscan(1, 0, name);
+	if (!_gr_stayopen)
+		endgrent();
+	return (rval) ? &_gr_group : NULL;
+}
+
+static struct group *
+gi_getgrgid(gid_t gid)
+{
+	int rval;
+
+	if (!grstart())
+		return NULL;
+	rval = grscan(1, gid, NULL);
+	if (!_gr_stayopen)
+		endgrent();
+	return (rval) ? &_gr_group : NULL;
+}
+
+static int
+gi_setgroupent(int stayopen)
+{
+
+	if (!grstart())
+		return 0;
+	_gr_stayopen = stayopen;
+	return 1;
+}
+
+static void
+gi_endgrent(void)
+{
+
+	_gr_filesdone = 0;
+	if (_gr_fp) {
+		(void)fclose(_gr_fp);
+		_gr_fp = NULL;
+	}
+}
+
+static int
+grstart(void)
+{
+
+	_gr_filesdone = 0;
+	if (_gr_fp) {
+		rewind(_gr_fp);
+		return 1;
+	}
+	if (grfile[0] == '\0')			/* sanity check */
+		return 0;
+
+	_gr_fp = fopen(grfile, "r");
+	if (_gr_fp != NULL)
+		return 1;
+	warn("Can't open `%s'", grfile);
+	return 0;
+}
+
+
+static int
+grscan(int search, gid_t gid, const char *name)
+{
+
+	if (_gr_filesdone)
+		return 0;
+	for (;;) {
+		if (!fgets(grline, sizeof(grline), _gr_fp)) {
+			if (!search)
+				_gr_filesdone = 1;
+			return 0;
+		}
+		/* skip lines that are too big */
+		if (!strchr(grline, '\n')) {
+			int ch;
+
+			while ((ch = getc(_gr_fp)) != '\n' && ch != EOF)
+				;
+			continue;
+		}
+		/* skip comments */
+		if (grline[0] == '#')
+			continue;
+		if (grmatchline(search, gid, name))
+			return 1;
+	}
+	/* NOTREACHED */
+}
+
+static int
+grmatchline(int search, gid_t gid, const char *name)
+{
+	unsigned long	id;
+	char		**m;
+	char		*cp, *bp, *ep;
+
+	/* name may be NULL if search is nonzero */
+
+	bp = grline;
+	memset(&_gr_group, 0, sizeof(_gr_group));
+	_gr_group.gr_name = strsep(&bp, ":\n");
+	if (search && name && strcmp(_gr_group.gr_name, name))
+		return 0;
+	_gr_group.gr_passwd = strsep(&bp, ":\n");
+	if (!(cp = strsep(&bp, ":\n")))
+		return 0;
+	id = strtoul(cp, &ep, 10);
+	if (id > GID_MAX || *ep != '\0')
+		return 0;
+	_gr_group.gr_gid = (gid_t)id;
+	if (search && name == NULL && _gr_group.gr_gid != gid)
+		return 0;
+	cp = NULL;
+	if (bp == NULL)
+		return 0;
+	for (_gr_group.gr_mem = m = members;; bp++) {
+		if (m == &members[MAXGRP - 1])
+			break;
+		if (*bp == ',') {
+			if (cp) {
+				*bp = '\0';
+				*m++ = cp;
+				cp = NULL;
+			}
+		} else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
+			if (cp) {
+				*bp = '\0';
+				*m++ = cp;
+			}
+			break;
+		} else if (cp == NULL)
+			cp = bp;
+	}
+	*m = NULL;
+	return 1;
+}
+
+
+/*
+ * user lookup functions
+ */
+
+static struct passwd *
+gi_getpwnam(const char *name)
+{
+	int rval;
+
+	if (!pwstart())
+		return NULL;
+	rval = pwscan(1, 0, name);
+	if (!_pw_stayopen)
+		endpwent();
+	return (rval) ? &_pw_passwd : NULL;
+}
+
+static struct passwd *
+gi_getpwuid(uid_t uid)
+{
+	int rval;
+
+	if (!pwstart())
+		return NULL;
+	rval = pwscan(1, uid, NULL);
+	if (!_pw_stayopen)
+		endpwent();
+	return (rval) ? &_pw_passwd : NULL;
+}
+
+static int
+gi_setpassent(int stayopen)
+{
+
+	if (!pwstart())
+		return 0;
+	_pw_stayopen = stayopen;
+	return 1;
+}
+
+static void
+gi_endpwent(void)
+{
+
+	_pw_filesdone = 0;
+	if (_pw_fp) {
+		(void)fclose(_pw_fp);
+		_pw_fp = NULL;
+	}
+}
+
+static int
+pwstart(void)
+{
+
+	_pw_filesdone = 0;
+	if (_pw_fp) {
+		rewind(_pw_fp);
+		return 1;
+	}
+	if (pwfile[0] == '\0')			/* sanity check */
+		return 0;
+	_pw_fp = fopen(pwfile, "r");
+	if (_pw_fp != NULL)
+		return 1;
+	warn("Can't open `%s'", pwfile);
+	return 0;
+}
+
+
+static int
+pwscan(int search, uid_t uid, const char *name)
+{
+
+	if (_pw_filesdone)
+		return 0;
+	for (;;) {
+		if (!fgets(pwline, sizeof(pwline), _pw_fp)) {
+			if (!search)
+				_pw_filesdone = 1;
+			return 0;
+		}
+		/* skip lines that are too big */
+		if (!strchr(pwline, '\n')) {
+			int ch;
+
+			while ((ch = getc(_pw_fp)) != '\n' && ch != EOF)
+				;
+			continue;
+		}
+		/* skip comments */
+		if (pwline[0] == '#')
+			continue;
+		if (pwmatchline(search, uid, name))
+			return 1;
+	}
+	/* NOTREACHED */
+}
+
+static int
+pwmatchline(int search, uid_t uid, const char *name)
+{
+	unsigned long	id;
+	char		*cp, *bp, *ep;
+
+	/* name may be NULL if search is nonzero */
+
+	bp = pwline;
+	memset(&_pw_passwd, 0, sizeof(_pw_passwd));
+	_pw_passwd.pw_name = strsep(&bp, ":\n");		/* name */
+	if (search && name && strcmp(_pw_passwd.pw_name, name))
+		return 0;
+
+	_pw_passwd.pw_passwd = strsep(&bp, ":\n");		/* passwd */
+
+	if (!(cp = strsep(&bp, ":\n")))				/* uid */
+		return 0;
+	id = strtoul(cp, &ep, 10);
+	if (id > UID_MAX || *ep != '\0')
+		return 0;
+	_pw_passwd.pw_uid = (uid_t)id;
+	if (search && name == NULL && _pw_passwd.pw_uid != uid)
+		return 0;
+
+	if (!(cp = strsep(&bp, ":\n")))				/* gid */
+		return 0;
+	id = strtoul(cp, &ep, 10);
+	if (id > GID_MAX || *ep != '\0')
+		return 0;
+	_pw_passwd.pw_gid = (gid_t)id;
+
+	if (!(ep = strsep(&bp, ":")))				/* class */
+		return 0;
+	if (!(ep = strsep(&bp, ":")))				/* change */
+		return 0;
+	if (!(ep = strsep(&bp, ":")))				/* expire */
+		return 0;
+
+	if (!(_pw_passwd.pw_gecos = strsep(&bp, ":\n")))	/* gecos */
+		return 0;
+	if (!(_pw_passwd.pw_dir = strsep(&bp, ":\n")))		/* directory */
+		return 0;
+	if (!(_pw_passwd.pw_shell = strsep(&bp, ":\n")))	/* shell */
+		return 0;
+
+	if (strchr(bp, ':') != NULL)
+		return 0;
+
+	return 1;
+}
+
diff --git a/contrib/mtree/misc.c b/contrib/mtree/misc.c
new file mode 100644
index 0000000000..b99f1ce1f7
--- /dev/null
+++ b/contrib/mtree/misc.c
@@ -0,0 +1,312 @@
+/*	$NetBSD: misc.c,v 1.34 2012/12/20 19:09:25 christos Exp $	*/
+
+/*-
+ * Copyright (c) 1991, 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.
+ *
+ *	@(#)misc.c	8.1 (Berkeley) 6/6/93
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(lint)
+__RCSID("$NetBSD: misc.c,v 1.34 2012/12/20 19:09:25 christos Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "extern.h"
+
+enum flavor	flavor = F_MTREE;
+
+typedef struct _key {
+	const char	*name;		/* key name */
+	u_int		val;		/* value */
+
+#define	NEEDVALUE	0x01
+	u_int		flags;
+} KEY;
+
+/* NB: the following tables must be sorted lexically. */
+static KEY keylist[] = {
+	{"cksum",	F_CKSUM,	NEEDVALUE},
+	{"device",	F_DEV,		NEEDVALUE},
+	{"flags",	F_FLAGS,	NEEDVALUE},
+	{"gid",		F_GID,		NEEDVALUE},
+	{"gname",	F_GNAME,	NEEDVALUE},
+	{"ignore",	F_IGN,		0},
+	{"link",	F_SLINK,	NEEDVALUE},
+	{"md5",		F_MD5,		NEEDVALUE},
+	{"md5digest",	F_MD5,		NEEDVALUE},
+	{"mode",	F_MODE,		NEEDVALUE},
+	{"nlink",	F_NLINK,	NEEDVALUE},
+	{"nochange",	F_NOCHANGE,	0},
+	{"optional",	F_OPT,		0},
+	{"ripemd160digest", F_RMD160,	NEEDVALUE},
+	{"rmd160",	F_RMD160,	NEEDVALUE},
+	{"rmd160digest",F_RMD160,	NEEDVALUE},
+	{"sha1",	F_SHA1,		NEEDVALUE},
+	{"sha1digest",	F_SHA1,		NEEDVALUE},
+	{"sha256",	F_SHA256,	NEEDVALUE},
+	{"sha256digest",F_SHA256,	NEEDVALUE},
+	{"sha384",	F_SHA384,	NEEDVALUE},
+	{"sha384digest",F_SHA384,	NEEDVALUE},
+	{"sha512",	F_SHA512,	NEEDVALUE},
+	{"sha512digest",F_SHA512,	NEEDVALUE},
+	{"size",	F_SIZE,		NEEDVALUE},
+	{"tags",	F_TAGS,		NEEDVALUE},
+	{"time",	F_TIME,		NEEDVALUE},
+	{"type",	F_TYPE,		NEEDVALUE},
+	{"uid",		F_UID,		NEEDVALUE},
+	{"uname",	F_UNAME,	NEEDVALUE}
+};
+
+static KEY typelist[] = {
+	{"block",	F_BLOCK,	0},
+	{"char",	F_CHAR,		0},
+	{"dir",		F_DIR,		0},
+#ifdef S_IFDOOR
+	{"door",	F_DOOR,		0},
+#endif
+	{"fifo",	F_FIFO,		0},
+	{"file",	F_FILE,		0},
+	{"link",	F_LINK,		0},
+	{"socket",	F_SOCK,		0},
+};
+
+slist_t	excludetags, includetags;
+int	keys = KEYDEFAULT;
+
+
+int keycompare(const void *, const void *);
+
+u_int
+parsekey(const char *name, int *needvaluep)
+{
+	static int allbits;
+	KEY *k, tmp;
+
+	if (allbits == 0) {
+		size_t i;
+
+		for (i = 0; i < sizeof(keylist) / sizeof(KEY); i++)
+			allbits |= keylist[i].val;
+	}
+	tmp.name = name;
+	if (strcmp(name, "all") == 0)
+		return (allbits);
+	k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY),
+	    sizeof(KEY), keycompare);
+	if (k == NULL)
+		mtree_err("unknown keyword `%s'", name);
+
+	if (needvaluep)
+		*needvaluep = k->flags & NEEDVALUE ? 1 : 0;
+
+	return (k->val);
+}
+
+u_int
+parsetype(const char *name)
+{
+	KEY *k, tmp;
+
+	tmp.name = name;
+	k = (KEY *)bsearch(&tmp, typelist, sizeof(typelist) / sizeof(KEY),
+	    sizeof(KEY), keycompare);
+	if (k == NULL)
+		mtree_err("unknown file type `%s'", name);
+
+	return (k->val);
+}
+
+int
+keycompare(const void *a, const void *b)
+{
+
+	return (strcmp(((const KEY *)a)->name, ((const KEY *)b)->name));
+}
+
+void
+mtree_err(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vwarnx(fmt, ap);
+	va_end(ap);
+	if (mtree_lineno)
+		warnx("failed at line %lu of the specification",
+		    (u_long) mtree_lineno);
+	exit(1);
+	/* NOTREACHED */
+}
+
+void
+addtag(slist_t *list, char *elem)
+{
+
+#define	TAG_CHUNK 20
+
+	if ((list->count % TAG_CHUNK) == 0) {
+		char **new;
+
+		new = (char **)realloc(list->list, (list->count + TAG_CHUNK)
+		    * sizeof(char *));
+		if (new == NULL)
+			mtree_err("memory allocation error");
+		list->list = new;
+	}
+	list->list[list->count] = elem;
+	list->count++;
+}
+
+void
+parsetags(slist_t *list, char *args)
+{
+	char	*p, *e;
+	int	len;
+
+	if (args == NULL) {
+		addtag(list, NULL);
+		return;
+	}
+	while ((p = strsep(&args, ",")) != NULL) {
+		if (*p == '\0')
+			continue;
+		len = strlen(p) + 3;	/* "," + p + ",\0" */
+		if ((e = malloc(len)) == NULL)
+			mtree_err("memory allocation error");
+		snprintf(e, len, ",%s,", p);
+		addtag(list, e);
+	}
+}
+
+/*
+ * matchtags
+ *	returns 0 if there's a match from the exclude list in the node's tags,
+ *	or there's an include list and no match.
+ *	return 1 otherwise.
+ */
+int
+matchtags(NODE *node)
+{
+	int	i;
+
+	if (node->tags) {
+		for (i = 0; i < excludetags.count; i++)
+			if (strstr(node->tags, excludetags.list[i]))
+				break;
+		if (i < excludetags.count)
+			return (0);
+
+		for (i = 0; i < includetags.count; i++)
+			if (strstr(node->tags, includetags.list[i]))
+				break;
+		if (i > 0 && i == includetags.count)
+			return (0);
+	} else if (includetags.count > 0) {
+		return (0);
+	}
+	return (1);
+}
+
+u_int
+nodetoino(u_int type)
+{
+
+	switch (type) {
+	case F_BLOCK:
+		return S_IFBLK;
+	case F_CHAR:
+		return S_IFCHR;
+	case F_DIR:
+		return S_IFDIR;
+	case F_FIFO:
+		return S_IFIFO;
+	case F_FILE:
+		return S_IFREG;
+	case F_LINK:
+		return S_IFLNK;
+#ifdef S_IFSOCK
+	case F_SOCK:
+		return S_IFSOCK;
+#endif
+	default:
+		printf("unknown type %d", type);
+		abort();
+	}
+	/* NOTREACHED */
+}
+
+const char *
+nodetype(u_int type)
+{
+
+	return (inotype(nodetoino(type)));
+}
+
+
+const char *
+inotype(u_int type)
+{
+
+	switch (type & S_IFMT) {
+	case S_IFBLK:
+		return ("block");
+	case S_IFCHR:
+		return ("char");
+	case S_IFDIR:
+		return ("dir");
+	case S_IFIFO:
+		return ("fifo");
+	case S_IFREG:
+		return ("file");
+	case S_IFLNK:
+		return ("link");
+#ifdef S_IFSOCK
+	case S_IFSOCK:
+		return ("socket");
+#endif
+#ifdef S_IFDOOR
+	case S_IFDOOR:
+		return ("door");
+#endif
+	default:
+		return ("unknown");
+	}
+	/* NOTREACHED */
+}
diff --git a/contrib/mtree/mtree.8 b/contrib/mtree/mtree.8
new file mode 100644
index 0000000000..830a2c037d
--- /dev/null
+++ b/contrib/mtree/mtree.8
@@ -0,0 +1,808 @@
+.\"	$NetBSD: mtree.8,v 1.69 2013/02/03 19:16:06 christos Exp $
+.\"
+.\" Copyright (c) 1989, 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.
+.\"
+.\" Copyright (c) 2001-2004 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Luke Mewburn of Wasabi Systems.
+.\"
+.\" 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+.\"
+.\"     @(#)mtree.8	8.2 (Berkeley) 12/11/93
+.\"
+.Dd February 3, 2013
+.Dt MTREE 8
+.Os
+.Sh NAME
+.Nm mtree
+.Nd map a directory hierarchy
+.Sh SYNOPSIS
+.Nm
+.Op Fl bCcDdejLlMnPqrStUuWx
+.Op Fl i | Fl m
+.Op Fl E Ar tags
+.Op Fl F Ar flavor
+.Op Fl f Ar spec
+.Op Fl I Ar tags
+.Op Fl K Ar keywords
+.Op Fl k Ar keywords
+.Op Fl N Ar dbdir
+.Op Fl O Ar onlyfile
+.Op Fl p Ar path
+.Op Fl R Ar keywords
+.Op Fl s Ar seed
+.Op Fl X Ar exclude-file
+.Sh DESCRIPTION
+The
+.Nm
+utility compares a file hierarchy against a specification,
+creates a specification for a file hierarchy, or modifies
+a specification.
+.Pp
+The default action, if not overridden by command line options,
+is to compare the file hierarchy rooted in the current directory
+against a specification read from the standard input.
+Messages are written to the standard output for any files whose
+characteristics do not match the specification, or which are
+missing from either the file hierarchy or the specification.
+.Pp
+The options are as follows:
+.Bl -tag -width Xxxexcludexfilexx
+.It Fl b
+Suppress blank lines before entering and after exiting directories.
+.It Fl C
+Convert a specification into
+a format that's easier to parse with various tools.
+The input specification is read from standard input or
+from the file given by
+.Fl f Ar spec .
+In the output, each file or directory is represented using a single line
+(which might be very long).
+The full path name
+(beginning with
+.Dq \&./ )
+is always printed as the first field;
+.Fl K ,
+.Fl k ,
+and
+.Fl R
+can be used to control which other keywords are printed;
+.Fl E
+and
+.Fl I
+can be used to control which files are printed;
+and the
+.Fl S
+option can be used to sort the output.
+.It Fl c
+Print a specification for the file hierarchy originating at
+the current working directory (or the directory provided by
+.Fl p Ar path )
+to the standard output.
+The output is in a style using relative path names.
+.It Fl D
+As per
+.Fl C ,
+except that the path name is always printed as the last field instead of
+the first.
+.It Fl d
+Ignore everything except directory type files.
+.It Fl E Ar tags
+Add the comma separated tags to the
+.Dq exclusion
+list.
+Non-directories with tags which are in the exclusion list are not printed with
+.Fl C
+and
+.Fl D .
+.It Fl e
+Don't complain about files that are in the file hierarchy, but not in the
+specification.
+.It Fl F Ar flavor
+Set the compatibility flavor of the
+.Nm
+utility.
+The
+.Ar flavor
+can be one of
+.Sy mtree ,
+.Sy freebsd9 ,
+or
+.Sy netbsd6 .
+The default is
+.Sy mtree .
+The
+.Sy freebsd9
+and
+.Sy netbsd6
+flavors attempt to preserve output compatiblity and command line option
+backward compatibility with
+.Fx 9.0
+and
+.Nx 6.0
+respectively.
+.It Fl f Ar spec
+Read the specification from
+.Ar file  ,
+instead of from the standard input.
+.Pp
+If this option is specified twice, the two specifications are compared
+to each other rather than to the file hierarchy.
+The specifications will be sorted like output generated using
+.Fl c .
+The output format in this case is somewhat reminiscent of
+.Xr comm 1 ,
+having "in first spec only", "in second spec only", and "different"
+columns, prefixed by zero, one and two TAB characters respectively.
+Each entry in the "different" column occupies two lines, one from each
+specification.
+.It Fl I Ar tags
+Add the comma separated tags to the
+.Dq inclusion
+list.
+Non-directories with tags which are in the inclusion list are printed with
+.Fl C
+and
+.Fl D .
+If no inclusion list is provided, the default is to display all files.
+.It Fl i
+If specified, set the schg and/or sappnd flags.
+.It Fl j
+Indent the output 4 spaces each time a directory level is descended when
+creating a specification with the
+.Fl c
+option.
+This does not affect either the /set statements or the comment before each
+directory.
+It does however affect the comment before the close of each directory.
+This is the equivalent of the
+.Fl i
+option in the
+.Fx
+version of
+.Nm .
+.It Fl K Ar keywords
+Add the specified (whitespace or comma separated) keywords to the current
+set of keywords.
+If
+.Ql all
+is specified, add all of the other keywords.
+.It Fl k Ar keywords
+Use the
+.Sy type
+keyword plus the specified (whitespace or comma separated)
+keywords instead of the current set of keywords.
+If
+.Ql all
+is specified, use all of the other keywords.
+If the
+.Sy type
+keyword is not desired, suppress it with
+.Fl R Ar type .
+.It Fl L
+Follow all symbolic links in the file hierarchy.
+.It Fl l
+Do
+.Dq loose
+permissions checks, in which more stringent permissions
+will match less stringent ones.
+For example, a file marked mode 0444
+will pass a check for mode 0644.
+.Dq Loose
+checks apply only to read, write and execute permissions -- in
+particular, if other bits like the sticky bit or suid/sgid bits are
+set either in the specification or the file, exact checking will be
+performed.
+This option may not be set at the same time as the
+.Fl U
+or
+.Fl u
+option.
+.It Fl M
+Permit merging of specification entries with different types,
+with the last entry taking precedence.
+.It Fl m
+If the schg and/or sappnd flags are specified, reset these flags.
+Note that this is only possible with securelevel less than 1 (i.e.,
+in single user mode or while the system is running in insecure
+mode).
+See
+.Xr init 8
+for information on security levels.
+.It Fl n
+Do not emit pathname comments when creating a specification.
+Normally
+a comment is emitted before each directory and before the close of that
+directory when using the
+.Fl c
+option.
+.It Fl N Ar dbdir
+Use the user database text file
+.Pa master.passwd
+and group database text file
+.Pa group
+from
+.Ar dbdir ,
+rather than using the results from the system's
+.Xr getpwnam 3
+and
+.Xr getgrnam 3
+(and related) library calls.
+.It Fl O Ar onlypaths
+Only include files included in this list of pathnames.
+.It Fl P
+Don't follow symbolic links in the file hierarchy, instead consider
+the symbolic link itself in any comparisons.
+This is the default.
+.It Fl p Ar path
+Use the file hierarchy rooted in
+.Ar path  ,
+instead of the current directory.
+.It Fl q
+Quiet mode.
+Do not complain when a
+.Dq missing
+directory cannot be created because it already exists.
+This occurs when the directory is a symbolic link.
+.It Fl R Ar keywords
+Remove the specified (whitespace or comma separated) keywords from the current
+set of keywords.
+If
+.Ql all
+is specified, remove all of the other keywords.
+.It Fl r
+Remove any files in the file hierarchy that are not described in the
+specification.
+.It Fl S
+When reading a specification into an internal data structure,
+sort the entries.
+Sorting will affect the order of the output produced by the
+.Fl C
+or
+.Fl D
+options, and will also affect the order in which
+missing entries are created or reported when a directory tree is checked
+against a specification.
+.Pp
+The sort order is the same as that used by the
+.Fl c
+option, which is that entries within the same directory are
+sorted in the order used by
+.Xr strcmp 3 ,
+except that entries for subdirectories sort after other entries.
+By default, if the
+.Fl S
+option is not used, entries within the same directory are collected
+together (separated from entries for other directories), but not sorted.
+.It Fl s Ar seed
+Display a single checksum to the standard error output that represents all
+of the files for which the keyword
+.Sy cksum
+was specified.
+The checksum is seeded with the specified value.
+.It Fl t
+Modify the modified time of existing files, the device type of devices, and
+symbolic link targets, to match the specification.
+.It Fl U
+Same as
+.Fl u
+except that a mismatch is not considered to be an error if it was corrected.
+.It Fl u
+Modify the owner, group, permissions, and flags of existing files,
+the device type of devices, and symbolic link targets,
+to match the specification.
+Create any missing directories, devices or symbolic links.
+User, group, and permissions must all be specified for missing directories
+to be created.
+Note that unless the
+.Fl i
+option is given, the schg and sappnd flags will not be set, even if
+specified.
+If
+.Fl m
+is given, these flags will be reset.
+Exit with a status of 0 on success,
+2 if the file hierarchy did not match the specification, and
+1 if any other error occurred.
+.It Fl W
+Don't attempt to set various file attributes such as the
+ownership, mode, flags, or time
+when creating new directories or changing existing entries.
+This option will be most useful when used in conjunction with
+.Fl U
+or
+.Fl u .
+.It Fl X Ar exclude-file
+The specified file contains
+.Xr fnmatch 3
+patterns matching files to be excluded from
+the specification, one to a line.
+If the pattern contains a
+.Ql \&/
+character, it will be matched against entire pathnames (relative to
+the starting directory); otherwise,
+it will be matched against basenames only.
+Comments are permitted in
+the
+.Ar exclude-list
+file.
+.It Fl x
+Don't descend below mount points in the file hierarchy.
+.El
+.Pp
+Specifications are mostly composed of
+.Dq keywords ,
+i.e. strings that
+that specify values relating to files.
+No keywords have default values, and if a keyword has no value set, no
+checks based on it are performed.
+.Pp
+Currently supported keywords are as follows:
+.Bl -tag -width sha384digestxx
+.It Sy cksum
+The checksum of the file using the default algorithm specified by
+the
+.Xr cksum 1
+utility.
+.It Sy device
+The device number to use for
+.Sy block
+or
+.Sy char
+file types.
+The argument must be one of the following forms:
+.Bl -tag -width 4n
+.It Ar format , Ns Ar major , Ns Ar minor
+A device with
+.Ar major
+and
+.Ar minor
+fields, for an operating system specified with
+.Ar format .
+See below for valid formats.
+.It Ar format , Ns Ar major , Ns Ar unit , Ns Ar subunit
+A device with
+.Ar major ,
+.Ar unit ,
+and
+.Ar subunit
+fields, for an operating system specified with
+.Ar format .
+(Currently this is only supported by the
+.Sy bsdos
+format.)
+.It Ar number
+Opaque number (as stored on the file system).
+.El
+.Pp
+The following values for
+.Ar format
+are recognized:
+.Sy native ,
+.Sy 386bsd ,
+.Sy 4bsd ,
+.Sy bsdos ,
+.Sy freebsd ,
+.Sy hpux ,
+.Sy isc ,
+.Sy linux ,
+.Sy netbsd ,
+.Sy osf1 ,
+.Sy sco ,
+.Sy solaris ,
+.Sy sunos ,
+.Sy svr3 ,
+.Sy svr4 ,
+and
+.Sy ultrix .
+.Pp
+See
+.Xr mknod 8
+for more details.
+.It Sy flags
+The file flags as a symbolic name.
+See
+.Xr chflags 1
+for information on these names.
+If no flags are to be set the string
+.Ql none
+may be used to override the current default.
+Note that the schg and sappnd flags are treated specially (see the
+.Fl i
+and
+.Fl m
+options).
+.It Sy ignore
+Ignore any file hierarchy below this file.
+.It Sy gid
+The file group as a numeric value.
+.It Sy gname
+The file group as a symbolic name.
+.It Sy link
+The file the symbolic link is expected to reference.
+.It Sy md5
+The
+.Tn MD5
+cryptographic message digest of the file.
+.It Sy md5digest
+Synonym for
+.Sy md5 .
+.It Sy mode
+The current file's permissions as a numeric (octal) or symbolic
+value.
+.It Sy nlink
+The number of hard links the file is expected to have.
+.It Sy nochange
+Make sure this file or directory exists but otherwise ignore all attributes.
+.It Sy optional
+The file is optional; don't complain about the file if it's
+not in the file hierarchy.
+.It Sy ripemd160digest
+Synonym for
+.Sy rmd160 .
+.It Sy rmd160
+The
+.Tn RMD-160
+cryptographic message digest of the file.
+.It Sy rmd160digest
+Synonym for
+.Sy rmd160 .
+.It Sy sha1
+The
+.Tn SHA-1
+cryptographic message digest of the file.
+.It Sy sha1digest
+Synonym for
+.Sy sha1 .
+.It Sy sha256
+The 256-bits
+.Tn SHA-2
+cryptographic message digest of the file.
+.It Sy sha256digest
+Synonym for
+.Sy sha256 .
+.It Sy sha384
+The 384-bits
+.Tn SHA-2
+cryptographic message digest of the file.
+.It Sy sha384digest
+Synonym for
+.Sy sha384 .
+.It Sy sha512
+The 512-bits
+.Tn SHA-2
+cryptographic message digest of the file.
+.It Sy sha512digest
+Synonym for
+.Sy sha512 .
+.It Sy size
+The size, in bytes, of the file.
+.It Sy tags
+Comma delimited tags to be matched with
+.Fl E
+and
+.Fl I .
+These may be specified without leading or trailing commas, but will be
+stored internally with them.
+.It Sy time
+The last modification time of the file,
+in second and nanoseconds.
+The value should include a period character and exactly nine digits after
+the period.
+.It Sy type
+The type of the file; may be set to any one of the following:
+.Pp
+.Bl -tag -width Sy -compact
+.It Sy block
+block special device
+.It Sy char
+character special device
+.It Sy dir
+directory
+.It Sy fifo
+fifo
+.It Sy file
+regular file
+.It Sy link
+symbolic link
+.It Sy socket
+socket
+.El
+.It Sy uid
+The file owner as a numeric value.
+.It Sy uname
+The file owner as a symbolic name.
+.El
+.Pp
+The default set of keywords are
+.Sy flags ,
+.Sy gid ,
+.Sy link ,
+.Sy mode ,
+.Sy nlink ,
+.Sy size ,
+.Sy time ,
+.Sy type ,
+and
+.Sy uid .
+.Pp
+There are four types of lines in a specification:
+.Bl -enum
+.It
+Set global values for a keyword.
+This consists of the string
+.Ql /set
+followed by whitespace, followed by sets of keyword/value
+pairs, separated by whitespace.
+Keyword/value pairs consist of a keyword, followed by an equals sign
+.Pq Ql = ,
+followed by a value, without whitespace characters.
+Once a keyword has been set, its value remains unchanged until either
+reset or unset.
+.It
+Unset global values for a keyword.
+This consists of the string
+.Ql /unset ,
+followed by whitespace, followed by one or more keywords,
+separated by whitespace.
+If
+.Ql all
+is specified, unset all of the keywords.
+.It
+A file specification, consisting of a path name, followed by whitespace,
+followed by zero or more whitespace separated keyword/value pairs.
+.Pp
+The path name may be preceded by whitespace characters.
+The path name may contain any of the standard path name matching
+characters
+.Po
+.Ql \&[ ,
+.Ql \&] ,
+.Ql \&?
+or
+.Ql *
+.Pc ,
+in which case files
+in the hierarchy will be associated with the first pattern that
+they match.
+.Nm
+uses
+.Xr strsvis 3
+(in VIS_CSTYLE format) to encode path names containing
+non-printable characters.
+Whitespace characters are encoded as
+.Ql \es
+(space),
+.Ql \et
+(tab), and
+.Ql \en
+(new line).
+.Ql #
+characters in path names are escaped by a preceding backslash
+.Ql \e
+to distinguish them from comments.
+.Pp
+Each of the keyword/value pairs consist of a keyword, followed by an
+equals sign
+.Pq Ql = ,
+followed by the keyword's value, without
+whitespace characters.
+These values override, without changing, the global value of the
+corresponding keyword.
+.Pp
+The first path name entry listed must be a directory named
+.Ql \&. ,
+as this ensures that intermixing full and relative path names will
+work consistently and correctly.
+Multiple entries for a directory named
+.Ql \&.
+are permitted; the settings for the last such entry override those
+of the existing entry.
+.Pp
+A path name that contains a slash
+.Pq Ql /
+that is not the first character will be treated as a full path
+(relative to the root of the tree).
+All parent directories referenced in the path name must exist.
+The current directory path used by relative path names will be updated
+appropriately.
+Multiple entries for the same full path are permitted if the types
+are the same (unless
+.Fl M
+is given, in which case the types may differ);
+in this case the settings for the last entry take precedence.
+.Pp
+A path name that does not contain a slash will be treated as a relative path.
+Specifying a directory will cause subsequent files to be searched
+for in that directory hierarchy.
+.It
+A line containing only the string
+.Ql \&..
+which causes the current directory path (used by relative paths)
+to ascend one level.
+.El
+.Pp
+Empty lines and lines whose first non-whitespace character is a hash
+mark
+.Pq Ql #
+are ignored.
+.Pp
+The
+.Nm
+utility exits with a status of 0 on success, 1 if any error occurred,
+and 2 if the file hierarchy did not match the specification.
+.Sh FILES
+.Bl -tag -width /etc/mtree -compact
+.It Pa /etc/mtree
+system specification directory
+.El
+.Sh EXAMPLES
+To detect system binaries that have been
+.Dq trojan horsed ,
+it is recommended that
+.Nm
+be run on the file systems, and a copy of the results stored on a different
+machine, or, at least, in encrypted form.
+The seed for the
+.Fl s
+option should not be an obvious value and the final checksum should not be
+stored on-line under any circumstances!
+Then, periodically,
+.Nm
+should be run against the on-line specifications and the final checksum
+compared with the previous value.
+While it is possible for the bad guys to change the on-line specifications
+to conform to their modified binaries, it shouldn't be possible for them
+to make it produce the same final checksum value.
+If the final checksum value changes, the off-line copies of the specification
+can be used to detect which of the binaries have actually been modified.
+.Pp
+The
+.Fl d
+option can be used in combination with
+.Fl U
+or
+.Fl u
+to create directory hierarchies for, for example, distributions.
+.Sh COMPATIBILITY
+The compatibility shims provided by the
+.Fl F
+option are incomplete by design.
+Known limitations are described below.
+.Pp
+The
+.Sy freebsd9
+flavor retains the default handling of lookup failures for the
+.Sy uname
+and
+.Sy group
+keywords by replacing them with appropriate
+.Sy uid
+and
+.Sy gid
+keywords rather than failing and reporting an error.
+The related
+.Fl w
+flag is a no-op rather than causing a warning to be printed and no
+keyword to be emitted.
+The latter behavior is not emulated as it is potentially dangerous in
+the face of /set statements.
+.Pp
+The
+.Sy netbsd6
+flavor does not replicate the historical bug that reported time as
+seconds.nanoseconds without zero padding nanosecond values less than
+100000000.
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr chgrp 1 ,
+.Xr chmod 1 ,
+.Xr cksum 1 ,
+.Xr stat 2 ,
+.Xr fnmatch 3 ,
+.Xr fts 3 ,
+.Xr strsvis 3 ,
+.Xr chown 8 ,
+.Xr mknod 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 Reno .
+The
+.Sy optional
+keyword appeared in
+.Nx 1.2 .
+The
+.Fl U
+option appeared in
+.Nx 1.3 .
+The
+.Sy flags
+and
+.Sy md5
+keywords, and
+.Fl i
+and
+.Fl m
+options
+appeared in
+.Nx 1.4 .
+The
+.Sy device ,
+.Sy rmd160 ,
+.Sy sha1 ,
+.Sy tags ,
+and
+.Sy all
+keywords,
+.Fl D ,
+.Fl E ,
+.Fl I ,
+.Fl L ,
+.Fl l ,
+.Fl N ,
+.Fl P ,
+.Fl R ,
+.Fl W ,
+and
+.Fl X
+options, and support for full paths appeared in
+.Nx 1.6 .
+The
+.Sy sha256 ,
+.Sy sha384 ,
+and
+.Sy sha512
+keywords appeared in
+.Nx 3.0 .
+The
+.Fl S
+option appeared in
+.Nx 6.0 .
diff --git a/contrib/mtree/mtree.c b/contrib/mtree/mtree.c
new file mode 100644
index 0000000000..8b4cb9494d
--- /dev/null
+++ b/contrib/mtree/mtree.c
@@ -0,0 +1,332 @@
+/*	$NetBSD: mtree.c,v 1.49 2014/04/24 17:22:41 christos Exp $	*/
+
+/*-
+ * Copyright (c) 1989, 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__COPYRIGHT) && !defined(lint)
+__COPYRIGHT("@(#) Copyright (c) 1989, 1990, 1993\
+ The Regents of the University of California.  All rights reserved.");
+#endif /* not lint */
+
+#if defined(__RCSID) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)mtree.c	8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: mtree.c,v 1.49 2014/04/24 17:22:41 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+int	ftsoptions = FTS_PHYSICAL;
+int	bflag, dflag, eflag, iflag, jflag, lflag, mflag, nflag, qflag, rflag,
+	sflag, tflag, uflag;
+char	fullpath[MAXPATHLEN];
+
+static struct {
+	enum flavor flavor;
+	const char name[9];
+} flavors[] = {
+	{F_MTREE, "mtree"},
+	{F_FREEBSD9, "freebsd9"},
+	{F_NETBSD6, "netbsd6"},
+};
+
+__dead static	void	usage(void);
+
+int
+main(int argc, char **argv)
+{
+	int	ch, status;
+	unsigned int	i;
+	int	cflag, Cflag, Dflag, Uflag, wflag;
+	char	*dir, *p;
+	FILE	*spec1, *spec2;
+
+	setprogname(argv[0]);
+
+	cflag = Cflag = Dflag = Uflag = wflag = 0;
+	dir = NULL;
+	init_excludes();
+	spec1 = stdin;
+	spec2 = NULL;
+
+	while ((ch = getopt(argc, argv,
+	    "bcCdDeE:f:F:I:ijk:K:lLmMnN:O:p:PqrR:s:StuUwWxX:"))
+	    != -1) {
+		switch((char)ch) {
+		case 'b':
+			bflag = 1;
+			break;
+		case 'c':
+			cflag = 1;
+			break;
+		case 'C':
+			Cflag = 1;
+			break;
+		case 'd':
+			dflag = 1;
+			break;
+		case 'D':
+			Dflag = 1;
+			break;
+		case 'E':
+			parsetags(&excludetags, optarg);
+			break;
+		case 'e':
+			eflag = 1;
+			break;
+		case 'f':
+			if (spec1 == stdin) {
+				spec1 = fopen(optarg, "r");
+				if (spec1 == NULL)
+					mtree_err("%s: %s", optarg,
+					    strerror(errno));
+			} else if (spec2 == NULL) {
+				spec2 = fopen(optarg, "r");
+				if (spec2 == NULL)
+					mtree_err("%s: %s", optarg,
+					    strerror(errno));
+			} else
+				usage();
+			break;
+		case 'F':
+			for (i = 0; i < __arraycount(flavors); i++)
+				if (strcmp(optarg, flavors[i].name) == 0) {
+					flavor = flavors[i].flavor;
+					break;
+				}
+			if (i == __arraycount(flavors))
+				usage();
+			break;
+		case 'i':
+			iflag = 1;
+			break;
+		case 'I':
+			parsetags(&includetags, optarg);
+			break;
+		case 'j':
+			jflag = 1;
+			break;
+		case 'k':
+			keys = F_TYPE;
+			while ((p = strsep(&optarg, " \t,")) != NULL)
+				if (*p != '\0')
+					keys |= parsekey(p, NULL);
+			break;
+		case 'K':
+			while ((p = strsep(&optarg, " \t,")) != NULL)
+				if (*p != '\0')
+					keys |= parsekey(p, NULL);
+			break;
+		case 'l':
+			lflag = 1;
+			break;
+		case 'L':
+			ftsoptions &= ~FTS_PHYSICAL;
+			ftsoptions |= FTS_LOGICAL;
+			break;
+		case 'm':
+			mflag = 1;
+			break;
+		case 'M':
+			mtree_Mflag = 1;
+			break;
+		case 'n':
+			nflag = 1;
+			break;
+		case 'N':
+			if (! setup_getid(optarg))
+				mtree_err(
+			    "Unable to use user and group databases in `%s'",
+				    optarg);
+			break;
+		case 'O':
+			load_only(optarg);
+			break;
+		case 'p':
+			dir = optarg;
+			break;
+		case 'P':
+			ftsoptions &= ~FTS_LOGICAL;
+			ftsoptions |= FTS_PHYSICAL;
+			break;
+		case 'q':
+			qflag = 1;
+			break;
+		case 'r':
+			rflag = 1;
+			break;
+		case 'R':
+			while ((p = strsep(&optarg, " \t,")) != NULL)
+				if (*p != '\0')
+					keys &= ~parsekey(p, NULL);
+			break;
+		case 's':
+			sflag = 1;
+			crc_total = ~strtol(optarg, &p, 0);
+			if (*p)
+				mtree_err("illegal seed value -- %s", optarg);
+			break;
+		case 'S':
+			mtree_Sflag = 1;
+			break;
+		case 't':
+			tflag = 1;
+			break;
+		case 'u':
+			uflag = 1;
+			break;
+		case 'U':
+			Uflag = uflag = 1;
+			break;
+		case 'w':
+			wflag = 1;
+			break;
+		case 'W':
+			mtree_Wflag = 1;
+			break;
+		case 'x':
+			ftsoptions |= FTS_XDEV;
+			break;
+		case 'X':
+			read_excludes_file(optarg);
+			break;
+		case '?':
+		default:
+			usage();
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	if (argc)
+		usage();
+
+	switch (flavor) {
+	case F_FREEBSD9:
+		if (cflag && iflag) {
+			warnx("-c and -i passed, replacing -i with -j for "
+			    "FreeBSD compatibility");
+			iflag = 0;
+			jflag = 1;
+		}
+		if (dflag && !bflag) {
+			warnx("Adding -b to -d for FreeBSD compatibility");
+			bflag = 1;
+		}
+		if (uflag && !iflag) {
+			warnx("Adding -i to -%c for FreeBSD compatibility",
+			    Uflag ? 'U' : 'u');
+			iflag = 1;
+		}
+		if (uflag && !tflag) {
+			warnx("Adding -t to -%c for FreeBSD compatibility",
+			    Uflag ? 'U' : 'u');
+			tflag = 1;
+		}
+		if (wflag)
+			warnx("The -w flag is a no-op");
+		break;
+	default:
+		if (wflag)
+			usage();
+	}
+
+	if (spec2 && (cflag || Cflag || Dflag))
+		mtree_err("Double -f, -c, -C and -D flags are mutually "
+		    "exclusive");
+
+	if (dir && spec2)
+		mtree_err("Double -f and -p flags are mutually exclusive");
+
+	if (dir && chdir(dir))
+		mtree_err("%s: %s", dir, strerror(errno));
+
+	if ((cflag || sflag) && !getcwd(fullpath, sizeof(fullpath)))
+		mtree_err("%s", strerror(errno));
+
+	if ((cflag && Cflag) || (cflag && Dflag) || (Cflag && Dflag))
+		mtree_err("-c, -C and -D flags are mutually exclusive");
+
+	if (iflag && mflag)
+		mtree_err("-i and -m flags are mutually exclusive");
+
+	if (lflag && uflag)
+		mtree_err("-l and -u flags are mutually exclusive");
+
+	if (cflag) {
+		cwalk(stdout);
+		exit(0);
+	}
+	if (Cflag || Dflag) {
+		dump_nodes(stdout, "", spec(spec1), Dflag);
+		exit(0);
+	}
+	if (spec2 != NULL)
+		status = mtree_specspec(spec1, spec2);
+	else
+		status = verify(spec1);
+	if (Uflag && (status == MISMATCHEXIT))
+		status = 0;
+	exit(status);
+}
+
+static void
+usage(void)
+{
+	unsigned int i;
+
+	fprintf(stderr,
+	    "usage: %s [-bCcDdejLlMnPqrStUuWx] [-i|-m] [-E tags]\n"
+	    "\t\t[-f spec] [-f spec]\n"
+	    "\t\t[-I tags] [-K keywords] [-k keywords] [-N dbdir] [-p path]\n"
+	    "\t\t[-R keywords] [-s seed] [-X exclude-file]\n"
+	    "\t\t[-F flavor]\n",
+	    getprogname());
+	fprintf(stderr, "\nflavors:");
+	for (i = 0; i < __arraycount(flavors); i++)
+		fprintf(stderr, " %s", flavors[i].name);
+	fprintf(stderr, "\n");
+	exit(1);
+}
diff --git a/contrib/mtree/mtree.h b/contrib/mtree/mtree.h
new file mode 100644
index 0000000000..93d6cdfb0e
--- /dev/null
+++ b/contrib/mtree/mtree.h
@@ -0,0 +1,158 @@
+/*	$NetBSD: mtree.h,v 1.31 2012/10/05 09:17:29 wiz Exp $	*/
+
+/*-
+ * 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.
+ *
+ *	@(#)mtree.h	8.1 (Berkeley) 6/6/93
+ */
+
+#ifndef _MTREE_H_
+#define	_MTREE_H_
+
+#define	KEYDEFAULT	(F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | \
+			F_TIME | F_TYPE | F_UID | F_FLAGS)
+
+#define	MISMATCHEXIT	2
+
+typedef struct _node {
+	struct _node	*parent, *child;	/* up, down */
+	struct _node	*prev, *next;		/* left, right */
+	off_t	st_size;			/* size */
+	struct timespec	st_mtimespec;		/* last modification time */
+	char	*slink;				/* symbolic link reference */
+	uid_t	st_uid;				/* uid */
+	gid_t	st_gid;				/* gid */
+#define	MBITS	(S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
+	mode_t	st_mode;			/* mode */
+	dev_t	st_rdev;			/* device type */
+	u_long	st_flags;			/* flags */
+	nlink_t	st_nlink;			/* link count */
+	u_long	cksum;				/* check sum */
+	char	*md5digest;			/* MD5 digest */
+	char	*rmd160digest;			/* RMD-160 digest */
+	char	*sha1digest;			/* SHA1 digest */
+	char	*sha256digest;			/* SHA256 digest */
+	char	*sha384digest;			/* SHA384 digest */
+	char	*sha512digest;			/* SHA512 digest */
+	char	*tags;				/* tags, comma delimited,
+						 * also with leading and
+						 * trailing commas */
+	size_t	lineno;				/* line # entry came from */
+
+#define	F_CKSUM		0x00000001		/* cksum(1) check sum */
+#define	F_DEV		0x00000002		/* device type */
+#define	F_DONE		0x00000004		/* directory done */
+#define	F_FLAGS		0x00000008		/* file flags */
+#define	F_GID		0x00000010		/* gid */
+#define	F_GNAME		0x00000020		/* group name */
+#define	F_IGN		0x00000040		/* ignore */
+#define	F_MAGIC		0x00000080		/* name has magic chars */
+#define	F_MD5		0x00000100		/* MD5 digest */
+#define	F_MODE		0x00000200		/* mode */
+#define	F_NLINK		0x00000400		/* number of links */
+#define	F_OPT		0x00000800		/* existence optional */
+#define	F_RMD160	0x00001000		/* RMD-160 digest */
+#define	F_SHA1		0x00002000		/* SHA1 digest */
+#define	F_SIZE		0x00004000		/* size */
+#define	F_SLINK		0x00008000		/* symbolic link */
+#define	F_TAGS		0x00010000		/* tags */
+#define	F_TIME		0x00020000		/* modification time */
+#define	F_TYPE		0x00040000		/* file type */
+#define	F_UID		0x00080000		/* uid */
+#define	F_UNAME		0x00100000		/* user name */
+#define	F_VISIT		0x00200000		/* file visited */
+#define	F_NOCHANGE	0x00400000		/* check existence, but not */
+						/* other properties */
+#define	F_SHA256	0x00800000		/* SHA256 digest */
+#define	F_SHA384	0x01000000		/* SHA384 digest */
+#define	F_SHA512	0x02000000		/* SHA512 digest */
+
+	int	flags;				/* items set */
+
+#define	F_BLOCK	0x001				/* block special */
+#define	F_CHAR	0x002				/* char special */
+#define	F_DIR	0x004				/* directory */
+#define	F_FIFO	0x008				/* fifo */
+#define	F_FILE	0x010				/* regular file */
+#define	F_LINK	0x020				/* symbolic link */
+#define	F_SOCK	0x040				/* socket */
+#define	F_DOOR	0x080				/* door */
+	int	type;				/* file type */
+
+	char	name[1];			/* file name (must be last) */
+} NODE;
+
+
+typedef struct {
+	char  **list;
+	int	count;
+} slist_t;
+
+
+/*
+ * prototypes for functions published to other programs which want to use
+ * the specfile parser but don't want to pull in all of "extern.h"
+ */
+const char	*inotype(u_int);
+u_int		 nodetoino(u_int);
+int		 setup_getid(const char *);
+NODE		*spec(FILE *);
+int		 mtree_specspec(FILE *, FILE *);
+void		 free_nodes(NODE *);
+char		*vispath(const char *);
+
+#ifdef __FreeBSD__
+#define KEY_DIGEST "digest"
+#else
+#define KEY_DIGEST
+#endif
+
+#define	MD5KEY		"md5"		KEY_DIGEST
+#ifdef __FreeBSD__
+#define	RMD160KEY	"ripemd160"	KEY_DIGEST
+#else
+#define	RMD160KEY	"rmd160"	KEY_DIGEST
+#endif
+#define	SHA1KEY		"sha1"		KEY_DIGEST
+#define	SHA256KEY	"sha256"	KEY_DIGEST
+#define	SHA384KEY	"sha384"
+#define	SHA512KEY	"sha512"
+
+#define	RP(p)	\
+	((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \
+	    (p)->fts_path + 2 : (p)->fts_path)
+
+#define	UF_MASK ((UF_NODUMP | UF_IMMUTABLE |   \
+                  UF_APPEND | UF_OPAQUE)       \
+                    & UF_SETTABLE)              /* user settable flags */
+#define	SF_MASK ((SF_ARCHIVED | SF_IMMUTABLE | \
+                  SF_APPEND) & SF_SETTABLE)     /* root settable flags */
+#define	CH_MASK  (UF_MASK | SF_MASK)            /* all settable flags */
+#define	SP_FLGS  (SF_IMMUTABLE | SF_APPEND)     /* special flags */
+
+#endif /* _MTREE_H_ */
diff --git a/contrib/mtree/only.c b/contrib/mtree/only.c
new file mode 100644
index 0000000000..d31b1cd05e
--- /dev/null
+++ b/contrib/mtree/only.c
@@ -0,0 +1,152 @@
+/*	$NetBSD: only.c,v 1.2 2013/02/05 00:59:03 christos Exp $	*/
+
+/*-
+ * Copyright (c) 2013 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Christos Zoulas.
+ *
+ * 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 NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+
+#if defined(__RCSID) && !defined(lint)
+__RCSID("$NetBSD: only.c,v 1.2 2013/02/05 00:59:03 christos Exp $");
+#endif
+
+#include <sys/param.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <time.h>
+#include <err.h>
+#include <util.h>
+
+#include "extern.h"
+
+struct hentry {
+	char *str;
+	uint32_t hash;
+	struct hentry *next;
+};
+
+static struct hentry *table[1024];
+static bool loaded;
+
+static uint32_t
+hash_str(const char *str)
+{
+	const uint8_t *s = (const uint8_t *)str;
+	uint8_t c;
+	uint32_t hash = 0;
+	while ((c = *s++) != '\0') 
+		hash = hash * 33 + c;           /* "perl": k=33, r=r+r/32 */
+	return hash + (hash >> 5);
+}
+
+static bool
+hash_find(const char *str, uint32_t *h)
+{
+	struct hentry *e;
+	*h = hash_str(str) % __arraycount(table);
+
+	for (e = table[*h]; e; e = e->next)
+		if (e->hash == *h && strcmp(e->str, str) == 0)
+			return true;
+	return false;
+}
+
+static void
+hash_insert(char *str, uint32_t h)
+{
+	struct hentry *e;
+
+	if ((e = malloc(sizeof(*e))) == NULL)
+		mtree_err("memory allocation error");
+
+	e->str = str;
+	e->hash = h;
+	e->next = table[h];
+	table[h] = e;
+}
+
+static void
+fill(char *str)
+{
+	uint32_t h;
+	char *ptr = strrchr(str, '/');
+
+	if (ptr == NULL)
+		return;
+
+	*ptr = '\0';
+	if (!hash_find(str, &h)) {
+		char *x = strdup(str);
+		if (x == NULL)
+			mtree_err("memory allocation error");
+		hash_insert(x, h);
+		fill(str);
+	}
+	*ptr = '/';
+}
+
+void
+load_only(const char *fname)
+{
+	FILE *fp;
+	char *line;
+	size_t len, lineno;
+
+	if ((fp = fopen(fname, "r")) == NULL)
+		err(1, "Cannot open `%s'", fname);
+
+	while ((line = fparseln(fp, &len, &lineno, NULL, FPARSELN_UNESCALL))) {
+		uint32_t h;
+		if (hash_find(line, &h))
+			err(1, "Duplicate entry %s", line);
+		hash_insert(line, h);
+		fill(line);
+	}
+
+	fclose(fp);
+	loaded = true;
+}
+
+bool
+find_only(const char *path)
+{
+	uint32_t h;
+
+	if (!loaded)
+		return true;
+	return hash_find(path, &h);
+}
diff --git a/contrib/mtree/spec.c b/contrib/mtree/spec.c
new file mode 100644
index 0000000000..0fb921d1c0
--- /dev/null
+++ b/contrib/mtree/spec.c
@@ -0,0 +1,852 @@
+/*	$NetBSD: spec.c,v 1.89 2014/04/24 17:22:41 christos Exp $	*/
+
+/*-
+ * Copyright (c) 1989, 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.
+ */
+
+/*-
+ * Copyright (c) 2001-2004 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn of Wasabi Systems.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)spec.c	8.2 (Berkeley) 4/28/95";
+#else
+__RCSID("$NetBSD: spec.c,v 1.89 2014/04/24 17:22:41 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <vis.h>
+#include <util.h>
+
+#include "extern.h"
+#include "pack_dev.h"
+
+size_t	mtree_lineno;			/* Current spec line number */
+int	mtree_Mflag;			/* Merge duplicate entries */
+int	mtree_Wflag;			/* Don't "whack" permissions */
+int	mtree_Sflag;			/* Sort entries */
+
+static	dev_t	parsedev(char *);
+static	void	replacenode(NODE *, NODE *);
+static	void	set(char *, NODE *);
+static	void	unset(char *, NODE *);
+static	void	addchild(NODE *, NODE *);
+static	int	nodecmp(const NODE *, const NODE *);
+static	int	appendfield(FILE *, int, const char *, ...) __printflike(3, 4);
+
+#define REPLACEPTR(x,v)	do { if ((x)) free((x)); (x) = (v); } while (0)
+
+NODE *
+spec(FILE *fp)
+{
+	NODE *centry, *last, *pathparent, *cur;
+	char *p, *e, *next;
+	NODE ginfo, *root;
+	char *buf, *tname, *ntname;
+	size_t tnamelen, plen;
+
+	root = NULL;
+	centry = last = NULL;
+	tname = NULL;
+	tnamelen = 0;
+	memset(&ginfo, 0, sizeof(ginfo));
+	for (mtree_lineno = 0;
+	    (buf = fparseln(fp, NULL, &mtree_lineno, NULL,
+		FPARSELN_UNESCCOMM));
+	    free(buf)) {
+		/* Skip leading whitespace. */
+		for (p = buf; *p && isspace((unsigned char)*p); ++p)
+			continue;
+
+		/* If nothing but whitespace, continue. */
+		if (!*p)
+			continue;
+
+#ifdef DEBUG
+		fprintf(stderr, "line %lu: {%s}\n",
+		    (u_long)mtree_lineno, p);
+#endif
+		/* Grab file name, "$", "set", or "unset". */
+		next = buf;
+		while ((p = strsep(&next, " \t")) != NULL && *p == '\0')
+			continue;
+		if (p == NULL)
+			mtree_err("missing field");
+
+		if (p[0] == '/') {
+			if (strcmp(p + 1, "set") == 0)
+				set(next, &ginfo);
+			else if (strcmp(p + 1, "unset") == 0)
+				unset(next, &ginfo);
+			else
+				mtree_err("invalid specification `%s'", p);
+			continue;
+		}
+
+		if (strcmp(p, "..") == 0) {
+			/* Don't go up, if haven't gone down. */
+			if (root == NULL)
+				goto noparent;
+			if (last->type != F_DIR || last->flags & F_DONE) {
+				if (last == root)
+					goto noparent;
+				last = last->parent;
+			}
+			last->flags |= F_DONE;
+			continue;
+
+noparent:		mtree_err("no parent node");
+		}
+
+		plen = strlen(p) + 1;
+		if (plen > tnamelen) {
+			if ((ntname = realloc(tname, plen)) == NULL)
+				mtree_err("realloc: %s", strerror(errno));
+			tname = ntname;
+			tnamelen = plen;
+		}
+		if (strunvis(tname, p) == -1)
+			mtree_err("strunvis failed on `%s'", p);
+		p = tname;
+
+		pathparent = NULL;
+		if (strchr(p, '/') != NULL) {
+			cur = root;
+			for (; (e = strchr(p, '/')) != NULL; p = e+1) {
+				if (p == e)
+					continue;	/* handle // */
+				*e = '\0';
+				if (strcmp(p, ".") != 0) {
+					while (cur &&
+					    strcmp(cur->name, p) != 0) {
+						cur = cur->next;
+					}
+				}
+				if (cur == NULL || cur->type != F_DIR) {
+					mtree_err("%s: %s", tname,
+					"missing directory in specification");
+				}
+				*e = '/';
+				pathparent = cur;
+				cur = cur->child;
+			}
+			if (*p == '\0')
+				mtree_err("%s: empty leaf element", tname);
+		}
+
+		if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
+			mtree_err("%s", strerror(errno));
+		*centry = ginfo;
+		centry->lineno = mtree_lineno;
+		strcpy(centry->name, p);
+#define	MAGIC	"?*["
+		if (strpbrk(p, MAGIC))
+			centry->flags |= F_MAGIC;
+		set(next, centry);
+
+		if (root == NULL) {
+				/*
+				 * empty tree
+				 */
+			/*
+			 * Allow a bare "." root node by forcing it to
+			 * type=dir for compatibility with FreeBSD.
+			 */
+			if (strcmp(centry->name, ".") == 0 && centry->type == 0)
+				centry->type = F_DIR;
+			if (strcmp(centry->name, ".") != 0 ||
+			    centry->type != F_DIR)
+				mtree_err(
+				    "root node must be the directory `.'");
+			last = root = centry;
+			root->parent = root;
+		} else if (pathparent != NULL) {
+				/*
+				 * full path entry; add or replace
+				 */
+			centry->parent = pathparent;
+			addchild(pathparent, centry);
+			last = centry;
+		} else if (strcmp(centry->name, ".") == 0) {
+				/*
+				 * duplicate "." entry; always replace
+				 */
+			replacenode(root, centry);
+		} else if (last->type == F_DIR && !(last->flags & F_DONE)) {
+				/*
+				 * new relative child in current dir;
+				 * add or replace
+				 */
+			centry->parent = last;
+			addchild(last, centry);
+			last = centry;
+		} else {
+				/*
+				 * new relative child in parent dir
+				 * (after encountering ".." entry);
+				 * add or replace
+				 */
+			centry->parent = last->parent;
+			addchild(last->parent, centry);
+			last = centry;
+		}
+	}
+	return (root);
+}
+
+void
+free_nodes(NODE *root)
+{
+	NODE	*cur, *next;
+
+	if (root == NULL)
+		return;
+
+	next = NULL;
+	for (cur = root; cur != NULL; cur = next) {
+		next = cur->next;
+		free_nodes(cur->child);
+		REPLACEPTR(cur->slink, NULL);
+		REPLACEPTR(cur->md5digest, NULL);
+		REPLACEPTR(cur->rmd160digest, NULL);
+		REPLACEPTR(cur->sha1digest, NULL);
+		REPLACEPTR(cur->sha256digest, NULL);
+		REPLACEPTR(cur->sha384digest, NULL);
+		REPLACEPTR(cur->sha512digest, NULL);
+		REPLACEPTR(cur->tags, NULL);
+		REPLACEPTR(cur, NULL);
+	}
+}
+
+/*
+ * appendfield --
+ *	Like fprintf(), but output a space either before or after
+ *	the regular output, according to the pathlast flag.
+ */
+static int
+appendfield(FILE *fp, int pathlast, const char *fmt, ...)
+{
+	va_list ap;
+	int result;
+
+	va_start(ap, fmt);
+	if (!pathlast)
+		fprintf(fp, " ");
+	result = vprintf(fmt, ap);
+	if (pathlast)
+		fprintf(fp, " ");
+	va_end(ap);
+	return result;
+}
+
+/*
+ * dump_nodes --
+ *	dump the NODEs from `cur', based in the directory `dir'.
+ *	if pathlast is none zero, print the path last, otherwise print
+ *	it first.
+ */
+void
+dump_nodes(FILE *fp, const char *dir, NODE *root, int pathlast)
+{
+	NODE	*cur;
+	char	path[MAXPATHLEN];
+	const char *name;
+	char	*str;
+	char	*p, *q;
+
+	for (cur = root; cur != NULL; cur = cur->next) {
+		if (cur->type != F_DIR && !matchtags(cur))
+			continue;
+
+		if (snprintf(path, sizeof(path), "%s%s%s",
+		    dir, *dir ? "/" : "", cur->name)
+		    >= (int)sizeof(path))
+			mtree_err("Pathname too long.");
+
+		if (!pathlast)
+			fprintf(fp, "%s", vispath(path));
+
+#define MATCHFLAG(f)	((keys & (f)) && (cur->flags & (f)))
+		if (MATCHFLAG(F_TYPE))
+			appendfield(fp, pathlast, "type=%s",
+			    nodetype(cur->type));
+		if (MATCHFLAG(F_UID | F_UNAME)) {
+			if (keys & F_UNAME &&
+			    (name = user_from_uid(cur->st_uid, 1)) != NULL)
+				appendfield(fp, pathlast, "uname=%s", name);
+			else
+				appendfield(fp, pathlast, "uid=%u",
+				    cur->st_uid);
+		}
+		if (MATCHFLAG(F_GID | F_GNAME)) {
+			if (keys & F_GNAME &&
+			    (name = group_from_gid(cur->st_gid, 1)) != NULL)
+				appendfield(fp, pathlast, "gname=%s", name);
+			else
+				appendfield(fp, pathlast, "gid=%u",
+				    cur->st_gid);
+		}
+		if (MATCHFLAG(F_MODE))
+			appendfield(fp, pathlast, "mode=%#o", cur->st_mode);
+		if (MATCHFLAG(F_DEV) &&
+		    (cur->type == F_BLOCK || cur->type == F_CHAR))
+			appendfield(fp, pathlast, "device=%#jx",
+			    (uintmax_t)cur->st_rdev);
+		if (MATCHFLAG(F_NLINK))
+			appendfield(fp, pathlast, "nlink=%d", cur->st_nlink);
+		if (MATCHFLAG(F_SLINK))
+			appendfield(fp, pathlast, "link=%s",
+			    vispath(cur->slink));
+		if (MATCHFLAG(F_SIZE))
+			appendfield(fp, pathlast, "size=%ju",
+			    (uintmax_t)cur->st_size);
+		if (MATCHFLAG(F_TIME))
+			appendfield(fp, pathlast, "time=%jd.%09ld",
+			    (intmax_t)cur->st_mtimespec.tv_sec,
+			    cur->st_mtimespec.tv_nsec);
+		if (MATCHFLAG(F_CKSUM))
+			appendfield(fp, pathlast, "cksum=%lu", cur->cksum);
+		if (MATCHFLAG(F_MD5))
+			appendfield(fp, pathlast, "%s=%s", MD5KEY,
+			    cur->md5digest);
+		if (MATCHFLAG(F_RMD160))
+			appendfield(fp, pathlast, "%s=%s", RMD160KEY,
+			    cur->rmd160digest);
+		if (MATCHFLAG(F_SHA1))
+			appendfield(fp, pathlast, "%s=%s", SHA1KEY,
+			    cur->sha1digest);
+		if (MATCHFLAG(F_SHA256))
+			appendfield(fp, pathlast, "%s=%s", SHA256KEY,
+			    cur->sha256digest);
+		if (MATCHFLAG(F_SHA384))
+			appendfield(fp, pathlast, "%s=%s", SHA384KEY,
+			    cur->sha384digest);
+		if (MATCHFLAG(F_SHA512))
+			appendfield(fp, pathlast, "%s=%s", SHA512KEY,
+			    cur->sha512digest);
+		if (MATCHFLAG(F_FLAGS)) {
+			str = flags_to_string(cur->st_flags, "none");
+			appendfield(fp, pathlast, "flags=%s", str);
+			free(str);
+		}
+		if (MATCHFLAG(F_IGN))
+			appendfield(fp, pathlast, "ignore");
+		if (MATCHFLAG(F_OPT))
+			appendfield(fp, pathlast, "optional");
+		if (MATCHFLAG(F_TAGS)) {
+			/* don't output leading or trailing commas */
+			p = cur->tags;
+			while (*p == ',')
+				p++;
+			q = p + strlen(p);
+			while(q > p && q[-1] == ',')
+				q--;
+			appendfield(fp, pathlast, "tags=%.*s", (int)(q - p), p);
+		}
+		puts(pathlast ? vispath(path) : "");
+
+		if (cur->child)
+			dump_nodes(fp, path, cur->child, pathlast);
+	}
+}
+
+/*
+ * vispath --
+ *	strsvis(3) encodes path, which must not be longer than MAXPATHLEN
+ *	characters long, and returns a pointer to a static buffer containing
+ *	the result.
+ */
+char *
+vispath(const char *path)
+{
+	static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
+	static const char extra_glob[] = { ' ', '\t', '\n', '\\', '#', '*',
+	    '?', '[', '\0' };
+	static char pathbuf[4*MAXPATHLEN + 1];
+
+	if (flavor == F_NETBSD6)
+		strsvis(pathbuf, path, VIS_CSTYLE, extra);
+	else
+		strsvis(pathbuf, path, VIS_OCTAL, extra_glob);
+	return pathbuf;
+}
+
+
+static dev_t
+parsedev(char *arg)
+{
+#define MAX_PACK_ARGS	3
+	u_long	numbers[MAX_PACK_ARGS];
+	char	*p, *ep, *dev;
+	int	argc;
+	pack_t	*pack;
+	dev_t	result;
+	const char *error = NULL;
+
+	if ((dev = strchr(arg, ',')) != NULL) {
+		*dev++='\0';
+		if ((pack = pack_find(arg)) == NULL)
+			mtree_err("unknown format `%s'", arg);
+		argc = 0;
+		while ((p = strsep(&dev, ",")) != NULL) {
+			if (*p == '\0')
+				mtree_err("missing number");
+			numbers[argc++] = strtoul(p, &ep, 0);
+			if (*ep != '\0')
+				mtree_err("invalid number `%s'",
+				    p);
+			if (argc > MAX_PACK_ARGS)
+				mtree_err("too many arguments");
+		}
+		if (argc < 2)
+			mtree_err("not enough arguments");
+		result = (*pack)(argc, numbers, &error);
+		if (error != NULL)
+			mtree_err("%s", error);
+	} else {
+		result = (dev_t)strtoul(arg, &ep, 0);
+		if (*ep != '\0')
+			mtree_err("invalid device `%s'", arg);
+	}
+	return (result);
+}
+
+static void
+replacenode(NODE *cur, NODE *new)
+{
+
+#define REPLACE(x)	cur->x = new->x
+#define REPLACESTR(x)	REPLACEPTR(cur->x,new->x)
+
+	if (cur->type != new->type) {
+		if (mtree_Mflag) {
+				/*
+				 * merge entries with different types; we
+				 * don't want children retained in this case.
+				 */
+			REPLACE(type);
+			free_nodes(cur->child);
+			cur->child = NULL;
+		} else {
+			mtree_err(
+			    "existing entry for `%s', type `%s'"
+			    " does not match type `%s'",
+			    cur->name, nodetype(cur->type),
+			    nodetype(new->type));
+		}
+	}
+
+	REPLACE(st_size);
+	REPLACE(st_mtimespec);
+	REPLACESTR(slink);
+	if (cur->slink != NULL) {
+		if ((cur->slink = strdup(new->slink)) == NULL)
+			mtree_err("memory allocation error");
+		if (strunvis(cur->slink, new->slink) == -1)
+			mtree_err("strunvis failed on `%s'", new->slink);
+		free(new->slink);
+	}
+	REPLACE(st_uid);
+	REPLACE(st_gid);
+	REPLACE(st_mode);
+	REPLACE(st_rdev);
+	REPLACE(st_flags);
+	REPLACE(st_nlink);
+	REPLACE(cksum);
+	REPLACESTR(md5digest);
+	REPLACESTR(rmd160digest);
+	REPLACESTR(sha1digest);
+	REPLACESTR(sha256digest);
+	REPLACESTR(sha384digest);
+	REPLACESTR(sha512digest);
+	REPLACESTR(tags);
+	REPLACE(lineno);
+	REPLACE(flags);
+	free(new);
+}
+
+static void
+set(char *t, NODE *ip)
+{
+	int	type, value, len;
+	gid_t	gid;
+	uid_t	uid;
+	char	*kw, *val, *md, *ep;
+	void	*m;
+
+	while ((kw = strsep(&t, "= \t")) != NULL) {
+		if (*kw == '\0')
+			continue;
+		if (strcmp(kw, "all") == 0)
+			mtree_err("invalid keyword `all'");
+		ip->flags |= type = parsekey(kw, &value);
+		if (!value)
+			/* Just set flag bit (F_IGN and F_OPT) */
+			continue;
+		while ((val = strsep(&t, " \t")) != NULL && *val == '\0')
+			continue;
+		if (val == NULL)
+			mtree_err("missing value");
+		switch (type) {
+		case F_CKSUM:
+			ip->cksum = strtoul(val, &ep, 10);
+			if (*ep)
+				mtree_err("invalid checksum `%s'", val);
+			break;
+		case F_DEV:
+			ip->st_rdev = parsedev(val);
+			break;
+		case F_FLAGS:
+			if (strcmp("none", val) == 0)
+				ip->st_flags = 0;
+			else if (string_to_flags(&val, &ip->st_flags, NULL)
+			    != 0)
+				mtree_err("invalid flag `%s'", val);
+			break;
+		case F_GID:
+			ip->st_gid = (gid_t)strtoul(val, &ep, 10);
+			if (*ep)
+				mtree_err("invalid gid `%s'", val);
+			break;
+		case F_GNAME:
+			if (mtree_Wflag)	/* don't parse if whacking */
+				break;
+			if (gid_from_group(val, &gid) == -1)
+				mtree_err("unknown group `%s'", val);
+			ip->st_gid = gid;
+			break;
+		case F_MD5:
+			if (val[0]=='0' && val[1]=='x')
+				md=&val[2];
+			else
+				md=val;
+			if ((ip->md5digest = strdup(md)) == NULL)
+				mtree_err("memory allocation error");
+			break;
+		case F_MODE:
+			if ((m = setmode(val)) == NULL)
+				mtree_err("cannot set file mode `%s' (%s)",
+				    val, strerror(errno));
+			ip->st_mode = getmode(m, 0);
+			free(m);
+			break;
+		case F_NLINK:
+			ip->st_nlink = (nlink_t)strtoul(val, &ep, 10);
+			if (*ep)
+				mtree_err("invalid link count `%s'", val);
+			break;
+		case F_RMD160:
+			if (val[0]=='0' && val[1]=='x')
+				md=&val[2];
+			else
+				md=val;
+			if ((ip->rmd160digest = strdup(md)) == NULL)
+				mtree_err("memory allocation error");
+			break;
+		case F_SHA1:
+			if (val[0]=='0' && val[1]=='x')
+				md=&val[2];
+			else
+				md=val;
+			if ((ip->sha1digest = strdup(md)) == NULL)
+				mtree_err("memory allocation error");
+			break;
+		case F_SIZE:
+			ip->st_size = (off_t)strtoll(val, &ep, 10);
+			if (*ep)
+				mtree_err("invalid size `%s'", val);
+			break;
+		case F_SLINK:
+			if ((ip->slink = strdup(val)) == NULL)
+				mtree_err("memory allocation error");
+			if (strunvis(ip->slink, val) == -1)
+				mtree_err("strunvis failed on `%s'", val);
+			break;
+		case F_TAGS:
+			len = strlen(val) + 3;	/* "," + str + ",\0" */
+			if ((ip->tags = malloc(len)) == NULL)
+				mtree_err("memory allocation error");
+			snprintf(ip->tags, len, ",%s,", val);
+			break;
+		case F_TIME:
+			ip->st_mtimespec.tv_sec =
+			    (time_t)strtoll(val, &ep, 10);
+			if (*ep != '.')
+				mtree_err("invalid time `%s'", val);
+			val = ep + 1;
+			ip->st_mtimespec.tv_nsec = strtol(val, &ep, 10);
+			if (*ep)
+				mtree_err("invalid time `%s'", val);
+			break;
+		case F_TYPE:
+			ip->type = parsetype(val);
+			break;
+		case F_UID:
+			ip->st_uid = (uid_t)strtoul(val, &ep, 10);
+			if (*ep)
+				mtree_err("invalid uid `%s'", val);
+			break;
+		case F_UNAME:
+			if (mtree_Wflag)	/* don't parse if whacking */
+				break;
+			if (uid_from_user(val, &uid) == -1)
+				mtree_err("unknown user `%s'", val);
+			ip->st_uid = uid;
+			break;
+		case F_SHA256:
+			if (val[0]=='0' && val[1]=='x')
+				md=&val[2];
+			else
+				md=val;
+			if ((ip->sha256digest = strdup(md)) == NULL)
+				mtree_err("memory allocation error");
+			break;
+		case F_SHA384:
+			if (val[0]=='0' && val[1]=='x')
+				md=&val[2];
+			else
+				md=val;
+			if ((ip->sha384digest = strdup(md)) == NULL)
+				mtree_err("memory allocation error");
+			break;
+		case F_SHA512:
+			if (val[0]=='0' && val[1]=='x')
+				md=&val[2];
+			else
+				md=val;
+			if ((ip->sha512digest = strdup(md)) == NULL)
+				mtree_err("memory allocation error");
+			break;
+		default:
+			mtree_err(
+			    "set(): unsupported key type 0x%x (INTERNAL ERROR)",
+			    type);
+			/* NOTREACHED */
+		}
+	}
+}
+
+static void
+unset(char *t, NODE *ip)
+{
+	char *p;
+
+	while ((p = strsep(&t, " \t")) != NULL) {
+		if (*p == '\0')
+			continue;
+		ip->flags &= ~parsekey(p, NULL);
+	}
+}
+
+/*
+ * addchild --
+ *	Add the centry node as a child of the pathparent node.	If
+ *	centry is a duplicate, call replacenode().  If centry is not
+ *	a duplicate, insert it into the linked list referenced by
+ *	pathparent->child.  Keep the list sorted if Sflag is set.
+ */
+static void
+addchild(NODE *pathparent, NODE *centry)
+{
+	NODE *samename;      /* node with the same name as centry */
+	NODE *replacepos;    /* if non-NULL, centry should replace this node */
+	NODE *insertpos;     /* if non-NULL, centry should be inserted
+			      * after this node */
+	NODE *cur;           /* for stepping through the list */
+	NODE *last;          /* the last node in the list */
+	int cmp;
+
+	samename = NULL;
+	replacepos = NULL;
+	insertpos = NULL;
+	last = NULL;
+	cur = pathparent->child;
+	if (cur == NULL) {
+		/* centry is pathparent's first and only child node so far */
+		pathparent->child = centry;
+		return;
+	}
+
+	/*
+	 * pathparent already has at least one other child, so add the
+	 * centry node to the list.
+	 *
+	 * We first scan through the list looking for an existing node
+	 * with the same name (setting samename), and also looking
+	 * for the correct position to replace or insert the new node
+	 * (setting replacepos and/or insertpos).
+	 */
+	for (; cur != NULL; last = cur, cur = cur->next) {
+		if (strcmp(centry->name, cur->name) == 0) {
+			samename = cur;
+		}
+		if (mtree_Sflag) {
+			cmp = nodecmp(centry, cur);
+			if (cmp == 0) {
+				replacepos = cur;
+			} else if (cmp > 0) {
+				insertpos = cur;
+			}
+		}
+	}
+	if (! mtree_Sflag) {
+		if (samename != NULL) {
+			/* replace node with same name */
+			replacepos = samename;
+		} else {
+			/* add new node at end of list */
+			insertpos = last;
+		}
+	}
+
+	if (samename != NULL) {
+		/*
+		 * We found a node with the same name above.  Call
+		 * replacenode(), which will either exit with an error,
+		 * or replace the information in the samename node and
+		 * free the information in the centry node.
+		 */
+		replacenode(samename, centry);
+		if (samename == replacepos) {
+			/* The just-replaced node was in the correct position */
+			return;
+		}
+		if (samename == insertpos || samename->prev == insertpos) {
+			/*
+			 * We thought the new node should be just before
+			 * or just after the replaced node, but that would
+			 * be equivalent to just retaining the replaced node.
+			 */
+			return;
+		}
+
+		/*
+		 * The just-replaced node is in the wrong position in
+		 * the list.  This can happen if sort order depends on
+		 * criteria other than the node name.
+		 *
+		 * Make centry point to the just-replaced node.	 Unlink
+		 * the just-replaced node from the list, and allow it to
+		 * be insterted in the correct position later.
+		 */
+		centry = samename;
+		if (centry->prev)
+			centry->prev->next = centry->next;
+		else {
+			/* centry->next is the new head of the list */
+			pathparent->child = centry->next;
+			assert(centry->next != NULL);
+		}
+		if (centry->next)
+			centry->next->prev = centry->prev;
+		centry->prev = NULL;
+		centry->next = NULL;
+	}
+
+	if (insertpos == NULL) {
+		/* insert centry at the beginning of the list */
+		pathparent->child->prev = centry;
+		centry->next = pathparent->child;
+		centry->prev = NULL;
+		pathparent->child = centry;
+	} else {
+		/* insert centry into the list just after insertpos */
+		centry->next = insertpos->next;
+		insertpos->next = centry;
+		centry->prev = insertpos;
+		if (centry->next)
+			centry->next->prev = centry;
+	}
+	return;
+}
+
+/*
+ * nodecmp --
+ *	used as a comparison function by addchild() to control the order
+ *	in which entries appear within a list of sibling nodes.	 We make
+ *	directories sort after non-directories, but otherwise sort in
+ *	strcmp() order.
+ *
+ * Keep this in sync with dcmp() in create.c.
+ */
+static int
+nodecmp(const NODE *a, const NODE *b)
+{
+
+	if ((a->type & F_DIR) != 0) {
+		if ((b->type & F_DIR) == 0)
+			return 1;
+	} else if ((b->type & F_DIR) != 0)
+		return -1;
+	return strcmp(a->name, b->name);
+}
diff --git a/contrib/mtree/specspec.c b/contrib/mtree/specspec.c
new file mode 100644
index 0000000000..2821fd1d8b
--- /dev/null
+++ b/contrib/mtree/specspec.c
@@ -0,0 +1,273 @@
+/*	$NetBSD: specspec.c,v 1.2 2012/10/05 01:27:29 christos Exp $	*/
+
+/*-
+ * Copyright (c) 2003 Poul-Henning Kamp
+ * 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 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 AUTHOR 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: specspec.c,v 1.2 2012/10/05 01:27:29 christos Exp $");
+
+#include <err.h>
+#include <grp.h>
+#include <pwd.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "mtree.h"
+#include "extern.h"
+
+#define FF(a, b, c, d) \
+	(((a)->flags & (c)) && ((b)->flags & (c)) && ((a)->d) != ((b)->d))
+#define FS(a, b, c, d) \
+	(((a)->flags & (c)) && ((b)->flags & (c)) && strcmp((a)->d,(b)->d))
+#define FM(a, b, c, d) \
+	(((a)->flags & (c)) && ((b)->flags & (c)) && memcmp(&(a)->d,&(b)->d, sizeof (a)->d))
+
+static void
+shownode(NODE *n, int f, char const *path)
+{
+	struct group *gr;
+	struct passwd *pw;
+
+	printf("%s%s %s", path, n->name, inotype(nodetoino(n->type)));
+	if (f & F_CKSUM)
+		printf(" cksum=%lu", n->cksum);
+	if (f & F_GID)
+		printf(" gid=%d", n->st_gid);
+	if (f & F_GNAME) {
+		gr = getgrgid(n->st_gid);
+		if (gr == NULL)
+			printf(" gid=%d", n->st_gid);
+		else
+			printf(" gname=%s", gr->gr_name);
+	}
+	if (f & F_MODE)
+		printf(" mode=%o", n->st_mode);
+	if (f & F_NLINK)
+		printf(" nlink=%d", n->st_nlink);
+	if (f & F_SIZE)
+		printf(" size=%jd", (intmax_t)n->st_size);
+	if (f & F_UID)
+		printf(" uid=%d", n->st_uid);
+	if (f & F_UNAME) {
+		pw = getpwuid(n->st_uid);
+		if (pw == NULL)
+			printf(" uid=%d", n->st_uid);
+		else
+			printf(" uname=%s", pw->pw_name);
+	}
+	if (f & F_MD5)
+		printf(" %s=%s", MD5KEY, n->md5digest);
+	if (f & F_SHA1)
+		printf(" %s=%s", SHA1KEY, n->sha1digest);
+	if (f & F_RMD160)
+		printf(" %s=%s", RMD160KEY, n->rmd160digest);
+	if (f & F_SHA256)
+		printf(" %s=%s", SHA256KEY, n->sha256digest);
+	if (f & F_SHA384)
+		printf(" %s=%s", SHA384KEY, n->sha384digest);
+	if (f & F_SHA512)
+		printf(" %s=%s", SHA512KEY, n->sha512digest);
+	if (f & F_FLAGS)
+		printf(" flags=%s", flags_to_string(n->st_flags, "none"));
+	printf("\n");
+}
+
+static int
+mismatch(NODE *n1, NODE *n2, int differ, char const *path)
+{
+
+	if (n2 == NULL) {
+		shownode(n1, differ, path);
+		return (1);
+	}
+	if (n1 == NULL) {
+		printf("\t");
+		shownode(n2, differ, path);
+		return (1);
+	}
+	if (!(differ & keys))
+		return(0);
+	printf("\t\t");
+	shownode(n1, differ, path);
+	printf("\t\t");
+	shownode(n2, differ, path);
+	return (1);
+}
+
+static int
+compare_nodes(NODE *n1, NODE *n2, char const *path)
+{
+	int differs;
+	
+	if (n1 != NULL && n1->type == F_LINK)
+		n1->flags &= ~F_MODE;
+	if (n2 != NULL && n2->type == F_LINK)
+		n2->flags &= ~F_MODE;
+	differs = 0;
+	if (n1 == NULL && n2 != NULL) {
+		differs = n2->flags;
+		mismatch(n1, n2, differs, path);
+		return (1);
+	}
+	if (n1 != NULL && n2 == NULL) {
+		differs = n1->flags;
+		mismatch(n1, n2, differs, path);
+		return (1);
+	}
+	if (n1->type != n2->type) {
+		differs = 0;
+		mismatch(n1, n2, differs, path);
+		return (1);
+	}
+	if (FF(n1, n2, F_CKSUM, cksum))
+		differs |= F_CKSUM;
+	if (FF(n1, n2, F_GID, st_gid))
+		differs |= F_GID;
+	if (FF(n1, n2, F_GNAME, st_gid))
+		differs |= F_GNAME;
+	if (FF(n1, n2, F_MODE, st_mode))
+		differs |= F_MODE;
+	if (FF(n1, n2, F_NLINK, st_nlink))
+		differs |= F_NLINK;
+	if (FF(n1, n2, F_SIZE, st_size))
+		differs |= F_SIZE;
+	if (FS(n1, n2, F_SLINK, slink))
+		differs |= F_SLINK;
+	if (FM(n1, n2, F_TIME, st_mtimespec))
+		differs |= F_TIME;
+	if (FF(n1, n2, F_UID, st_uid))
+		differs |= F_UID;
+	if (FF(n1, n2, F_UNAME, st_uid))
+		differs |= F_UNAME;
+	if (FS(n1, n2, F_MD5, md5digest))
+		differs |= F_MD5;
+	if (FS(n1, n2, F_SHA1, sha1digest))
+		differs |= F_SHA1;
+	if (FS(n1, n2, F_RMD160, rmd160digest))
+		differs |= F_RMD160;
+	if (FS(n1, n2, F_SHA256, sha256digest))
+		differs |= F_SHA256;
+	if (FS(n1, n2, F_SHA384, sha384digest))
+		differs |= F_SHA384;
+	if (FS(n1, n2, F_SHA512, sha512digest))
+		differs |= F_SHA512;
+	if (FF(n1, n2, F_FLAGS, st_flags))
+		differs |= F_FLAGS;
+	if (differs) {
+		mismatch(n1, n2, differs, path);
+		return (1);
+	}
+	return (0);	
+}
+static int
+walk_in_the_forest(NODE *t1, NODE *t2, char const *path)
+{
+	int r, i;
+	NODE *c1, *c2, *n1, *n2;
+	char *np;
+
+	r = 0;
+
+	if (t1 != NULL)
+		c1 = t1->child;
+	else
+		c1 = NULL;
+	if (t2 != NULL)
+		c2 = t2->child;
+	else
+		c2 = NULL;
+	while (c1 != NULL || c2 != NULL) {
+		n1 = n2 = NULL;
+		if (c1 != NULL)
+			n1 = c1->next;
+		if (c2 != NULL)
+			n2 = c2->next;
+		if (c1 != NULL && c2 != NULL) {
+			if (c1->type != F_DIR && c2->type == F_DIR) {
+				n2 = c2;
+				c2 = NULL;
+			} else if (c1->type == F_DIR && c2->type != F_DIR) {
+				n1 = c1;
+				c1 = NULL;
+			} else {
+				i = strcmp(c1->name, c2->name);
+				if (i > 0) {
+					n1 = c1;
+					c1 = NULL;
+				} else if (i < 0) {
+					n2 = c2;
+					c2 = NULL;
+				}
+			}
+		}
+		if (c1 == NULL && c2->type == F_DIR) {
+			asprintf(&np, "%s%s/", path, c2->name);
+			i = walk_in_the_forest(c1, c2, np);
+			free(np);
+			i += compare_nodes(c1, c2, path);
+		} else if (c2 == NULL && c1->type == F_DIR) {
+			asprintf(&np, "%s%s/", path, c1->name);
+			i = walk_in_the_forest(c1, c2, np);
+			free(np);
+			i += compare_nodes(c1, c2, path);
+		} else if (c1 == NULL || c2 == NULL) {
+			i = compare_nodes(c1, c2, path);
+		} else if (c1->type == F_DIR && c2->type == F_DIR) {
+			asprintf(&np, "%s%s/", path, c1->name);
+			i = walk_in_the_forest(c1, c2, np);
+			free(np);
+			i += compare_nodes(c1, c2, path);
+		} else {
+			i = compare_nodes(c1, c2, path);
+		}
+		r += i;
+		c1 = n1;
+		c2 = n2;
+	}
+	return (r);	
+}
+
+int
+mtree_specspec(FILE *fi, FILE *fj)
+{
+	int rval;
+	NODE *root1, *root2;
+
+	root1 = spec(fi);
+	root2 = spec(fj);
+	rval = walk_in_the_forest(root1, root2, "");
+	rval += compare_nodes(root1, root2, "");
+	if (rval > 0)
+		return (MISMATCHEXIT);
+	return (0);
+}
diff --git a/contrib/mtree/verify.c b/contrib/mtree/verify.c
new file mode 100644
index 0000000000..66b020ac8b
--- /dev/null
+++ b/contrib/mtree/verify.c
@@ -0,0 +1,307 @@
+/*	$NetBSD: verify.c,v 1.44 2013/02/03 19:15:17 christos Exp $	*/
+
+/*-
+ * 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.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)verify.c	8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: verify.c,v 1.44 2013/02/03 19:15:17 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#if ! HAVE_NBTOOL_CONFIG_H
+#include <dirent.h>
+#endif
+
+#include <errno.h>
+#include <fnmatch.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static NODE *root;
+static char path[MAXPATHLEN];
+
+static void	miss(NODE *, char *);
+static int	vwalk(void);
+
+int
+verify(FILE *fi)
+{
+	int rval;
+
+	root = spec(fi);
+	rval = vwalk();
+	miss(root, path);
+	return (rval);
+}
+
+static int
+vwalk(void)
+{
+	FTS *t;
+	FTSENT *p;
+	NODE *ep, *level;
+	int specdepth, rval;
+	char *argv[2];
+	char  dot[] = ".";
+	argv[0] = dot;
+	argv[1] = NULL;
+
+	if ((t = fts_open(argv, ftsoptions, NULL)) == NULL)
+		mtree_err("fts_open: %s", strerror(errno));
+	level = root;
+	specdepth = rval = 0;
+	while ((p = fts_read(t)) != NULL) {
+		if (check_excludes(p->fts_name, p->fts_path)) {
+			fts_set(t, p, FTS_SKIP);
+			continue;
+		}
+		if (!find_only(p->fts_path)) {
+			fts_set(t, p, FTS_SKIP);
+			continue;
+		}
+		switch(p->fts_info) {
+		case FTS_D:
+		case FTS_SL:
+			break;
+		case FTS_DP:
+			if (specdepth > p->fts_level) {
+				for (level = level->parent; level->prev;
+				    level = level->prev)
+					continue;
+				--specdepth;
+			}
+			continue;
+		case FTS_DNR:
+		case FTS_ERR:
+		case FTS_NS:
+			warnx("%s: %s", RP(p), strerror(p->fts_errno));
+			continue;
+		default:
+			if (dflag)
+				continue;
+		}
+
+		if (specdepth != p->fts_level)
+			goto extra;
+		for (ep = level; ep; ep = ep->next)
+			if ((ep->flags & F_MAGIC &&
+			    !fnmatch(ep->name, p->fts_name, FNM_PATHNAME)) ||
+			    !strcmp(ep->name, p->fts_name)) {
+				ep->flags |= F_VISIT;
+				if ((ep->flags & F_NOCHANGE) == 0 &&
+				    compare(ep, p))
+					rval = MISMATCHEXIT;
+				if (!(ep->flags & F_IGN) &&
+				    ep->type == F_DIR &&
+				    p->fts_info == FTS_D) {
+					if (ep->child) {
+						level = ep->child;
+						++specdepth;
+					}
+				} else
+					fts_set(t, p, FTS_SKIP);
+				break;
+			}
+
+		if (ep)
+			continue;
+ extra:
+		if (!eflag && !(dflag && p->fts_info == FTS_SL)) {
+			printf("extra: %s", RP(p));
+			if (rflag) {
+				if ((S_ISDIR(p->fts_statp->st_mode)
+				    ? rmdir : unlink)(p->fts_accpath)) {
+					printf(", not removed: %s",
+					    strerror(errno));
+				} else
+					printf(", removed");
+			}
+			putchar('\n');
+		}
+		fts_set(t, p, FTS_SKIP);
+	}
+	fts_close(t);
+	if (sflag)
+		warnx("%s checksum: %u", fullpath, crc_total);
+	return (rval);
+}
+
+static void
+miss(NODE *p, char *tail)
+{
+	int create;
+	char *tp;
+	const char *type;
+	u_int32_t flags;
+
+	for (; p; p = p->next) {
+		if (p->flags & F_OPT && !(p->flags & F_VISIT))
+			continue;
+		if (p->type != F_DIR && (dflag || p->flags & F_VISIT))
+			continue;
+		strcpy(tail, p->name);
+		if (!(p->flags & F_VISIT)) {
+			/* Don't print missing message if file exists as a 
+			   symbolic link and the -q flag is set. */
+			struct stat statbuf;
+
+			if (qflag && stat(path, &statbuf) == 0 &&
+			    S_ISDIR(statbuf.st_mode))
+				p->flags |= F_VISIT;
+			else
+				(void)printf("%s missing", path);
+		}
+		switch (p->type) {
+		case F_BLOCK:
+		case F_CHAR:
+			type = "device";
+			break;
+		case F_DIR:
+			type = "directory";
+			break;
+		case F_LINK:
+			type = "symlink";
+			break;
+		default:
+			putchar('\n');
+			continue;
+		}
+
+		create = 0;
+		if (!(p->flags & F_VISIT) && uflag) {
+			if (mtree_Wflag || p->type == F_LINK)
+				goto createit;
+			if (!(p->flags & (F_UID | F_UNAME)))
+			    printf(
+				" (%s not created: user not specified)", type);
+			else if (!(p->flags & (F_GID | F_GNAME)))
+			    printf(
+				" (%s not created: group not specified)", type);
+			else if (!(p->flags & F_MODE))
+			    printf(
+				" (%s not created: mode not specified)", type);
+			else
+ createit:
+			switch (p->type) {
+			case F_BLOCK:
+			case F_CHAR:
+				if (mtree_Wflag)
+					continue;
+				if (!(p->flags & F_DEV))
+					printf(
+				    " (%s not created: device not specified)",
+					    type);
+				else if (mknod(path,
+				    p->st_mode | nodetoino(p->type),
+				    p->st_rdev) == -1)
+					printf(" (%s not created: %s)\n",
+					    type, strerror(errno));
+				else
+					create = 1;
+				break;
+			case F_LINK:
+				if (!(p->flags & F_SLINK))
+					printf(
+				    " (%s not created: link not specified)\n",
+					    type);
+				else if (symlink(p->slink, path))
+					printf(
+					    " (%s not created: %s)\n",
+					    type, strerror(errno));
+				else
+					create = 1;
+				break;
+			case F_DIR:
+				if (mkdir(path, S_IRWXU|S_IRWXG|S_IRWXO))
+					printf(" (not created: %s)",
+					    strerror(errno));
+				else
+					create = 1;
+				break;
+			default:
+				mtree_err("can't create create %s",
+				    nodetype(p->type));
+			}
+		}
+		if (create)
+			printf(" (created)");
+		if (p->type == F_DIR) {
+			if (!(p->flags & F_VISIT))
+				putchar('\n');
+			for (tp = tail; *tp; ++tp)
+				continue;
+			*tp = '/';
+			miss(p->child, tp + 1);
+			*tp = '\0';
+		} else
+			putchar('\n');
+
+		if (!create || mtree_Wflag)
+			continue;
+		if ((p->flags & (F_UID | F_UNAME)) &&
+		    (p->flags & (F_GID | F_GNAME)) &&
+		    (lchown(path, p->st_uid, p->st_gid))) {
+			printf("%s: user/group/mode not modified: %s\n",
+			    path, strerror(errno));
+			printf("%s: warning: file mode %snot set\n", path,
+			    (p->flags & F_FLAGS) ? "and file flags " : "");
+			continue;
+		}
+		if (p->flags & F_MODE) {
+			if (lchmod(path, p->st_mode))
+				printf("%s: permissions not set: %s\n",
+				    path, strerror(errno));
+		}
+#if HAVE_STRUCT_STAT_ST_FLAGS
+		if ((p->flags & F_FLAGS) && p->st_flags) {
+			if (iflag)
+				flags = p->st_flags;
+			else
+				flags = p->st_flags & ~SP_FLGS;
+			if (lchflags(path, flags))
+				printf("%s: file flags not set: %s\n",
+				    path, strerror(errno));
+		}
+#endif	/* HAVE_STRUCT_STAT_ST_FLAGS */
+	}
+}
diff --git a/lib/Makefile b/lib/Makefile
index c021ac135c..fa0e2ffe6f 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -79,6 +79,7 @@ SUBDIR=	${SUBDIR_ORDERED} \
 	libm \
 	libmagic \
 	${_libnetgraph} \
+	libnetbsd \
 	libopie \
 	libpam \
 	libpcap \
diff --git a/lib/libnetbsd/Makefile b/lib/libnetbsd/Makefile
new file mode 100644
index 0000000000..2ee009569a
--- /dev/null
+++ b/lib/libnetbsd/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD: head/lib/libnetbsd/Makefile 244557 2012-12-21 20:37:38Z brooks $
+
+.include <bsd.own.mk>
+
+LIB=	netbsd
+
+CFLAGS+=	-I${.CURDIR}
+
+SRCS+=	strsuftoll.c util.c util.h
+
+INTERNALLIB=
+
+.include <bsd.lib.mk>
diff --git a/lib/libnetbsd/Makefile.depend b/lib/libnetbsd/Makefile.depend
new file mode 100644
index 0000000000..cf7e2a133e
--- /dev/null
+++ b/lib/libnetbsd/Makefile.depend
@@ -0,0 +1,14 @@
+# $FreeBSD: head/lib/libnetbsd/Makefile.depend 284345 2015-06-13 19:20:56Z sjg $
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+	include \
+	include/xlocale \
+	lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libnetbsd/README b/lib/libnetbsd/README
new file mode 100644
index 0000000000..c73c8a942e
--- /dev/null
+++ b/lib/libnetbsd/README
@@ -0,0 +1,7 @@
+$FreeBSD: head/lib/libnetbsd/README 290378 2015-11-05 01:54:38Z rodrigc $
+
+libnetbsd is a thin compatibility layer intended to allow a limited
+set of NetBSD software to compile as part of the FreeBSD build with
+little or no modification.  It is built as a static library and not
+installed for general use.  Likewise, its header files are not
+installed.
diff --git a/lib/libnetbsd/netinet/in.h b/lib/libnetbsd/netinet/in.h
new file mode 100644
index 0000000000..1a77fabdc2
--- /dev/null
+++ b/lib/libnetbsd/netinet/in.h
@@ -0,0 +1,72 @@
+/* $FreeBSD: head/lib/libnetbsd/netinet/in.h 273482 2014-10-22 21:04:54Z ngie $ */
+
+/*
+ * Copyright (c) 1982, 1986, 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.
+ *
+ *	@(#)in.h	8.3 (Berkeley) 1/3/94
+ */
+
+#ifndef _LIBNETBSD_NETINET_IN_H_
+#define _LIBNETBSD_NETINET_IN_H_
+
+#include_next <netinet/in.h>
+
+/*
+ * Local port number conventions:
+ *
+ * Ports < IPPORT_RESERVED are reserved for privileged processes (e.g. root),
+ * unless a kernel is compiled with IPNOPRIVPORTS defined.
+ *
+ * When a user does a bind(2) or connect(2) with a port number of zero,
+ * a non-conflicting local port address is chosen.
+ *
+ * The default range is IPPORT_ANONMIN to IPPORT_ANONMAX, although
+ * that is settable by sysctl(3); net.inet.ip.anonportmin and
+ * net.inet.ip.anonportmax respectively.
+ *
+ * A user may set the IPPROTO_IP option IP_PORTRANGE to change this
+ * default assignment range.
+ *
+ * The value IP_PORTRANGE_DEFAULT causes the default behavior.
+ *
+ * The value IP_PORTRANGE_HIGH is the same as IP_PORTRANGE_DEFAULT,
+ * and exists only for FreeBSD compatibility purposes.
+ *
+ * The value IP_PORTRANGE_LOW changes the range to the "low" are
+ * that is (by convention) restricted to privileged processes.
+ * This convention is based on "vouchsafe" principles only.
+ * It is only secure if you trust the remote host to restrict these ports.
+ * The range is IPPORT_RESERVEDMIN to IPPORT_RESERVEDMAX.
+ */
+
+#define	IPPORT_ANONMIN		49152
+#define	IPPORT_ANONMAX		65535
+#define	IPPORT_RESERVEDMIN	600
+#define	IPPORT_RESERVEDMAX	(IPPORT_RESERVED-1)
+
+#endif
diff --git a/lib/libnetbsd/rmd160.h b/lib/libnetbsd/rmd160.h
new file mode 100644
index 0000000000..4a850c84cb
--- /dev/null
+++ b/lib/libnetbsd/rmd160.h
@@ -0,0 +1,44 @@
+/*	$FreeBSD: head/lib/libnetbsd/rmd160.h 244557 2012-12-21 20:37:38Z brooks $	 */
+
+/*-
+ * Copyright (c) 2012 SRI International
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 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 AUTHOR 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.
+ */
+
+#ifndef _RMD160_H_
+#define _RMD160_H_
+
+#include <ripemd.h>
+
+#define RMD160_CTX	RIPEMD160_CTX
+#define	RMD160End	RIPEMD160_End
+#define	RMD160File	RIPEMD160_File
+#define	RMD160Init	RIPEMD160_Init
+#define	RMD160Update	RIPEMD160_Update
+
+#endif /* _RMD160_H_ */
diff --git a/lib/libnetbsd/sha1.h b/lib/libnetbsd/sha1.h
new file mode 100644
index 0000000000..d3310aaa2f
--- /dev/null
+++ b/lib/libnetbsd/sha1.h
@@ -0,0 +1,43 @@
+/*	$FreeBSD: head/lib/libnetbsd/sha1.h 244557 2012-12-21 20:37:38Z brooks $	*/
+
+/*-
+ * Copyright (c) 2012 SRI International
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 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 AUTHOR 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.
+ */
+
+#ifndef _SHA1_H_
+#define _SHA1_H_
+
+#include <sha.h>
+
+#define	SHA1End		SHA1_End
+#define	SHA1File	SHA1_File
+#define	SHA1Init	SHA1_Init
+#define	SHA1Update	SHA1_Update
+
+#endif /* _SHA1_H_ */
diff --git a/lib/libnetbsd/sha2.h b/lib/libnetbsd/sha2.h
new file mode 100644
index 0000000000..5ca9adead2
--- /dev/null
+++ b/lib/libnetbsd/sha2.h
@@ -0,0 +1,39 @@
+/*	$FreeBSD: head/lib/libnetbsd/sha2.h 244557 2012-12-21 20:37:38Z brooks $	*/
+
+/*-
+ * Copyright (c) 2012 SRI International
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 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 AUTHOR 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.
+ */
+
+#ifndef _SHA2_H_
+#define _SHA2_H_
+
+#include <sha256.h>
+#include <sha512.h>
+
+#endif /* _SHA2_H_ */
diff --git a/lib/libnetbsd/stdlib.h b/lib/libnetbsd/stdlib.h
new file mode 100644
index 0000000000..985b3aae7a
--- /dev/null
+++ b/lib/libnetbsd/stdlib.h
@@ -0,0 +1,70 @@
+/* $FreeBSD: head/lib/libnetbsd/stdlib.h 244557 2012-12-21 20:37:38Z brooks $ */
+
+/*-
+ * Copyright (c) 2012 SRI International
+ * Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+/*-
+ * Copyright (c) 1992, 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.
+ */
+
+#ifndef _LIBNETBSD_STDLIB_H_
+#define _LIBNETBSD_STDLIB_H_
+
+#include_next <stdlib.h>
+
+long long strsuftoll(const char *, const char *, long long, long long);
+long long strsuftollx(const char *, const char *,
+    long long, long long, char *, size_t);
+
+#endif /* _LIBNETBSD_STDLIB_H_ */
diff --git a/lib/libnetbsd/strsuftoll.c b/lib/libnetbsd/strsuftoll.c
new file mode 100644
index 0000000000..4e9d53ed13
--- /dev/null
+++ b/lib/libnetbsd/strsuftoll.c
@@ -0,0 +1,222 @@
+/*	$NetBSD: strsuftoll.c,v 1.6 2004/03/05 05:58:29 lukem Exp $	*/
+/*-
+ * Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * 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>
+__FBSDID("$FreeBSD: head/lib/libnetbsd/strsuftoll.c 244557 2012-12-21 20:37:38Z brooks $");
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _LIBC
+# ifdef __weak_alias
+__weak_alias(strsuftoll, _strsuftoll)
+__weak_alias(strsuftollx, _strsuftollx)
+# endif
+#endif /* LIBC */
+
+/*
+ * Convert an expression of the following forms to a (u)int64_t.
+ * 	1) A positive decimal number.
+ *	2) A positive decimal number followed by a b (mult by 512).
+ *	3) A positive decimal number followed by a k (mult by 1024).
+ *	4) A positive decimal number followed by a m (mult by 1048576).
+ *	5) A positive decimal number followed by a g (mult by 1073741824).
+ *	6) A positive decimal number followed by a t (mult by 1099511627776).
+ *	7) A positive decimal number followed by a w (mult by sizeof int)
+ *	8) Two or more positive decimal numbers (with/without k,b or w).
+ *	   separated by x (also * for backwards compatibility), specifying
+ *	   the product of the indicated values.
+ * Returns the result upon successful conversion, or exits with an
+ * appropriate error.
+ * 
+ */
+
+/*
+ * As strsuftoll(), but returns the error message into the provided buffer
+ * rather than exiting with it.
+ */
+/* LONGLONG */
+long long
+strsuftollx(const char *desc, const char *val,
+    long long min, long long max, char *ebuf, size_t ebuflen)
+{
+	long long num, t;
+	char	*expr;
+
+	errno = 0;
+	ebuf[0] = '\0';
+
+	while (isspace((unsigned char)*val))	/* Skip leading space */
+		val++;
+
+	num = strtoll(val, &expr, 10);
+	if (errno == ERANGE)
+		goto erange;			/* Overflow */
+
+	if (expr == val)			/* No digits */
+		goto badnum;
+
+	switch (*expr) {
+	case 'b':
+		t = num;
+		num *= 512;			/* 1 block */
+		if (t > num)
+			goto erange;
+		++expr;
+		break;
+	case 'k':
+		t = num;
+		num *= 1024;			/* 1 kilobyte */
+		if (t > num)
+			goto erange;
+		++expr;
+		break;
+	case 'm':
+		t = num;
+		num *= 1048576;			/* 1 megabyte */
+		if (t > num)
+			goto erange;
+		++expr;
+		break;
+	case 'g':
+		t = num;
+		num *= 1073741824;		/* 1 gigabyte */
+		if (t > num)
+			goto erange;
+		++expr;
+		break;
+	case 't':
+		t = num;
+		num *= 1099511627776LL;		/* 1 terabyte */
+		if (t > num)
+			goto erange;
+		++expr;
+		break;
+	case 'w':
+		t = num;
+		num *= sizeof(int);		/* 1 word */
+		if (t > num)
+			goto erange;
+		++expr;
+		break;
+	}
+
+	switch (*expr) {
+	case '\0':
+		break;
+	case '*':				/* Backward compatible */
+	case 'x':
+		t = num;
+		num *= strsuftollx(desc, expr + 1, min, max, ebuf, ebuflen);
+		if (*ebuf != '\0')
+			return (0);
+		if (t > num) {
+ erange:	 	
+			snprintf(ebuf, ebuflen,
+			    "%s: %s", desc, strerror(ERANGE));
+			return (0);
+		}
+		break;
+	default:
+ badnum:	snprintf(ebuf, ebuflen,
+		    "%s `%s': illegal number", desc, val);
+		return (0);
+	}
+	if (num < min) {
+			/* LONGLONG */
+		snprintf(ebuf, ebuflen, "%s %lld is less than %lld.",
+		    desc, (long long)num, (long long)min);
+		return (0);
+	}
+	if (num > max) {
+			/* LONGLONG */
+		snprintf(ebuf, ebuflen,
+		    "%s %lld is greater than %lld.",
+		    desc, (long long)num, (long long)max);
+		return (0);
+	}
+	*ebuf = '\0';
+	return (num);
+}
+
+/* LONGLONG */
+long long
+strsuftoll(const char *desc, const char *val,
+    long long min, long long max)
+{
+	long long result;
+	char	errbuf[100];
+
+	result = strsuftollx(desc, val, min, max, errbuf, sizeof(errbuf));
+	if (*errbuf != '\0')
+		errx(1, "%s", errbuf);
+	return (result);
+}
diff --git a/lib/libnetbsd/sys/cdefs.h b/lib/libnetbsd/sys/cdefs.h
new file mode 100644
index 0000000000..a2c56e0a79
--- /dev/null
+++ b/lib/libnetbsd/sys/cdefs.h
@@ -0,0 +1,74 @@
+/* $FreeBSD: head/lib/libnetbsd/sys/cdefs.h 307870 2016-10-24 17:56:08Z marcel $ */
+
+/*-
+ * Copyright (c) 2012 SRI International
+ * Copyright (c) 1992, 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.
+ */
+
+#ifndef _LIBNETBSD_SYS_CDEFS_H_
+#define _LIBNETBSD_SYS_CDEFS_H_
+
+#include_next <sys/cdefs.h>
+
+#ifndef __dead
+#ifdef __dead2
+#define __dead __dead2
+#else
+#define __dead
+#endif
+#endif /* !__dead */
+
+/*
+ * The __CONCAT macro is used to concatenate parts of symbol names, e.g.
+ * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo.
+ * The __CONCAT macro is a bit tricky -- make sure you don't put spaces
+ * in between its arguments.  __CONCAT can also concatenate double-quoted
+ * strings produced by the __STRING macro, but this only works with ANSI C.
+ */
+
+#define	___STRING(x)	__STRING(x)
+#define	___CONCAT(x,y)	__CONCAT(x,y)
+
+/*
+ * The following macro is used to remove const cast-away warnings
+ * from gcc -Wcast-qual; it should be used with caution because it
+ * can hide valid errors; in particular most valid uses are in
+ * situations where the API requires it, not to cast away string
+ * constants. We don't use *intptr_t on purpose here and we are
+ * explicit about unsigned long so that we don't have additional
+ * dependencies.
+ */
+#define __UNCONST(a)	((void *)(unsigned long)(const void *)(a))
+
+/*
+ * Return the number of elements in a statically-allocated array,
+ * __x.
+ */
+#define	__arraycount(__x)	(sizeof(__x) / sizeof(__x[0]))
+
+#endif /* _LIBNETBSD_SYS_CDEFS_H_ */
diff --git a/lib/libnetbsd/sys/time.h b/lib/libnetbsd/sys/time.h
new file mode 100644
index 0000000000..02b1b741a1
--- /dev/null
+++ b/lib/libnetbsd/sys/time.h
@@ -0,0 +1,65 @@
+/* $FreeBSD: head/lib/libnetbsd/sys/time.h 274078 2014-11-04 02:00:07Z ngie $ */
+
+/*
+ * Copyright (c) 1982, 1986, 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.
+ *
+ *	@(#)time.h	8.5 (Berkeley) 5/4/95
+ */
+
+#ifndef _LIBNETBSD_SYS_TIME_H_
+#define	_LIBNETBSD_SYS_TIME_H_
+
+#include_next <sys/time.h>
+
+/* Operations on timespecs. */
+#define	timespecclear(tsp)	(tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L)
+#define	timespecisset(tsp)	((tsp)->tv_sec || (tsp)->tv_nsec)
+#define	timespeccmp(tsp, usp, cmp)					\
+	(((tsp)->tv_sec == (usp)->tv_sec) ?				\
+	    ((tsp)->tv_nsec cmp (usp)->tv_nsec) :			\
+	    ((tsp)->tv_sec cmp (usp)->tv_sec))
+#define	timespecadd(tsp, usp, vsp)					\
+	do {								\
+		(vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec;		\
+		(vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec;	\
+		if ((vsp)->tv_nsec >= 1000000000L) {			\
+			(vsp)->tv_sec++;				\
+			(vsp)->tv_nsec -= 1000000000L;			\
+		}							\
+	} while (/* CONSTCOND */ 0)
+#define	timespecsub(tsp, usp, vsp)					\
+	do {								\
+		(vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec;		\
+		(vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec;	\
+		if ((vsp)->tv_nsec < 0) {				\
+			(vsp)->tv_sec--;				\
+			(vsp)->tv_nsec += 1000000000L;			\
+		}							\
+	} while (/* CONSTCOND */ 0)
+
+#endif
diff --git a/lib/libnetbsd/util.c b/lib/libnetbsd/util.c
new file mode 100644
index 0000000000..4248545b76
--- /dev/null
+++ b/lib/libnetbsd/util.c
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 2012 SRI International
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * 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 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 AUTHOR 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.
+ *
+ * $FreeBSD: head/lib/libnetbsd/util.c 307871 2016-10-24 17:57:46Z marcel $
+ */
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "util.h"
+
+char *
+flags_to_string(u_long flags, const char *def)
+{
+	char *str;
+
+	str = fflagstostr(flags);
+	if (*str == '\0') {
+		free(str);
+		str = strdup(def);
+	}
+	return (str);
+}
+
+int
+string_to_flags(char **stringp, u_long *setp, u_long *clrp)
+{
+
+	return strtofflags(stringp, setp, clrp);
+}
diff --git a/lib/libnetbsd/util.h b/lib/libnetbsd/util.h
new file mode 100644
index 0000000000..9cc919580c
--- /dev/null
+++ b/lib/libnetbsd/util.h
@@ -0,0 +1,41 @@
+/*	$FreeBSD: head/lib/libnetbsd/util.h 244557 2012-12-21 20:37:38Z brooks $	*/
+
+/*-
+ * Copyright (c) 2012 SRI International
+ * Copyright (c) 1995
+ *	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.
+ */
+
+#ifndef _UTIL_H_
+#define _UTIL_H_
+
+#include <libutil.h>
+
+char	*flags_to_string(u_long flags, const char *def);
+int	 string_to_flags(char **stringp, u_long *setp, u_long *clrp);
+
+#endif	/* _UTIL_H_ */
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index fb5f6ec31a..2ea309d794 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -54,6 +54,7 @@ SUBDIR= 802_11 \
 	keyserv \
 	lastlogin \
 	mailwrapper \
+	makefs \
 	makewhatis \
 	manctl \
 	memcontrol \
diff --git a/usr.sbin/makefs/Makefile b/usr.sbin/makefs/Makefile
new file mode 100644
index 0000000000..c5a87b1cfe
--- /dev/null
+++ b/usr.sbin/makefs/Makefile
@@ -0,0 +1,46 @@
+#	$FreeBSD: head/usr.sbin/makefs/Makefile 289739 2015-10-22 05:32:45Z ngie $
+
+PROG=	makefs
+
+CFLAGS+=-I${.CURDIR}
+
+SRCS=	cd9660.c ffs.c \
+	makefs.c \
+	walk.c
+MAN=	makefs.8
+
+WARNS?=	2
+
+.include "${.CURDIR}/cd9660/Makefile.inc"
+.include "${.CURDIR}/ffs/Makefile.inc"
+
+CFLAGS+=-DHAVE_STRUCT_STAT_ST_FLAGS=1
+CFLAGS+=-DHAVE_STRUCT_STAT_ST_GEN=1
+
+#.PATH: ${.CURDIR}/../../contrib/mtree
+#CFLAGS+=-I${.CURDIR}/../../contrib/mtree
+#SRCS+=	getid.c misc.c spec.c
+
+.PATH: ${.CURDIR}/../../contrib/mknod
+CFLAGS+=-I${.CURDIR}/../../contrib/mknod
+SRCS+=	pack_dev.c
+
+.PATH:	${.CURDIR}/../../sys/ufs/ffs
+SRCS+=	ffs_tables.c
+
+.if exists(${.OBJDIR}/../../lib/libnetbsd)
+LIBNETBSD=	${.OBJDIR}/../../lib/libnetbsd/libnetbsd.a
+.else
+LIBNETBSD=	${.CURDIR}/../../lib/libnetbsd/libnetbsd.a
+.endif
+
+CFLAGS+=	-I${.CURDIR}/../../lib/libnetbsd
+
+DPADD=	${LIBNETBSD} ${LIBUTIL} ${LIBSBUF}
+LDADD=	${LIBNETBSD} -lutil -lsbuf
+
+#.if ${MK_TESTS} != "no"
+#SUBDIR+=	tests
+#.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/makefs/Makefile.depend b/usr.sbin/makefs/Makefile.depend
new file mode 100644
index 0000000000..80a73a410b
--- /dev/null
+++ b/usr.sbin/makefs/Makefile.depend
@@ -0,0 +1,21 @@
+# $FreeBSD: head/usr.sbin/makefs/Makefile.depend 284345 2015-06-13 19:20:56Z sjg $
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+	gnu/lib/csu \
+	gnu/lib/libgcc \
+	include \
+	include/xlocale \
+	lib/${CSU_DIR} \
+	lib/libc \
+	lib/libcompiler_rt \
+	lib/libnetbsd \
+	lib/libsbuf \
+	lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.sbin/makefs/cd9660.c b/usr.sbin/makefs/cd9660.c
new file mode 100644
index 0000000000..2866e3f890
--- /dev/null
+++ b/usr.sbin/makefs/cd9660.c
@@ -0,0 +1,2136 @@
+/*	$NetBSD: cd9660.c,v 1.32 2011/08/23 17:09:11 christos Exp $	*/
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+/*
+ * Copyright (c) 1982, 1986, 1989, 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>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/cd9660.c 309192 2016-11-26 13:26:29Z sevan $");
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "makefs.h"
+#include "cd9660.h"
+#include "cd9660/iso9660_rrip.h"
+#include "cd9660/cd9660_archimedes.h"
+
+/*
+ * Global variables
+ */
+iso9660_disk diskStructure;
+
+static void cd9660_finalize_PVD(void);
+static cd9660node *cd9660_allocate_cd9660node(void);
+static void cd9660_set_defaults(void);
+static int cd9660_arguments_set_string(const char *, const char *, int,
+    char, char *);
+static void cd9660_populate_iso_dir_record(
+    struct _iso_directory_record_cd9660 *, u_char, u_char, u_char,
+    const char *);
+static void cd9660_setup_root_node(void);
+static int cd9660_setup_volume_descriptors(void);
+#if 0
+static int cd9660_fill_extended_attribute_record(cd9660node *);
+#endif
+static void cd9660_sort_nodes(cd9660node *);
+static int cd9660_translate_node_common(cd9660node *);
+static int cd9660_translate_node(fsnode *, cd9660node *);
+static int cd9660_compare_filename(const char *, const char *);
+static void cd9660_sorted_child_insert(cd9660node *, cd9660node *);
+static int cd9660_handle_collisions(cd9660node *, int);
+static cd9660node *cd9660_rename_filename(cd9660node *, int, int);
+static void cd9660_copy_filenames(cd9660node *);
+static void cd9660_sorting_nodes(cd9660node *);
+static int cd9660_count_collisions(cd9660node *);
+static cd9660node *cd9660_rrip_move_directory(cd9660node *);
+static int cd9660_add_dot_records(cd9660node *);
+
+static void cd9660_convert_structure(fsnode *, cd9660node *, int,
+    int *, int *);
+static void cd9660_free_structure(cd9660node *);
+static int cd9660_generate_path_table(void);
+static int cd9660_level1_convert_filename(const char *, char *, int);
+static int cd9660_level2_convert_filename(const char *, char *, int);
+#if 0
+static int cd9660_joliet_convert_filename(const char *, char *, int);
+#endif
+static int cd9660_convert_filename(const char *, char *, int);
+static void cd9660_populate_dot_records(cd9660node *);
+static int64_t cd9660_compute_offsets(cd9660node *, int64_t);
+#if 0
+static int cd9660_copy_stat_info(cd9660node *, cd9660node *, int);
+#endif
+static cd9660node *cd9660_create_virtual_entry(const char *, cd9660node *, int,
+    int);
+static cd9660node *cd9660_create_file(const char *, cd9660node *, cd9660node *);
+static cd9660node *cd9660_create_directory(const char *, cd9660node *,
+    cd9660node *);
+static cd9660node *cd9660_create_special_directory(u_char, cd9660node *);
+
+
+/*
+ * Allocate and initialize a cd9660node
+ * @returns struct cd9660node * Pointer to new node, or NULL on error
+ */
+static cd9660node *
+cd9660_allocate_cd9660node(void)
+{
+	cd9660node *temp;
+
+	if ((temp = calloc(1, sizeof(cd9660node))) == NULL)
+		err(EXIT_FAILURE, "%s: calloc", __func__);
+	TAILQ_INIT(&temp->cn_children);
+	temp->parent = temp->dot_record = temp->dot_dot_record = NULL;
+	temp->ptnext = temp->ptprev = temp->ptlast = NULL;
+	temp->node = NULL;
+	temp->isoDirRecord = NULL;
+	temp->isoExtAttributes = NULL;
+	temp->rr_real_parent = temp->rr_relocated = NULL;
+	temp->su_tail_data = NULL;
+	return temp;
+}
+
+int cd9660_defaults_set = 0;
+
+/**
+* Set default values for cd9660 extension to makefs
+*/
+static void
+cd9660_set_defaults(void)
+{
+	/*Fix the sector size for now, though the spec allows for other sizes*/
+	diskStructure.sectorSize = 2048;
+
+	/* Set up defaults in our own structure */
+	diskStructure.verbose_level = 0;
+	diskStructure.keep_bad_images = 0;
+	diskStructure.follow_sym_links = 0;
+	diskStructure.isoLevel = 2;
+
+	diskStructure.rock_ridge_enabled = 0;
+	diskStructure.rock_ridge_renamed_dir_name = 0;
+	diskStructure.rock_ridge_move_count = 0;
+	diskStructure.rr_moved_dir = 0;
+
+	diskStructure.archimedes_enabled = 0;
+	diskStructure.chrp_boot = 0;
+
+	diskStructure.include_padding_areas = 1;
+
+	/* Spec breaking functionality */
+	diskStructure.allow_deep_trees =
+	    diskStructure.allow_start_dot =
+	    diskStructure.allow_max_name =
+	    diskStructure.allow_illegal_chars =
+	    diskStructure.allow_lowercase =
+	    diskStructure.allow_multidot =
+	    diskStructure.omit_trailing_period = 0;
+
+	/* Make sure the PVD is clear */
+	memset(&diskStructure.primaryDescriptor, 0, 2048);
+
+	memset(diskStructure.primaryDescriptor.publisher_id,	0x20,128);
+	memset(diskStructure.primaryDescriptor.preparer_id,	0x20,128);
+	memset(diskStructure.primaryDescriptor.application_id,	0x20,128);
+	memset(diskStructure.primaryDescriptor.copyright_file_id, 0x20,37);
+	memset(diskStructure.primaryDescriptor.abstract_file_id, 0x20,37);
+	memset(diskStructure.primaryDescriptor.bibliographic_file_id, 0x20,37);
+
+	strcpy(diskStructure.primaryDescriptor.system_id, "FreeBSD");
+
+	cd9660_defaults_set = 1;
+
+	/* Boot support: Initially disabled */
+	diskStructure.has_generic_bootimage = 0;
+	diskStructure.generic_bootimage = NULL;
+
+	diskStructure.boot_image_directory = 0;
+	/*memset(diskStructure.boot_descriptor, 0, 2048);*/
+
+	diskStructure.is_bootable = 0;
+	TAILQ_INIT(&diskStructure.boot_images);
+	LIST_INIT(&diskStructure.boot_entries);
+}
+
+void
+cd9660_prep_opts(fsinfo_t *fsopts __unused)
+{
+	cd9660_set_defaults();
+}
+
+void
+cd9660_cleanup_opts(fsinfo_t *fsopts __unused)
+{
+
+}
+
+static int
+cd9660_arguments_set_string(const char *val, const char *fieldtitle, int length,
+			    char testmode, char * dest)
+{
+	int len, test;
+
+	if (val == NULL)
+		warnx("error: The %s requires a string argument", fieldtitle);
+	else if ((len = strlen(val)) <= length) {
+		if (testmode == 'd')
+			test = cd9660_valid_d_chars(val);
+		else
+			test = cd9660_valid_a_chars(val);
+		if (test) {
+			memcpy(dest, val, len);
+			if (test == 2)
+				cd9660_uppercase_characters(dest, len);
+			return 1;
+		} else
+			warnx("error: The %s must be composed of "
+			      "%c-characters", fieldtitle, testmode);
+	} else
+		warnx("error: The %s must be at most 32 characters long",
+		    fieldtitle);
+	return 0;
+}
+
+/*
+ * Command-line parsing function
+ */
+
+int
+cd9660_parse_opts(const char *option, fsinfo_t *fsopts)
+{
+	char *var, *val;
+	int	rv;
+	/* Set up allowed options - integer options ONLY */
+	option_t cd9660_options[] = {
+		{ "l", &diskStructure.isoLevel, 1, 2, "ISO Level" },
+		{ "isolevel", &diskStructure.isoLevel, 1, 2, "ISO Level" },
+		{ "verbose",  &diskStructure.verbose_level, 0, 2,
+		  "Turns on verbose output" },
+		{ "v", &diskStructure.verbose_level, 0 , 2,
+		  "Turns on verbose output"},
+		{ .name = NULL }
+	};
+
+	if (cd9660_defaults_set == 0)
+		cd9660_set_defaults();
+
+	/*
+	 * Todo : finish implementing this, and make a function that
+	 * parses them
+	 */
+	/*
+	string_option_t cd9660_string_options[] = {
+		{ "L", "Label", &diskStructure.primaryDescriptor.volume_id, 1, 32, "Disk Label", ISO_STRING_FILTER_DCHARS },
+		{ NULL }
+	}
+	*/
+
+	assert(option != NULL);
+
+	if (debug & DEBUG_FS_PARSE_OPTS)
+		printf("cd9660_parse_opts: got `%s'\n", option);
+
+	if ((var = strdup(option)) == NULL)
+		err(1, "allocating memory for copy of option string");
+	rv = 1;
+
+	val = strchr(var, '=');
+	if (val != NULL)
+		*val++ = '\0';
+
+	/* First handle options with no parameters */
+	if (strcmp(var, "h") == 0) {
+		diskStructure.displayHelp = 1;
+		rv = 1;
+	} else if (CD9660_IS_COMMAND_ARG_DUAL(var, "S", "follow-symlinks")) {
+		/* this is not handled yet */
+		diskStructure.follow_sym_links = 1;
+		rv = 1;
+	} else if (CD9660_IS_COMMAND_ARG_DUAL(var, "L", "label")) {
+		rv = cd9660_arguments_set_string(val, "Disk Label", 32, 'd',
+			diskStructure.primaryDescriptor.volume_id);
+	} else if (CD9660_IS_COMMAND_ARG_DUAL(var, "A", "applicationid")) {
+		rv = cd9660_arguments_set_string(val, "Application Identifier", 128, 'a',
+			diskStructure.primaryDescriptor.application_id);
+	} else if(CD9660_IS_COMMAND_ARG_DUAL(var, "P", "publisher")) {
+		rv = cd9660_arguments_set_string(val, "Publisher Identifier",
+			128, 'a', diskStructure.primaryDescriptor.publisher_id);
+	} else if (CD9660_IS_COMMAND_ARG_DUAL(var, "p", "preparer")) {
+		rv = cd9660_arguments_set_string(val, "Preparer Identifier",
+		    128, 'a', diskStructure.primaryDescriptor.preparer_id);
+	} else if (CD9660_IS_COMMAND_ARG_DUAL(var, "V", "volumeid")) {
+		rv = cd9660_arguments_set_string(val, "Volume Set Identifier",
+		    128, 'a', diskStructure.primaryDescriptor.volume_set_id);
+	/* Boot options */
+	} else if (CD9660_IS_COMMAND_ARG_DUAL(var, "B", "bootimage")) {
+		if (val == NULL)
+			warnx("error: The Boot Image parameter requires a valid boot information string");
+		else
+			rv = cd9660_add_boot_disk(val);
+	} else if (CD9660_IS_COMMAND_ARG(var, "bootimagedir")) {
+		/*
+		 * XXXfvdl this is unused.
+		 */
+		if (val == NULL)
+			errx(1, "error: The Boot Image Directory parameter"
+			     " requires a directory name\n");
+		else {
+			if ((diskStructure.boot_image_directory =
+			     malloc(strlen(val) + 1)) == NULL) {
+				CD9660_MEM_ALLOC_ERROR("cd9660_parse_opts");
+				exit(1);
+			}
+
+			/* BIG TODO: Add the max length function here */
+			cd9660_arguments_set_string(val, "Boot Image Directory",
+			    12 , 'd', diskStructure.boot_image_directory);
+		}
+	} else if (CD9660_IS_COMMAND_ARG_DUAL(var, "G", "generic-bootimage")) {
+		if (val == NULL)
+			warnx("error: The Boot Image parameter requires a valid boot information string");
+		else
+			rv = cd9660_add_generic_bootimage(val);
+	} else if (CD9660_IS_COMMAND_ARG(var, "no-trailing-padding"))
+		diskStructure.include_padding_areas = 0;
+	/* RRIP */
+	else if (CD9660_IS_COMMAND_ARG_DUAL(var, "R", "rockridge"))
+		diskStructure.rock_ridge_enabled = 1;
+	else if (CD9660_IS_COMMAND_ARG_DUAL(var, "A", "archimedes"))
+		diskStructure.archimedes_enabled = 1;
+	else if (CD9660_IS_COMMAND_ARG(var, "chrp-boot"))
+		diskStructure.chrp_boot = 1;
+	else if (CD9660_IS_COMMAND_ARG_DUAL(var, "K", "keep-bad-images"))
+		diskStructure.keep_bad_images = 1;
+	else if (CD9660_IS_COMMAND_ARG(var, "allow-deep-trees"))
+		diskStructure.allow_deep_trees = 1;
+	else if (CD9660_IS_COMMAND_ARG(var, "allow-max-name"))
+		diskStructure.allow_max_name = 1;
+	else if (CD9660_IS_COMMAND_ARG(var, "allow-illegal-chars"))
+		diskStructure.allow_illegal_chars = 1;
+	else if (CD9660_IS_COMMAND_ARG(var, "allow-lowercase"))
+		diskStructure.allow_lowercase = 1;
+	else if (CD9660_IS_COMMAND_ARG(var,"allow-multidot"))
+		diskStructure.allow_multidot = 1;
+	else if (CD9660_IS_COMMAND_ARG(var, "omit-trailing-period"))
+		diskStructure.omit_trailing_period = 1;
+	else if (CD9660_IS_COMMAND_ARG(var, "no-emul-boot") ||
+		 CD9660_IS_COMMAND_ARG(var, "no-boot") ||
+		 CD9660_IS_COMMAND_ARG(var, "hard-disk-boot")) {
+		cd9660_eltorito_add_boot_option(var, 0);
+		
+		/* End of flag variables */
+        } else if (CD9660_IS_COMMAND_ARG(var, "boot-load-segment")) {
+		if (val == NULL) {
+			warnx("Option `%s' doesn't contain a value", var);
+			rv = 0;
+		} else {
+			cd9660_eltorito_add_boot_option(var, val);
+		}
+	} else {
+		if (val == NULL) {
+			warnx("Option `%s' doesn't contain a value", var);
+			rv = 0;
+		} else
+			rv = set_option(cd9660_options, var, val);
+	}
+
+	free(var);
+	return (rv);
+}
+
+/*
+ * Main function for cd9660_makefs
+ * Builds the ISO image file
+ * @param const char *image The image filename to create
+ * @param const char *dir The directory that is being read
+ * @param struct fsnode *root The root node of the filesystem tree
+ * @param struct fsinfo_t *fsopts Any options
+ */
+void
+cd9660_makefs(const char *image, const char *dir, fsnode *root,
+    fsinfo_t *fsopts)
+{
+	int64_t startoffset;
+	int numDirectories;
+	uint64_t pathTableSectors;
+	int64_t firstAvailableSector;
+	int64_t totalSpace;
+	int error;
+	cd9660node *real_root;
+
+	if (diskStructure.verbose_level > 0)
+		printf("cd9660_makefs: ISO level is %i\n",
+		    diskStructure.isoLevel);
+	if (diskStructure.isoLevel < 2 &&
+	    diskStructure.allow_multidot)
+		errx(1, "allow-multidot requires iso level of 2\n");
+
+	assert(image != NULL);
+	assert(dir != NULL);
+	assert(root != NULL);
+
+	if (diskStructure.displayHelp) {
+		/*
+		 * Display help here - probably want to put it in
+		 * a separate function
+		 */
+		return;
+	}
+
+	if (diskStructure.verbose_level > 0)
+		printf("cd9660_makefs: image %s directory %s root %p\n",
+		    image, dir, root);
+
+	/* Set up some constants. Later, these will be defined with options */
+
+	/* Counter needed for path tables */
+	numDirectories = 0;
+
+	/* Convert tree to our own format */
+	/* Actually, we now need to add the REAL root node, at level 0 */
+
+	real_root = cd9660_allocate_cd9660node();
+	if ((real_root->isoDirRecord =
+		malloc( sizeof(iso_directory_record_cd9660) )) == NULL) {
+		CD9660_MEM_ALLOC_ERROR("cd9660_makefs");
+		exit(1);
+	}
+
+	/* Leave filename blank for root */
+	memset(real_root->isoDirRecord->name, 0,
+	    ISO_FILENAME_MAXLENGTH_WITH_PADDING);
+
+	real_root->level = 0;
+	diskStructure.rootNode = real_root;
+	real_root->type = CD9660_TYPE_DIR;
+	error = 0;
+	real_root->node = root;
+	cd9660_convert_structure(root, real_root, 1, &numDirectories, &error);
+
+	if (TAILQ_EMPTY(&real_root->cn_children)) {
+		errx(1, "cd9660_makefs: converted directory is empty. "
+			"Tree conversion failed\n");
+	} else if (error != 0) {
+		errx(1, "cd9660_makefs: tree conversion failed\n");
+	} else {
+		if (diskStructure.verbose_level > 0)
+			printf("cd9660_makefs: tree converted\n");
+	}
+
+	/* Add the dot and dot dot records */
+	cd9660_add_dot_records(real_root);
+
+	cd9660_setup_root_node();
+
+	if (diskStructure.verbose_level > 0)
+		printf("cd9660_makefs: done converting tree\n");
+
+	/* non-SUSP extensions */
+	if (diskStructure.archimedes_enabled)
+		archimedes_convert_tree(diskStructure.rootNode);
+
+	/* Rock ridge / SUSP init pass */
+	if (diskStructure.rock_ridge_enabled) {
+		cd9660_susp_initialize(diskStructure.rootNode,
+		    diskStructure.rootNode, NULL);
+	}
+
+	/* Build path table structure */
+	diskStructure.pathTableLength = cd9660_generate_path_table();
+
+	pathTableSectors = CD9660_BLOCKS(diskStructure.sectorSize,
+		diskStructure.pathTableLength);
+
+	firstAvailableSector = cd9660_setup_volume_descriptors();
+	if (diskStructure.is_bootable) {
+		firstAvailableSector = cd9660_setup_boot(firstAvailableSector);
+		if (firstAvailableSector < 0)
+			errx(1, "setup_boot failed");
+	}
+	/* LE first, then BE */
+	diskStructure.primaryLittleEndianTableSector = firstAvailableSector;
+	diskStructure.primaryBigEndianTableSector =
+		diskStructure.primaryLittleEndianTableSector + pathTableSectors;
+
+	/* Set the secondary ones to -1, not going to use them for now */
+	diskStructure.secondaryBigEndianTableSector = -1;
+	diskStructure.secondaryLittleEndianTableSector = -1;
+
+	diskStructure.dataFirstSector =
+	    diskStructure.primaryBigEndianTableSector + pathTableSectors;
+	if (diskStructure.verbose_level > 0)
+		printf("cd9660_makefs: Path table conversion complete. "
+		       "Each table is %i bytes, or %" PRIu64 " sectors.\n",
+		    diskStructure.pathTableLength, pathTableSectors);
+
+	startoffset = diskStructure.sectorSize*diskStructure.dataFirstSector;
+
+	totalSpace = cd9660_compute_offsets(real_root, startoffset);
+
+	diskStructure.totalSectors = diskStructure.dataFirstSector +
+		CD9660_BLOCKS(diskStructure.sectorSize, totalSpace);
+
+	/* Disabled until pass 1 is done */
+	if (diskStructure.rock_ridge_enabled) {
+		diskStructure.susp_continuation_area_start_sector =
+		    diskStructure.totalSectors;
+		diskStructure.totalSectors +=
+		    CD9660_BLOCKS(diskStructure.sectorSize,
+			diskStructure.susp_continuation_area_size);
+		cd9660_susp_finalize(diskStructure.rootNode);
+	}
+
+
+	cd9660_finalize_PVD();
+
+	/* Add padding sectors, just for testing purposes right now */
+	/* diskStructure.totalSectors+=150; */
+
+	/* Debugging output */
+	if (diskStructure.verbose_level > 0) {
+		printf("cd9660_makefs: Sectors 0-15 reserved\n");
+		printf("cd9660_makefs: Primary path tables starts in sector %"
+		    PRId64 "\n", diskStructure.primaryLittleEndianTableSector);
+		printf("cd9660_makefs: File data starts in sector %"
+		    PRId64 "\n", diskStructure.dataFirstSector);
+		printf("cd9660_makefs: Total sectors: %"
+		    PRId64 "\n", diskStructure.totalSectors);
+	}
+
+	/*
+	 * Add padding sectors at the end
+	 * TODO: Clean this up and separate padding
+	 */
+	if (diskStructure.include_padding_areas)
+		diskStructure.totalSectors += 150;
+
+	cd9660_write_image(image);
+
+	if (diskStructure.verbose_level > 1) {
+		debug_print_volume_descriptor_information();
+		debug_print_tree(real_root,0);
+		debug_print_path_tree(real_root);
+	}
+
+	/* Clean up data structures */
+	cd9660_free_structure(real_root);
+
+	if (diskStructure.verbose_level > 0)
+		printf("cd9660_makefs: done\n");
+}
+
+/* Generic function pointer - implement later */
+typedef int (*cd9660node_func)(cd9660node *);
+
+static void
+cd9660_finalize_PVD(void)
+{
+	time_t tstamp = stampst.st_ino ? stampst.st_mtime : time(NULL);
+
+	/* root should be a fixed size of 34 bytes since it has no name */
+	memcpy(diskStructure.primaryDescriptor.root_directory_record,
+		diskStructure.rootNode->dot_record->isoDirRecord, 34);
+
+	/* In RRIP, this might be longer than 34 */
+	diskStructure.primaryDescriptor.root_directory_record[0] = 34;
+
+	/* Set up all the important numbers in the PVD */
+	cd9660_bothendian_dword(diskStructure.totalSectors,
+	    (unsigned char *)diskStructure.primaryDescriptor.volume_space_size);
+	cd9660_bothendian_word(1,
+	    (unsigned char *)diskStructure.primaryDescriptor.volume_set_size);
+	cd9660_bothendian_word(1,
+	    (unsigned char *)
+		diskStructure.primaryDescriptor.volume_sequence_number);
+	cd9660_bothendian_word(diskStructure.sectorSize,
+	    (unsigned char *)
+		diskStructure.primaryDescriptor.logical_block_size);
+	cd9660_bothendian_dword(diskStructure.pathTableLength,
+	    (unsigned char *)diskStructure.primaryDescriptor.path_table_size);
+
+	cd9660_731(diskStructure.primaryLittleEndianTableSector,
+		(u_char *)diskStructure.primaryDescriptor.type_l_path_table);
+	cd9660_732(diskStructure.primaryBigEndianTableSector,
+		(u_char *)diskStructure.primaryDescriptor.type_m_path_table);
+
+	diskStructure.primaryDescriptor.file_structure_version[0] = 1;
+
+	/* Pad all strings with spaces instead of nulls */
+	cd9660_pad_string_spaces(diskStructure.primaryDescriptor.volume_id, 32);
+	cd9660_pad_string_spaces(diskStructure.primaryDescriptor.system_id, 32);
+	cd9660_pad_string_spaces(diskStructure.primaryDescriptor.volume_set_id,
+	    128);
+	cd9660_pad_string_spaces(diskStructure.primaryDescriptor.publisher_id,
+	    128);
+	cd9660_pad_string_spaces(diskStructure.primaryDescriptor.preparer_id,
+	    128);
+	cd9660_pad_string_spaces(diskStructure.primaryDescriptor.application_id,
+	    128);
+	cd9660_pad_string_spaces(
+	    diskStructure.primaryDescriptor.copyright_file_id, 37);
+	cd9660_pad_string_spaces(
+		diskStructure.primaryDescriptor.abstract_file_id, 37);
+	cd9660_pad_string_spaces(
+		diskStructure.primaryDescriptor.bibliographic_file_id, 37);
+
+	/* Setup dates */
+	cd9660_time_8426(
+	    (unsigned char *)diskStructure.primaryDescriptor.creation_date,
+	    tstamp);
+	cd9660_time_8426(
+	    (unsigned char *)diskStructure.primaryDescriptor.modification_date,
+	    tstamp);
+
+#if 0
+	cd9660_set_date(diskStructure.primaryDescriptor.expiration_date,
+	    tstamp);
+#endif
+
+	memset(diskStructure.primaryDescriptor.expiration_date, '0', 16);
+	diskStructure.primaryDescriptor.expiration_date[16] = 0;
+	cd9660_time_8426(
+	    (unsigned char *)diskStructure.primaryDescriptor.effective_date,
+	    tstamp);
+	/* make this sane */
+	cd9660_time_915(diskStructure.rootNode->dot_record->isoDirRecord->date,
+	    tstamp);
+}
+
+static void
+cd9660_populate_iso_dir_record(struct _iso_directory_record_cd9660 *record,
+			       u_char ext_attr_length, u_char flags,
+			       u_char name_len, const char * name)
+{
+	record->ext_attr_length[0] = ext_attr_length;
+	record->flags[0] = ISO_FLAG_CLEAR | flags;
+	record->file_unit_size[0] = 0;
+	record->interleave[0] = 0;
+	cd9660_bothendian_word(1, record->volume_sequence_number);
+	record->name_len[0] = name_len;
+	memset(record->name, '\0', sizeof (record->name));
+	memcpy(record->name, name, name_len);
+	record->length[0] = 33 + name_len;
+
+	/* Todo : better rounding */
+	record->length[0] += (record->length[0] & 1) ? 1 : 0;
+}
+
+static void
+cd9660_setup_root_node(void)
+{
+	cd9660_populate_iso_dir_record(diskStructure.rootNode->isoDirRecord,
+	    0, ISO_FLAG_DIRECTORY, 1, "\0");
+
+}
+
+/*********** SUPPORT FUNCTIONS ***********/
+static int
+cd9660_setup_volume_descriptors(void)
+{
+	/* Boot volume descriptor should come second */
+	int sector = 16;
+	/* For now, a fixed 2 : PVD and terminator */
+	volume_descriptor *temp, *t;
+
+	/* Set up the PVD */
+	if ((temp = malloc(sizeof(volume_descriptor))) == NULL) {
+		CD9660_MEM_ALLOC_ERROR("cd9660_setup_volume_descriptors");
+		exit(1);
+	}
+
+	temp->volumeDescriptorData =
+	   (unsigned char *)&diskStructure.primaryDescriptor;
+	temp->volumeDescriptorData[0] = ISO_VOLUME_DESCRIPTOR_PVD;
+	temp->volumeDescriptorData[6] = 1;
+	temp->sector = sector;
+	memcpy(temp->volumeDescriptorData + 1,
+	    ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
+	diskStructure.firstVolumeDescriptor = temp;
+
+	sector++;
+	/* Set up boot support if enabled. BVD must reside in sector 17 */
+	if (diskStructure.is_bootable) {
+		if ((t = malloc(sizeof(volume_descriptor))) == NULL) {
+			CD9660_MEM_ALLOC_ERROR(
+			    "cd9660_setup_volume_descriptors");
+			exit(1);
+		}
+		if ((t->volumeDescriptorData = malloc(2048)) == NULL) {
+			CD9660_MEM_ALLOC_ERROR(
+			    "cd9660_setup_volume_descriptors");
+			exit(1);
+		}
+		temp->next = t;
+		temp = t;
+		memset(t->volumeDescriptorData, 0, 2048);
+		t->sector = 17;
+		if (diskStructure.verbose_level > 0)
+			printf("Setting up boot volume descriptor\n");
+		cd9660_setup_boot_volume_descriptor(t);
+		sector++;
+	}
+
+	/* Set up the terminator */
+	if ((t = malloc(sizeof(volume_descriptor))) == NULL) {
+		CD9660_MEM_ALLOC_ERROR("cd9660_setup_volume_descriptors");
+		exit(1);
+	}
+	if ((t->volumeDescriptorData = malloc(2048)) == NULL) {
+		CD9660_MEM_ALLOC_ERROR("cd9660_setup_volume_descriptors");
+		exit(1);
+	}
+
+	temp->next = t;
+	memset(t->volumeDescriptorData, 0, 2048);
+	t->volumeDescriptorData[0] = ISO_VOLUME_DESCRIPTOR_TERMINATOR;
+	t->next = NULL;
+	t->volumeDescriptorData[6] = 1;
+	t->sector = sector;
+	memcpy(t->volumeDescriptorData + 1,
+	    ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
+
+	sector++;
+	return sector;
+}
+
+#if 0
+/*
+ * Populate EAR at some point. Not required, but is used by NetBSD's
+ * cd9660 support
+ */
+static int
+cd9660_fill_extended_attribute_record(cd9660node *node)
+{
+	if ((node->isoExtAttributes =
+	    malloc(sizeof(struct iso_extended_attributes))) == NULL) {
+		CD9660_MEM_ALLOC_ERROR("cd9660_fill_extended_attribute_record");
+		exit(1);
+	};
+
+	return 1;
+}
+#endif
+
+static int
+cd9660_translate_node_common(cd9660node *newnode)
+{
+	time_t tstamp = stampst.st_ino ? stampst.st_mtime : time(NULL);
+	int test;
+	u_char flag;
+	char temp[ISO_FILENAME_MAXLENGTH_WITH_PADDING];
+
+	/* Now populate the isoDirRecord structure */
+	memset(temp, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING);
+
+	test = cd9660_convert_filename(newnode->node->name,
+		temp, !(S_ISDIR(newnode->node->type)));
+
+	flag = ISO_FLAG_CLEAR;
+	if (S_ISDIR(newnode->node->type))
+		flag |= ISO_FLAG_DIRECTORY;
+
+	cd9660_populate_iso_dir_record(newnode->isoDirRecord, 0,
+	    flag, strlen(temp), temp);
+
+	/* Set the various dates */
+
+	/* If we want to use the current date and time */
+
+	cd9660_time_915(newnode->isoDirRecord->date, tstamp);
+
+	cd9660_bothendian_dword(newnode->fileDataLength,
+	    newnode->isoDirRecord->size);
+	/* If the file is a link, we want to set the size to 0 */
+	if (S_ISLNK(newnode->node->type))
+		newnode->fileDataLength = 0;
+
+	return 1;
+}
+
+/*
+ * Translate fsnode to cd9660node
+ * Translate filenames and other metadata, including dates, sizes,
+ * permissions, etc
+ * @param struct fsnode * The node generated by makefs
+ * @param struct cd9660node * The intermediate node to be written to
+ * @returns int 0 on failure, 1 on success
+ */
+static int
+cd9660_translate_node(fsnode *node, cd9660node *newnode)
+{
+	if (node == NULL) {
+		if (diskStructure.verbose_level > 0)
+			printf("cd9660_translate_node: NULL node passed, "
+			       "returning\n");
+		return 0;
+	}
+	if ((newnode->isoDirRecord =
+		malloc(sizeof(iso_directory_record_cd9660))) == NULL) {
+		CD9660_MEM_ALLOC_ERROR("cd9660_translate_node");
+		return 0;
+	}
+
+	/* Set the node pointer */
+	newnode->node = node;
+
+	/* Set the size */
+	if (!(S_ISDIR(node->type)))
+		newnode->fileDataLength = node->inode->st.st_size;
+
+	if (cd9660_translate_node_common(newnode) == 0)
+		return 0;
+
+	/* Finally, overwrite some of the values that are set by default */
+	cd9660_time_915(newnode->isoDirRecord->date,
+	    stampst.st_ino ? stampst.st_mtime : node->inode->st.st_mtime);
+
+	return 1;
+}
+
+/*
+ * Compares two ISO filenames
+ * @param const char * The first file name
+ * @param const char * The second file name
+ * @returns : -1 if first is less than second, 0 if they are the same, 1 if
+ * 	the second is greater than the first
+ */
+static int
+cd9660_compare_filename(const char *first, const char *second)
+{
+	/*
+	 * This can be made more optimal once it has been tested
+	 * (the extra character, for example, is for testing)
+	 */
+
+	int p1 = 0;
+	int p2 = 0;
+	char c1, c2;
+	/* First, on the filename */
+
+	while (p1 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION-1
+		&& p2 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION-1) {
+		c1 = first[p1];
+		c2 = second[p2];
+		if (c1 == '.' && c2 =='.')
+			break;
+		else if (c1 == '.') {
+			p2++;
+			c1 = ' ';
+		} else if (c2 == '.') {
+			p1++;
+			c2 = ' ';
+		} else {
+			p1++;
+			p2++;
+		}
+
+		if (c1 < c2)
+			return -1;
+		else if (c1 > c2) {
+			return 1;
+		}
+	}
+
+	if (first[p1] == '.' && second[p2] == '.') {
+		p1++;
+		p2++;
+		while (p1 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION - 1
+			&& p2 < ISO_FILENAME_MAXLENGTH_BEFORE_VERSION - 1) {
+			c1 = first[p1];
+			c2 = second[p2];
+			if (c1 == ';' && c2 == ';')
+				break;
+			else if (c1 == ';') {
+				p2++;
+				c1 = ' ';
+			} else if (c2 == ';') {
+				p1++;
+				c2 = ' ';
+			} else {
+				p1++;
+				p2++;
+			}
+
+			if (c1 < c2)
+				return -1;
+			else if (c1 > c2)
+				return 1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Insert a node into list with ISO sorting rules
+ * @param cd9660node * The head node of the list
+ * @param cd9660node * The node to be inserted
+ */
+static void
+cd9660_sorted_child_insert(cd9660node *parent, cd9660node *cn_new)
+{
+	int compare;
+	cd9660node *cn;
+	struct cd9660_children_head *head = &parent->cn_children;
+
+	/* TODO: Optimize? */
+	cn_new->parent = parent;
+
+	/*
+	 * first will either be 0, the . or the ..
+	 * if . or .., this means no other entry may be written before first
+	 * if 0, the new node may be inserted at the head
+	 */
+
+	TAILQ_FOREACH(cn, head, cn_next_child) {
+		/*
+		 * Dont insert a node twice -
+		 * that would cause an infinite loop
+		 */
+		if (cn_new == cn)
+			return;
+
+		compare = cd9660_compare_filename(cn_new->isoDirRecord->name,
+			cn->isoDirRecord->name);
+
+		if (compare == 0)
+			compare = cd9660_compare_filename(cn_new->node->name,
+				cn->node->name);
+
+		if (compare < 0)
+			break;
+	}
+	if (cn == NULL)
+		TAILQ_INSERT_TAIL(head, cn_new, cn_next_child);
+	else
+		TAILQ_INSERT_BEFORE(cn, cn_new, cn_next_child);
+}
+
+/*
+ * Called After cd9660_sorted_child_insert
+ * handles file collisions by suffixing each filname with ~n
+ * where n represents the files respective place in the ordering
+ */
+static int
+cd9660_handle_collisions(cd9660node *colliding, int past)
+{
+	cd9660node *iter, *next, *prev;
+	int skip;
+	int delete_chars = 0;
+	int temp_past = past;
+	int temp_skip;
+	int flag = 0;
+	cd9660node *end_of_range;
+
+	for (iter = TAILQ_FIRST(&colliding->cn_children);
+	     iter != NULL && (next = TAILQ_NEXT(iter, cn_next_child)) != NULL;) {
+		if (strcmp(iter->isoDirRecord->name,
+		           next->isoDirRecord->name) != 0) {
+			iter = TAILQ_NEXT(iter, cn_next_child);
+			continue;
+		}
+		flag = 1;
+		temp_skip = skip = cd9660_count_collisions(iter);
+		end_of_range = iter;
+		while (temp_skip > 0) {
+			temp_skip--;
+			end_of_range = TAILQ_NEXT(end_of_range, cn_next_child);
+		}
+		temp_past = past;
+		while (temp_past > 0) {
+			if ((next = TAILQ_NEXT(end_of_range, cn_next_child)) != NULL)
+				end_of_range = next;
+			else if ((prev = TAILQ_PREV(iter, cd9660_children_head, cn_next_child)) != NULL)
+				iter = prev;
+			else
+				delete_chars++;
+			temp_past--;
+		}
+		skip += past;
+		iter = cd9660_rename_filename(iter, skip, delete_chars);
+	}
+	return flag;
+}
+
+
+static cd9660node *
+cd9660_rename_filename(cd9660node *iter, int num, int delete_chars)
+{
+	int i = 0;
+	int numbts, digit, digits, temp, powers, count;
+	char *naming;
+	int maxlength;
+        char *tmp;
+
+	if (diskStructure.verbose_level > 0)
+		printf("Rename_filename called\n");
+
+	assert(1 <= diskStructure.isoLevel && diskStructure.isoLevel <= 2);
+	/* TODO : A LOT of chanes regarding 8.3 filenames */
+	if (diskStructure.isoLevel == 1)
+		maxlength = 8;
+	else if (diskStructure.isoLevel == 2)
+		maxlength = 31;
+	else
+		maxlength = ISO_FILENAME_MAXLENGTH_BEFORE_VERSION;
+
+	tmp = malloc(ISO_FILENAME_MAXLENGTH_WITH_PADDING);
+
+	while (i < num && iter) {
+		powers = 1;
+		count = 0;
+		digits = 1;
+		while (((int)(i / powers) ) >= 10) {
+			digits++;
+			powers = powers * 10;
+		}
+
+		naming = iter->o_name;
+
+		/*
+		while ((*naming != '.') && (*naming != ';')) {
+			naming++;
+			count++;
+		}
+		*/
+
+		while (count < maxlength) {
+			if (*naming == ';')
+				break;
+			naming++;
+			count++;
+		}
+
+		if ((count + digits) < maxlength)
+			numbts = count;
+		else
+			numbts = maxlength - (digits);
+		numbts -= delete_chars;
+
+		/* 8.3 rules - keep the extension, add before the dot */
+
+		/*
+		 * This code makes a bunch of assumptions.
+		 * See if you can spot them all :)
+		 */
+
+		/*
+		if (diskStructure.isoLevel == 1) {
+			numbts = 8 - digits - delete_chars;
+			if (dot < 0) {
+
+			} else {
+				if (dot < 8) {
+					memmove(&tmp[numbts],&tmp[dot],4);
+				}
+			}
+		}
+		*/
+
+		/* (copying just the filename before the '.' */
+		memcpy(tmp, (iter->o_name), numbts);
+
+		/* adding the appropriate number following the name */
+		temp = i;
+		while (digits > 0) {
+			digit = (int)(temp / powers);
+			temp = temp - digit * powers;
+			sprintf(&tmp[numbts] , "%d", digit);
+			digits--;
+			numbts++;
+			powers = powers / 10;
+		}
+
+		while ((*naming != ';')  && (numbts < maxlength)) {
+			tmp[numbts] = (*naming);
+			naming++;
+			numbts++;
+		}
+
+		tmp[numbts] = ';';
+		tmp[numbts+1] = '1';
+		tmp[numbts+2] = '\0';
+
+		/*
+		 * now tmp has exactly the identifier
+		 * we want so we'll copy it back to record
+		 */
+		memcpy((iter->isoDirRecord->name), tmp, numbts + 3);
+
+		iter = TAILQ_NEXT(iter, cn_next_child);
+		i++;
+	}
+
+	free(tmp);
+	return iter;
+}
+
+/* Todo: Figure out why these functions are nec. */
+static void
+cd9660_copy_filenames(cd9660node *node)
+{
+	cd9660node *cn;
+
+	if (TAILQ_EMPTY(&node->cn_children))
+		return;
+
+	if (TAILQ_FIRST(&node->cn_children)->isoDirRecord == NULL) {
+		debug_print_tree(diskStructure.rootNode, 0);
+		exit(1);
+	}
+
+	TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) {
+		cd9660_copy_filenames(cn);
+		memcpy(cn->o_name, cn->isoDirRecord->name,
+		    ISO_FILENAME_MAXLENGTH_WITH_PADDING);
+	}
+}
+
+static void
+cd9660_sorting_nodes(cd9660node *node)
+{
+	cd9660node *cn;
+
+	TAILQ_FOREACH(cn, &node->cn_children, cn_next_child)
+		cd9660_sorting_nodes(cn);
+	cd9660_sort_nodes(node);
+}
+
+/* XXX Bubble sort. */
+static void
+cd9660_sort_nodes(cd9660node *node)
+{
+	cd9660node *cn, *next;
+
+	do {
+		TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) {
+			if ((next = TAILQ_NEXT(cn, cn_next_child)) == NULL)
+				return;
+			else if (strcmp(next->isoDirRecord->name,
+				        cn->isoDirRecord->name) >= 0)
+				continue;
+			TAILQ_REMOVE(&node->cn_children, next, cn_next_child);
+			TAILQ_INSERT_BEFORE(cn, next, cn_next_child);
+			break;
+		}
+	} while (cn != NULL);
+}
+
+static int
+cd9660_count_collisions(cd9660node *copy)
+{
+	int count = 0;
+	cd9660node *iter, *next;
+
+	for (iter = copy;
+	     (next = TAILQ_NEXT(iter, cn_next_child)) != NULL;
+	     iter = next) {
+		if (cd9660_compare_filename(iter->isoDirRecord->name,
+			next->isoDirRecord->name) == 0)
+			count++;
+		else
+			return count;
+	}
+#if 0
+	if ((next = TAILQ_NEXT(iter, cn_next_child)) != NULL) {
+		printf("cd9660_recurse_on_collision: count is %i \n", count);
+		compare = cd9660_compare_filename(iter->isoDirRecord->name,
+			next->isoDirRecord->name);
+		if (compare == 0) {
+			count++;
+			return cd9660_recurse_on_collision(next, count);
+		} else
+			return count;
+	}
+#endif
+	return count;
+}
+
+static cd9660node *
+cd9660_rrip_move_directory(cd9660node *dir)
+{
+	char newname[9];
+	cd9660node *tfile;
+
+	/*
+	 * This function needs to:
+	 * 1) Create an empty virtual file in place of the old directory
+	 * 2) Point the virtual file to the new directory
+	 * 3) Point the relocated directory to its old parent
+	 * 4) Move the directory specified by dir into rr_moved_dir,
+	 * and rename it to "diskStructure.rock_ridge_move_count" (as a string)
+	 */
+
+	/* First see if the moved directory even exists */
+	if (diskStructure.rr_moved_dir == NULL) {
+		diskStructure.rr_moved_dir =
+			cd9660_create_directory(ISO_RRIP_DEFAULT_MOVE_DIR_NAME,
+				diskStructure.rootNode, dir);
+		if (diskStructure.rr_moved_dir == NULL)
+			return 0;
+		cd9660_time_915(diskStructure.rr_moved_dir->isoDirRecord->date,
+		    stampst.st_ino ? stampst.st_mtime : start_time.tv_sec);
+	}
+
+	/* Create a file with the same ORIGINAL name */
+	tfile = cd9660_create_file(dir->node->name, dir->parent, dir);
+	if (tfile == NULL)
+		return NULL;
+
+	diskStructure.rock_ridge_move_count++;
+	snprintf(newname, sizeof(newname), "%08i",
+	    diskStructure.rock_ridge_move_count);
+
+	/* Point to old parent */
+	dir->rr_real_parent = dir->parent;
+
+	/* Place the placeholder file */
+	if (TAILQ_EMPTY(&dir->rr_real_parent->cn_children)) {
+		TAILQ_INSERT_HEAD(&dir->rr_real_parent->cn_children, tfile,
+		    cn_next_child);
+	} else {
+		cd9660_sorted_child_insert(dir->rr_real_parent, tfile);
+	}
+
+	/* Point to new parent */
+	dir->parent = diskStructure.rr_moved_dir;
+
+	/* Point the file to the moved directory */
+	tfile->rr_relocated = dir;
+
+	/* Actually move the directory */
+	cd9660_sorted_child_insert(diskStructure.rr_moved_dir, dir);
+
+	/* TODO: Inherit permissions / ownership (basically the entire inode) */
+
+	/* Set the new name */
+	memset(dir->isoDirRecord->name, 0, ISO_FILENAME_MAXLENGTH_WITH_PADDING);
+	strncpy(dir->isoDirRecord->name, newname, 8);
+	dir->isoDirRecord->length[0] = 34 + 8;
+	dir->isoDirRecord->name_len[0] = 8;
+
+	return dir;
+}
+
+static int
+cd9660_add_dot_records(cd9660node *root)
+{
+	struct cd9660_children_head *head = &root->cn_children;
+	cd9660node *cn;
+
+	TAILQ_FOREACH(cn, head, cn_next_child) {
+		if ((cn->type & CD9660_TYPE_DIR) == 0)
+			continue;
+		/* Recursion first */
+		cd9660_add_dot_records(cn);
+	}
+	cd9660_create_special_directory(CD9660_TYPE_DOT, root);
+	cd9660_create_special_directory(CD9660_TYPE_DOTDOT, root);
+	return 1;
+}
+
+/*
+ * Convert node to cd9660 structure
+ * This function is designed to be called recursively on the root node of
+ * the filesystem
+ * Lots of recursion going on here, want to make sure it is efficient
+ * @param struct fsnode * The root node to be converted
+ * @param struct cd9660* The parent node (should not be NULL)
+ * @param int Current directory depth
+ * @param int* Running count of the number of directories that are being created
+ */
+static void
+cd9660_convert_structure(fsnode *root, cd9660node *parent_node, int level,
+			 int *numDirectories, int *error)
+{
+	fsnode *iterator = root;
+	cd9660node *this_node;
+	int working_level;
+	int add;
+	int flag = 0;
+	int counter = 0;
+
+	/*
+	 * Newer, more efficient method, reduces recursion depth
+	 */
+	if (root == NULL) {
+		warnx("%s: root is null\n", __func__);
+		return;
+	}
+
+	/* Test for an empty directory - makefs still gives us the . record */
+	if ((S_ISDIR(root->type)) && (root->name[0] == '.')
+		&& (root->name[1] == '\0')) {
+		root = root->next;
+		if (root == NULL)
+			return;
+	}
+	if ((this_node = cd9660_allocate_cd9660node()) == NULL) {
+		CD9660_MEM_ALLOC_ERROR(__func__);
+	}
+
+	/*
+	 * To reduce the number of recursive calls, we will iterate over
+	 * the next pointers to the right.
+	 */
+	while (iterator != NULL) {
+		add = 1;
+		/*
+		 * Increment the directory count if this is a directory
+		 * Ignore "." entries. We will generate them later
+		 */
+		if (!S_ISDIR(iterator->type) ||
+		    strcmp(iterator->name, ".") != 0) {
+
+			/* Translate the node, including its filename */
+			this_node->parent = parent_node;
+			cd9660_translate_node(iterator, this_node);
+			this_node->level = level;
+
+			if (S_ISDIR(iterator->type)) {
+				(*numDirectories)++;
+				this_node->type = CD9660_TYPE_DIR;
+				working_level = level + 1;
+
+				/*
+				 * If at level 8, directory would be at 8
+				 * and have children at 9 which is not
+				 * allowed as per ISO spec
+				 */
+				if (level == 8) {
+					if ((!diskStructure.allow_deep_trees) &&
+					  (!diskStructure.rock_ridge_enabled)) {
+						warnx("error: found entry "
+						     "with depth greater "
+						     "than 8.");
+						(*error) = 1;
+						return;
+					} else if (diskStructure.
+						   rock_ridge_enabled) {
+						working_level = 3;
+						/*
+						 * Moved directory is actually
+						 * at level 2.
+						 */
+						this_node->level =
+						    working_level - 1;
+						if (cd9660_rrip_move_directory(
+							this_node) == NULL) {
+							warnx("Failure in "
+							      "cd9660_rrip_"
+							      "move_directory"
+							);
+							(*error) = 1;
+							return;
+						}
+						add = 0;
+					}
+				}
+
+				/* Do the recursive call on the children */
+				if (iterator->child != NULL) {
+					cd9660_convert_structure(
+					    iterator->child, this_node,
+						working_level,
+						numDirectories, error);
+
+					if ((*error) == 1) {
+						warnx("%s: Error on recursive "
+						    "call", __func__);
+						return;
+					}
+				}
+
+			} else {
+				/* Only directories should have children */
+				assert(iterator->child == NULL);
+
+				this_node->type = CD9660_TYPE_FILE;
+			}
+
+			/*
+			 * Finally, do a sorted insert
+			 */
+			if (add) {
+				cd9660_sorted_child_insert(
+				    parent_node, this_node);
+			}
+
+			/*Allocate new temp_node */
+			if (iterator->next != NULL) {
+				this_node = cd9660_allocate_cd9660node();
+				if (this_node == NULL)
+					CD9660_MEM_ALLOC_ERROR(__func__);
+			}
+		}
+		iterator = iterator->next;
+	}
+
+	/* cd9660_handle_collisions(first_node); */
+
+	/* TODO: need cleanup */
+	cd9660_copy_filenames(parent_node);
+
+	do {
+		flag = cd9660_handle_collisions(parent_node, counter);
+		counter++;
+		cd9660_sorting_nodes(parent_node);
+	} while ((flag == 1) && (counter < 100));
+}
+
+/*
+ * Clean up the cd9660node tree
+ * This is designed to be called recursively on the root node
+ * @param struct cd9660node *root The node to free
+ * @returns void
+ */
+static void
+cd9660_free_structure(cd9660node *root)
+{
+	cd9660node *cn;
+
+	while ((cn = TAILQ_FIRST(&root->cn_children)) != NULL) {
+		TAILQ_REMOVE(&root->cn_children, cn, cn_next_child);
+		cd9660_free_structure(cn);
+	}
+	free(root);
+}
+
+/*
+ * Be a little more memory conservative:
+ * instead of having the TAILQ_ENTRY as part of the cd9660node,
+ * just create a temporary structure
+ */
+struct ptq_entry
+{
+	TAILQ_ENTRY(ptq_entry) ptq;
+	cd9660node *node;
+} *n;
+
+#define PTQUEUE_NEW(n,s,r,t){\
+	n = malloc(sizeof(struct s));	\
+	if (n == NULL)	\
+		return r; \
+	n->node = t;\
+}
+
+/*
+ * Generate the path tables
+ * The specific implementation of this function is left as an exercise to the
+ * programmer. It could be done recursively. Make sure you read how the path
+ * table has to be laid out, it has levels.
+ * @param struct iso9660_disk *disk The disk image
+ * @returns int The number of built path tables (between 1 and 4), 0 on failure
+ */
+static int
+cd9660_generate_path_table(void)
+{
+	cd9660node *cn, *dirNode = diskStructure.rootNode;
+	cd9660node *last = dirNode;
+	int pathTableSize = 0;	/* computed as we go */
+	int counter = 1;	/* root gets a count of 0 */
+
+	TAILQ_HEAD(cd9660_pt_head, ptq_entry) pt_head;
+	TAILQ_INIT(&pt_head);
+
+	PTQUEUE_NEW(n, ptq_entry, -1, diskStructure.rootNode);
+
+	/* Push the root node */
+	TAILQ_INSERT_HEAD(&pt_head, n, ptq);
+
+	/* Breadth-first traversal of file structure */
+	while (pt_head.tqh_first != 0) {
+		n = pt_head.tqh_first;
+		dirNode = n->node;
+		TAILQ_REMOVE(&pt_head, pt_head.tqh_first, ptq);
+		free(n);
+
+		/* Update the size */
+		pathTableSize += ISO_PATHTABLE_ENTRY_BASESIZE
+		    + dirNode->isoDirRecord->name_len[0]+
+			(dirNode->isoDirRecord->name_len[0] % 2 == 0 ? 0 : 1);
+			/* includes the padding bit */
+
+		dirNode->ptnumber=counter;
+		if (dirNode != last) {
+			last->ptnext = dirNode;
+			dirNode->ptprev = last;
+		}
+		last = dirNode;
+
+		/* Push children onto queue */
+		TAILQ_FOREACH(cn, &dirNode->cn_children, cn_next_child) {
+			/*
+			 * Dont add the DOT and DOTDOT types to the path
+			 * table.
+			 */
+			if ((cn->type != CD9660_TYPE_DOT)
+				&& (cn->type != CD9660_TYPE_DOTDOT)) {
+
+				if (S_ISDIR(cn->node->type)) {
+					PTQUEUE_NEW(n, ptq_entry, -1, cn);
+					TAILQ_INSERT_TAIL(&pt_head, n, ptq);
+				}
+			}
+		}
+		counter++;
+	}
+	return pathTableSize;
+}
+
+void
+cd9660_compute_full_filename(cd9660node *node, char *buf)
+{
+	int len;
+
+	len = CD9660MAXPATH + 1;
+	len = snprintf(buf, len, "%s/%s/%s", node->node->root,
+	    node->node->path, node->node->name);
+	if (len > CD9660MAXPATH)
+		errx(1, "Pathname too long.");
+}
+
+/* NEW filename conversion method */
+typedef int(*cd9660_filename_conversion_functor)(const char *, char *, int);
+
+
+/*
+ * TODO: These two functions are almost identical.
+ * Some code cleanup is possible here
+ *
+ * XXX bounds checking!
+ */
+static int
+cd9660_level1_convert_filename(const char *oldname, char *newname, int is_file)
+{
+	/*
+	 * ISO 9660 : 10.1
+	 * File Name shall not contain more than 8 d or d1 characters
+	 * File Name Extension shall not contain more than 3 d or d1 characters
+	 * Directory Identifier shall not contain more than 8 d or d1 characters
+	 */
+	int namelen = 0;
+	int extlen = 0;
+	int found_ext = 0;
+
+	while (*oldname != '\0' && extlen < 3) {
+		/* Handle period first, as it is special */
+		if (*oldname == '.') {
+			if (found_ext) {
+				*newname++ = '_';
+				extlen ++;
+			}
+			else {
+				*newname++ = '.';
+				found_ext = 1;
+			}
+		} else {
+			/* cut RISC OS file type off ISO name */
+			if (diskStructure.archimedes_enabled &&
+			    *oldname == ',' && strlen(oldname) == 4)
+				break;
+
+			/* Enforce 12.3 / 8 */
+			if (namelen == 8 && !found_ext)
+				break;
+
+			if (islower((unsigned char)*oldname))
+				*newname++ = toupper((unsigned char)*oldname);
+			else if (isupper((unsigned char)*oldname)
+			    || isdigit((unsigned char)*oldname))
+				*newname++ = *oldname;
+			else
+				*newname++ = '_';
+
+			if (found_ext)
+				extlen++;
+			else
+				namelen++;
+		}
+		oldname++;
+	}
+	if (is_file) {
+		if (!found_ext && !diskStructure.omit_trailing_period)
+			*newname++ = '.';
+		/* Add version */
+		sprintf(newname, ";%i", 1);
+	}
+	return namelen + extlen + found_ext;
+}
+
+/* XXX bounds checking! */
+static int
+cd9660_level2_convert_filename(const char *oldname, char *newname, int is_file)
+{
+	/*
+	 * ISO 9660 : 7.5.1
+	 * File name : 0+ d or d1 characters
+	 * separator 1 (.)
+	 * File name extension : 0+ d or d1 characters
+	 * separator 2 (;)
+	 * File version number (5 characters, 1-32767)
+	 * 1 <= Sum of File name and File name extension <= 30
+	 */
+	int namelen = 0;
+	int extlen = 0;
+	int found_ext = 0;
+
+	while (*oldname != '\0' && namelen + extlen < 30) {
+		/* Handle period first, as it is special */
+		if (*oldname == '.') {
+			if (found_ext) {
+				if (diskStructure.allow_multidot) {
+					*newname++ = '.';
+				} else {
+					*newname++ = '_';
+				}
+				extlen ++;
+			}
+			else {
+				*newname++ = '.';
+				found_ext = 1;
+			}
+		} else {
+			/* cut RISC OS file type off ISO name */
+			if (diskStructure.archimedes_enabled &&
+			    *oldname == ',' && strlen(oldname) == 4)
+				break;
+
+			 if (islower((unsigned char)*oldname))
+				*newname++ = toupper((unsigned char)*oldname);
+			else if (isupper((unsigned char)*oldname) ||
+			    isdigit((unsigned char)*oldname))
+				*newname++ = *oldname;
+			else if (diskStructure.allow_multidot &&
+			    *oldname == '.') {
+			    	*newname++ = '.';
+			} else {
+				*newname++ = '_';
+			}
+
+			if (found_ext)
+				extlen++;
+			else
+				namelen++;
+		}
+		oldname ++;
+	}
+	if (is_file) {
+		if (!found_ext && !diskStructure.omit_trailing_period)
+			*newname++ = '.';
+		/* Add version */
+		sprintf(newname, ";%i", 1);
+	}
+	return namelen + extlen + found_ext;
+}
+
+#if 0
+static int
+cd9660_joliet_convert_filename(const char *oldname, char *newname, int is_file)
+{
+	/* TODO: implement later, move to cd9660_joliet.c ?? */
+}
+#endif
+
+
+/*
+ * Convert a file name to ISO compliant file name
+ * @param char * oldname The original filename
+ * @param char ** newname The new file name, in the appropriate character
+ *                        set and of appropriate length
+ * @param int 1 if file, 0 if directory
+ * @returns int The length of the new string
+ */
+static int
+cd9660_convert_filename(const char *oldname, char *newname, int is_file)
+{
+	assert(1 <= diskStructure.isoLevel && diskStructure.isoLevel <= 2);
+	/* NEW */
+	cd9660_filename_conversion_functor conversion_function = NULL;
+	if (diskStructure.isoLevel == 1)
+		conversion_function = &cd9660_level1_convert_filename;
+	else if (diskStructure.isoLevel == 2)
+		conversion_function = &cd9660_level2_convert_filename;
+	return (*conversion_function)(oldname, newname, is_file);
+}
+
+int
+cd9660_compute_record_size(cd9660node *node)
+{
+	int size = node->isoDirRecord->length[0];
+
+	if (diskStructure.rock_ridge_enabled)
+		size += node->susp_entry_size;
+	size += node->su_tail_size;
+	size += size & 1; /* Ensure length of record is even. */
+	assert(size <= 254);
+	return size;
+}
+
+static void
+cd9660_populate_dot_records(cd9660node *node)
+{
+	node->dot_record->fileDataSector = node->fileDataSector;
+	memcpy(node->dot_record->isoDirRecord,node->isoDirRecord, 34);
+	node->dot_record->isoDirRecord->name_len[0] = 1;
+	node->dot_record->isoDirRecord->name[0] = 0;
+	node->dot_record->isoDirRecord->name[1] = 0;
+	node->dot_record->isoDirRecord->length[0] = 34;
+	node->dot_record->fileRecordSize =
+	    cd9660_compute_record_size(node->dot_record);
+
+	if (node == diskStructure.rootNode) {
+		node->dot_dot_record->fileDataSector = node->fileDataSector;
+		memcpy(node->dot_dot_record->isoDirRecord,node->isoDirRecord,
+		    34);
+	} else {
+		node->dot_dot_record->fileDataSector =
+		    node->parent->fileDataSector;
+		memcpy(node->dot_dot_record->isoDirRecord,
+		    node->parent->isoDirRecord,34);
+	}
+	node->dot_dot_record->isoDirRecord->name_len[0] = 1;
+	node->dot_dot_record->isoDirRecord->name[0] = 1;
+	node->dot_dot_record->isoDirRecord->name[1] = 0;
+	node->dot_dot_record->isoDirRecord->length[0] = 34;
+	node->dot_dot_record->fileRecordSize =
+	    cd9660_compute_record_size(node->dot_dot_record);
+}
+
+/*
+ * @param struct cd9660node *node The node
+ * @param int The offset (in bytes) - SHOULD align to the beginning of a sector
+ * @returns int The total size of files and directory entries (should be
+ *              a multiple of sector size)
+*/
+static int64_t
+cd9660_compute_offsets(cd9660node *node, int64_t startOffset)
+{
+	/*
+	 * This function needs to compute the size of directory records and
+	 * runs, file lengths, and set the appropriate variables both in
+	 * cd9660node and isoDirEntry
+	 */
+	int64_t used_bytes = 0;
+	int64_t current_sector_usage = 0;
+	cd9660node *child;
+	fsinode *inode;
+	int64_t r;
+
+	assert(node != NULL);
+
+
+	/*
+	 * NOTE : There needs to be some special case detection for
+	 * the "real root" node, since for it, node->node is undefined
+	 */
+
+	node->fileDataSector = -1;
+
+	if (node->type & CD9660_TYPE_DIR) {
+		node->fileRecordSize = cd9660_compute_record_size(node);
+		/*Set what sector this directory starts in*/
+		node->fileDataSector =
+		    CD9660_BLOCKS(diskStructure.sectorSize,startOffset);
+
+		cd9660_bothendian_dword(node->fileDataSector,
+		    node->isoDirRecord->extent);
+
+		/*
+		 * First loop over children, need to know the size of
+		 * their directory records
+		 */
+		node->fileSectorsUsed = 1;
+		TAILQ_FOREACH(child, &node->cn_children, cn_next_child) {
+			node->fileDataLength +=
+			    cd9660_compute_record_size(child);
+			if ((cd9660_compute_record_size(child) +
+			    current_sector_usage) >=
+		 	    diskStructure.sectorSize) {
+				current_sector_usage = 0;
+				node->fileSectorsUsed++;
+			}
+
+			current_sector_usage +=
+			    cd9660_compute_record_size(child);
+		}
+
+		cd9660_bothendian_dword(node->fileSectorsUsed *
+			diskStructure.sectorSize,node->isoDirRecord->size);
+
+		/*
+		 * This should point to the sector after the directory
+		 * record (or, the first byte in that sector)
+		 */
+		used_bytes += node->fileSectorsUsed * diskStructure.sectorSize;
+
+		for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child);
+		     child != NULL; child = TAILQ_NEXT(child, cn_next_child)) {
+			/* Directories need recursive call */
+			if (S_ISDIR(child->node->type)) {
+				r = cd9660_compute_offsets(child,
+				    used_bytes + startOffset);
+
+				if (r != -1)
+					used_bytes += r;
+				else
+					return -1;
+			}
+		}
+
+		/* Explicitly set the . and .. records */
+		cd9660_populate_dot_records(node);
+
+		/* Finally, do another iteration to write the file data*/
+		for (child = TAILQ_NEXT(node->dot_dot_record, cn_next_child);
+		     child != NULL;
+		     child = TAILQ_NEXT(child, cn_next_child)) {
+			/* Files need extent set */
+			if (S_ISDIR(child->node->type))
+				continue;
+			child->fileRecordSize =
+			    cd9660_compute_record_size(child);
+
+			child->fileSectorsUsed =
+			    CD9660_BLOCKS(diskStructure.sectorSize,
+				child->fileDataLength);
+
+			inode = child->node->inode;
+			if ((inode->flags & FI_ALLOCATED) == 0) {
+				inode->ino =
+				    CD9660_BLOCKS(diskStructure.sectorSize,
+				        used_bytes + startOffset);
+				inode->flags |= FI_ALLOCATED;
+				used_bytes += child->fileSectorsUsed *
+				    diskStructure.sectorSize;
+			} else {
+				INODE_WARNX(("%s: already allocated inode %d "
+				      "data sectors at %" PRIu32, __func__,
+				      (int)inode->st.st_ino, inode->ino));
+			}
+			child->fileDataSector = inode->ino;
+			cd9660_bothendian_dword(child->fileDataSector,
+				child->isoDirRecord->extent);
+		}
+	}
+
+	return used_bytes;
+}
+
+#if 0
+/* Might get rid of this func */
+static int
+cd9660_copy_stat_info(cd9660node *from, cd9660node *to, int file)
+{
+	to->node->inode->st.st_dev = 0;
+	to->node->inode->st.st_ino = 0;
+	to->node->inode->st.st_size = 0;
+	to->node->inode->st.st_blksize = from->node->inode->st.st_blksize;
+	to->node->inode->st.st_atime = from->node->inode->st.st_atime;
+	to->node->inode->st.st_mtime = from->node->inode->st.st_mtime;
+	to->node->inode->st.st_ctime = from->node->inode->st.st_ctime;
+	to->node->inode->st.st_uid = from->node->inode->st.st_uid;
+	to->node->inode->st.st_gid = from->node->inode->st.st_gid;
+	to->node->inode->st.st_mode = from->node->inode->st.st_mode;
+	/* Clear out type */
+	to->node->inode->st.st_mode = to->node->inode->st.st_mode & ~(S_IFMT);
+	if (file)
+		to->node->inode->st.st_mode |= S_IFREG;
+	else
+		to->node->inode->st.st_mode |= S_IFDIR;
+	return 1;
+}
+#endif
+
+static cd9660node *
+cd9660_create_virtual_entry(const char *name, cd9660node *parent, int file,
+			    int insert)
+{
+	cd9660node *temp;
+	fsnode * tfsnode;
+
+	assert(parent != NULL);
+
+	temp = cd9660_allocate_cd9660node();
+	if (temp == NULL)
+		return NULL;
+
+	if ((tfsnode = malloc(sizeof(fsnode))) == NULL) {
+		CD9660_MEM_ALLOC_ERROR("cd9660_create_virtual_entry");
+		return NULL;
+	}
+
+	/* Assume for now name is a valid length */
+	if ((tfsnode->name = malloc(strlen(name) + 1)) == NULL) {
+		CD9660_MEM_ALLOC_ERROR("cd9660_create_virtual_entry");
+		return NULL;
+	}
+
+	if ((temp->isoDirRecord =
+	    malloc(sizeof(iso_directory_record_cd9660))) == NULL) {
+		CD9660_MEM_ALLOC_ERROR("cd9660_create_virtual_entry");
+		return NULL;
+	}
+
+	strcpy(tfsnode->name, name);
+
+	cd9660_convert_filename(tfsnode->name, temp->isoDirRecord->name, file);
+
+	temp->node = tfsnode;
+	temp->parent = parent;
+
+	if (insert) {
+		if (temp->parent != NULL) {
+			temp->level = temp->parent->level + 1;
+			if (!TAILQ_EMPTY(&temp->parent->cn_children))
+				cd9660_sorted_child_insert(temp->parent, temp);
+			else
+				TAILQ_INSERT_HEAD(&temp->parent->cn_children,
+				    temp, cn_next_child);
+		}
+	}
+
+	if (parent->node != NULL) {
+		tfsnode->type = parent->node->type;
+	}
+
+	/* Clear out file type bits */
+	tfsnode->type &= ~(S_IFMT);
+	if (file)
+		tfsnode->type |= S_IFREG;
+	else
+		tfsnode->type |= S_IFDIR;
+
+	/* Indicate that there is no spec entry (inode) */
+	tfsnode->flags &= ~(FSNODE_F_HASSPEC);
+#if 0
+	cd9660_copy_stat_info(parent, temp, file);
+#endif
+	return temp;
+}
+
+static cd9660node *
+cd9660_create_file(const char * name, cd9660node *parent, cd9660node *me)
+{
+	cd9660node *temp;
+
+	temp = cd9660_create_virtual_entry(name,parent,1,1);
+	if (temp == NULL)
+		return NULL;
+
+	temp->fileDataLength = 0;
+
+	temp->type = CD9660_TYPE_FILE | CD9660_TYPE_VIRTUAL;
+
+	if ((temp->node->inode = calloc(1, sizeof(fsinode))) == NULL)
+		return NULL;
+	*temp->node->inode = *me->node->inode;
+
+	if (cd9660_translate_node_common(temp) == 0)
+		return NULL;
+	return temp;
+}
+
+/*
+ * Create a new directory which does not exist on disk
+ * @param const char * name The name to assign to the directory
+ * @param const char * parent Pointer to the parent directory
+ * @returns cd9660node * Pointer to the new directory
+ */
+static cd9660node *
+cd9660_create_directory(const char *name, cd9660node *parent, cd9660node *me)
+{
+	cd9660node *temp;
+
+	temp = cd9660_create_virtual_entry(name,parent,0,1);
+	if (temp == NULL)
+		return NULL;
+	temp->node->type |= S_IFDIR;
+
+	temp->type = CD9660_TYPE_DIR | CD9660_TYPE_VIRTUAL;
+
+	if ((temp->node->inode = calloc(1, sizeof(fsinode))) == NULL)
+		return NULL;
+	*temp->node->inode = *me->node->inode;
+
+	if (cd9660_translate_node_common(temp) == 0)
+		return NULL;
+	return temp;
+}
+
+static cd9660node *
+cd9660_create_special_directory(u_char type, cd9660node *parent)
+{
+	cd9660node *temp, *first;
+	char na[2];
+
+	assert(parent != NULL);
+
+	if (type == CD9660_TYPE_DOT)
+		na[0] = 0;
+	else if (type == CD9660_TYPE_DOTDOT)
+		na[0] = 1;
+	else
+		return 0;
+
+	na[1] = 0;
+	if ((temp = cd9660_create_virtual_entry(na, parent, 0, 0)) == NULL)
+		return NULL;
+
+	temp->parent = parent;
+	temp->type = type;
+	temp->isoDirRecord->length[0] = 34;
+	/* Dot record is always first */
+	if (type == CD9660_TYPE_DOT) {
+		parent->dot_record = temp;
+		TAILQ_INSERT_HEAD(&parent->cn_children, temp, cn_next_child);
+	/* DotDot should be second */
+	} else if (type == CD9660_TYPE_DOTDOT) {
+		parent->dot_dot_record = temp;
+		/*
+                 * If the first child is the dot record, insert
+                 * this second.  Otherwise, insert it at the head.
+		 */
+		if ((first = TAILQ_FIRST(&parent->cn_children)) == NULL ||
+		    (first->type & CD9660_TYPE_DOT) == 0) {
+			TAILQ_INSERT_HEAD(&parent->cn_children, temp,
+			    cn_next_child);
+		} else {
+			TAILQ_INSERT_AFTER(&parent->cn_children, first, temp,
+			    cn_next_child);
+		}
+	}
+
+	return temp;
+}
+
+int
+cd9660_add_generic_bootimage(const char *bootimage)
+{
+	struct stat stbuf;
+
+	assert(bootimage != NULL);
+
+	if (*bootimage == '\0') {
+		warnx("Error: Boot image must be a filename");
+		return 0;
+	}
+
+	if ((diskStructure.generic_bootimage = strdup(bootimage)) == NULL) {
+		warn("%s: strdup", __func__);
+		return 0;
+	}
+
+	/* Get information about the file */
+	if (lstat(diskStructure.generic_bootimage, &stbuf) == -1)
+		err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__,
+		    diskStructure.generic_bootimage);
+
+	if (stbuf.st_size > 32768) {
+		warnx("Error: Boot image must be no greater than 32768 bytes");
+		return 0;
+	}
+
+	if (diskStructure.verbose_level > 0) {
+		printf("Generic boot image image has size %lld\n",
+		    (long long)stbuf.st_size);
+	}
+
+	diskStructure.has_generic_bootimage = 1;
+
+	return 1;
+}
diff --git a/usr.sbin/makefs/cd9660.h b/usr.sbin/makefs/cd9660.h
new file mode 100644
index 0000000000..86a0e4a5c9
--- /dev/null
+++ b/usr.sbin/makefs/cd9660.h
@@ -0,0 +1,364 @@
+/*	$NetBSD: cd9660.h,v 1.17 2011/06/23 02:35:56 enami Exp $	*/
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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: head/usr.sbin/makefs/cd9660.h 230795 2012-01-31 00:32:37Z jkim $
+ */
+
+#ifndef _MAKEFS_CD9660_H
+#define _MAKEFS_CD9660_H
+
+#include <inttypes.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/endian.h>
+
+#include "makefs.h"
+#include "iso.h"
+#include "iso_rrip.h"
+#include "cd9660/cd9660_eltorito.h"
+
+#ifdef DEBUG
+#define	INODE_WARNX(__x)	warnx __x
+#else /* DEBUG */
+#define	INODE_WARNX(__x)
+#endif /* DEBUG */
+
+#define CD9660MAXPATH 4096
+
+#define ISO_STRING_FILTER_NONE = 0x00
+#define ISO_STRING_FILTER_DCHARS = 0x01
+#define ISO_STRING_FILTER_ACHARS = 0x02
+
+/*
+Extended preferences type, in the spirit of what makefs gives us (only ints)
+*/
+typedef struct {
+	const char  *shortName;		/* Short option */
+	const char	*name;		/* option name */
+	char		*value;		/* where to stuff the value */
+	int		minLength;	/* minimum for value */
+	int		maxLength;	/* maximum for value */
+	const char	*desc;		/* option description */
+	int		filterFlags;
+} string_option_t;
+
+/******** STRUCTURES **********/
+
+/*Defaults*/
+#define ISO_DEFAULT_VOLUMEID "MAKEFS_CD9660_IMAGE"
+#define ISO_DEFAULT_APPID "MAKEFS"
+#define ISO_DEFAULT_PUBLISHER "MAKEFS"
+#define ISO_DEFAULT_PREPARER "MAKEFS"
+
+#define ISO_VOLUME_DESCRIPTOR_STANDARD_ID "CD001"
+#define ISO_VOLUME_DESCRIPTOR_BOOT 0
+#define ISO_VOLUME_DESCRIPTOR_PVD 1
+#define ISO_VOLUME_DESCRIPTOR_TERMINATOR 255
+
+/*30 for name and extension, as well as version number and padding bit*/
+#define ISO_FILENAME_MAXLENGTH_BEFORE_VERSION 30
+#define ISO_FILENAME_MAXLENGTH	36
+#define ISO_FILENAME_MAXLENGTH_WITH_PADDING 37
+
+#define ISO_FLAG_CLEAR 0x00
+#define ISO_FLAG_HIDDEN 0x01
+#define ISO_FLAG_DIRECTORY 0x02
+#define ISO_FLAG_ASSOCIATED 0x04
+#define ISO_FLAG_PERMISSIONS 0x08
+#define ISO_FLAG_RESERVED5 0x10
+#define ISO_FLAG_RESERVED6 0x20
+#define ISO_FLAG_FINAL_RECORD 0x40
+
+#define ISO_PATHTABLE_ENTRY_BASESIZE 8
+
+#define ISO_RRIP_DEFAULT_MOVE_DIR_NAME "RR_MOVED"
+#define RRIP_DEFAULT_MOVE_DIR_NAME ".rr_moved"
+
+#define	CD9660_BLOCKS(__sector_size, __bytes)	\
+	howmany((__bytes), (__sector_size))
+
+#define CD9660_MEM_ALLOC_ERROR(_F)	\
+    err(EXIT_FAILURE, "%s, %s l. %d", _F, __FILE__, __LINE__)
+
+#define CD9660_IS_COMMAND_ARG_DUAL(var,short,long)\
+		(strcmp((var),(short)) == 0) || (strcmp((var),(long))==0)
+
+#define CD9660_IS_COMMAND_ARG(var,arg)\
+		(strcmp((var),(arg)) == 0)
+
+#define CD9660_TYPE_FILE	0x01
+#define CD9660_TYPE_DIR		0x02
+#define CD9660_TYPE_DOT		0x04
+#define CD9660_TYPE_DOTDOT	0x08
+#define CD9660_TYPE_VIRTUAL	0x80
+
+#define CD9660_INODE_HASH_SIZE	1024
+#define CD9660_SECTOR_SIZE	2048
+
+#define CD9660_END_PADDING	150
+
+/* Slight modification of the ISO structure in iso.h */
+typedef struct _iso_directory_record_cd9660 {
+	u_char length			[ISODCL (1, 1)];	/* 711 */
+	u_char ext_attr_length		[ISODCL (2, 2)];	/* 711 */
+	u_char extent			[ISODCL (3, 10)];	/* 733 */
+	u_char size			[ISODCL (11, 18)];	/* 733 */
+	u_char date			[ISODCL (19, 25)];	/* 7 by 711 */
+	u_char flags			[ISODCL (26, 26)];
+	u_char file_unit_size		[ISODCL (27, 27)];	/* 711 */
+	u_char interleave		[ISODCL (28, 28)];	/* 711 */
+	u_char volume_sequence_number	[ISODCL (29, 32)];	/* 723 */
+	u_char name_len			[ISODCL (33, 33)];	/* 711 */
+	char name			[ISO_FILENAME_MAXLENGTH_WITH_PADDING];
+} iso_directory_record_cd9660;
+
+/* TODO: Lots of optimization of this structure */
+typedef struct _cd9660node {
+	u_char	type;/* Used internally */
+	/* Tree structure */
+	struct _cd9660node	*parent;	/* parent (NULL if root) */
+	TAILQ_HEAD(cd9660_children_head, _cd9660node)	cn_children;
+	TAILQ_ENTRY(_cd9660node)		cn_next_child;
+
+	struct _cd9660node *dot_record; /* For directories, used mainly in RRIP */
+	struct _cd9660node *dot_dot_record;
+
+	fsnode		*node;		/* pointer to fsnode */
+	struct _iso_directory_record_cd9660	*isoDirRecord;
+	struct iso_extended_attributes	*isoExtAttributes;
+
+	/***** SIZE CALCULATION *****/
+	/*already stored in isoDirRecord, but this is an int version, and will be
+		copied to isoDirRecord on writing*/
+	uint32_t fileDataSector;
+
+	/*
+	 * same thing, though some notes:
+	 * If a file, this is the file size
+	 * If a directory, this is the size of all its children's
+	 *	directory records
+	 * plus necessary padding
+	 */
+	int64_t fileDataLength;
+
+	int64_t fileSectorsUsed;
+	int fileRecordSize;/*copy of a variable, int for quicker calculations*/
+
+	/* Old name, used for renaming - needs to be optimized but low priority */
+	char o_name [ISO_FILENAME_MAXLENGTH_WITH_PADDING];
+
+	/***** SPACE RESERVED FOR EXTENSIONS *****/
+	/* For memory efficiency's sake - we should move this to a separate struct
+		and point to null if not needed */
+	/* For Rock Ridge */
+	struct _cd9660node *rr_real_parent, *rr_relocated;
+
+	int64_t susp_entry_size;
+	int64_t susp_dot_entry_size;
+	int64_t susp_dot_dot_entry_size;
+
+	/* Continuation area stuff */
+	int64_t susp_entry_ce_start;
+	int64_t susp_dot_ce_start;
+	int64_t susp_dot_dot_ce_start;
+
+	int64_t susp_entry_ce_length;
+	int64_t susp_dot_ce_length;
+	int64_t susp_dot_dot_ce_length;
+
+	/* Data to put at the end of the System Use field */
+	int64_t su_tail_size;
+	char *su_tail_data;
+
+	/*** PATH TABLE STUFF ***/
+	int level;			/*depth*/
+	int ptnumber;
+	struct _cd9660node *ptnext, *ptprev, *ptlast;
+
+	/* SUSP entries */
+	TAILQ_HEAD(susp_linked_list, ISO_SUSP_ATTRIBUTES) head;
+} cd9660node;
+
+typedef struct _path_table_entry
+{
+	u_char length[ISODCL (1, 1)];
+	u_char extended_attribute_length[ISODCL (2, 2)];
+	u_char first_sector[ISODCL (3, 6)];
+	u_char parent_number[ISODCL (7, 8)];
+	u_char name[ISO_FILENAME_MAXLENGTH_WITH_PADDING];
+} path_table_entry;
+
+typedef struct _volume_descriptor
+{
+	u_char *volumeDescriptorData; /*ALWAYS 2048 bytes long*/
+	int64_t sector;
+	struct _volume_descriptor *next;
+} volume_descriptor;
+
+typedef struct _iso9660_disk {
+	int sectorSize;
+	struct iso_primary_descriptor		primaryDescriptor;
+	struct iso_supplementary_descriptor	supplementaryDescriptor;
+
+	volume_descriptor *firstVolumeDescriptor;
+
+	cd9660node *rootNode;
+
+	/* Important sector numbers here */
+	/* primaryDescriptor.type_l_path_table*/
+	int64_t primaryBigEndianTableSector;
+
+	/* primaryDescriptor.type_m_path_table*/
+	int64_t primaryLittleEndianTableSector;
+
+	/* primaryDescriptor.opt_type_l_path_table*/
+	int64_t secondaryBigEndianTableSector;
+
+	/* primaryDescriptor.opt_type_m_path_table*/
+	int64_t secondaryLittleEndianTableSector;
+
+	/* primaryDescriptor.path_table_size*/
+	int pathTableLength;
+	int64_t dataFirstSector;
+
+	int64_t totalSectors;
+	/* OPTIONS GO HERE */
+	int	isoLevel;
+
+	int include_padding_areas;
+
+	int follow_sym_links;
+	int verbose_level;
+	int displayHelp;
+	int keep_bad_images;
+
+	/* SUSP options and variables */
+	int64_t susp_continuation_area_start_sector;
+	int64_t susp_continuation_area_size;
+	int64_t susp_continuation_area_current_free;
+
+	int rock_ridge_enabled;
+	/* Other Rock Ridge Variables */
+	char *rock_ridge_renamed_dir_name;
+	int rock_ridge_move_count;
+	cd9660node *rr_moved_dir;
+
+	int archimedes_enabled;
+	int chrp_boot;
+
+	/* Spec breaking options */
+	u_char allow_deep_trees;
+	u_char allow_start_dot;
+	u_char allow_max_name; /* Allow 37 char filenames*/
+	u_char allow_illegal_chars; /* ~, !, # */
+	u_char allow_lowercase;
+	u_char allow_multidot;
+	u_char omit_trailing_period;
+
+	/* BOOT INFORMATION HERE */
+	int has_generic_bootimage; /* Default to 0 */
+	char *generic_bootimage;
+
+	int is_bootable;/* Default to 0 */
+	int64_t boot_catalog_sector;
+	boot_volume_descriptor *boot_descriptor;
+	char * boot_image_directory;
+
+	TAILQ_HEAD(boot_image_list,cd9660_boot_image) boot_images;
+	int image_serialno;
+	LIST_HEAD(boot_catalog_entries,boot_catalog_entry) boot_entries;
+
+} iso9660_disk;
+
+/******** GLOBAL VARIABLES ***********/
+extern iso9660_disk diskStructure;
+
+/************ FUNCTIONS **************/
+int			cd9660_valid_a_chars(const char *);
+int			cd9660_valid_d_chars(const char *);
+void			cd9660_uppercase_characters(char *, int);
+
+/* ISO Data Types */
+void			cd9660_721(uint16_t, unsigned char *);
+void			cd9660_731(uint32_t, unsigned char *);
+void			cd9660_722(uint16_t, unsigned char *);
+void			cd9660_732(uint32_t, unsigned char *);
+void 			cd9660_bothendian_dword(uint32_t dw, unsigned char *);
+void 			cd9660_bothendian_word(uint16_t dw, unsigned char *);
+void			cd9660_set_date(char *, time_t);
+void			cd9660_time_8426(unsigned char *, time_t);
+void			cd9660_time_915(unsigned char *, time_t);
+
+/*** Boot Functions ***/
+int	cd9660_write_generic_bootimage(FILE *);
+int	cd9660_add_generic_bootimage(const char *);
+int	cd9660_write_boot(FILE *);
+int	cd9660_add_boot_disk(const char *);
+int	cd9660_eltorito_add_boot_option(const char *, const char *);
+int	cd9660_setup_boot(int);
+int	cd9660_setup_boot_volume_descriptor(volume_descriptor *);
+
+
+/*** Write Functions ***/
+int	cd9660_write_image(const char *image);
+int	cd9660_copy_file(FILE *, off_t, const char *);
+
+void	cd9660_compute_full_filename(cd9660node *, char *);
+int	cd9660_compute_record_size(cd9660node *);
+
+/* Debugging functions */
+void	debug_print_tree(cd9660node *,int);
+void	debug_print_path_tree(cd9660node *);
+void	debug_print_volume_descriptor_information(void);
+void	debug_dump_to_xml_ptentry(path_table_entry *,int, int);
+void	debug_dump_to_xml_path_table(FILE *, off_t, int, int);
+void	debug_dump_to_xml(FILE *);
+int	debug_get_encoded_number(unsigned char *, int);
+void	debug_dump_integer(const char *, char *,int);
+void	debug_dump_string(const char *,unsigned char *,int);
+void	debug_dump_directory_record_9_1(unsigned char *);
+void	debug_dump_to_xml_volume_descriptor(unsigned char *,int);
+
+void	cd9660_pad_string_spaces(char *, int);
+
+#endif
diff --git a/usr.sbin/makefs/cd9660/Makefile.inc b/usr.sbin/makefs/cd9660/Makefile.inc
new file mode 100644
index 0000000000..4dd7caefc8
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/Makefile.inc
@@ -0,0 +1,9 @@
+#	$FreeBSD: head/usr.sbin/makefs/cd9660/Makefile.inc 214921 2010-11-07 16:05:04Z cognet $
+#
+
+.PATH:	${.CURDIR}/cd9660 ${.CURDIR}/../../sys/vfs/isofs/
+
+CFLAGS+=-I${.CURDIR}/../../sys/vfs/isofs/cd9660/
+
+SRCS+=	cd9660_strings.c cd9660_debug.c cd9660_eltorito.c \
+	cd9660_write.c cd9660_conversion.c iso9660_rrip.c cd9660_archimedes.c
diff --git a/usr.sbin/makefs/cd9660/cd9660_archimedes.c b/usr.sbin/makefs/cd9660/cd9660_archimedes.c
new file mode 100644
index 0000000000..5e39309f9f
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_archimedes.c
@@ -0,0 +1,128 @@
+/* $NetBSD: cd9660_archimedes.c,v 1.1 2009/01/10 22:06:29 bjh21 Exp $ */
+
+/*-
+ * Copyright (c) 1998, 2009 Ben Harris
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * 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.
+ */
+/*
+ * cd9660_archimedes.c - support for RISC OS "ARCHIMEDES" extension
+ *
+ * RISC OS CDFS looks for a special block at the end of the System Use
+ * Field for each file.  If present, this contains the RISC OS load
+ * and exec address (used to hold the file timestamp and type), the
+ * file attributes, and a flag indicating whether the first character
+ * of the filename should be replaced with '!' (since many special
+ * RISC OS filenames do).
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/cd9660/cd9660_archimedes.c 307927 2016-10-25 16:29:15Z marcel $");
+
+#include <sys/types.h>
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "makefs.h"
+#include "cd9660.h"
+#include "cd9660_archimedes.h"
+
+/*
+ * Convert a Unix time_t (non-leap seconds since 1970-01-01) to a RISC
+ * OS time (non-leap(?) centiseconds since 1900-01-01(?)).
+ */
+
+static u_int64_t
+riscos_date(time_t unixtime)
+{
+	u_int64_t base;
+
+	base = 31536000ULL * 70 + 86400 * 17;
+	return (((u_int64_t)unixtime) + base)*100;
+}
+
+/*
+ * Add "ARCHIMEDES" metadata to a node if that seems appropriate.
+ *
+ * We touch regular files with names matching /,[0-9a-f]{3}$/ and
+ * directories matching /^!/.
+ */
+static void
+archimedes_convert_node(cd9660node *node)
+{
+	struct ISO_ARCHIMEDES *arc;
+	size_t len;
+	int type = -1;
+	uint64_t stamp;
+
+	if (node->su_tail_data != NULL)
+		/* Something else already has the tail. */
+		return;
+
+	len = strlen(node->node->name);
+	if (len < 1) return;
+
+	if (len >= 4 && node->node->name[len-4] == ',')
+		/* XXX should support ,xxx and ,lxa */
+		type = strtoul(node->node->name + len - 3, NULL, 16);
+	if (type == -1 && node->node->name[0] != '!')
+		return;
+	if (type == -1) type = 0;
+
+	assert(sizeof(struct ISO_ARCHIMEDES) == 32);
+	if ((arc = calloc(1, sizeof(struct ISO_ARCHIMEDES))) == NULL) {
+		CD9660_MEM_ALLOC_ERROR("archimedes_convert_node");
+		exit(1);
+	}
+
+	stamp = riscos_date(node->node->inode->st.st_mtime);
+
+	memcpy(arc->magic, "ARCHIMEDES", 10);
+	cd9660_731(0xfff00000 | (type << 8) | (stamp >> 32), arc->loadaddr);
+	cd9660_731(stamp & 0x00ffffffffULL, arc->execaddr);
+	arc->ro_attr = RO_ACCESS_UR | RO_ACCESS_OR;
+	arc->cdfs_attr = node->node->name[0] == '!' ? CDFS_PLING : 0;
+	node->su_tail_data = (void *)arc;
+	node->su_tail_size = sizeof(*arc);
+}
+
+/*
+ * Add "ARCHIMEDES" metadata to an entire tree recursively.
+ */
+void
+archimedes_convert_tree(cd9660node *node)
+{
+	cd9660node *cn;
+
+	assert(node != NULL);
+
+	archimedes_convert_node(node);
+
+		/* Recurse on children. */
+	TAILQ_FOREACH(cn, &node->cn_children, cn_next_child)
+		archimedes_convert_tree(cn);
+}
diff --git a/usr.sbin/makefs/cd9660/cd9660_archimedes.h b/usr.sbin/makefs/cd9660/cd9660_archimedes.h
new file mode 100644
index 0000000000..60b4921a56
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_archimedes.h
@@ -0,0 +1,50 @@
+/* $NetBSD: cd9660_archimedes.h,v 1.1 2009/01/10 22:06:29 bjh21 Exp $ */
+
+/*-
+ * Copyright (c) 1998, 2009 Ben Harris
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ * 
+ * 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.
+ */
+/*
+ * cd9660_archimedes.c - support for RISC OS "ARCHIMEDES" extension
+ *
+ * $FreeBSD: head/usr.sbin/makefs/cd9660/cd9660_archimedes.h 214921 2010-11-07 16:05:04Z cognet $
+ */
+
+struct ISO_ARCHIMEDES {
+	char		magic[10];	/* "ARCHIMEDES" */
+	unsigned char	loadaddr[4];	/* Load address, little-endian */
+	unsigned char	execaddr[4];	/* Exec address, little-endian */
+	unsigned char	ro_attr;	/* RISC OS attributes */
+#define RO_ACCESS_UR	0x01 /* Owner read */
+#define RO_ACCESS_UW	0x02 /* Owner write */
+#define RO_ACCESS_L	0x04 /* Locked */
+#define RO_ACCESS_OR	0x10 /* Public read */
+#define RO_ACCESS_OW	0x20 /* Public write */
+	unsigned char	cdfs_attr;	/* Extra attributes for CDFS */
+#define CDFS_PLING	0x01	/* Filename begins with '!' */
+	char		reserved[12];
+};
+
+extern void archimedes_convert_tree(cd9660node *);
diff --git a/usr.sbin/makefs/cd9660/cd9660_conversion.c b/usr.sbin/makefs/cd9660/cd9660_conversion.c
new file mode 100644
index 0000000000..33a26385ce
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_conversion.c
@@ -0,0 +1,200 @@
+/*	$NetBSD: cd9660_conversion.c,v 1.4 2007/03/14 14:11:17 christos Exp $	*/
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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 "cd9660.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/cd9660/cd9660_conversion.c 224762 2011-08-10 19:12:21Z marius $");
+
+static char cd9660_compute_gm_offset(time_t);
+
+#if 0
+static inline int
+cd9660_pad_even(length)
+int length;
+{
+	return length + (length & 0x01);
+}
+#endif
+
+/*
+* These can probably be implemented using a macro
+*/
+
+/* Little endian */
+void
+cd9660_721(uint16_t w, unsigned char *twochar)
+{
+#if BYTE_ORDER == BIG_ENDIAN
+	w = bswap16(w);
+#endif
+	memcpy(twochar,&w,2);
+}
+
+void
+cd9660_731(uint32_t w, unsigned char *fourchar)
+{
+#if BYTE_ORDER == BIG_ENDIAN
+	w = bswap32(w);
+#endif
+	memcpy(fourchar,&w,4);
+}
+
+/* Big endian */
+void
+cd9660_722(uint16_t w, unsigned char *twochar)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+	w = bswap16(w);
+#endif
+	memcpy(twochar,&w,2);
+}
+
+void
+cd9660_732(uint32_t w, unsigned char *fourchar)
+{
+#if BYTE_ORDER == LITTLE_ENDIAN
+	w = bswap32(w);
+#endif
+	memcpy(fourchar,&w,4);
+}
+
+/**
+* Convert a dword into a double endian string of eight characters
+* @param int The double word to convert
+* @param char* The string to write the both endian double word to - It is assumed this is allocated and at least
+*		eight characters long
+*/
+void
+cd9660_bothendian_dword(uint32_t dw, unsigned char *eightchar)
+{
+	uint32_t le, be;
+#if BYTE_ORDER == LITTLE_ENDIAN
+	le = dw;
+	be = bswap32(dw);
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+	be = dw;
+	le = bswap32(dw);
+#endif
+	memcpy(eightchar, &le, 4);
+	memcpy((eightchar+4), &be, 4);
+}
+
+/**
+* Convert a word into a double endian string of four characters
+* @param int The word to convert
+* @param char* The string to write the both endian word to - It is assumed this is allocated and at least
+*		four characters long
+*/
+void
+cd9660_bothendian_word(uint16_t dw, unsigned char *fourchar)
+{
+	uint16_t le, be;
+#if BYTE_ORDER == LITTLE_ENDIAN
+	le = dw;
+	be = bswap16(dw);
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+	be = dw;
+	le = bswap16(dw);
+#endif
+	memcpy(fourchar, &le, 2);
+	memcpy((fourchar+2), &be, 2);
+}
+
+void
+cd9660_pad_string_spaces(char *str, int len)
+{
+	int i;
+
+	for (i = 0; i < len; i ++) {
+		if (str[i] == '\0')
+			str[i] = 0x20;
+	}
+}
+
+static char
+cd9660_compute_gm_offset(time_t tim)
+{
+	struct tm t, gm;
+
+	(void)localtime_r(&tim, &t);
+	(void)gmtime_r(&tim, &gm);
+	gm.tm_year -= t.tm_year;
+	gm.tm_yday -= t.tm_yday;
+	gm.tm_hour -= t.tm_hour;
+	gm.tm_min  -= t.tm_min;
+	if (gm.tm_year < 0)
+		gm.tm_yday = -1;
+	else if (gm.tm_year > 0)
+		gm.tm_yday = 1;
+
+	return (char)(-(gm.tm_min + 60* (24 * gm.tm_yday + gm.tm_hour)) / 15);
+}
+
+/* Long dates: 17 characters */
+void
+cd9660_time_8426(unsigned char *buf, time_t tim)
+{
+	struct tm t;
+	char temp[18];
+
+	(void)localtime_r(&tim, &t);
+	(void)snprintf(temp, sizeof(temp), "%04i%02i%02i%02i%02i%02i%02i",
+		1900+(int)t.tm_year,
+		(int)t.tm_mon+1,
+		(int)t.tm_mday,
+		(int)t.tm_hour,
+		(int)t.tm_min,
+		(int)t.tm_sec,
+		0);
+	(void)memcpy(buf, temp, 16);
+	buf[16] = cd9660_compute_gm_offset(tim);
+}
+
+/* Short dates: 7 characters */
+void
+cd9660_time_915(unsigned char *buf, time_t tim)
+{
+	struct tm t;
+
+	(void)localtime_r(&tim, &t);
+	buf[0] = t.tm_year;
+	buf[1] = t.tm_mon+1;
+	buf[2] = t.tm_mday;
+	buf[3] = t.tm_hour;
+	buf[4] = t.tm_min;
+	buf[5] = t.tm_sec;
+	buf[6] = cd9660_compute_gm_offset(tim);
+}
diff --git a/usr.sbin/makefs/cd9660/cd9660_debug.c b/usr.sbin/makefs/cd9660/cd9660_debug.c
new file mode 100644
index 0000000000..7088a2c8de
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_debug.c
@@ -0,0 +1,488 @@
+/*	$NetBSD: cd9660_debug.c,v 1.11 2010/10/27 18:51:35 christos Exp $	*/
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/cd9660/cd9660_debug.c 214921 2010-11-07 16:05:04Z cognet $");
+#include <sys/param.h>
+
+#include <sys/mount.h>
+
+#include "makefs.h"
+#include "cd9660.h"
+#include "iso9660_rrip.h"
+
+static void debug_print_susp_attrs(cd9660node *, int);
+static void debug_dump_to_xml_padded_hex_output(const char *, unsigned char *,
+						int);
+
+static inline void
+print_n_tabs(int n)
+{
+	int i;
+
+	for (i = 1; i <= n; i ++)
+		printf("\t");
+}
+
+#if 0
+void
+debug_print_rrip_info(n)
+cd9660node *n;
+{
+	struct ISO_SUSP_ATTRIBUTES *t;
+	TAILQ_FOREACH(t, &node->head, rr_ll) {
+
+	}
+}
+#endif
+
+static void
+debug_print_susp_attrs(cd9660node *n, int indent)
+{
+	struct ISO_SUSP_ATTRIBUTES *t;
+
+	TAILQ_FOREACH(t, &n->head, rr_ll) {
+		print_n_tabs(indent);
+		printf("-");
+		printf("%c%c: L:%i",t->attr.su_entry.SP.h.type[0],
+		    t->attr.su_entry.SP.h.type[1],
+		    (int)t->attr.su_entry.SP.h.length[0]);
+		printf("\n");
+	}
+}
+
+void
+debug_print_tree(cd9660node *node, int level)
+{
+#if !HAVE_NBTOOL_CONFIG_H
+	cd9660node *cn;
+
+	print_n_tabs(level);
+	if (node->type & CD9660_TYPE_DOT) {
+		printf(". (%i)\n",
+		    isonum_733(node->isoDirRecord->extent));
+	} else if (node->type & CD9660_TYPE_DOTDOT) {
+		printf("..(%i)\n",
+		    isonum_733(node->isoDirRecord->extent));
+	} else if (node->isoDirRecord->name[0]=='\0') {
+		printf("(ROOT) (%" PRIu32 " to %" PRId64 ")\n",
+		    node->fileDataSector,
+		    node->fileDataSector +
+			node->fileSectorsUsed - 1);
+	} else {
+		printf("%s (%s) (%" PRIu32 " to %" PRId64 ")\n",
+		    node->isoDirRecord->name,
+		    (node->isoDirRecord->flags[0]
+			& ISO_FLAG_DIRECTORY) ?  "DIR" : "FILE",
+		    node->fileDataSector,
+		    (node->fileSectorsUsed == 0) ?
+			node->fileDataSector :
+			node->fileDataSector
+			    + node->fileSectorsUsed - 1);
+	}
+	if (diskStructure.rock_ridge_enabled)
+		debug_print_susp_attrs(node, level + 1);
+	TAILQ_FOREACH(cn, &node->cn_children, cn_next_child)
+		debug_print_tree(cn, level + 1);
+#else
+	printf("Sorry, debugging is not supported in host-tools mode.\n");
+#endif
+}
+
+void
+debug_print_path_tree(cd9660node *n)
+{
+	cd9660node *iterator = n;
+
+	/* Only display this message when called with the root node */
+	if (n->parent == NULL)
+		printf("debug_print_path_table: Dumping path table contents\n");
+
+	while (iterator != NULL) {
+		if (iterator->isoDirRecord->name[0] == '\0')
+			printf("0) (ROOT)\n");
+		else
+			printf("%i) %s\n", iterator->level,
+			    iterator->isoDirRecord->name);
+
+		iterator = iterator->ptnext;
+	}
+}
+
+void
+debug_print_volume_descriptor_information(void)
+{
+	volume_descriptor *tmp = diskStructure.firstVolumeDescriptor;
+	char temp[CD9660_SECTOR_SIZE];
+
+	printf("==Listing Volume Descriptors==\n");
+
+	while (tmp != NULL) {
+		memset(temp, 0, CD9660_SECTOR_SIZE);
+		memcpy(temp, tmp->volumeDescriptorData + 1, 5);
+		printf("Volume descriptor in sector %" PRId64
+		    ": type %i, ID %s\n",
+		    tmp->sector, tmp->volumeDescriptorData[0], temp);
+		switch(tmp->volumeDescriptorData[0]) {
+		case 0:/*boot record*/
+			break;
+
+		case 1:		/* PVD */
+			break;
+
+		case 2:		/* SVD */
+			break;
+
+		case 3:		/* Volume Partition Descriptor */
+			break;
+
+		case 255:	/* terminator */
+			break;
+		}
+		tmp = tmp->next;
+	}
+
+	printf("==Done Listing Volume Descriptors==\n");
+}
+
+void
+debug_dump_to_xml_ptentry(path_table_entry *pttemp, int num, int mode)
+{
+	printf("<ptentry num=\"%i\">\n" ,num);
+	printf("<length>%i</length>\n", pttemp->length[0]);
+	printf("<extended_attribute_length>%i</extended_attribute_length>\n",
+	    pttemp->extended_attribute_length[0]);
+	printf("<parent_number>%i</parent_number>\n",
+	    debug_get_encoded_number(pttemp->parent_number,mode));
+	debug_dump_to_xml_padded_hex_output("name",
+	    pttemp->name, pttemp->length[0]);
+	printf("</ptentry>\n");
+}
+
+void
+debug_dump_to_xml_path_table(FILE *fd, off_t sector, int size, int mode)
+{
+	path_table_entry pttemp;
+	int t = 0;
+	int n = 0;
+
+	if (fseeko(fd, CD9660_SECTOR_SIZE * sector, SEEK_SET) == -1)
+		err(1, "fseeko");
+
+	while (t < size) {
+		/* Read fixed data first */
+		fread(&pttemp, 1, 8, fd);
+		t += 8;
+		/* Read variable */
+		fread(((unsigned char*)&pttemp) + 8, 1, pttemp.length[0], fd);
+		t += pttemp.length[0];
+		debug_dump_to_xml_ptentry(&pttemp, n, mode);
+		n++;
+	}
+
+}
+
+/*
+ * XML Debug output functions
+ * Dump hierarchy of CD, as well as volume info, to XML
+ * Can be used later to diff against a standard,
+ * or just provide easy to read detailed debug output
+ */
+void
+debug_dump_to_xml(FILE *fd)
+{
+	unsigned char buf[CD9660_SECTOR_SIZE];
+	off_t sector;
+	int t, t2;
+	struct iso_primary_descriptor primaryVD;
+	struct _boot_volume_descriptor bootVD;
+
+	printf("<cd9660dump>\n");
+
+	/* Display Volume Descriptors */
+	sector = 16;
+	do {
+		if (fseeko(fd, CD9660_SECTOR_SIZE * sector, SEEK_SET) == -1)
+			err(1, "fseeko");
+		fread(buf, 1, CD9660_SECTOR_SIZE, fd);
+		t = (int)((unsigned char)buf[0]);
+		switch (t) {
+		case 0:
+			memcpy(&bootVD, buf, CD9660_SECTOR_SIZE);
+			break;
+		case 1:
+			memcpy(&primaryVD, buf, CD9660_SECTOR_SIZE);
+			break;
+		}
+		debug_dump_to_xml_volume_descriptor(buf, sector);
+		sector++;
+	} while (t != 255);
+
+	t = debug_get_encoded_number((u_char *)primaryVD.type_l_path_table,
+	    731);
+	t2 = debug_get_encoded_number((u_char *)primaryVD.path_table_size, 733);
+	printf("Path table 1 located at sector %i and is %i bytes long\n",
+	    t,t2);
+	debug_dump_to_xml_path_table(fd, t, t2, 721);
+
+	t = debug_get_encoded_number((u_char *)primaryVD.type_m_path_table,
+	    731);
+	debug_dump_to_xml_path_table(fd, t, t2, 722);
+
+	printf("</cd9660dump>\n");
+}
+
+static void
+debug_dump_to_xml_padded_hex_output(const char *element, unsigned char *buf,
+				    int len)
+{
+	int i;
+	int t;
+
+	printf("<%s>",element);
+	for (i = 0; i < len; i++) {
+		t = (unsigned char)buf[i];
+		if (t >= 32 && t < 127)
+			printf("%c",t);
+	}
+	printf("</%s>\n",element);
+
+	printf("<%s:hex>",element);
+	for (i = 0; i < len; i++) {
+		t = (unsigned char)buf[i];
+		printf(" %x",t);
+	}
+	printf("</%s:hex>\n",element);
+}
+
+int
+debug_get_encoded_number(unsigned char* buf, int mode)
+{
+#if !HAVE_NBTOOL_CONFIG_H
+	switch (mode) {
+	/* 711: Single bite */
+	case 711:
+		return isonum_711(buf);
+
+	/* 712: Single signed byte */
+	case 712:
+		return isonum_712((signed char *)buf);
+
+	/* 721: 16 bit LE */
+	case 721:
+		return isonum_721(buf);
+
+	/* 731: 32 bit LE */
+	case 731:
+		return isonum_731(buf);
+
+	/* 722: 16 bit BE */
+	case 722:
+		return isonum_722(buf);
+
+	/* 732: 32 bit BE */
+	case 732:
+		return isonum_732(buf);
+
+	/* 723: 16 bit bothE */
+	case 723:
+		return isonum_723(buf);
+
+	/* 733: 32 bit bothE */
+	case 733:
+		return isonum_733(buf);
+	}
+#endif
+	return 0;
+}
+
+void
+debug_dump_integer(const char *element, char* buf, int mode)
+{
+	printf("<%s>%i</%s>\n", element,
+	    debug_get_encoded_number((unsigned char *)buf, mode), element);
+}
+
+void
+debug_dump_string(const char *element __unused, unsigned char *buf __unused, int len __unused)
+{
+
+}
+
+void
+debug_dump_directory_record_9_1(unsigned char* buf)
+{
+	printf("<directoryrecord>\n");
+	debug_dump_integer("length",
+	    ((struct iso_directory_record*) buf)->length, 711);
+	debug_dump_integer("ext_attr_length",
+	    ((struct iso_directory_record*) buf)->ext_attr_length,711);
+	debug_dump_integer("extent",
+	    (char *)((struct iso_directory_record*) buf)->extent, 733);
+	debug_dump_integer("size",
+	    (char *)((struct iso_directory_record*) buf)->size, 733);
+	debug_dump_integer("flags",
+	    ((struct iso_directory_record*) buf)->flags, 711);
+	debug_dump_integer("file_unit_size",
+	    ((struct iso_directory_record*) buf)->file_unit_size,711);
+	debug_dump_integer("interleave",
+	    ((struct iso_directory_record*) buf)->interleave, 711);
+	debug_dump_integer("volume_sequence_number",
+	    ((struct iso_directory_record*) buf)->volume_sequence_number,
+	    723);
+	debug_dump_integer("name_len",
+	    ((struct iso_directory_record*) buf)->name_len, 711);
+	debug_dump_to_xml_padded_hex_output("name",
+	    (u_char *)((struct iso_directory_record*) buf)->name,
+		debug_get_encoded_number((u_char *)
+		    ((struct iso_directory_record*) buf)->length, 711));
+	printf("</directoryrecord>\n");
+}
+
+
+void
+debug_dump_to_xml_volume_descriptor(unsigned char* buf, int sector)
+{
+	printf("<volumedescriptor sector=\"%i\">\n", sector);
+	printf("<vdtype>");
+	switch(buf[0]) {
+	case 0:
+		printf("boot");
+		break;
+
+	case 1:
+		printf("primary");
+		break;
+
+	case 2:
+		printf("supplementary");
+		break;
+
+	case 3:
+		printf("volume partition descriptor");
+		break;
+
+	case 255:
+		printf("terminator");
+		break;
+	}
+
+	printf("</vdtype>\n");
+	switch(buf[0]) {
+	case 1:
+		debug_dump_integer("type",
+		    ((struct iso_primary_descriptor*)buf)->type, 711);
+		debug_dump_to_xml_padded_hex_output("id",
+		    (u_char *)((struct iso_primary_descriptor*) buf)->id,
+		    ISODCL (  2,   6));
+		debug_dump_integer("version",
+		    ((struct iso_primary_descriptor*)buf)->version,
+		     711);
+		debug_dump_to_xml_padded_hex_output("system_id",
+		    (u_char *)((struct iso_primary_descriptor*)buf)->system_id,
+		    ISODCL(9,40));
+		debug_dump_to_xml_padded_hex_output("volume_id",
+		    (u_char *)((struct iso_primary_descriptor*)buf)->volume_id,
+		    ISODCL(41,72));
+		debug_dump_integer("volume_space_size",
+		    ((struct iso_primary_descriptor*)buf)->volume_space_size,
+		    733);
+		debug_dump_integer("volume_set_size",
+		    ((struct iso_primary_descriptor*)buf)->volume_set_size,
+			    733);
+		debug_dump_integer("volume_sequence_number",
+		    ((struct iso_primary_descriptor*)buf)->volume_sequence_number,
+		    723);
+		debug_dump_integer("logical_block_size",
+		    ((struct iso_primary_descriptor*)buf)->logical_block_size,
+			    723);
+		debug_dump_integer("path_table_size",
+		    ((struct iso_primary_descriptor*)buf)->path_table_size,
+			    733);
+		debug_dump_integer("type_l_path_table",
+		    ((struct iso_primary_descriptor*)buf)->type_l_path_table,
+		    731);
+		debug_dump_integer("opt_type_l_path_table",
+		    ((struct iso_primary_descriptor*)buf)->opt_type_l_path_table,
+		    731);
+		debug_dump_integer("type_m_path_table",
+		    ((struct iso_primary_descriptor*)buf)->type_m_path_table,
+		    732);
+		debug_dump_integer("opt_type_m_path_table",
+			((struct iso_primary_descriptor*)buf)->opt_type_m_path_table,732);
+		debug_dump_directory_record_9_1(
+		    (u_char *)((struct iso_primary_descriptor*)buf)->root_directory_record);
+		debug_dump_to_xml_padded_hex_output("volume_set_id",
+		    (u_char *)((struct iso_primary_descriptor*) buf)->volume_set_id,
+		    ISODCL (191, 318));
+		debug_dump_to_xml_padded_hex_output("publisher_id",
+		    (u_char *)((struct iso_primary_descriptor*) buf)->publisher_id,
+		    ISODCL (319, 446));
+		debug_dump_to_xml_padded_hex_output("preparer_id",
+		    (u_char *)((struct iso_primary_descriptor*) buf)->preparer_id,
+		    ISODCL (447, 574));
+		debug_dump_to_xml_padded_hex_output("application_id",
+		    (u_char *)((struct iso_primary_descriptor*) buf)->application_id,
+		    ISODCL (575, 702));
+		debug_dump_to_xml_padded_hex_output("copyright_file_id",
+		    (u_char *)((struct iso_primary_descriptor*) buf)->copyright_file_id,
+		    ISODCL (703, 739));
+		debug_dump_to_xml_padded_hex_output("abstract_file_id",
+		    (u_char *)((struct iso_primary_descriptor*) buf)->abstract_file_id,
+		    ISODCL (740, 776));
+		debug_dump_to_xml_padded_hex_output("bibliographic_file_id",
+		    (u_char *)((struct iso_primary_descriptor*) buf)->bibliographic_file_id,
+		    ISODCL (777, 813));
+
+		debug_dump_to_xml_padded_hex_output("creation_date",
+		    (u_char *)((struct iso_primary_descriptor*) buf)->creation_date,
+		    ISODCL (814, 830));
+		debug_dump_to_xml_padded_hex_output("modification_date",
+		    (u_char *)((struct iso_primary_descriptor*) buf)->modification_date,
+		    ISODCL (831, 847));
+		debug_dump_to_xml_padded_hex_output("expiration_date",
+		    (u_char *)((struct iso_primary_descriptor*) buf)->expiration_date,
+		    ISODCL (848, 864));
+		debug_dump_to_xml_padded_hex_output("effective_date",
+		    (u_char *)((struct iso_primary_descriptor*) buf)->effective_date,
+		    ISODCL (865, 881));
+
+		debug_dump_to_xml_padded_hex_output("file_structure_version",
+		    (u_char *)((struct iso_primary_descriptor*) buf)->file_structure_version,
+		    ISODCL(882,882));
+		break;
+	}
+	printf("</volumedescriptor>\n");
+}
+
diff --git a/usr.sbin/makefs/cd9660/cd9660_eltorito.c b/usr.sbin/makefs/cd9660/cd9660_eltorito.c
new file mode 100644
index 0000000000..4220f82631
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_eltorito.c
@@ -0,0 +1,711 @@
+/*	$NetBSD: cd9660_eltorito.c,v 1.17 2011/06/23 02:35:56 enami Exp $	*/
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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 "cd9660.h"
+#include "cd9660_eltorito.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/cd9660/cd9660_eltorito.c 233783 2012-04-02 16:26:32Z andreast $");
+
+#ifdef DEBUG
+#define	ELTORITO_DPRINTF(__x)	printf __x
+#else
+#define	ELTORITO_DPRINTF(__x)
+#endif
+
+static struct boot_catalog_entry *cd9660_init_boot_catalog_entry(void);
+static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char);
+static struct boot_catalog_entry *cd9660_boot_setup_default_entry(
+    struct cd9660_boot_image *);
+static struct boot_catalog_entry *cd9660_boot_setup_section_head(char);
+static struct boot_catalog_entry *cd9660_boot_setup_validation_entry(char);
+#if 0
+static u_char cd9660_boot_get_system_type(struct cd9660_boot_image *);
+#endif
+
+int
+cd9660_add_boot_disk(const char *boot_info)
+{
+	struct stat stbuf;
+	const char *mode_msg;
+	char *temp;
+	char *sysname;
+	char *filename;
+	struct cd9660_boot_image *new_image, *tmp_image;
+
+	assert(boot_info != NULL);
+
+	if (*boot_info == '\0') {
+		warnx("Error: Boot disk information must be in the "
+		      "format 'system;filename'");
+		return 0;
+	}
+
+	/* First decode the boot information */
+	if ((temp = strdup(boot_info)) == NULL) {
+		warn("%s: strdup", __func__);
+		return 0;
+	}
+
+	sysname = temp;
+	filename = strchr(sysname, ';');
+	if (filename == NULL) {
+		warnx("supply boot disk information in the format "
+		    "'system;filename'");
+		free(temp);
+		return 0;
+	}
+
+	*filename++ = '\0';
+
+	if (diskStructure.verbose_level > 0) {
+		printf("Found bootdisk with system %s, and filename %s\n",
+		    sysname, filename);
+	}
+	if ((new_image = malloc(sizeof(*new_image))) == NULL) {
+		warn("%s: malloc", __func__);
+		free(temp);
+		return 0;
+	}
+	(void)memset(new_image, 0, sizeof(*new_image));
+	new_image->loadSegment = 0;	/* default for now */
+
+	/* Decode System */
+	if (strcmp(sysname, "i386") == 0)
+		new_image->system = ET_SYS_X86;
+	else if (strcmp(sysname, "powerpc") == 0)
+		new_image->system = ET_SYS_PPC;
+	else if (strcmp(sysname, "macppc") == 0 ||
+	         strcmp(sysname, "mac68k") == 0)
+		new_image->system = ET_SYS_MAC;
+	else {
+		warnx("boot disk system must be "
+		      "i386, powerpc, macppc, or mac68k");
+		free(temp);
+		free(new_image);
+		return 0;
+	}
+
+
+	if ((new_image->filename = strdup(filename)) == NULL) {
+		warn("%s: strdup", __func__);
+		free(temp);
+		free(new_image);
+		return 0;
+	}
+
+	free(temp);
+
+	/* Get information about the file */
+	if (lstat(new_image->filename, &stbuf) == -1)
+		err(EXIT_FAILURE, "%s: lstat(\"%s\")", __func__,
+		    new_image->filename);
+
+	switch (stbuf.st_size) {
+	case 1440 * 1024:
+		new_image->targetMode = ET_MEDIA_144FDD;
+		mode_msg = "Assigned boot image to 1.44 emulation mode";
+		break;
+	case 1200 * 1024:
+		new_image->targetMode = ET_MEDIA_12FDD;
+		mode_msg = "Assigned boot image to 1.2 emulation mode";
+		break;
+	case 2880 * 1024:
+		new_image->targetMode = ET_MEDIA_288FDD;
+		mode_msg = "Assigned boot image to 2.88 emulation mode";
+		break;
+	default:
+		new_image->targetMode = ET_MEDIA_NOEM;
+		mode_msg = "Assigned boot image to no emulation mode";
+		break;
+	}
+
+	if (diskStructure.verbose_level > 0)
+		printf("%s\n", mode_msg);
+
+	new_image->size = stbuf.st_size;
+	new_image->num_sectors =
+	    howmany(new_image->size, diskStructure.sectorSize) *
+	    howmany(diskStructure.sectorSize, 512);
+	if (diskStructure.verbose_level > 0) {
+		printf("New image has size %d, uses %d 512-byte sectors\n",
+		    new_image->size, new_image->num_sectors);
+	}
+	new_image->sector = -1;
+	/* Bootable by default */
+	new_image->bootable = ET_BOOTABLE;
+	/* Add boot disk */
+
+	/* Group images for the same platform together. */
+	TAILQ_FOREACH(tmp_image, &diskStructure.boot_images, image_list) {
+		if (tmp_image->system != new_image->system)
+			break;
+	}
+
+	if (tmp_image == NULL) {
+		TAILQ_INSERT_HEAD(&diskStructure.boot_images, new_image,
+		    image_list);
+	} else
+		TAILQ_INSERT_BEFORE(tmp_image, new_image, image_list);
+
+	new_image->serialno = diskStructure.image_serialno++;
+
+	/* TODO : Need to do anything about the boot image in the tree? */
+	diskStructure.is_bootable = 1;
+
+	return 1;
+}
+
+int
+cd9660_eltorito_add_boot_option(const char *option_string, const char *value)
+{
+	char *eptr;
+	struct cd9660_boot_image *image;
+
+	assert(option_string != NULL);
+
+	/* Find the last image added */
+	TAILQ_FOREACH(image, &diskStructure.boot_images, image_list) {
+		if (image->serialno + 1 == diskStructure.image_serialno)
+			break;
+	}
+	if (image == NULL)
+		errx(EXIT_FAILURE, "Attempted to add boot option, "
+		    "but no boot images have been specified");
+
+	if (strcmp(option_string, "no-emul-boot") == 0) {
+		image->targetMode = ET_MEDIA_NOEM;
+	} else if (strcmp(option_string, "no-boot") == 0) {
+		image->bootable = ET_NOT_BOOTABLE;
+	} else if (strcmp(option_string, "hard-disk-boot") == 0) {
+		image->targetMode = ET_MEDIA_HDD;
+	} else if (strcmp(option_string, "boot-load-segment") == 0) {
+		image->loadSegment = strtoul(value, &eptr, 16);
+		if (eptr == value || *eptr != '\0' || errno != ERANGE) {
+			warn("%s: strtoul", __func__);
+			return 0;
+		}
+	} else {
+		return 0;
+	}
+	return 1;
+}
+
+static struct boot_catalog_entry *
+cd9660_init_boot_catalog_entry(void)
+{
+	struct boot_catalog_entry *temp;
+
+	if ((temp = malloc(sizeof(*temp))) == NULL)
+		return NULL;
+
+	return memset(temp, 0, sizeof(*temp));
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_validation_entry(char sys)
+{
+	struct boot_catalog_entry *entry;
+	boot_catalog_validation_entry *ve;
+	int16_t checksum;
+	unsigned char *csptr;
+	int i;
+	entry = cd9660_init_boot_catalog_entry();
+
+	if (entry == NULL) {
+		warnx("Error: memory allocation failed in "
+		      "cd9660_boot_setup_validation_entry");
+		return 0;
+	}
+	ve = &entry->entry_data.VE;
+
+	ve->header_id[0] = 1;
+	ve->platform_id[0] = sys;
+	ve->key[0] = 0x55;
+	ve->key[1] = 0xAA;
+
+	/* Calculate checksum */
+	checksum = 0;
+	cd9660_721(0, ve->checksum);
+	csptr = (unsigned char*)ve;
+	for (i = 0; i < sizeof(*ve); i += 2) {
+		checksum += (int16_t)csptr[i];
+		checksum += 256 * (int16_t)csptr[i + 1];
+	}
+	checksum = -checksum;
+	cd9660_721(checksum, ve->checksum);
+
+        ELTORITO_DPRINTF(("%s: header_id %d, platform_id %d, key[0] %d, key[1] %d, "
+	    "checksum %04x\n", __func__, ve->header_id[0], ve->platform_id[0],
+	    ve->key[0], ve->key[1], checksum));
+	return entry;
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_default_entry(struct cd9660_boot_image *disk)
+{
+	struct boot_catalog_entry *default_entry;
+	boot_catalog_initial_entry *ie;
+
+	default_entry = cd9660_init_boot_catalog_entry();
+	if (default_entry == NULL)
+		return NULL;
+
+	ie = &default_entry->entry_data.IE;
+
+	ie->boot_indicator[0] = disk->bootable;
+	ie->media_type[0] = disk->targetMode;
+	cd9660_721(disk->loadSegment, ie->load_segment);
+	ie->system_type[0] = disk->system;
+	cd9660_721(disk->num_sectors, ie->sector_count);
+	cd9660_731(disk->sector, ie->load_rba);
+
+	ELTORITO_DPRINTF(("%s: boot indicator %d, media type %d, "
+	    "load segment %04x, system type %d, sector count %d, "
+	    "load rba %d\n", __func__, ie->boot_indicator[0],
+	    ie->media_type[0], disk->loadSegment, ie->system_type[0],
+	    disk->num_sectors, disk->sector));
+	return default_entry;
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_section_head(char platform)
+{
+	struct boot_catalog_entry *entry;
+	boot_catalog_section_header *sh;
+
+	entry = cd9660_init_boot_catalog_entry();
+	if (entry == NULL)
+		return NULL;
+
+	sh = &entry->entry_data.SH;
+	/* More by default. The last one will manually be set to 0x91 */
+	sh->header_indicator[0] = ET_SECTION_HEADER_MORE;
+	sh->platform_id[0] = platform;
+	sh->num_section_entries[0] = 0;
+	return entry;
+}
+
+static struct boot_catalog_entry *
+cd9660_boot_setup_section_entry(struct cd9660_boot_image *disk)
+{
+	struct boot_catalog_entry *entry;
+	boot_catalog_section_entry *se;
+	if ((entry = cd9660_init_boot_catalog_entry()) == NULL)
+		return NULL;
+
+	se = &entry->entry_data.SE;
+
+	se->boot_indicator[0] = ET_BOOTABLE;
+	se->media_type[0] = disk->targetMode;
+	cd9660_721(disk->loadSegment, se->load_segment);
+	cd9660_721(disk->num_sectors, se->sector_count);
+	cd9660_731(disk->sector, se->load_rba);
+	return entry;
+}
+
+#if 0
+static u_char
+cd9660_boot_get_system_type(struct cd9660_boot_image *disk)
+{
+	/*
+		For hard drive booting, we need to examine the MBR to figure
+		out what the partition type is
+	*/
+	return 0;
+}
+#endif
+
+/*
+ * Set up the BVD, Boot catalog, and the boot entries, but do no writing
+ */
+int
+cd9660_setup_boot(int first_sector)
+{
+	int sector;
+	int used_sectors;
+	int num_entries = 0;
+	int catalog_sectors;
+	struct boot_catalog_entry *x86_head, *mac_head, *ppc_head,
+		*valid_entry, *default_entry, *temp, *head, **headp, *next;
+	struct cd9660_boot_image *tmp_disk;
+
+	headp = NULL;
+	x86_head = mac_head = ppc_head = NULL;
+
+	/* If there are no boot disks, don't bother building boot information */
+	if (TAILQ_EMPTY(&diskStructure.boot_images))
+		return 0;
+
+	/* Point to catalog: For now assume it consumes one sector */
+	ELTORITO_DPRINTF(("Boot catalog will go in sector %d\n", first_sector));
+	diskStructure.boot_catalog_sector = first_sector;
+	cd9660_bothendian_dword(first_sector,
+		diskStructure.boot_descriptor->boot_catalog_pointer);
+
+	/* Step 1: Generate boot catalog */
+	/* Step 1a: Validation entry */
+	valid_entry = cd9660_boot_setup_validation_entry(ET_SYS_X86);
+	if (valid_entry == NULL)
+		return -1;
+
+	/*
+	 * Count how many boot images there are,
+	 * and how many sectors they consume.
+	 */
+	num_entries = 1;
+	used_sectors = 0;
+
+	TAILQ_FOREACH(tmp_disk, &diskStructure.boot_images, image_list) {
+		used_sectors += tmp_disk->num_sectors;
+
+		/* One default entry per image */
+		num_entries++;
+	}
+	catalog_sectors = howmany(num_entries * 0x20, diskStructure.sectorSize);
+	used_sectors += catalog_sectors;
+
+	if (diskStructure.verbose_level > 0) {
+		printf("%s: there will be %i entries consuming %i sectors. "
+		       "Catalog is %i sectors\n", __func__, num_entries,
+		       used_sectors, catalog_sectors);
+	}
+
+	/* Populate sector numbers */
+	sector = first_sector + catalog_sectors;
+	TAILQ_FOREACH(tmp_disk, &diskStructure.boot_images, image_list) {
+		tmp_disk->sector = sector;
+		sector += tmp_disk->num_sectors;
+	}
+
+	LIST_INSERT_HEAD(&diskStructure.boot_entries, valid_entry, ll_struct);
+
+	/* Step 1b: Initial/default entry */
+	/* TODO : PARAM */
+	tmp_disk = TAILQ_FIRST(&diskStructure.boot_images);
+	default_entry = cd9660_boot_setup_default_entry(tmp_disk);
+	if (default_entry == NULL) {
+		warnx("Error: memory allocation failed in cd9660_setup_boot");
+		return -1;
+	}
+
+	LIST_INSERT_AFTER(valid_entry, default_entry, ll_struct);
+
+	/* Todo: multiple default entries? */
+
+	tmp_disk = TAILQ_NEXT(tmp_disk, image_list);
+
+	temp = default_entry;
+
+	/* If multiple boot images are given : */
+	while (tmp_disk != NULL) {
+		/* Step 2: Section header */
+		switch (tmp_disk->system) {
+		case ET_SYS_X86:
+			headp = &x86_head;
+			break;
+		case ET_SYS_PPC:
+			headp = &ppc_head;
+			break;
+		case ET_SYS_MAC:
+			headp = &mac_head;
+			break;
+		default:
+			warnx("%s: internal error: unknown system type",
+			    __func__);
+			return -1;
+		}
+
+		if (*headp == NULL) {
+			head =
+			    cd9660_boot_setup_section_head(tmp_disk->system);
+			if (head == NULL) {
+				warnx("Error: memory allocation failed in "
+				      "cd9660_setup_boot");
+				return -1;
+			}
+			LIST_INSERT_AFTER(default_entry, head, ll_struct);
+			*headp = head;
+		} else
+			head = *headp;
+
+		head->entry_data.SH.num_section_entries[0]++;
+
+		/* Step 2a: Section entry and extensions */
+		temp = cd9660_boot_setup_section_entry(tmp_disk);
+		if (temp == NULL) {
+			warn("%s: cd9660_boot_setup_section_entry", __func__);
+			return -1;
+		}
+
+		while ((next = LIST_NEXT(head, ll_struct)) != NULL &&
+		       next->entry_type == ET_ENTRY_SE)
+			head = next;
+
+		LIST_INSERT_AFTER(head, temp, ll_struct);
+		tmp_disk = TAILQ_NEXT(tmp_disk, image_list);
+	}
+
+	/* TODO: Remaining boot disks when implemented */
+
+	return first_sector + used_sectors;
+}
+
+int
+cd9660_setup_boot_volume_descriptor(volume_descriptor *bvd)
+{
+	boot_volume_descriptor *bvdData =
+	    (boot_volume_descriptor*)bvd->volumeDescriptorData;
+
+	bvdData->boot_record_indicator[0] = ISO_VOLUME_DESCRIPTOR_BOOT;
+	memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
+	bvdData->version[0] = 1;
+	memcpy(bvdData->boot_system_identifier, ET_ID, 23);
+	memcpy(bvdData->identifier, ISO_VOLUME_DESCRIPTOR_STANDARD_ID, 5);
+	diskStructure.boot_descriptor =
+	    (boot_volume_descriptor*) bvd->volumeDescriptorData;
+	return 1;
+}
+
+static int
+cd9660_write_mbr_partition_entry(FILE *fd, int idx, off_t sector_start,
+    off_t nsectors, int type)
+{
+	uint8_t val;
+	uint32_t lba;
+
+	if (fseeko(fd, (off_t)(idx) * 16 + 0x1be, SEEK_SET) == -1)
+		err(1, "fseeko");
+	
+	val = 0x80; /* Bootable */
+	fwrite(&val, sizeof(val), 1, fd);
+
+	val = 0xff; /* CHS begin */
+	fwrite(&val, sizeof(val), 1, fd);
+	fwrite(&val, sizeof(val), 1, fd);
+	fwrite(&val, sizeof(val), 1, fd);
+
+	val = type; /* Part type */
+	fwrite(&val, sizeof(val), 1, fd);
+
+	val = 0xff; /* CHS end */
+	fwrite(&val, sizeof(val), 1, fd);
+	fwrite(&val, sizeof(val), 1, fd);
+	fwrite(&val, sizeof(val), 1, fd);
+
+	/* LBA extent */
+	lba = htole32(sector_start);
+	fwrite(&lba, sizeof(lba), 1, fd);
+	lba = htole32(nsectors);
+	fwrite(&lba, sizeof(lba), 1, fd);
+
+	return 0;
+}
+
+static int
+cd9660_write_apm_partition_entry(FILE *fd, int idx, int total_partitions,
+    off_t sector_start, off_t nsectors, off_t sector_size,
+    const char *part_name, const char *part_type)
+{
+	uint32_t apm32, part_status;
+	uint16_t apm16;
+
+	/* See Apple Tech Note 1189 for the details about the pmPartStatus
+	 * flags.
+	 * Below the flags which are default:
+	 * - IsValid     0x01
+	 * - IsAllocated 0x02
+	 * - IsReadable  0x10
+	 * - IsWritable  0x20
+	 */
+	part_status = 0x01 | 0x02 | 0x10 | 0x20;
+
+	if (fseeko(fd, (off_t)(idx + 1) * sector_size, SEEK_SET) == -1)
+		err(1, "fseeko");
+
+	/* Signature */
+	apm16 = htobe16(0x504d);
+	fwrite(&apm16, sizeof(apm16), 1, fd);
+	apm16 = 0;
+	fwrite(&apm16, sizeof(apm16), 1, fd);
+
+	/* Total number of partitions */
+	apm32 = htobe32(total_partitions);
+	fwrite(&apm32, sizeof(apm32), 1, fd);
+	/* Bounds */
+	apm32 = htobe32(sector_start);
+	fwrite(&apm32, sizeof(apm32), 1, fd);
+	apm32 = htobe32(nsectors);
+	fwrite(&apm32, sizeof(apm32), 1, fd);
+
+	fwrite(part_name, strlen(part_name) + 1, 1, fd);
+	fseek(fd, 32 - strlen(part_name) - 1, SEEK_CUR);
+	fwrite(part_type, strlen(part_type) + 1, 1, fd);
+	fseek(fd, 32 - strlen(part_type) - 1, SEEK_CUR);
+
+	apm32 = 0;
+	/* pmLgDataStart */
+        fwrite(&apm32, sizeof(apm32), 1, fd);
+	/* pmDataCnt */ 
+	apm32 = htobe32(nsectors);
+        fwrite(&apm32, sizeof(apm32), 1, fd);
+	/* pmPartStatus */
+	apm32 = htobe32(part_status);
+        fwrite(&apm32, sizeof(apm32), 1, fd);
+
+	return 0;
+}
+
+int
+cd9660_write_boot(FILE *fd)
+{
+	struct boot_catalog_entry *e;
+	struct cd9660_boot_image *t;
+	int apm_partitions = 0;
+	int mbr_partitions = 0;
+
+	/* write boot catalog */
+	if (fseeko(fd, (off_t)diskStructure.boot_catalog_sector *
+	    diskStructure.sectorSize, SEEK_SET) == -1)
+		err(1, "fseeko");
+
+	if (diskStructure.verbose_level > 0) {
+		printf("Writing boot catalog to sector %" PRId64 "\n",
+		    diskStructure.boot_catalog_sector);
+	}
+	LIST_FOREACH(e, &diskStructure.boot_entries, ll_struct) {
+		if (diskStructure.verbose_level > 0) {
+			printf("Writing catalog entry of type %d\n",
+			    e->entry_type);
+		}
+		/*
+		 * It doesn't matter which one gets written
+		 * since they are the same size
+		 */
+		fwrite(&(e->entry_data.VE), 1, 32, fd);
+	}
+	if (diskStructure.verbose_level > 0)
+		printf("Finished writing boot catalog\n");
+
+	/* copy boot images */
+	TAILQ_FOREACH(t, &diskStructure.boot_images, image_list) {
+		if (diskStructure.verbose_level > 0) {
+			printf("Writing boot image from %s to sectors %d\n",
+			    t->filename, t->sector);
+		}
+		cd9660_copy_file(fd, t->sector, t->filename);
+
+		if (t->system == ET_SYS_MAC) 
+			apm_partitions++;
+		if (t->system == ET_SYS_PPC) 
+			mbr_partitions++;
+	}
+
+	/* some systems need partition tables as well */
+	if (mbr_partitions > 0 || diskStructure.chrp_boot) {
+		uint16_t sig;
+
+		fseek(fd, 0x1fe, SEEK_SET);
+		sig = htole16(0xaa55);
+		fwrite(&sig, sizeof(sig), 1, fd);
+
+		mbr_partitions = 0;
+
+		/* Write ISO9660 descriptor, enclosing the whole disk */
+		if (diskStructure.chrp_boot)
+			cd9660_write_mbr_partition_entry(fd, mbr_partitions++,
+			    0, diskStructure.totalSectors *
+			    (diskStructure.sectorSize / 512), 0x96);
+
+		/* Write all partition entries */
+		TAILQ_FOREACH(t, &diskStructure.boot_images, image_list) {
+			if (t->system != ET_SYS_PPC)
+				continue;
+			cd9660_write_mbr_partition_entry(fd, mbr_partitions++,
+			    t->sector * (diskStructure.sectorSize / 512),
+			    t->num_sectors * (diskStructure.sectorSize / 512),
+			    0x41 /* PReP Boot */);
+		}
+	}
+
+	if (apm_partitions > 0) {
+		/* Write DDR and global APM info */
+		uint32_t apm32;
+		uint16_t apm16;
+		int total_parts;
+
+		fseek(fd, 0, SEEK_SET);
+		apm16 = htobe16(0x4552);
+		fwrite(&apm16, sizeof(apm16), 1, fd);
+		/* Device block size */
+		apm16 = htobe16(512);
+		fwrite(&apm16, sizeof(apm16), 1, fd);
+		/* Device block count */
+		apm32 = htobe32(diskStructure.totalSectors *
+		    (diskStructure.sectorSize / 512));
+		fwrite(&apm32, sizeof(apm32), 1, fd);
+		/* Device type/id */
+		apm16 = htobe16(1);
+		fwrite(&apm16, sizeof(apm16), 1, fd);
+		fwrite(&apm16, sizeof(apm16), 1, fd);
+
+		/* Count total needed entries */
+		total_parts = 2 + apm_partitions; /* Self + ISO9660 */
+
+		/* Write self-descriptor */
+		cd9660_write_apm_partition_entry(fd, 0, total_parts, 1,
+		    total_parts, 512, "Apple", "Apple_partition_map");
+
+		/* Write all partition entries */
+		apm_partitions = 0;
+		TAILQ_FOREACH(t, &diskStructure.boot_images, image_list) {
+			if (t->system != ET_SYS_MAC)
+				continue;
+
+			cd9660_write_apm_partition_entry(fd,
+			    1 + apm_partitions++, total_parts,
+			    t->sector * (diskStructure.sectorSize / 512),
+			    t->num_sectors * (diskStructure.sectorSize / 512),
+			    512, "CD Boot", "Apple_Bootstrap");
+		}
+		/* Write ISO9660 descriptor, enclosing the whole disk */
+                cd9660_write_apm_partition_entry(fd, 2 + apm_partitions,
+		    total_parts, 0, diskStructure.totalSectors *
+		    (diskStructure.sectorSize / 512), 512, "ISO9660",
+		    "CD_ROM_Mode_1");
+	}
+
+	return 0;
+}
+
diff --git a/usr.sbin/makefs/cd9660/cd9660_eltorito.h b/usr.sbin/makefs/cd9660/cd9660_eltorito.h
new file mode 100644
index 0000000000..52f162b086
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_eltorito.h
@@ -0,0 +1,164 @@
+/*	$NetBSD: cd9660_eltorito.h,v 1.5 2009/07/04 14:31:38 ahoka Exp $	*/
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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: head/usr.sbin/makefs/cd9660/cd9660_eltorito.h 214921 2010-11-07 16:05:04Z cognet $
+ */
+
+#ifndef _CD9660_ELTORITO_H_
+#define _CD9660_ELTORITO_H_
+
+/* Boot defines */
+#define	ET_ID		"EL TORITO SPECIFICATION"
+#define	ET_SYS_X86	0
+#define	ET_SYS_PPC	1
+#define	ET_SYS_MAC	2
+
+#define ET_BOOT_ENTRY_SIZE 0x20
+
+#define	ET_BOOTABLE		0x88
+#define	ET_NOT_BOOTABLE	0
+
+#define	ET_MEDIA_NOEM	0
+#define	ET_MEDIA_12FDD			1
+#define	ET_MEDIA_144FDD			2
+#define	ET_MEDIA_288FDD			3
+#define	ET_MEDIA_HDD			4
+
+#define ET_INDICATOR_HEADERMORE	0x90
+#define ET_INDICATOR_HEADERLAST	0x91
+#define ET_INDICATOR_EXTENSION	0x44
+
+/*** Boot Structures ***/
+
+typedef struct _boot_volume_descriptor {
+	u_char boot_record_indicator	[ISODCL(0x00,0x00)];
+	u_char identifier		[ISODCL(0x01,0x05)];
+	u_char version			[ISODCL(0x06,0x06)];
+	u_char boot_system_identifier	[ISODCL(0x07,0x26)];
+	u_char unused1			[ISODCL(0x27,0x46)];
+	u_char boot_catalog_pointer	[ISODCL(0x47,0x4A)];
+	u_char unused2			[ISODCL(0x4B,0x7FF)];
+} boot_volume_descriptor;
+
+typedef struct _boot_catalog_validation_entry {
+	u_char header_id		[ISODCL(0x00,0x00)];
+	u_char platform_id		[ISODCL(0x01,0x01)];
+	u_char reserved1		[ISODCL(0x02,0x03)];
+	u_char manufacturer		[ISODCL(0x04,0x1B)];
+	u_char checksum			[ISODCL(0x1C,0x1D)];
+	u_char key			[ISODCL(0x1E,0x1F)];
+} boot_catalog_validation_entry;
+
+typedef struct _boot_catalog_initial_entry {
+	u_char boot_indicator		[ISODCL(0x00,0x00)];
+	u_char media_type		[ISODCL(0x01,0x01)];
+	u_char load_segment		[ISODCL(0x02,0x03)];
+	u_char system_type		[ISODCL(0x04,0x04)];
+	u_char unused_1			[ISODCL(0x05,0x05)];
+	u_char sector_count		[ISODCL(0x06,0x07)];
+	u_char load_rba			[ISODCL(0x08,0x0B)];
+	u_char unused_2			[ISODCL(0x0C,0x1F)];
+} boot_catalog_initial_entry;
+
+#define ET_SECTION_HEADER_MORE		0x90
+#define ET_SECTION_HEADER_LAST		0x91
+
+typedef struct _boot_catalog_section_header {
+	u_char header_indicator		[ISODCL(0x00,0x00)];
+	u_char platform_id		[ISODCL(0x01,0x01)];
+	u_char num_section_entries	[ISODCL(0x02,0x03)];
+	u_char id_string		[ISODCL(0x04,0x1F)];
+} boot_catalog_section_header;
+
+typedef struct _boot_catalog_section_entry {
+	u_char boot_indicator		[ISODCL(0x00,0x00)];
+	u_char media_type		[ISODCL(0x01,0x01)];
+	u_char load_segment		[ISODCL(0x02,0x03)];
+	u_char system_type		[ISODCL(0x04,0x04)];
+	u_char unused_1			[ISODCL(0x05,0x05)];
+	u_char sector_count		[ISODCL(0x06,0x07)];
+	u_char load_rba			[ISODCL(0x08,0x0B)];
+	u_char selection_criteria	[ISODCL(0x0C,0x0C)];
+	u_char vendor_criteria		[ISODCL(0x0D,0x1F)];
+} boot_catalog_section_entry;
+
+typedef struct _boot_catalog_section_entry_extension {
+	u_char extension_indicator	[ISODCL(0x00,0x00)];
+	u_char flags			[ISODCL(0x01,0x01)];
+	u_char vendor_criteria		[ISODCL(0x02,0x1F)];
+} boot_catalog_section_entry_extension;
+
+#define ET_ENTRY_VE 1
+#define ET_ENTRY_IE 2
+#define ET_ENTRY_SH 3
+#define ET_ENTRY_SE 4
+#define ET_ENTRY_EX 5
+
+struct boot_catalog_entry {
+	char entry_type;
+	union {
+		boot_catalog_validation_entry		VE;
+		boot_catalog_initial_entry 		IE;
+		boot_catalog_section_header		SH;
+		boot_catalog_section_entry		SE;
+		boot_catalog_section_entry_extension	EX;
+	} entry_data;
+
+	LIST_ENTRY(boot_catalog_entry) ll_struct;
+};
+
+/* Temporary structure */
+struct cd9660_boot_image {
+	char *filename;
+	int size;
+	int sector; 			/* copied to LoadRBA */
+	int num_sectors;
+	unsigned int loadSegment;
+	u_char targetMode;
+	u_char system;
+	u_char bootable;
+	/*
+	 * If the boot image exists in the filesystem
+	 * already, this is a pointer to that node. For the sake
+	 * of simplicity in future versions, this pointer is only
+	 * to the node in the primary volume. This SHOULD be done
+	 * via a hashtable lookup.
+	 */
+	struct _cd9660node *boot_image_node;
+	TAILQ_ENTRY(cd9660_boot_image) image_list;
+	int serialno;
+};
+
+
+#endif /* _CD9660_ELTORITO_H_ */
+
diff --git a/usr.sbin/makefs/cd9660/cd9660_strings.c b/usr.sbin/makefs/cd9660/cd9660_strings.c
new file mode 100644
index 0000000000..bcdee9acd9
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_strings.c
@@ -0,0 +1,120 @@
+/*	$NetBSD: cd9660_strings.c,v 1.4 2007/01/16 17:32:05 hubertf Exp $	*/
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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/mount.h>
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/cd9660/cd9660_strings.c 219956 2011-03-24 12:35:59Z bapt $");
+#include <sys/param.h>
+#include <ctype.h>
+
+#include "makefs.h"
+#include "cd9660.h"
+
+
+void
+cd9660_uppercase_characters(char *str, int len)
+{
+	int p;
+
+	for (p = 0; p < len; p++) {
+		if (islower((unsigned char)str[p]) )
+			str[p] -= 32;
+	}
+}
+
+static inline int
+cd9660_is_d_char(char c)
+{
+	return (isupper((unsigned char)c)
+		|| c == '_'
+		|| (c >= '0' && c <= '9'));
+}
+
+static inline int
+cd9660_is_a_char(char c)
+{
+	return (isupper((unsigned char)c)
+			|| c == '_'
+			|| (c >= '%' && c <= '?')
+			|| (c >= ' ' && c <= '\"'));
+}
+
+/*
+ * Test a string to see if it is composed of valid a characters
+ * @param const char* The string to test
+ * @returns int 1 if valid, 2 if valid if characters are converted to
+ *              upper case, 0 otherwise
+ */
+int
+cd9660_valid_a_chars(const char *str)
+{
+	const char *c = str;
+	int upperFound = 0;
+
+	while ((*c) != '\0') {
+		if (!(cd9660_is_a_char(*c))) {
+			if (islower((unsigned char)*c) )
+				upperFound = 1;
+			else
+				return 0;
+		}
+		c++;
+	}
+	return upperFound + 1;
+}
+
+/*
+ * Test a string to see if it is composed of valid d characters
+ * @param const char* The string to test
+ * @returns int 1 if valid, 2 if valid if characters are converted to
+ *                upper case, 0 otherwise
+ */
+int
+cd9660_valid_d_chars(const char *str)
+{
+	const char *c=str;
+	int upperFound = 0;
+
+	while ((*c) != '\0') {
+		if (!(cd9660_is_d_char(*c))) {
+			if (islower((unsigned char)*c) )
+				upperFound = 1;
+			else
+				return 0;
+		}
+		c++;
+	}
+	return upperFound + 1;
+}
diff --git a/usr.sbin/makefs/cd9660/cd9660_write.c b/usr.sbin/makefs/cd9660/cd9660_write.c
new file mode 100644
index 0000000000..a9275e9a58
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/cd9660_write.c
@@ -0,0 +1,524 @@
+/*	$NetBSD: cd9660_write.c,v 1.14 2011/01/04 09:48:21 wiz Exp $	*/
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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 "cd9660.h"
+#include "iso9660_rrip.h"
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/cd9660/cd9660_write.c 289693 2015-10-21 12:54:15Z ngie $");
+
+static int cd9660_write_volume_descriptors(FILE *);
+static int cd9660_write_path_table(FILE *, off_t, int);
+static int cd9660_write_path_tables(FILE *);
+static int cd9660_write_file(FILE *, cd9660node *);
+static int cd9660_write_filedata(FILE *, off_t, const unsigned char *, int);
+#if 0
+static int cd9660_write_buffered(FILE *, off_t, int, const unsigned char *);
+#endif
+static void cd9660_write_rr(FILE *, cd9660node *, off_t, off_t);
+
+/*
+ * Write the image
+ * Writes the entire image
+ * @param const char* The filename for the image
+ * @returns int 1 on success, 0 on failure
+ */
+int
+cd9660_write_image(const char* image)
+{
+	FILE *fd;
+	int status;
+	char buf[CD9660_SECTOR_SIZE];
+
+	if ((fd = fopen(image, "w+")) == NULL) {
+		err(EXIT_FAILURE, "%s: Can't open `%s' for writing", __func__,
+		    image);
+	}
+
+	if (diskStructure.verbose_level > 0)
+		printf("Writing image\n");
+
+	if (diskStructure.has_generic_bootimage) {
+		status = cd9660_copy_file(fd, 0,
+		    diskStructure.generic_bootimage);
+		if (status == 0) {
+			warnx("%s: Error writing generic boot image",
+			    __func__);
+			goto cleanup_bad_image;
+		}
+	}
+
+	/* Write the volume descriptors */
+	status = cd9660_write_volume_descriptors(fd);
+	if (status == 0) {
+		warnx("%s: Error writing volume descriptors to image",
+		    __func__);
+		goto cleanup_bad_image;
+	}
+
+	if (diskStructure.verbose_level > 0)
+		printf("Volume descriptors written\n");
+
+	/*
+	 * Write the path tables: there are actually four, but right
+	 * now we are only concearned with two.
+	 */
+	status = cd9660_write_path_tables(fd);
+	if (status == 0) {
+		warnx("%s: Error writing path tables to image", __func__);
+		goto cleanup_bad_image;
+	}
+
+	if (diskStructure.verbose_level > 0)
+		printf("Path tables written\n");
+
+	/* Write the directories and files */
+	status = cd9660_write_file(fd, diskStructure.rootNode);
+	if (status == 0) {
+		warnx("%s: Error writing files to image", __func__);
+		goto cleanup_bad_image;
+	}
+
+	if (diskStructure.is_bootable) {
+		cd9660_write_boot(fd);
+	}
+
+	/* Write padding bits. This is temporary */
+	memset(buf, 0, CD9660_SECTOR_SIZE);
+	cd9660_write_filedata(fd, diskStructure.totalSectors - 1, buf, 1);
+
+	if (diskStructure.verbose_level > 0)
+		printf("Files written\n");
+	fclose(fd);
+
+	if (diskStructure.verbose_level > 0)
+		printf("Image closed\n");
+	return 1;
+
+cleanup_bad_image:
+	fclose(fd);
+	if (!diskStructure.keep_bad_images)
+		unlink(image);
+	if (diskStructure.verbose_level > 0)
+		printf("Bad image cleaned up\n");
+	return 0;
+}
+
+static int
+cd9660_write_volume_descriptors(FILE *fd)
+{
+	volume_descriptor *vd_temp = diskStructure.firstVolumeDescriptor;
+	int pos;
+
+	while (vd_temp != NULL) {
+		pos = vd_temp->sector * diskStructure.sectorSize;
+		cd9660_write_filedata(fd, vd_temp->sector,
+		    vd_temp->volumeDescriptorData, 1);
+		vd_temp = vd_temp->next;
+	}
+	return 1;
+}
+
+/*
+ * Write out an individual path table
+ * Used just to keep redundant code to a minimum
+ * @param FILE *fd Valid file pointer
+ * @param int Sector to start writing path table to
+ * @param int Endian mode : BIG_ENDIAN or LITTLE_ENDIAN
+ * @returns int 1 on success, 0 on failure
+ */
+static int
+cd9660_write_path_table(FILE *fd, off_t sector, int mode)
+{
+	int path_table_sectors = CD9660_BLOCKS(diskStructure.sectorSize,
+	    diskStructure.pathTableLength);
+	unsigned char *buffer;
+	unsigned char *buffer_head;
+	int len, ret;
+	path_table_entry temp_entry;
+	cd9660node *ptcur;
+
+	buffer = malloc(diskStructure.sectorSize * path_table_sectors);
+	if (buffer == NULL) {
+		warnx("%s: Memory allocation error allocating buffer",
+		    __func__);
+		return 0;
+	}
+	buffer_head = buffer;
+	memset(buffer, 0, diskStructure.sectorSize * path_table_sectors);
+
+	ptcur = diskStructure.rootNode;
+
+	while (ptcur != NULL) {
+		memset(&temp_entry, 0, sizeof(path_table_entry));
+		temp_entry.length[0] = ptcur->isoDirRecord->name_len[0];
+		temp_entry.extended_attribute_length[0] =
+		    ptcur->isoDirRecord->ext_attr_length[0];
+		memcpy(temp_entry.name, ptcur->isoDirRecord->name,
+		    temp_entry.length[0] + 1);
+
+		/* round up */
+		len = temp_entry.length[0] + 8 + (temp_entry.length[0] & 0x01);
+
+                /* todo: function pointers instead */
+		if (mode == LITTLE_ENDIAN) {
+			cd9660_731(ptcur->fileDataSector,
+			    temp_entry.first_sector);
+			cd9660_721((ptcur->parent == NULL ?
+				1 : ptcur->parent->ptnumber),
+			    temp_entry.parent_number);
+		} else {
+			cd9660_732(ptcur->fileDataSector,
+			    temp_entry.first_sector);
+			cd9660_722((ptcur->parent == NULL ?
+				1 : ptcur->parent->ptnumber),
+			    temp_entry.parent_number);
+		}
+
+
+		memcpy(buffer, &temp_entry, len);
+		buffer += len;
+
+		ptcur = ptcur->ptnext;
+	}
+
+	ret = cd9660_write_filedata(fd, sector, buffer_head,
+	    path_table_sectors);
+	free(buffer_head);
+	return ret;
+}
+
+
+/*
+ * Write out the path tables to disk
+ * Each file descriptor should be pointed to by the PVD, so we know which
+ * sector to copy them to. One thing to watch out for: the only path tables
+ * stored are in the endian mode that the application is compiled for. So,
+ * the first thing to do is write out that path table, then to write the one
+ * in the other endian mode requires to convert the endianness of each entry
+ * in the table. The best way to do this would be to create a temporary
+ * path_table_entry structure, then for each path table entry, copy it to
+ * the temporary entry, translate, then copy that to disk.
+ *
+ * @param FILE* Valid file descriptor
+ * @returns int 0 on failure, 1 on success
+ */
+static int
+cd9660_write_path_tables(FILE *fd)
+{
+	if (cd9660_write_path_table(fd,
+	    diskStructure.primaryLittleEndianTableSector, LITTLE_ENDIAN) == 0)
+		return 0;
+
+	if (cd9660_write_path_table(fd,
+	    diskStructure.primaryBigEndianTableSector, BIG_ENDIAN) == 0)
+		return 0;
+
+	/* @TODO: handle remaining two path tables */
+	return 1;
+}
+
+/*
+ * Write a file to disk
+ * Writes a file, its directory record, and its data to disk
+ * This file is designed to be called RECURSIVELY, so initially call it
+ * with the root node. All of the records should store what sector the
+ * file goes in, so no computation should be  necessary.
+ *
+ * @param int fd Valid file descriptor
+ * @param struct cd9660node* writenode Pointer to the file to be written
+ * @returns int 0 on failure, 1 on success
+ */
+static int
+cd9660_write_file(FILE *fd, cd9660node *writenode)
+{
+	char *buf;
+	char *temp_file_name;
+	int ret;
+	off_t working_sector;
+	int cur_sector_offset;
+	int written;
+	iso_directory_record_cd9660 temp_record;
+	cd9660node *temp;
+	int rv = 0;
+
+	/* Todo : clean up variables */
+
+	temp_file_name = malloc(CD9660MAXPATH + 1);
+	if (temp_file_name == NULL)
+		err(EXIT_FAILURE, "%s: malloc", __func__);
+
+	memset(temp_file_name, 0, CD9660MAXPATH + 1);
+
+	buf = malloc(diskStructure.sectorSize);
+	if (buf == NULL)
+		err(EXIT_FAILURE, "%s: malloc", __func__);
+
+	if ((writenode->level != 0) &&
+	    !(writenode->node->type & S_IFDIR)) {
+		fsinode *inode = writenode->node->inode;
+		/* Only attempt to write unwritten files that have length. */
+		if ((inode->flags & FI_WRITTEN) != 0) {
+			INODE_WARNX(("%s: skipping written inode %d", __func__,
+			    (int)inode->st.st_ino));
+		} else if (writenode->fileDataLength > 0) {
+			INODE_WARNX(("%s: writing inode %d blocks at %" PRIu32,
+			    __func__, (int)inode->st.st_ino, inode->ino));
+			inode->flags |= FI_WRITTEN;
+			if (writenode->node->contents == NULL)
+				cd9660_compute_full_filename(writenode,
+				    temp_file_name);
+			ret = cd9660_copy_file(fd, writenode->fileDataSector,
+			    (writenode->node->contents != NULL) ?
+			    writenode->node->contents : temp_file_name);
+			if (ret == 0)
+				goto out;
+		}
+	} else {
+		/*
+		 * Here is a new revelation that ECMA didn't explain
+		 * (at least not well).
+		 * ALL . and .. records store the name "\0" and "\1"
+		 * respectively. So, for each directory, we have to
+		 * make a new node.
+		 *
+		 * This is where it gets kinda messy, since we have to
+		 * be careful of sector boundaries
+		 */
+		cur_sector_offset = 0;
+		working_sector = writenode->fileDataSector;
+		if (fseeko(fd, working_sector * diskStructure.sectorSize,
+		    SEEK_SET) == -1)
+			err(1, "fseeko");
+
+		/*
+		 * Now loop over children, writing out their directory
+		 * records - beware of sector boundaries
+	 	 */
+		TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
+			/*
+			 * Copy the temporary record and adjust its size
+			 * if necessary
+			 */
+			memcpy(&temp_record, temp->isoDirRecord,
+			    sizeof(iso_directory_record_cd9660));
+
+			temp_record.length[0] =
+			    cd9660_compute_record_size(temp);
+
+			if (temp_record.length[0] + cur_sector_offset >=
+			    diskStructure.sectorSize) {
+				cur_sector_offset = 0;
+				working_sector++;
+
+				/* Seek to the next sector. */
+				if (fseeko(fd, working_sector *
+				    diskStructure.sectorSize, SEEK_SET) == -1)
+					err(1, "fseeko");
+			}
+			/* Write out the basic ISO directory record */
+			written = fwrite(&temp_record, 1,
+			    temp->isoDirRecord->length[0], fd);
+			if (diskStructure.rock_ridge_enabled) {
+				cd9660_write_rr(fd, temp,
+				    cur_sector_offset, working_sector);
+			}
+			if (fseeko(fd, working_sector *
+			    diskStructure.sectorSize + cur_sector_offset +
+			    temp_record.length[0] - temp->su_tail_size,
+			    SEEK_SET) == -1)
+				err(1, "fseeko");
+			if (temp->su_tail_size > 0)
+				fwrite(temp->su_tail_data, 1,
+				    temp->su_tail_size, fd);
+			if (ferror(fd)) {
+				warnx("%s: write error", __func__);
+				goto out;
+			}
+			cur_sector_offset += temp_record.length[0];
+
+		}
+
+		/*
+		 * Recurse on children.
+		 */
+		TAILQ_FOREACH(temp, &writenode->cn_children, cn_next_child) {
+			if ((ret = cd9660_write_file(fd, temp)) == 0)
+				goto out;
+		}
+	}
+	rv = 1;
+out:
+	free(temp_file_name);
+	free(buf);
+	return rv;
+}
+
+/*
+ * Wrapper function to write a buffer (one sector) to disk.
+ * Seeks and writes the buffer.
+ * NOTE: You dont NEED to use this function, but it might make your
+ * life easier if you have to write things that align to a sector
+ * (such as volume descriptors).
+ *
+ * @param int fd Valid file descriptor
+ * @param int sector Sector number to write to
+ * @param const unsigned char* Buffer to write. This should be the
+ *                             size of a sector, and if only a portion
+ *                             is written, the rest should be set to 0.
+ */
+static int
+cd9660_write_filedata(FILE *fd, off_t sector, const unsigned char *buf,
+		      int numsecs)
+{
+	off_t curpos;
+	size_t success;
+
+	curpos = ftello(fd);
+
+	if (fseeko(fd, sector * diskStructure.sectorSize, SEEK_SET) == -1)
+		err(1, "fseeko");
+
+	success = fwrite(buf, diskStructure.sectorSize * numsecs, 1, fd);
+
+	if (fseeko(fd, curpos, SEEK_SET) == -1)
+		err(1, "fseeko");
+
+	if (success == 1)
+		success = diskStructure.sectorSize * numsecs;
+	return success;
+}
+
+#if 0
+static int
+cd9660_write_buffered(FILE *fd, off_t offset, int buff_len,
+		      const unsigned char* buffer)
+{
+	static int working_sector = -1;
+	static char buf[CD9660_SECTOR_SIZE];
+
+	return 0;
+}
+#endif
+
+int
+cd9660_copy_file(FILE *fd, off_t start_sector, const char *filename)
+{
+	FILE *rf;
+	int bytes_read;
+	off_t sector = start_sector;
+	int buf_size = diskStructure.sectorSize;
+	char *buf;
+
+	buf = malloc(buf_size);
+	if (buf == NULL)
+		err(EXIT_FAILURE, "%s: malloc", __func__);
+
+	if ((rf = fopen(filename, "rb")) == NULL) {
+		warn("%s: cannot open %s", __func__, filename);
+		free(buf);
+		return 0;
+	}
+
+	if (diskStructure.verbose_level > 1)
+		printf("Writing file: %s\n",filename);
+
+	if (fseeko(fd, start_sector * diskStructure.sectorSize, SEEK_SET) == -1)
+		err(1, "fseeko");
+
+	while (!feof(rf)) {
+		bytes_read = fread(buf,1,buf_size,rf);
+		if (ferror(rf)) {
+			warn("%s: fread", __func__);
+			free(buf);
+			(void)fclose(rf);
+			return 0;
+		}
+
+		fwrite(buf,1,bytes_read,fd);
+		if (ferror(fd)) {
+			warn("%s: fwrite", __func__);
+			free(buf);
+			(void)fclose(rf);
+			return 0;
+		}
+		sector++;
+	}
+
+	fclose(rf);
+	free(buf);
+	return 1;
+}
+
+static void
+cd9660_write_rr(FILE *fd, cd9660node *writenode, off_t offset, off_t sector)
+{
+	int in_ca = 0;
+	struct ISO_SUSP_ATTRIBUTES *myattr;
+
+	offset += writenode->isoDirRecord->length[0];
+	if (fseeko(fd, sector * diskStructure.sectorSize + offset, SEEK_SET) ==
+	    -1)
+		err(1, "fseeko");
+	/* Offset now points at the end of the record */
+	TAILQ_FOREACH(myattr, &writenode->head, rr_ll) {
+		fwrite(&(myattr->attr), CD9660_SUSP_ENTRY_SIZE(myattr), 1, fd);
+
+		if (!in_ca) {
+			offset += CD9660_SUSP_ENTRY_SIZE(myattr);
+			if (myattr->last_in_suf) {
+				/*
+				 * Point the offset to the start of this
+				 * record's CE area
+				 */
+				if (fseeko(fd, ((off_t)diskStructure.
+				    susp_continuation_area_start_sector *
+				    diskStructure.sectorSize)
+				    + writenode->susp_entry_ce_start,
+				    SEEK_SET) == -1)
+					err(1, "fseeko");
+				in_ca = 1;
+			}
+		}
+	}
+
+	/*
+	 * If we had to go to the continuation area, head back to
+	 * where we should be.
+	 */
+	if (in_ca)
+		if (fseeko(fd, sector * diskStructure.sectorSize + offset,
+		    SEEK_SET) == -1)
+			err(1, "fseeko");
+}
diff --git a/usr.sbin/makefs/cd9660/iso9660_rrip.c b/usr.sbin/makefs/cd9660/iso9660_rrip.c
new file mode 100644
index 0000000000..c1ef573f5f
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/iso9660_rrip.c
@@ -0,0 +1,840 @@
+/*	$NetBSD: iso9660_rrip.c,v 1.14 2014/05/30 13:14:47 martin Exp $	*/
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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.
+ */
+/* This will hold all the function definitions
+ * defined in iso9660_rrip.h
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/cd9660/iso9660_rrip.c 307927 2016-10-25 16:29:15Z marcel $");
+
+#include <sys/queue.h>
+#include <sys/types.h>
+#include <stdio.h>
+
+#include "makefs.h"
+#include "cd9660.h"
+#include "iso9660_rrip.h"
+
+static void cd9660_rrip_initialize_inode(cd9660node *);
+static int cd9660_susp_handle_continuation(cd9660node *);
+static int cd9660_susp_handle_continuation_common(cd9660node *, int);
+
+int
+cd9660_susp_initialize(cd9660node *node, cd9660node *parent,
+    cd9660node *grandparent)
+{
+	cd9660node *cn;
+	int r;
+
+	/* Make sure the node is not NULL. If it is, there are major problems */
+	assert(node != NULL);
+
+	if (!(node->type & CD9660_TYPE_DOT) &&
+	    !(node->type & CD9660_TYPE_DOTDOT))
+		TAILQ_INIT(&(node->head));
+	if (node->dot_record != 0)
+		TAILQ_INIT(&(node->dot_record->head));
+	if (node->dot_dot_record != 0)
+		TAILQ_INIT(&(node->dot_dot_record->head));
+
+	 /* SUSP specific entries here */
+	if ((r = cd9660_susp_initialize_node(node)) < 0)
+		return r;
+
+	/* currently called cd9660node_rrip_init_links */
+	r = cd9660_rrip_initialize_node(node, parent, grandparent);
+	if (r < 0)
+		return r;
+
+	/*
+	 * See if we need a CE record, and set all of the
+	 * associated counters.
+	 *
+	 * This should be called after all extensions. After
+	 * this is called, no new records should be added.
+	 */
+	if ((r = cd9660_susp_handle_continuation(node)) < 0)
+		return r;
+
+	/* Recurse on children. */
+	TAILQ_FOREACH(cn, &node->cn_children, cn_next_child) {
+		if ((r = cd9660_susp_initialize(cn, node, parent)) < 0)
+			return 0;
+	}
+	return 1;
+}
+
+int
+cd9660_susp_finalize(cd9660node *node)
+{
+	cd9660node *temp;
+	int r;
+
+	assert(node != NULL);
+
+	if (node == diskStructure.rootNode)
+		diskStructure.susp_continuation_area_current_free = 0;
+
+	if ((r = cd9660_susp_finalize_node(node)) < 0)
+		return r;
+	if ((r = cd9660_rrip_finalize_node(node)) < 0)
+		return r;
+
+	TAILQ_FOREACH(temp, &node->cn_children, cn_next_child) {
+		if ((r = cd9660_susp_finalize(temp)) < 0)
+			return r;
+	}
+	return 1;
+}
+
+/*
+ * If we really wanted to speed things up, we could have some sort of
+ * lookup table on the SUSP entry type that calls a functor. Or, we could
+ * combine the functions. These functions are kept separate to allow
+ * easier addition of other extensions.
+
+ * For the sake of simplicity and clarity, we won't be doing that for now.
+ */
+
+/*
+ * SUSP needs to update the following types:
+ * CE (continuation area)
+ */
+int
+cd9660_susp_finalize_node(cd9660node *node)
+{
+	struct ISO_SUSP_ATTRIBUTES *t;
+
+	/* Handle CE counters */
+	if (node->susp_entry_ce_length > 0) {
+		node->susp_entry_ce_start =
+		    diskStructure.susp_continuation_area_current_free;
+		diskStructure.susp_continuation_area_current_free +=
+		    node->susp_entry_ce_length;
+	}
+
+	TAILQ_FOREACH(t, &node->head, rr_ll) {
+		if (t->susp_type != SUSP_TYPE_SUSP ||
+		    t->entry_type != SUSP_ENTRY_SUSP_CE)
+			continue;
+		cd9660_bothendian_dword(
+			diskStructure.
+			  susp_continuation_area_start_sector,
+			t->attr.su_entry.CE.ca_sector);
+
+		cd9660_bothendian_dword(
+			diskStructure.
+			  susp_continuation_area_start_sector,
+			t->attr.su_entry.CE.ca_sector);
+		cd9660_bothendian_dword(node->susp_entry_ce_start,
+			t->attr.su_entry.CE.offset);
+		cd9660_bothendian_dword(node->susp_entry_ce_length,
+			t->attr.su_entry.CE.length);
+	}
+	return 0;
+}
+
+int
+cd9660_rrip_finalize_node(cd9660node *node)
+{
+	struct ISO_SUSP_ATTRIBUTES *t;
+
+	TAILQ_FOREACH(t, &node->head, rr_ll) {
+		if (t->susp_type != SUSP_TYPE_RRIP)
+			continue;
+		switch (t->entry_type) {
+		case SUSP_ENTRY_RRIP_CL:
+			/* Look at rr_relocated*/
+			if (node->rr_relocated == NULL)
+				return -1;
+			cd9660_bothendian_dword(
+				node->rr_relocated->fileDataSector,
+				(unsigned char *)
+				    t->attr.rr_entry.CL.dir_loc);
+			break;
+		case SUSP_ENTRY_RRIP_PL:
+			/* Look at rr_real_parent */
+			if (node->parent == NULL ||
+			    node->parent->rr_real_parent == NULL)
+				return -1;
+			cd9660_bothendian_dword(
+				node->parent->rr_real_parent->fileDataSector,
+				(unsigned char *)
+				    t->attr.rr_entry.PL.dir_loc);
+			break;
+		}
+	}
+	return 0;
+}
+
+static int
+cd9660_susp_handle_continuation_common(cd9660node *node, int space)
+{
+	int ca_used, susp_used, susp_used_pre_ce, working;
+	struct ISO_SUSP_ATTRIBUTES *temp, *pre_ce, *last, *CE, *ST;
+
+	pre_ce = last = NULL;
+	working = 254 - space;
+	if (node->su_tail_size > 0)
+		/* Allow 4 bytes for "ST" record. */
+		working -= node->su_tail_size + 4;
+	/* printf("There are %i bytes to work with\n",working); */
+
+	susp_used_pre_ce = susp_used = 0;
+	ca_used = 0;
+	TAILQ_FOREACH(temp, &node->head, rr_ll) {
+		if (working < 0)
+			break;
+		/*
+		 * printf("SUSP Entry found, length is %i\n",
+		 * CD9660_SUSP_ENTRY_SIZE(temp));
+		 */
+		working -= CD9660_SUSP_ENTRY_SIZE(temp);
+		if (working >= 0) {
+			last = temp;
+			susp_used += CD9660_SUSP_ENTRY_SIZE(temp);
+		}
+		if (working >= 28) {
+			/*
+			 * Remember the last entry after which we
+			 * could insert a "CE" entry.
+			 */
+			pre_ce = last;
+			susp_used_pre_ce = susp_used;
+		}
+	}
+
+	/* A CE entry is needed */
+	if (working <= 0) {
+		CE = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
+			SUSP_ENTRY_SUSP_CE, "CE", SUSP_LOC_ENTRY);
+		cd9660_susp_ce(CE, node);
+		/* This will automatically insert at the appropriate location */
+		if (pre_ce != NULL)
+			TAILQ_INSERT_AFTER(&node->head, pre_ce, CE, rr_ll);
+		else
+			TAILQ_INSERT_HEAD(&node->head, CE, rr_ll);
+		last = CE;
+		susp_used = susp_used_pre_ce + 28;
+		/* Count how much CA data is necessary */
+		for (temp = TAILQ_NEXT(last, rr_ll); temp != NULL;
+		     temp = TAILQ_NEXT(temp, rr_ll)) {
+			ca_used += CD9660_SUSP_ENTRY_SIZE(temp);
+		}
+	}
+
+	/* An ST entry is needed */
+	if (node->su_tail_size > 0) {
+		ST = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
+		    SUSP_ENTRY_SUSP_ST, "ST", SUSP_LOC_ENTRY);
+		cd9660_susp_st(ST, node);
+		if (last != NULL)
+			TAILQ_INSERT_AFTER(&node->head, last, ST, rr_ll);
+		else
+			TAILQ_INSERT_HEAD(&node->head, ST, rr_ll);
+		last = ST;
+		susp_used += 4;
+	}
+	if (last != NULL)
+		last->last_in_suf = 1;
+
+	node->susp_entry_size = susp_used;
+	node->susp_entry_ce_length = ca_used;
+
+	diskStructure.susp_continuation_area_size += ca_used;
+	return 1;
+}
+
+/* See if a continuation entry is needed for each of the different types */
+static int
+cd9660_susp_handle_continuation(cd9660node *node)
+{
+	assert (node != NULL);
+
+	/* Entry */
+	if (cd9660_susp_handle_continuation_common(
+		node,(int)(node->isoDirRecord->length[0])) < 0)
+		return 0;
+
+	return 1;
+}
+
+int
+cd9660_susp_initialize_node(cd9660node *node)
+{
+	struct ISO_SUSP_ATTRIBUTES *temp;
+
+	/*
+	 * Requirements/notes:
+	 * CE: is added for us where needed
+	 * ST: not sure if it is even required, but if so, should be
+	 *     handled by the CE code
+	 * PD: isn't needed (though might be added for testing)
+	 * SP: is stored ONLY on the . record of the root directory
+	 * ES: not sure
+	 */
+
+	/* Check for root directory, add SP and ER if needed. */
+	if (node->type & CD9660_TYPE_DOT) {
+		if (node->parent == diskStructure.rootNode) {
+			temp = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
+				SUSP_ENTRY_SUSP_SP, "SP", SUSP_LOC_DOT);
+			cd9660_susp_sp(temp, node);
+
+			/* Should be first entry. */
+			TAILQ_INSERT_HEAD(&node->head, temp, rr_ll);
+		}
+	}
+	return 1;
+}
+
+static void
+cd9660_rrip_initialize_inode(cd9660node *node)
+{
+	struct ISO_SUSP_ATTRIBUTES *attr;
+
+	/*
+	 * Inode dependent values - this may change,
+	 * but for now virtual files and directories do
+	 * not have an inode structure
+	 */
+
+	if ((node->node != NULL) && (node->node->inode != NULL)) {
+		/* PX - POSIX attributes */
+		attr = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+			SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY);
+		cd9660node_rrip_px(attr, node->node);
+
+		TAILQ_INSERT_TAIL(&node->head, attr, rr_ll);
+
+		/* TF - timestamp */
+		attr = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+			SUSP_ENTRY_RRIP_TF, "TF", SUSP_LOC_ENTRY);
+		cd9660node_rrip_tf(attr, node->node);
+		TAILQ_INSERT_TAIL(&node->head, attr, rr_ll);
+
+		/* SL - Symbolic link */
+		/* ?????????? Dan - why is this here? */
+		if (TAILQ_EMPTY(&node->cn_children) &&
+		    node->node->inode != NULL &&
+		    S_ISLNK(node->node->inode->st.st_mode))
+			cd9660_createSL(node);
+
+		/* PN - device number */
+		if (node->node->inode != NULL &&
+		    ((S_ISCHR(node->node->inode->st.st_mode) ||
+		     S_ISBLK(node->node->inode->st.st_mode)))) {
+			attr =
+			    cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+				SUSP_ENTRY_RRIP_PN, "PN",
+				SUSP_LOC_ENTRY);
+			cd9660node_rrip_pn(attr, node->node);
+			TAILQ_INSERT_TAIL(&node->head, attr, rr_ll);
+		}
+	}
+}
+
+int
+cd9660_rrip_initialize_node(cd9660node *node, cd9660node *parent,
+    cd9660node *grandparent)
+{
+	struct ISO_SUSP_ATTRIBUTES *current = NULL;
+
+	assert(node != NULL);
+
+	if (node->type & CD9660_TYPE_DOT) {
+		/*
+		 * Handle ER - should be the only entry to appear on
+		 * a "." record
+		 */
+		if (node->parent == diskStructure.rootNode) {
+			cd9660_susp_ER(node, 1, SUSP_RRIP_ER_EXT_ID,
+				SUSP_RRIP_ER_EXT_DES, SUSP_RRIP_ER_EXT_SRC);
+		}
+		if (parent != NULL && parent->node != NULL &&
+		    parent->node->inode != NULL) {
+			/* PX - POSIX attributes */
+			current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+				SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY);
+			cd9660node_rrip_px(current, parent->node);
+			TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+		}
+	} else if (node->type & CD9660_TYPE_DOTDOT) {
+		if (grandparent != NULL && grandparent->node != NULL &&
+		    grandparent->node->inode != NULL) {
+			/* PX - POSIX attributes */
+			current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+				SUSP_ENTRY_RRIP_PX, "PX", SUSP_LOC_ENTRY);
+			cd9660node_rrip_px(current, grandparent->node);
+			TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+		}
+		/* Handle PL */
+		if (parent != NULL && parent->rr_real_parent != NULL) {
+			current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+			    SUSP_ENTRY_RRIP_PL, "PL", SUSP_LOC_DOTDOT);
+			cd9660_rrip_PL(current,node);
+			TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+		}
+	} else {
+		cd9660_rrip_initialize_inode(node);
+
+		/*
+		 * Not every node needs a NM set - only if the name is
+		 * actually different. IE: If a file is TEST -> TEST,
+		 * no NM. test -> TEST, need a NM
+		 *
+		 * The rr_moved_dir needs to be assigned a NM record as well.
+		 */
+		if (node == diskStructure.rr_moved_dir) {
+			cd9660_rrip_add_NM(node, RRIP_DEFAULT_MOVE_DIR_NAME);
+		}
+		else if ((node->node != NULL) &&
+			((strlen(node->node->name) !=
+			    (uint8_t)node->isoDirRecord->name_len[0]) ||
+			(memcmp(node->node->name,node->isoDirRecord->name,
+				(uint8_t)node->isoDirRecord->name_len[0]) != 0))) {
+			cd9660_rrip_NM(node);
+		}
+
+
+
+		/* Rock ridge directory relocation code here. */
+
+		/* First handle the CL for the placeholder file. */
+		if (node->rr_relocated != NULL) {
+			current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+				SUSP_ENTRY_RRIP_CL, "CL", SUSP_LOC_ENTRY);
+			cd9660_rrip_CL(current, node);
+			TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+		}
+
+		/* Handle RE*/
+		if (node->rr_real_parent != NULL) {
+			current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+				SUSP_ENTRY_RRIP_RE, "RE", SUSP_LOC_ENTRY);
+			cd9660_rrip_RE(current,node);
+			TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+		}
+	}
+	return 1;
+}
+
+struct ISO_SUSP_ATTRIBUTES*
+cd9660node_susp_create_node(int susp_type, int entry_type, const char *type_id,
+			    int write_loc)
+{
+	struct ISO_SUSP_ATTRIBUTES* temp;
+
+	if ((temp = malloc(sizeof(struct ISO_SUSP_ATTRIBUTES))) == NULL) {
+		CD9660_MEM_ALLOC_ERROR("cd9660node_susp_create_node");
+		exit(1);
+	}
+
+	temp->susp_type = susp_type;
+	temp->entry_type = entry_type;
+	temp->last_in_suf = 0;
+	/* Phase this out */
+	temp->type_of[0] = type_id[0];
+	temp->type_of[1] = type_id[1];
+	temp->write_location = write_loc;
+
+	/*
+	 * Since the first four bytes is common, lets go ahead and
+	 * set the type identifier, since we are passing that to this
+	 * function anyhow.
+	 */
+	temp->attr.su_entry.SP.h.type[0] = type_id[0];
+	temp->attr.su_entry.SP.h.type[1] = type_id[1];
+	return temp;
+}
+
+int
+cd9660_rrip_PL(struct ISO_SUSP_ATTRIBUTES* p, cd9660node *node __unused)
+{
+	p->attr.rr_entry.PL.h.length[0] = 12;
+	p->attr.rr_entry.PL.h.version[0] = 1;
+	return 1;
+}
+
+int
+cd9660_rrip_CL(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *node __unused)
+{
+	p->attr.rr_entry.CL.h.length[0] = 12;
+	p->attr.rr_entry.CL.h.version[0] = 1;
+	return 1;
+}
+
+int
+cd9660_rrip_RE(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *node __unused)
+{
+	p->attr.rr_entry.RE.h.length[0] = 4;
+	p->attr.rr_entry.RE.h.version[0] = 1;
+	return 1;
+}
+
+void
+cd9660_createSL(cd9660node *node)
+{
+	struct ISO_SUSP_ATTRIBUTES* current;
+	int path_count, dir_count, done, i, j, dir_copied;
+	char temp_cr[255];
+	char temp_sl[255]; /* used in copying continuation entry*/
+	char* sl_ptr;
+
+	sl_ptr = node->node->symlink;
+
+	done = 0;
+	path_count = 0;
+	dir_count = 0;
+	dir_copied = 0;
+	current = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+	    SUSP_ENTRY_RRIP_SL, "SL", SUSP_LOC_ENTRY);
+
+	current->attr.rr_entry.SL.h.version[0] = 1;
+	current->attr.rr_entry.SL.flags[0] = SL_FLAGS_NONE;
+
+	if (*sl_ptr == '/') {
+		temp_cr[0] = SL_FLAGS_ROOT;
+		temp_cr[1] = 0;
+		memcpy(current->attr.rr_entry.SL.component + path_count,
+		    temp_cr, 2);
+		path_count += 2;
+		sl_ptr++;
+	}
+
+	for (i = 0; i < (dir_count + 2); i++)
+		temp_cr[i] = '\0';
+
+	while (!done) {
+		while ((*sl_ptr != '/') && (*sl_ptr != '\0')) {
+			dir_copied = 1;
+			if (*sl_ptr == '.') {
+				if ((*(sl_ptr + 1) == '/') || (*(sl_ptr + 1)
+				     == '\0')) {
+					temp_cr[0] = SL_FLAGS_CURRENT;
+					sl_ptr++;
+				} else if(*(sl_ptr + 1) == '.') {
+					if ((*(sl_ptr + 2) == '/') ||
+					    (*(sl_ptr + 2) == '\0')) {
+						temp_cr[0] = SL_FLAGS_PARENT;
+						sl_ptr += 2;
+					}
+				} else {
+					temp_cr[dir_count+2] = *sl_ptr;
+					sl_ptr++;
+					dir_count++;
+				}
+			} else {
+				temp_cr[dir_count + 2] = *sl_ptr;
+				sl_ptr++;
+				dir_count++;
+			}
+		}
+
+		if ((path_count + dir_count) >= 249) {
+			current->attr.rr_entry.SL.flags[0] |= SL_FLAGS_CONTINUE;
+
+			j = 0;
+
+			if (path_count <= 249) {
+				while(j != (249 - path_count)) {
+					temp_sl[j] = temp_cr[j];
+					j++;
+				}
+				temp_sl[0] = SL_FLAGS_CONTINUE;
+				temp_sl[1] = j - 2;
+				memcpy(
+				    current->attr.rr_entry.SL.component +
+					path_count,
+				    temp_sl, j);
+			}
+
+			path_count += j;
+			current->attr.rr_entry.SL.h.length[0] = path_count + 5;
+			TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+			current= cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+			       SUSP_ENTRY_RRIP_SL, "SL", SUSP_LOC_ENTRY);
+			current->attr.rr_entry.SL.h.version[0] = 1;
+			current->attr.rr_entry.SL.flags[0] = SL_FLAGS_NONE;
+
+			path_count = 0;
+
+			if (dir_count > 2) {
+				while (j != dir_count + 2) {
+					current->attr.rr_entry.SL.component[
+					    path_count + 2] = temp_cr[j];
+					j++;
+					path_count++;
+				}
+				current->attr.rr_entry.SL.component[1]
+				    = path_count;
+				path_count+= 2;
+			} else {
+				while(j != dir_count) {
+					current->attr.rr_entry.SL.component[
+					    path_count+2] = temp_cr[j];
+					j++;
+					path_count++;
+				}
+			}
+		} else {
+			if (dir_copied == 1) {
+				temp_cr[1] = dir_count;
+				memcpy(current->attr.rr_entry.SL.component +
+					path_count,
+				    temp_cr, dir_count + 2);
+				path_count += dir_count + 2;
+			}
+		}
+
+		if (*sl_ptr == '\0') {
+			done = 1;
+			current->attr.rr_entry.SL.h.length[0] = path_count + 5;
+			TAILQ_INSERT_TAIL(&node->head, current, rr_ll);
+		} else {
+			sl_ptr++;
+			dir_count = 0;
+			dir_copied = 0;
+			for(i = 0; i < 255; i++) {
+				temp_cr[i] = '\0';
+			}
+		}
+	}
+}
+
+int
+cd9660node_rrip_px(struct ISO_SUSP_ATTRIBUTES *v, fsnode *pxinfo)
+{
+	v->attr.rr_entry.PX.h.length[0] = 44;
+	v->attr.rr_entry.PX.h.version[0] = 1;
+	cd9660_bothendian_dword(pxinfo->inode->st.st_mode,
+	    v->attr.rr_entry.PX.mode);
+	cd9660_bothendian_dword(pxinfo->inode->st.st_nlink,
+	    v->attr.rr_entry.PX.links);
+	cd9660_bothendian_dword(pxinfo->inode->st.st_uid,
+	    v->attr.rr_entry.PX.uid);
+	cd9660_bothendian_dword(pxinfo->inode->st.st_gid,
+	    v->attr.rr_entry.PX.gid);
+	cd9660_bothendian_dword(pxinfo->inode->st.st_ino,
+	    v->attr.rr_entry.PX.serial);
+
+	return 1;
+}
+
+int
+cd9660node_rrip_pn(struct ISO_SUSP_ATTRIBUTES *pn_field, fsnode *fnode)
+{
+	pn_field->attr.rr_entry.PN.h.length[0] = 20;
+	pn_field->attr.rr_entry.PN.h.version[0] = 1;
+
+	if (sizeof (fnode->inode->st.st_rdev) > 4)
+		cd9660_bothendian_dword(
+		    (uint64_t)fnode->inode->st.st_rdev >> 32,
+		    pn_field->attr.rr_entry.PN.high);
+	else
+		cd9660_bothendian_dword(0, pn_field->attr.rr_entry.PN.high);
+
+	cd9660_bothendian_dword(fnode->inode->st.st_rdev & 0xffffffff,
+		pn_field->attr.rr_entry.PN.low);
+	return 1;
+}
+
+#if 0
+int
+cd9660node_rrip_nm(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *file_node)
+{
+	int nm_length = strlen(file_node->isoDirRecord->name) + 5;
+        p->attr.rr_entry.NM.h.type[0] = 'N';
+	p->attr.rr_entry.NM.h.type[1] = 'M';
+	sprintf(p->attr.rr_entry.NM.altname, "%s", file_node->isoDirRecord->name);
+	p->attr.rr_entry.NM.h.length[0] = (unsigned char)nm_length;
+	p->attr.rr_entry.NM.h.version[0] = (unsigned char)1;
+	p->attr.rr_entry.NM.flags[0] = (unsigned char) NM_PARENT;
+	return 1;
+}
+#endif
+
+int
+cd9660node_rrip_tf(struct ISO_SUSP_ATTRIBUTES *p, fsnode *_node)
+{
+	p->attr.rr_entry.TF.flags[0] = TF_MODIFY | TF_ACCESS | TF_ATTRIBUTES;
+	p->attr.rr_entry.TF.h.length[0] = 5;
+	p->attr.rr_entry.TF.h.version[0] = 1;
+
+	/*
+	 * Need to add creation time, backup time,
+	 * expiration time, and effective time.
+	 */
+
+	cd9660_time_915(p->attr.rr_entry.TF.timestamp,
+		_node->inode->st.st_atime);
+	p->attr.rr_entry.TF.h.length[0] += 7;
+
+	cd9660_time_915(p->attr.rr_entry.TF.timestamp + 7,
+		_node->inode->st.st_mtime);
+	p->attr.rr_entry.TF.h.length[0] += 7;
+
+	cd9660_time_915(p->attr.rr_entry.TF.timestamp + 14,
+		_node->inode->st.st_ctime);
+	p->attr.rr_entry.TF.h.length[0] += 7;
+	return 1;
+}
+
+int
+cd9660_susp_sp(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *spinfo __unused)
+{
+	p->attr.su_entry.SP.h.length[0] = 7;
+	p->attr.su_entry.SP.h.version[0] = 1;
+	p->attr.su_entry.SP.check[0] = 0xBE;
+	p->attr.su_entry.SP.check[1] = 0xEF;
+	p->attr.su_entry.SP.len_skp[0] = 0;
+	return 1;
+}
+
+int
+cd9660_susp_st(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *stinfo __unused)
+{
+	p->attr.su_entry.ST.h.type[0] = 'S';
+	p->attr.su_entry.ST.h.type[1] = 'T';
+	p->attr.su_entry.ST.h.length[0] = 4;
+	p->attr.su_entry.ST.h.version[0] = 1;
+	return 1;
+}
+
+int
+cd9660_susp_ce(struct ISO_SUSP_ATTRIBUTES *p, cd9660node *spinfo __unused)
+{
+	p->attr.su_entry.CE.h.length[0] = 28;
+	p->attr.su_entry.CE.h.version[0] = 1;
+	/* Other attributes dont matter right now, will be updated later */
+	return 1;
+}
+
+int
+cd9660_susp_pd(struct ISO_SUSP_ATTRIBUTES *p __unused, int length __unused)
+{
+	return 1;
+}
+
+void
+cd9660_rrip_add_NM(cd9660node *node, const char *name)
+{
+	int working,len;
+	const char *p;
+	struct ISO_SUSP_ATTRIBUTES *r;
+
+	/*
+	 * Each NM record has 254 byes to work with. This means that
+	 * the name data itself only has 249 bytes to work with. So, a
+	 * name with 251 characters would require two nm records.
+	 */
+	p = name;
+	working = 1;
+	while (working) {
+		r = cd9660node_susp_create_node(SUSP_TYPE_RRIP,
+		    SUSP_ENTRY_RRIP_NM, "NM", SUSP_LOC_ENTRY);
+		r->attr.rr_entry.NM.h.version[0] = 1;
+		r->attr.rr_entry.NM.flags[0] = RRIP_NM_FLAGS_NONE;
+		len = strlen(p);
+
+		if (len > 249) {
+			len = 249;
+			r->attr.rr_entry.NM.flags[0] = RRIP_NM_FLAGS_CONTINUE;
+		} else {
+			working = 0;
+		}
+		memcpy(r->attr.rr_entry.NM.altname, p, len);
+		r->attr.rr_entry.NM.h.length[0] = 5 + len;
+
+		TAILQ_INSERT_TAIL(&node->head, r, rr_ll);
+
+		p += len;
+	}
+}
+
+void
+cd9660_rrip_NM(cd9660node *node)
+{
+	cd9660_rrip_add_NM(node, node->node->name);
+}
+
+struct ISO_SUSP_ATTRIBUTES*
+cd9660_susp_ER(cd9660node *node,
+	       u_char ext_version, const char* ext_id, const char* ext_des,
+	       const char* ext_src)
+{
+	int l;
+	struct ISO_SUSP_ATTRIBUTES *r;
+
+	r = cd9660node_susp_create_node(SUSP_TYPE_SUSP,
+			SUSP_ENTRY_SUSP_ER, "ER", SUSP_LOC_DOT);
+
+	/* Fixed data is 8 bytes */
+	r->attr.su_entry.ER.h.length[0] = 8;
+	r->attr.su_entry.ER.h.version[0] = 1;
+
+	r->attr.su_entry.ER.len_id[0] = (u_char)strlen(ext_id);
+	r->attr.su_entry.ER.len_des[0] = (u_char)strlen(ext_des);
+	r->attr.su_entry.ER.len_src[0] = (u_char)strlen(ext_src);
+
+	l = r->attr.su_entry.ER.len_id[0] +
+		r->attr.su_entry.ER.len_src[0] +
+		r->attr.su_entry.ER.len_des[0];
+
+	/* Everything must fit. */
+	assert(l + r->attr.su_entry.ER.h.length[0] <= 254);
+
+	r->attr.su_entry.ER.h.length[0] += (u_char)l;
+
+
+	r->attr.su_entry.ER.ext_ver[0] = ext_version;
+	memcpy(r->attr.su_entry.ER.ext_data, ext_id,
+		(int)r->attr.su_entry.ER.len_id[0]);
+	l = (int) r->attr.su_entry.ER.len_id[0];
+	memcpy(r->attr.su_entry.ER.ext_data + l,ext_des,
+		(int)r->attr.su_entry.ER.len_des[0]);
+
+	l += (int)r->attr.su_entry.ER.len_des[0];
+	memcpy(r->attr.su_entry.ER.ext_data + l,ext_src,
+		(int)r->attr.su_entry.ER.len_src[0]);
+
+	TAILQ_INSERT_TAIL(&node->head, r, rr_ll);
+	return r;
+}
+
+struct ISO_SUSP_ATTRIBUTES*
+cd9660_susp_ES(struct ISO_SUSP_ATTRIBUTES *last __unused, cd9660node *node __unused)
+{
+	return NULL;
+}
diff --git a/usr.sbin/makefs/cd9660/iso9660_rrip.h b/usr.sbin/makefs/cd9660/iso9660_rrip.h
new file mode 100644
index 0000000000..27a42360cf
--- /dev/null
+++ b/usr.sbin/makefs/cd9660/iso9660_rrip.h
@@ -0,0 +1,290 @@
+/*	$NetBSD: iso9660_rrip.h,v 1.5 2009/01/10 22:06:29 bjh21 Exp $	*/
+
+/*
+ * Copyright (c) 2005 Daniel Watt, Walter Deignan, Ryan Gabrys, Alan
+ * Perez-Rathke and Ram Vedam.  All rights reserved.
+ *
+ * This code was written by Daniel Watt, Walter Deignan, Ryan Gabrys,
+ * Alan Perez-Rathke and Ram Vedam.
+ *
+ * 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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM ``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 DANIEL WATT, WALTER DEIGNAN, RYAN
+ * GABRYS, ALAN PEREZ-RATHKE AND RAM VEDAM 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: head/usr.sbin/makefs/cd9660/iso9660_rrip.h 260041 2013-12-29 16:43:35Z marius $
+ */
+#ifndef __ISO9660_RRIP_H__
+#define __ISO9660_RRIP_H__
+
+/*
+ * This will hold all the functions needed to
+ * write an ISO 9660 image with Rock Ridge Extensions
+ */
+
+/* For writing must use ISO_RRIP_EXTREF structure */
+
+#include "makefs.h"
+#include <cd9660_rrip.h>
+#include "cd9660.h"
+#include <sys/queue.h>
+
+#define	 PX_LENGTH	   0x2C
+#define	 PN_LENGTH	   0x14
+#define	 TF_CREATION	   0x00
+#define	 TF_MODIFY	   0x01
+#define	 TF_ACCESS	   0x02
+#define	 TF_ATTRIBUTES	   0x04
+#define	 TF_BACKUP	   0x08
+#define	 TF_EXPIRATION	   0x10
+#define	 TF_EFFECTIVE	   0x20
+#define	 TF_LONGFORM	   0x40
+#define  NM_CONTINUE	   0x80
+#define	 NM_CURRENT	   0x100
+#define	 NM_PARENT	   0x200
+
+
+#define	 SUSP_LOC_ENTRY	   0x01
+#define	 SUSP_LOC_DOT	   0x02
+#define	 SUSP_LOC_DOTDOT   0x04
+
+#define SUSP_TYPE_SUSP		1
+#define SUSP_TYPE_RRIP		2
+
+#define SUSP_ENTRY_SUSP_CE	1
+#define SUSP_ENTRY_SUSP_PD	2
+#define SUSP_ENTRY_SUSP_SP	3
+#define SUSP_ENTRY_SUSP_ST	4
+#define SUSP_ENTRY_SUSP_ER	5
+#define SUSP_ENTRY_SUSP_ES	6
+
+#define SUSP_ENTRY_RRIP_PX	1
+#define SUSP_ENTRY_RRIP_PN	2
+#define SUSP_ENTRY_RRIP_SL	3
+#define SUSP_ENTRY_RRIP_NM	4
+#define SUSP_ENTRY_RRIP_CL	5
+#define SUSP_ENTRY_RRIP_PL	6
+#define SUSP_ENTRY_RRIP_RE	7
+#define SUSP_ENTRY_RRIP_TF	8
+#define SUSP_ENTRY_RRIP_SF	9
+
+#define SUSP_RRIP_ER_EXT_ID "IEEE_P1282"
+#define SUSP_RRIP_ER_EXT_DES "THE IEEE P1282 PROTOCOL PROVIDES SUPPORT FOR POSIX FILE SYSTEM SEMANTICS."
+#define SUSP_RRIP_ER_EXT_SRC "PLEASE CONTACT THE IEEE STANDARDS DEPARTMENT, PISCATAWAY, NJ, USA FOR THE P1282 SPECIFICATION."
+
+#define SL_FLAGS_NONE	        0
+#define SL_FLAGS_CONTINUE       1
+#define SL_FLAGS_CURRENT        2
+#define SL_FLAGS_PARENT	        4
+#define SL_FLAGS_ROOT	        8
+
+typedef struct {
+	ISO_SUSP_HEADER		 h;
+	u_char mode		[ISODCL(5,12)];
+	u_char links		[ISODCL(13,20)];
+	u_char uid		[ISODCL(21,28)];
+	u_char gid		[ISODCL(29,36)];
+	u_char serial		[ISODCL(37,44)];
+} ISO_RRIP_PX;
+
+typedef struct {
+	ISO_SUSP_HEADER		 h;
+	u_char high		[ISODCL(5,12)];
+	u_char low		[ISODCL(13,20)];
+} ISO_RRIP_PN;
+
+typedef struct {
+	ISO_SUSP_HEADER		 h;
+	u_char flags		 [ISODCL ( 4, 4)];
+	u_char component	 [ISODCL ( 4, 256)];
+	u_int  nBytes;
+} ISO_RRIP_SL;
+
+typedef struct {
+	ISO_SUSP_HEADER		 h;
+	u_char flags		 [ISODCL ( 4, 4)];
+	u_char timestamp	 [ISODCL ( 5, 256)];
+} ISO_RRIP_TF;
+
+#define RRIP_NM_FLAGS_NONE 0x00
+#define RRIP_NM_FLAGS_CONTINUE 0x01
+#define RRIP_NM_FLAGS_CURRENT 0x02
+#define RRIP_NM_FLAGS_PARENT 0x04
+
+typedef struct {
+	ISO_SUSP_HEADER		 h;
+	u_char flags		 [ISODCL ( 4, 4)];
+	u_char altname		 [ISODCL ( 4, 256)];
+} ISO_RRIP_NM;
+
+/* Note that this is the same structure as cd9660_rrip.h : ISO_RRIP_CONT */
+typedef struct {
+	ISO_SUSP_HEADER		 h;
+	u_char ca_sector	 [ISODCL ( 5, 12)];
+	u_char offset		 [ISODCL ( 13, 20)];
+	u_char length		 [ISODCL ( 21, 28)];
+} ISO_SUSP_CE;
+
+typedef struct {
+	ISO_SUSP_HEADER		 h;
+	u_char padding_area	 [ISODCL ( 4, 256)];
+} ISO_SUSP_PD;
+
+typedef struct {
+	ISO_SUSP_HEADER		 h;
+	u_char check		 [ISODCL ( 4, 5)];
+	u_char len_skp		 [ISODCL ( 6, 6)];
+} ISO_SUSP_SP;
+
+typedef struct {
+	ISO_SUSP_HEADER		 h;
+} ISO_SUSP_ST;
+
+typedef struct {
+	ISO_SUSP_HEADER		 h;
+	u_char len_id		 [ISODCL ( 4, 4)];
+	u_char len_des		 [ISODCL ( 5, 5)];
+	u_char len_src		 [ISODCL ( 6, 6)];
+	u_char ext_ver		 [ISODCL ( 7, 7)];
+	u_char ext_data		 [ISODCL (8,256)];
+/*	u_char ext_id		 [ISODCL ( 8, 256)];
+	u_char ext_des		 [ISODCL ( 257, 513)];
+	u_char ext_src		 [ISODCL ( 514, 770)];*/
+} ISO_SUSP_ER;
+
+typedef struct {
+	ISO_SUSP_HEADER		 h;
+	u_char ext_seq		 [ISODCL ( 4, 4)];
+} ISO_SUSP_ES;
+
+typedef union {
+	ISO_RRIP_PX			PX;
+	ISO_RRIP_PN			PN;
+	ISO_RRIP_SL			SL;
+	ISO_RRIP_NM			NM;
+	ISO_RRIP_CLINK			CL;
+	ISO_RRIP_PLINK			PL;
+	ISO_RRIP_RELDIR			RE;
+	ISO_RRIP_TF			TF;
+} rrip_entry;
+
+typedef union {
+	ISO_SUSP_CE			CE;
+	ISO_SUSP_PD			PD;
+	ISO_SUSP_SP			SP;
+	ISO_SUSP_ST			ST;
+	ISO_SUSP_ER			ER;
+	ISO_SUSP_ES			ES;
+} susp_entry;
+
+typedef union {
+	susp_entry		  su_entry;
+	rrip_entry		  rr_entry;
+} SUSP_ENTRIES;
+
+struct ISO_SUSP_ATTRIBUTES {
+	SUSP_ENTRIES attr;
+	int type;
+	char type_of[2];
+	char last_in_suf;	/* last entry in the System Use Field? */
+	/* Dan's addons - will merge later. This allows use of a switch */
+	char susp_type; 	/* SUSP or RRIP */
+	char entry_type;	/* Record type */
+	char write_location;
+	TAILQ_ENTRY(ISO_SUSP_ATTRIBUTES) rr_ll;
+};
+
+#define CD9660_SUSP_ENTRY_SIZE(entry)\
+	((int) ((entry)->attr.su_entry.SP.h.length[0]))
+
+/* Recursive function - move later to func pointer code*/
+int cd9660_susp_finalize(cd9660node *);
+
+/* These two operate on single nodes */
+int cd9660_susp_finalize_node(cd9660node *);
+int cd9660_rrip_finalize_node(cd9660node *);
+
+/* POSIX File attribute */
+int cd9660node_rrip_px(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+/* Device number */
+int cd9660node_rrip_pn(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+/* Symbolic link */
+int cd9660node_rrip_SL(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+/* Alternate Name function */
+void cd9660_rrip_NM(cd9660node *);
+void cd9660_rrip_add_NM(cd9660node *,const char *);
+
+/* Parent and child link function */
+int cd9660_rrip_PL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_rrip_CL(struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_rrip_RE(struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+
+int cd9660node_rrip_tf(struct ISO_SUSP_ATTRIBUTES *, fsnode *);
+
+
+
+/*
+ * Relocation directory function. I'm not quite sure what
+ * sort of parameters are needed, but personally I don't think
+ * any parameters are needed except for the memory address where
+ * the information needs to be put in
+ */
+int cd9660node_rrip_re(void *, fsnode *);
+
+/*
+ * Don't know if this function is needed because it apparently is an
+ * optional feature that does not really need to be implemented but I
+ * thought I should add it anyway.
+ */
+int cd9660_susp_ce (struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_susp_pd (struct ISO_SUSP_ATTRIBUTES *, int);
+int cd9660_susp_sp (struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+int cd9660_susp_st (struct ISO_SUSP_ATTRIBUTES *, cd9660node *);
+
+struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ER(cd9660node *, u_char, const char *,
+    const char *, const char *);
+struct ISO_SUSP_ATTRIBUTES *cd9660_susp_ES(struct ISO_SUSP_ATTRIBUTES*,
+    cd9660node *);
+
+
+/* Helper functions */
+
+/* Common SUSP/RRIP functions */
+int cd9660_susp_initialize(cd9660node *, cd9660node *, cd9660node *);
+int cd9660_susp_initialize_node(cd9660node *);
+struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_create_node(int, int, const char *,
+    int);
+struct ISO_SUSP_ATTRIBUTES *cd9660node_susp_add_entry(cd9660node *,
+    struct ISO_SUSP_ATTRIBUTES *, struct ISO_SUSP_ATTRIBUTES *, int);
+
+/* RRIP specific functions */
+int cd9660_rrip_initialize_node(cd9660node *, cd9660node *, cd9660node *);
+void cd9660_createSL(cd9660node *);
+
+/* Functions that probably can be removed */
+/* int cd9660node_initialize_node(int, char *); */
+
+
+#endif
diff --git a/usr.sbin/makefs/ffs.c b/usr.sbin/makefs/ffs.c
new file mode 100644
index 0000000000..4e7a331317
--- /dev/null
+++ b/usr.sbin/makefs/ffs.c
@@ -0,0 +1,1072 @@
+/*	$NetBSD: ffs.c,v 1.45 2011/10/09 22:49:26 christos Exp $	*/
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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.
+ */
+/*
+ * Copyright (c) 1982, 1986, 1989, 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.
+ *
+ *	@(#)ffs_alloc.c	8.19 (Berkeley) 7/13/95
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/ffs.c 308344 2016-11-05 16:23:33Z marcel $");
+
+#include <sys/param.h>
+
+#include <sys/mount.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "makefs.h"
+#include "ffs.h"
+
+#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
+#include <sys/statvfs.h>
+#endif
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+
+#include "ffs/ufs_bswap.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/newfs_extern.h"
+#include "ffs/ffs_extern.h"
+
+#undef DIP
+#define DIP(dp, field) (dp)->ffs1_din.di_##field
+
+/*
+ * Various file system defaults (cribbed from newfs(8)).
+ */
+#define	DFL_FRAGSIZE		1024		/* fragment size */
+#define	DFL_BLKSIZE		8192		/* block size */
+#define	DFL_SECSIZE		512		/* sector size */
+#define	DFL_CYLSPERGROUP	65536		/* cylinders per group */
+#define	DFL_FRAGSPERINODE	4		/* fragments per inode */
+#define	DFL_ROTDELAY		0		/* rotational delay */
+#define	DFL_NRPOS		1		/* rotational positions */
+#define	DFL_RPM			3600		/* rpm of disk */
+#define	DFL_NSECTORS		64		/* # of sectors */
+#define	DFL_NTRACKS		16		/* # of tracks */
+
+
+typedef struct {
+	u_char		*buf;		/* buf for directory */
+	doff_t		size;		/* full size of buf */
+	doff_t		cur;		/* offset of current entry */
+} dirbuf_t;
+
+
+static	int	ffs_create_image(const char *, fsinfo_t *);
+static	void	ffs_dump_fsinfo(fsinfo_t *);
+static	void	ffs_dump_dirbuf(dirbuf_t *, const char *, int);
+static	void	ffs_make_dirbuf(dirbuf_t *, const char *, fsnode *, int);
+static	int	ffs_populate_dir(const char *, fsnode *, fsinfo_t *);
+static	void	ffs_size_dir(fsnode *, fsinfo_t *);
+static	void	ffs_validate(const char *, fsnode *, fsinfo_t *);
+static	void	ffs_write_file(union dinode *, uint32_t, void *, fsinfo_t *);
+static	void	ffs_write_inode(union dinode *, uint32_t, const fsinfo_t *);
+static  void	*ffs_build_dinode1(struct ufs1_dinode *, dirbuf_t *, fsnode *,
+				 fsnode *, fsinfo_t *);
+
+
+
+int	sectorsize;		/* XXX: for buf.c::getblk() */
+
+	/* publicly visible functions */
+
+void
+ffs_prep_opts(fsinfo_t *fsopts)
+{
+	ffs_opt_t *ffs_opts;
+
+	if ((ffs_opts = calloc(1, sizeof(ffs_opt_t))) == NULL)
+		err(1, "Allocating memory for ffs_options");
+
+	fsopts->fs_specific = ffs_opts;
+
+	ffs_opts->bsize= -1;
+	ffs_opts->fsize= -1;
+	ffs_opts->cpg= -1;
+	ffs_opts->density= -1;
+	ffs_opts->minfree= -1;
+	ffs_opts->optimization= -1;
+	ffs_opts->maxcontig= -1;
+	ffs_opts->maxbpg= -1;
+	ffs_opts->avgfilesize= -1;
+	ffs_opts->avgfpdir= -1;
+	ffs_opts->version = 1;
+}
+
+void
+ffs_cleanup_opts(fsinfo_t *fsopts)
+{
+	if (fsopts->fs_specific)
+		free(fsopts->fs_specific);
+}
+
+int
+ffs_parse_opts(const char *option, fsinfo_t *fsopts)
+{
+	ffs_opt_t	*ffs_opts = fsopts->fs_specific;
+
+	option_t ffs_options[] = {
+		{ "bsize",	&ffs_opts->bsize,	1,	INT_MAX,
+					"block size" },
+		{ "fsize",	&ffs_opts->fsize,	1,	INT_MAX,
+					"fragment size" },
+		{ "density",	&ffs_opts->density,	1,	INT_MAX,
+					"bytes per inode" },
+		{ "minfree",	&ffs_opts->minfree,	0,	99,
+					"minfree" },
+		{ "maxbpg",	&ffs_opts->maxbpg,	1,	INT_MAX,
+					"max blocks per file in a cg" },
+		{ "avgfilesize", &ffs_opts->avgfilesize,1,	INT_MAX,
+					"expected average file size" },
+		{ "avgfpdir",	&ffs_opts->avgfpdir,	1,	INT_MAX,
+					"expected # of files per directory" },
+		{ "maxbpcg",	&ffs_opts->maxblkspercg,1,	INT_MAX,
+					"max # of blocks per group" },
+		{ "version",	&ffs_opts->version,	1,	1,
+					"UFS version" },
+		{ .name = NULL }
+	};
+
+	char	*var, *val;
+	int	rv;
+
+	assert(option != NULL);
+	assert(fsopts != NULL);
+	assert(ffs_opts != NULL);
+
+	if (debug & DEBUG_FS_PARSE_OPTS)
+		printf("ffs_parse_opts: got `%s'\n", option);
+
+	if ((var = strdup(option)) == NULL)
+		err(1, "Allocating memory for copy of option string");
+	rv = 0;
+
+	if ((val = strchr(var, '=')) == NULL) {
+		warnx("Option `%s' doesn't contain a value", var);
+		goto leave_ffs_parse_opts;
+	}
+	*val++ = '\0';
+
+	if (strcmp(var, "optimization") == 0) {
+		if (strcmp(val, "time") == 0) {
+			ffs_opts->optimization = FS_OPTTIME;
+		} else if (strcmp(val, "space") == 0) {
+			ffs_opts->optimization = FS_OPTSPACE;
+		} else {
+			warnx("Invalid optimization `%s'", val);
+			goto leave_ffs_parse_opts;
+		}
+		rv = 1;
+	} else if (strcmp(var, "label") == 0) {
+		strlcpy(ffs_opts->label, val, sizeof(ffs_opts->label));
+		rv = 1;
+	} else
+		rv = set_option(ffs_options, var, val);
+
+ leave_ffs_parse_opts:
+	if (var)
+		free(var);
+	return (rv);
+}
+
+
+void
+ffs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+	struct fs	*superblock;
+	struct timeval	start;
+
+	assert(image != NULL);
+	assert(dir != NULL);
+	assert(root != NULL);
+	assert(fsopts != NULL);
+
+	if (debug & DEBUG_FS_MAKEFS)
+		printf("ffs_makefs: image %s directory %s root %p\n",
+		    image, dir, root);
+
+		/* validate tree and options */
+	TIMER_START(start);
+	ffs_validate(dir, root, fsopts);
+	TIMER_RESULTS(start, "ffs_validate");
+
+	printf("Calculated size of `%s': %lld bytes, %lld inodes\n",
+	    image, (long long)fsopts->size, (long long)fsopts->inodes);
+
+		/* create image */
+	TIMER_START(start);
+	if (ffs_create_image(image, fsopts) == -1)
+		errx(1, "Image file `%s' not created.", image);
+	TIMER_RESULTS(start, "ffs_create_image");
+
+	fsopts->curinode = ROOTINO;
+
+	if (debug & DEBUG_FS_MAKEFS)
+		putchar('\n');
+
+		/* populate image */
+	printf("Populating `%s'\n", image);
+	TIMER_START(start);
+	if (! ffs_populate_dir(dir, root, fsopts))
+		errx(1, "Image file `%s' not populated.", image);
+	TIMER_RESULTS(start, "ffs_populate_dir");
+
+		/* ensure no outstanding buffers remain */
+	if (debug & DEBUG_FS_MAKEFS)
+		bcleanup();
+
+		/* update various superblock parameters */
+	superblock = fsopts->superblock;
+	superblock->fs_fmod = 0;
+
+		/* write out superblock; image is now complete */
+	ffs_write_superblock(fsopts->superblock, fsopts);
+	if (close(fsopts->fd) == -1)
+		err(1, "Closing `%s'", image);
+	fsopts->fd = -1;
+	printf("Image `%s' complete\n", image);
+}
+
+	/* end of public functions */
+
+
+static void
+ffs_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+	int32_t	ncg = 1;
+#if notyet
+	int32_t	spc, nspf, ncyl, fssize;
+#endif
+	ffs_opt_t	*ffs_opts = fsopts->fs_specific;
+
+	assert(dir != NULL);
+	assert(root != NULL);
+	assert(fsopts != NULL);
+	assert(ffs_opts != NULL);
+
+	if (debug & DEBUG_FS_VALIDATE) {
+		printf("ffs_validate: before defaults set:\n");
+		ffs_dump_fsinfo(fsopts);
+	}
+
+		/* set FFS defaults */
+	if (fsopts->sectorsize == -1)
+		fsopts->sectorsize = DFL_SECSIZE;
+	if (ffs_opts->fsize == -1)
+		ffs_opts->fsize = MAX(DFL_FRAGSIZE, fsopts->sectorsize);
+	if (ffs_opts->bsize == -1)
+		ffs_opts->bsize = MIN(DFL_BLKSIZE, 8 * ffs_opts->fsize);
+	if (ffs_opts->cpg == -1)
+		ffs_opts->cpg = DFL_CYLSPERGROUP;
+	else
+		ffs_opts->cpgflg = 1;
+				/* fsopts->density is set below */
+	if (ffs_opts->nsectors == -1)
+		ffs_opts->nsectors = DFL_NSECTORS;
+	if (ffs_opts->minfree == -1)
+		ffs_opts->minfree = MINFREE;
+	if (ffs_opts->optimization == -1)
+		ffs_opts->optimization = DEFAULTOPT;
+	if (ffs_opts->maxcontig == -1)
+		ffs_opts->maxcontig =
+		    MAX(1, MIN(MAXPHYS, FFS_MAXBSIZE) / ffs_opts->bsize);
+	/* XXX ondisk32 */
+	if (ffs_opts->maxbpg == -1)
+		ffs_opts->maxbpg = ffs_opts->bsize / sizeof(int32_t);
+	if (ffs_opts->avgfilesize == -1)
+		ffs_opts->avgfilesize = AVFILESIZ;
+	if (ffs_opts->avgfpdir == -1)
+		ffs_opts->avgfpdir = AFPDIR;
+
+	if (fsopts->maxsize > 0 &&
+	    roundup(fsopts->minsize, ffs_opts->bsize) > fsopts->maxsize)
+		errx(1, "`%s' minsize of %lld rounded up to ffs bsize of %d "
+		    "exceeds maxsize %lld.  Lower bsize, or round the minimum "
+		    "and maximum sizes to bsize.", dir,
+		    (long long)fsopts->minsize, ffs_opts->bsize,
+		    (long long)fsopts->maxsize);
+
+		/* calculate size of tree */
+	ffs_size_dir(root, fsopts);
+	fsopts->inodes += ROOTINO;		/* include first two inodes */
+
+	if (debug & DEBUG_FS_VALIDATE)
+		printf("ffs_validate: size of tree: %lld bytes, %lld inodes\n",
+		    (long long)fsopts->size, (long long)fsopts->inodes);
+
+		/* add requested slop */
+	fsopts->size += fsopts->freeblocks;
+	fsopts->inodes += fsopts->freefiles;
+	if (fsopts->freefilepc > 0)
+		fsopts->inodes =
+		    fsopts->inodes * (100 + fsopts->freefilepc) / 100;
+	if (fsopts->freeblockpc > 0)
+		fsopts->size =
+		    fsopts->size * (100 + fsopts->freeblockpc) / 100;
+
+		/* add space needed for superblocks */
+	/*
+	 * The old SBOFF (SBLOCK_UFS1) is used here because makefs is
+	 * typically used for small filesystems where space matters.
+	 * XXX make this an option.
+	 */
+	fsopts->size += (8192 + SBSIZE) * ncg;
+		/* add space needed to store inodes, x3 for blockmaps, etc */
+	fsopts->size += ncg * DINODE1_SIZE *
+	    roundup(fsopts->inodes / ncg, ffs_opts->bsize / DINODE1_SIZE);
+
+		/* add minfree */
+	if (ffs_opts->minfree > 0)
+		fsopts->size =
+		    fsopts->size * (100 + ffs_opts->minfree) / 100;
+	/*
+	 * XXX	any other fs slop to add, such as csum's, bitmaps, etc ??
+	 */
+
+	if (fsopts->size < fsopts->minsize)	/* ensure meets minimum size */
+		fsopts->size = fsopts->minsize;
+
+		/* round up to the next block */
+	fsopts->size = roundup(fsopts->size, ffs_opts->bsize);
+
+		/* round up to requested block size, if any */
+	if (fsopts->roundup > 0)
+		fsopts->size = roundup(fsopts->size, fsopts->roundup);
+
+		/* calculate density if necessary */
+	if (ffs_opts->density == -1)
+		ffs_opts->density = fsopts->size / fsopts->inodes + 1;
+
+	if (debug & DEBUG_FS_VALIDATE) {
+		printf("ffs_validate: after defaults set:\n");
+		ffs_dump_fsinfo(fsopts);
+		printf("ffs_validate: dir %s; %lld bytes, %lld inodes\n",
+		    dir, (long long)fsopts->size, (long long)fsopts->inodes);
+	}
+	sectorsize = fsopts->sectorsize;	/* XXX - see earlier */
+
+		/* now check calculated sizes vs requested sizes */
+	if (fsopts->maxsize > 0 && fsopts->size > fsopts->maxsize) {
+		errx(1, "`%s' size of %lld is larger than the maxsize of %lld.",
+		    dir, (long long)fsopts->size, (long long)fsopts->maxsize);
+	}
+}
+
+
+static void
+ffs_dump_fsinfo(fsinfo_t *f)
+{
+
+	ffs_opt_t	*fs = f->fs_specific;
+
+	printf("fsopts at %p\n", f);
+
+	printf("\tsize %lld, inodes %lld, curinode %u\n",
+	    (long long)f->size, (long long)f->inodes, f->curinode);
+
+	printf("\tminsize %lld, maxsize %lld\n",
+	    (long long)f->minsize, (long long)f->maxsize);
+	printf("\tfree files %lld, freefile %% %d\n",
+	    (long long)f->freefiles, f->freefilepc);
+	printf("\tfree blocks %lld, freeblock %% %d\n",
+	    (long long)f->freeblocks, f->freeblockpc);
+	printf("\tneedswap %d, sectorsize %d\n", f->needswap, f->sectorsize);
+
+	printf("\tbsize %d, fsize %d, cpg %d, density %d\n",
+	    fs->bsize, fs->fsize, fs->cpg, fs->density);
+	printf("\tnsectors %d, rpm %d, minfree %d\n",
+	    fs->nsectors, fs->rpm, fs->minfree);
+	printf("\tmaxcontig %d, maxbpg %d\n",
+	    fs->maxcontig, fs->maxbpg);
+	printf("\toptimization %s\n",
+	    fs->optimization == FS_OPTSPACE ? "space" : "time");
+}
+
+
+static int
+ffs_create_image(const char *image, fsinfo_t *fsopts)
+{
+#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
+	struct statvfs	sfs;
+#endif
+	struct fs	*fs;
+	char	*buf;
+	int	i, bufsize;
+	off_t	bufrem;
+	time_t	tstamp;
+
+	assert (image != NULL);
+	assert (fsopts != NULL);
+
+		/* create image */
+	if ((fsopts->fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666))
+	    == -1) {
+		warn("Can't open `%s' for writing", image);
+		return (-1);
+	}
+
+		/* zero image */
+#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
+	if (fstatvfs(fsopts->fd, &sfs) == -1) {
+#endif
+		bufsize = 8192;
+#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
+		warn("can't fstatvfs `%s', using default %d byte chunk",
+		    image, bufsize);
+	} else
+		bufsize = sfs.f_iosize;
+#endif
+	bufrem = fsopts->size;
+	if (fsopts->sparse) {
+		if (ftruncate(fsopts->fd, bufrem) == -1) {
+			warn("sparse option disabled.\n");
+			fsopts->sparse = 0;
+		}
+	}
+	if (fsopts->sparse) {
+		/* File truncated at bufrem. Remaining is 0 */
+		bufrem = 0;
+		buf = NULL;
+	} else {
+		if (debug & DEBUG_FS_CREATE_IMAGE)
+			printf("zero-ing image `%s', %lld sectors, "
+			    "using %d byte chunks\n", image, (long long)bufrem,
+			    bufsize);
+		if ((buf = calloc(1, bufsize)) == NULL) {
+			warn("Can't create buffer for sector");
+			return (-1);
+		}
+	}
+	while (bufrem > 0) {
+		i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
+		if (i == -1) {
+			warn("zeroing image, %lld bytes to go",
+			    (long long)bufrem);
+			free(buf);
+			return (-1);
+		}
+		bufrem -= i;
+	}
+	if (buf)
+		free(buf);
+
+		/* make the file system */
+	if (debug & DEBUG_FS_CREATE_IMAGE)
+		printf("calling mkfs(\"%s\", ...)\n", image);
+
+	if (stampst.st_ino != 0)
+		tstamp = stampst.st_ctime;
+	else
+		tstamp = start_time.tv_sec;
+
+	srandom(tstamp);
+
+	fs = ffs_mkfs(image, fsopts, tstamp);
+	fsopts->superblock = (void *)fs;
+	if (debug & DEBUG_FS_CREATE_IMAGE) {
+		time_t t;
+
+		t = (time_t)((struct fs *)fsopts->superblock)->fs_time;
+		printf("mkfs returned %p; fs_time %s",
+		    fsopts->superblock, ctime(&t));
+		printf("fs totals: nbfree %lld, nffree %lld, nifree %lld, ndir %lld\n",
+		    (long long)fs->fs_cstotal.cs_nbfree,
+		    (long long)fs->fs_cstotal.cs_nffree,
+		    (long long)fs->fs_cstotal.cs_nifree,
+		    (long long)fs->fs_cstotal.cs_ndir);
+	}
+
+	if (fs->fs_cstotal.cs_nifree + ROOTINO < fsopts->inodes) {
+		warnx(
+		"Image file `%s' has %lld free inodes; %lld are required.",
+		    image,
+		    (long long)(fs->fs_cstotal.cs_nifree + ROOTINO),
+		    (long long)fsopts->inodes);
+		return (-1);
+	}
+	return (fsopts->fd);
+}
+
+
+static void
+ffs_size_dir(fsnode *root, fsinfo_t *fsopts)
+{
+	struct direct	tmpdir;
+	fsnode *	node;
+	int		curdirsize, this;
+	ffs_opt_t	*ffs_opts = fsopts->fs_specific;
+
+	/* node may be NULL (empty directory) */
+	assert(fsopts != NULL);
+	assert(ffs_opts != NULL);
+
+	if (debug & DEBUG_FS_SIZE_DIR)
+		printf("ffs_size_dir: entry: bytes %lld inodes %lld\n",
+		    (long long)fsopts->size, (long long)fsopts->inodes);
+
+#define	ADDDIRENT(e) do {						\
+	tmpdir.d_namlen = strlen((e));					\
+	this = DIRSIZ_SWAP(0, &tmpdir, 0);					\
+	if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT)			\
+		printf("ADDDIRENT: was: %s (%d) this %d cur %d\n",	\
+		    e, tmpdir.d_namlen, this, curdirsize);		\
+	if (this + curdirsize > roundup(curdirsize, DIRBLKSIZ))		\
+		curdirsize = roundup(curdirsize, DIRBLKSIZ);		\
+	curdirsize += this;						\
+	if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT)			\
+		printf("ADDDIRENT: now: %s (%d) this %d cur %d\n",	\
+		    e, tmpdir.d_namlen, this, curdirsize);		\
+} while (0);
+
+	/*
+	 * XXX	this needs to take into account extra space consumed
+	 *	by indirect blocks, etc.
+	 */
+#define	ADDSIZE(x) do {							\
+	fsopts->size += roundup((x), ffs_opts->fsize);			\
+} while (0);
+
+	curdirsize = 0;
+	for (node = root; node != NULL; node = node->next) {
+		ADDDIRENT(node->name);
+		if (node == root) {			/* we're at "." */
+			assert(strcmp(node->name, ".") == 0);
+			ADDDIRENT("..");
+		} else if ((node->inode->flags & FI_SIZED) == 0) {
+				/* don't count duplicate names */
+			node->inode->flags |= FI_SIZED;
+			if (debug & DEBUG_FS_SIZE_DIR_NODE)
+				printf("ffs_size_dir: `%s' size %lld\n",
+				    node->name,
+				    (long long)node->inode->st.st_size);
+			fsopts->inodes++;
+			if (node->type == S_IFREG)
+				ADDSIZE(node->inode->st.st_size);
+			if (node->type == S_IFLNK) {
+				int	slen;
+
+				slen = strlen(node->symlink) + 1;
+				if (slen >= MAXSYMLINKLEN_UFS1)
+					ADDSIZE(slen);
+			}
+		}
+		if (node->type == S_IFDIR)
+			ffs_size_dir(node->child, fsopts);
+	}
+	ADDSIZE(curdirsize);
+
+	if (debug & DEBUG_FS_SIZE_DIR)
+		printf("ffs_size_dir: exit: size %lld inodes %lld\n",
+		    (long long)fsopts->size, (long long)fsopts->inodes);
+}
+
+static void *
+ffs_build_dinode1(struct ufs1_dinode *dinp, dirbuf_t *dbufp, fsnode *cur,
+		 fsnode *root, fsinfo_t *fsopts)
+{
+	int slen;
+	void *membuf;
+	struct stat *st = stampst.st_ino != 0 ? &stampst : &cur->inode->st;
+
+	memset(dinp, 0, sizeof(*dinp));
+	dinp->di_mode = cur->inode->st.st_mode;
+	dinp->di_nlink = cur->inode->nlink;
+	dinp->di_size = cur->inode->st.st_size;
+#if HAVE_STRUCT_STAT_ST_FLAGS
+	dinp->di_flags = cur->inode->st.st_flags;
+#endif
+	dinp->di_gen = random();
+	dinp->di_uid = cur->inode->st.st_uid;
+	dinp->di_gid = cur->inode->st.st_gid;
+
+	dinp->di_atime = st->st_atime;
+	dinp->di_mtime = st->st_mtime;
+	dinp->di_ctime = st->st_ctime;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+	dinp->di_atimensec = st->st_atimensec;
+	dinp->di_mtimensec = st->st_mtimensec;
+	dinp->di_ctimensec = st->st_ctimensec;
+#endif
+		/* not set: di_db, di_ib, di_blocks, di_spare */
+
+	membuf = NULL;
+	if (cur == root) {			/* "."; write dirbuf */
+		membuf = dbufp->buf;
+		dinp->di_size = dbufp->size;
+	} else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) {
+		dinp->di_size = 0;	/* a device */
+		dinp->di_rdev =
+		    ufs_rw32(cur->inode->st.st_rdev, fsopts->needswap);
+	} else if (S_ISLNK(cur->type)) {	/* symlink */
+		slen = strlen(cur->symlink);
+		if (slen < MAXSYMLINKLEN_UFS1) {	/* short link */
+			memcpy(dinp->di_db, cur->symlink, slen);
+		} else
+			membuf = cur->symlink;
+		dinp->di_size = slen;
+	}
+	return membuf;
+}
+
+static int
+ffs_populate_dir(const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+	fsnode		*cur;
+	dirbuf_t	dirbuf;
+	union dinode	din;
+	void		*membuf;
+	char		path[MAXPATHLEN + 1];
+	ffs_opt_t	*ffs_opts = fsopts->fs_specific;
+
+	assert(dir != NULL);
+	assert(root != NULL);
+	assert(fsopts != NULL);
+	assert(ffs_opts != NULL);
+
+	(void)memset(&dirbuf, 0, sizeof(dirbuf));
+
+	if (debug & DEBUG_FS_POPULATE)
+		printf("ffs_populate_dir: PASS 1  dir %s node %p\n", dir, root);
+
+		/*
+		 * pass 1: allocate inode numbers, build directory `file'
+		 */
+	for (cur = root; cur != NULL; cur = cur->next) {
+		if ((cur->inode->flags & FI_ALLOCATED) == 0) {
+			cur->inode->flags |= FI_ALLOCATED;
+			if (cur == root && cur->parent != NULL)
+				cur->inode->ino = cur->parent->inode->ino;
+			else {
+				cur->inode->ino = fsopts->curinode;
+				fsopts->curinode++;
+			}
+		}
+		ffs_make_dirbuf(&dirbuf, cur->name, cur, fsopts->needswap);
+		if (cur == root) {		/* we're at "."; add ".." */
+			ffs_make_dirbuf(&dirbuf, "..",
+			    cur->parent == NULL ? cur : cur->parent->first,
+			    fsopts->needswap);
+			root->inode->nlink++;	/* count my parent's link */
+		} else if (cur->child != NULL)
+			root->inode->nlink++;	/* count my child's link */
+
+		/*
+		 * XXX	possibly write file and long symlinks here,
+		 *	ensuring that blocks get written before inodes?
+		 *	otoh, this isn't a real filesystem, so who
+		 *	cares about ordering? :-)
+		 */
+	}
+	if (debug & DEBUG_FS_POPULATE_DIRBUF)
+		ffs_dump_dirbuf(&dirbuf, dir, fsopts->needswap);
+
+		/*
+		 * pass 2: write out dirbuf, then non-directories at this level
+		 */
+	if (debug & DEBUG_FS_POPULATE)
+		printf("ffs_populate_dir: PASS 2  dir %s\n", dir);
+	for (cur = root; cur != NULL; cur = cur->next) {
+		if (cur->inode->flags & FI_WRITTEN)
+			continue;		/* skip hard-linked entries */
+		cur->inode->flags |= FI_WRITTEN;
+
+		if (cur->contents == NULL) {
+			if (snprintf(path, sizeof(path), "%s/%s/%s", cur->root,
+			    cur->path, cur->name) >= (int)sizeof(path))
+				errx(1, "Pathname too long.");
+		}
+
+		if (cur->child != NULL)
+			continue;		/* child creates own inode */
+
+				/* build on-disk inode */
+		membuf = ffs_build_dinode1(&din.ffs1_din, &dirbuf, cur,
+		    root, fsopts);
+
+		if (debug & DEBUG_FS_POPULATE_NODE) {
+			printf("ffs_populate_dir: writing ino %d, %s",
+			    cur->inode->ino, inode_type(cur->type));
+			if (cur->inode->nlink > 1)
+				printf(", nlink %d", cur->inode->nlink);
+			putchar('\n');
+		}
+
+		if (membuf != NULL) {
+			ffs_write_file(&din, cur->inode->ino, membuf, fsopts);
+		} else if (S_ISREG(cur->type)) {
+			ffs_write_file(&din, cur->inode->ino,
+			    (cur->contents) ?  cur->contents : path, fsopts);
+		} else {
+			assert (! S_ISDIR(cur->type));
+			ffs_write_inode(&din, cur->inode->ino, fsopts);
+		}
+	}
+
+		/*
+		 * pass 3: write out sub-directories
+		 */
+	if (debug & DEBUG_FS_POPULATE)
+		printf("ffs_populate_dir: PASS 3  dir %s\n", dir);
+	for (cur = root; cur != NULL; cur = cur->next) {
+		if (cur->child == NULL)
+			continue;
+		if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name)
+		    >= sizeof(path))
+			errx(1, "Pathname too long.");
+		if (! ffs_populate_dir(path, cur->child, fsopts))
+			return (0);
+	}
+
+	if (debug & DEBUG_FS_POPULATE)
+		printf("ffs_populate_dir: DONE dir %s\n", dir);
+
+		/* cleanup */
+	if (dirbuf.buf != NULL)
+		free(dirbuf.buf);
+	return (1);
+}
+
+
+static void
+ffs_write_file(union dinode *din, uint32_t ino, void *buf, fsinfo_t *fsopts)
+{
+	int 	isfile, ffd;
+	char	*fbuf, *p;
+	off_t	bufleft, chunk, offset;
+	ssize_t nread;
+	struct inode	in;
+	struct buf *	bp;
+	ffs_opt_t	*ffs_opts = fsopts->fs_specific;
+
+	assert (din != NULL);
+	assert (buf != NULL);
+	assert (fsopts != NULL);
+	assert (ffs_opts != NULL);
+
+	isfile = S_ISREG(DIP(din, mode));
+	fbuf = NULL;
+	ffd = -1;
+	p = NULL;
+
+	in.i_fs = (struct fs *)fsopts->superblock;
+
+	if (debug & DEBUG_FS_WRITE_FILE) {
+		printf(
+		    "ffs_write_file: ino %u, din %p, isfile %d, %s, size %lld",
+		    ino, din, isfile, inode_type(DIP(din, mode) & S_IFMT),
+		    (long long)DIP(din, size));
+		if (isfile)
+			printf(", file '%s'\n", (char *)buf);
+		else
+			printf(", buffer %p\n", buf);
+	}
+
+	in.i_number = ino;
+	in.i_size = DIP(din, size);
+	memcpy(&in.i_din.ffs1_din, &din->ffs1_din, sizeof(in.i_din.ffs1_din));
+	in.i_fd = fsopts->fd;
+
+	if (DIP(din, size) == 0)
+		goto write_inode_and_leave;		/* mmm, cheating */
+
+	if (isfile) {
+		if ((fbuf = malloc(ffs_opts->bsize)) == NULL)
+			err(1, "Allocating memory for write buffer");
+		if ((ffd = open((char *)buf, O_RDONLY, 0444)) == -1) {
+			warn("Can't open `%s' for reading", (char *)buf);
+			goto leave_ffs_write_file;
+		}
+	} else {
+		p = buf;
+	}
+
+	chunk = 0;
+	for (bufleft = DIP(din, size); bufleft > 0; bufleft -= chunk) {
+		chunk = MIN(bufleft, ffs_opts->bsize);
+		if (!isfile)
+			;
+		else if ((nread = read(ffd, fbuf, chunk)) == -1)
+			err(EXIT_FAILURE, "Reading `%s', %lld bytes to go",
+			    (char *)buf, (long long)bufleft);
+		else if (nread != chunk)
+			errx(EXIT_FAILURE, "Reading `%s', %lld bytes to go, "
+			    "read %zd bytes, expected %ju bytes, does "
+			    "metalog size= attribute mismatch source size?",
+			    (char *)buf, (long long)bufleft, nread,
+			    (uintmax_t)chunk);
+		else
+			p = fbuf;
+		offset = DIP(din, size) - bufleft;
+		if (debug & DEBUG_FS_WRITE_FILE_BLOCK)
+			printf(
+		"ffs_write_file: write %p offset %lld size %lld left %lld\n",
+			    p, (long long)offset,
+			    (long long)chunk, (long long)bufleft);
+	/*
+	 * XXX	if holey support is desired, do the check here
+	 *
+	 * XXX	might need to write out last bit in fragroundup
+	 *	sized chunk. however, ffs_balloc() handles this for us
+	 */
+		errno = ffs_balloc(&in, offset, chunk, &bp);
+ bad_ffs_write_file:
+		if (errno != 0)
+			err(1,
+			    "Writing inode %d (%s), bytes %lld + %lld",
+			    ino,
+			    isfile ? (char *)buf :
+			      inode_type(DIP(din, mode) & S_IFMT),
+			    (long long)offset, (long long)chunk);
+		memcpy(bp->b_data, p, chunk);
+		errno = bwrite(bp);
+		if (errno != 0)
+			goto bad_ffs_write_file;
+		brelse(bp);
+		if (!isfile)
+			p += chunk;
+	}
+  
+ write_inode_and_leave:
+	ffs_write_inode(&in.i_din, in.i_number, fsopts);
+
+ leave_ffs_write_file:
+	if (fbuf)
+		free(fbuf);
+	if (ffd != -1)
+		close(ffd);
+}
+
+
+static void
+ffs_dump_dirbuf(dirbuf_t *dbuf, const char *dir, int needswap)
+{
+	doff_t		i;
+	struct direct	*de;
+	uint16_t	reclen;
+
+	assert (dbuf != NULL);
+	assert (dir != NULL);
+	printf("ffs_dump_dirbuf: dir %s size %d cur %d\n",
+	    dir, dbuf->size, dbuf->cur);
+
+	for (i = 0; i < dbuf->size; ) {
+		de = (struct direct *)(dbuf->buf + i);
+		reclen = ufs_rw16(de->d_reclen, needswap);
+		printf(
+	    " inode %4d %7s offset %4d reclen %3d namlen %3d name %s\n",
+		    ufs_rw32(de->d_ino, needswap),
+		    inode_type(DTTOIF(de->d_type)), i, reclen,
+		    de->d_namlen, de->d_name);
+		i += reclen;
+		assert(reclen > 0);
+	}
+}
+
+static void
+ffs_make_dirbuf(dirbuf_t *dbuf, const char *name, fsnode *node, int needswap)
+{
+	struct direct	de, *dp;
+	uint16_t	llen, reclen;
+	u_char		*newbuf;
+
+	assert (dbuf != NULL);
+	assert (name != NULL);
+	assert (node != NULL);
+					/* create direct entry */
+	(void)memset(&de, 0, sizeof(de));
+	de.d_ino = ufs_rw32(node->inode->ino, needswap);
+	de.d_type = IFTODT(node->type);
+	de.d_namlen = (uint8_t)strlen(name);
+	strcpy(de.d_name, name);
+	reclen = DIRSIZ_SWAP(0, &de, needswap);
+	de.d_reclen = ufs_rw16(reclen, needswap);
+
+	dp = (struct direct *)(dbuf->buf + dbuf->cur);
+	llen = 0;
+	if (dp != NULL)
+		llen = DIRSIZ_SWAP(0, dp, needswap);
+
+	if (debug & DEBUG_FS_MAKE_DIRBUF)
+		printf(
+		    "ffs_make_dirbuf: dbuf siz %d cur %d lastlen %d\n"
+		    "  ino %d type %d reclen %d namlen %d name %.30s\n",
+		    dbuf->size, dbuf->cur, llen,
+		    ufs_rw32(de.d_ino, needswap), de.d_type, reclen,
+		    de.d_namlen, de.d_name);
+
+	if (reclen + dbuf->cur + llen > roundup(dbuf->size, DIRBLKSIZ)) {
+		if (debug & DEBUG_FS_MAKE_DIRBUF)
+			printf("ffs_make_dirbuf: growing buf to %d\n",
+			    dbuf->size + DIRBLKSIZ);
+		if ((newbuf = realloc(dbuf->buf, dbuf->size + DIRBLKSIZ)) == NULL)
+			err(1, "Allocating memory for directory buffer");
+		dbuf->buf = newbuf;
+		dbuf->size += DIRBLKSIZ;
+		memset(dbuf->buf + dbuf->size - DIRBLKSIZ, 0, DIRBLKSIZ);
+		dbuf->cur = dbuf->size - DIRBLKSIZ;
+	} else if (dp) {			/* shrink end of previous */
+		dp->d_reclen = ufs_rw16(llen,needswap);
+		dbuf->cur += llen;
+	}
+	dp = (struct direct *)(dbuf->buf + dbuf->cur);
+	memcpy(dp, &de, reclen);
+	dp->d_reclen = ufs_rw16(dbuf->size - dbuf->cur, needswap);
+}
+
+/*
+ * cribbed from sys/ufs/ffs/ffs_alloc.c
+ */
+static void
+ffs_write_inode(union dinode *dp, uint32_t ino, const fsinfo_t *fsopts)
+{
+	char 		*buf;
+	struct ufs1_dinode *dp1;
+	struct cg	*cgp;
+	struct fs	*fs;
+	int		cg, cgino;
+	daddr_t		d;
+	char		sbbuf[FFS_MAXBSIZE];
+	ffs_opt_t	*ffs_opts = fsopts->fs_specific;
+
+	assert (dp != NULL);
+	assert (ino > 0);
+	assert (fsopts != NULL);
+	assert (ffs_opts != NULL);
+
+	fs = (struct fs *)fsopts->superblock;
+	cg = ino_to_cg(fs, ino);
+	cgino = ino % fs->fs_ipg;
+	if (debug & DEBUG_FS_WRITE_INODE)
+		printf("ffs_write_inode: din %p ino %u cg %d cgino %d\n",
+		    dp, ino, cg, cgino);
+
+	ffs_rdfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf,
+	    fsopts);
+	cgp = (struct cg *)sbbuf;
+	if (!cg_chkmagic_swap(cgp, fsopts->needswap))
+		errx(1, "ffs_write_inode: cg %d: bad magic number", cg);
+
+	assert (isclr(cg_inosused_swap(cgp, fsopts->needswap), cgino));
+
+	buf = malloc(fs->fs_bsize);
+	if (buf == NULL)
+		errx(1, "ffs_write_inode: cg %d: can't alloc inode block", cg);
+
+	dp1 = (struct ufs1_dinode *)buf;
+
+	if (fs->fs_cstotal.cs_nifree == 0)
+		errx(1, "ffs_write_inode: fs out of inodes for ino %u",
+		    ino);
+	if (fs->fs_cs(fs, cg).cs_nifree == 0)
+		errx(1,
+		    "ffs_write_inode: cg %d out of inodes for ino %u",
+		    cg, ino);
+	setbit(cg_inosused_swap(cgp, fsopts->needswap), cgino);
+	ufs_add32(cgp->cg_cs.cs_nifree, -1, fsopts->needswap);
+	fs->fs_cstotal.cs_nifree--;
+	fs->fs_cs(fs, cg).cs_nifree--;
+	if (S_ISDIR(DIP(dp, mode))) {
+		ufs_add32(cgp->cg_cs.cs_ndir, 1, fsopts->needswap);
+		fs->fs_cstotal.cs_ndir++;
+		fs->fs_cs(fs, cg).cs_ndir++; 
+	}
+
+
+
+	ffs_wtfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf,
+	    fsopts);
+
+					/* now write inode */
+	d = fsbtodb(fs, ino_to_fsba(fs, ino));
+	ffs_rdfs(d, fs->fs_bsize, buf, fsopts);
+	if (fsopts->needswap)
+		ffs_dinode1_swap(&dp->ffs1_din, &dp1[ino_to_fsbo(fs, ino)]);
+	else
+		dp1[ino_to_fsbo(fs, ino)] = dp->ffs1_din;
+	ffs_wtfs(d, fs->fs_bsize, buf, fsopts);
+	free(buf);
+}
+
+void
+panic(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	vwarnx(fmt, ap);
+	va_end(ap);
+	exit(1);
+}
diff --git a/usr.sbin/makefs/ffs.h b/usr.sbin/makefs/ffs.h
new file mode 100644
index 0000000000..84a46c7966
--- /dev/null
+++ b/usr.sbin/makefs/ffs.h
@@ -0,0 +1,69 @@
+/*	$NetBSD: ffs.h,v 1.2 2004/12/20 20:51:42 jmc Exp $	*/
+
+/*
+ * Copyright (c) 2001-2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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: head/usr.sbin/makefs/ffs.h 303038 2016-07-19 18:40:54Z emaste $
+ */
+
+#ifndef _FFS_H
+#define _FFS_H
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+typedef struct {
+	char	label[MAXVOLLEN];	/* volume name/label */
+	int	bsize;		/* block size */
+	int	fsize;		/* fragment size */
+	int	cpg;		/* cylinders per group */
+	int	cpgflg;		/* cpg was specified by user */
+	int	density;	/* bytes per inode */
+	int	ntracks;	/* number of tracks */
+	int	nsectors;	/* number of sectors */
+	int	rpm;		/* rpm */
+	int	minfree;	/* free space threshold */
+	int	optimization;	/* optimization (space or time) */
+	int	maxcontig;	/* max contiguous blocks to allocate */
+	int	rotdelay;	/* rotational delay between blocks */
+	int	maxbpg;		/* maximum blocks per file in a cyl group */
+	int	nrpos;		/* # of distinguished rotational positions */
+	int	avgfilesize;	/* expected average file size */
+	int	avgfpdir;	/* expected # of files per directory */
+	int	version;	/* filesystem version (1 = FFS, 2 = UFS2) */
+	int	maxblkspercg;	/* max # of blocks per cylinder group */
+		/* XXX: support `old' file systems ? */
+} ffs_opt_t;
+
+#endif /* _FFS_H */
diff --git a/usr.sbin/makefs/ffs/Makefile.inc b/usr.sbin/makefs/ffs/Makefile.inc
new file mode 100644
index 0000000000..1400383d33
--- /dev/null
+++ b/usr.sbin/makefs/ffs/Makefile.inc
@@ -0,0 +1,9 @@
+#	$FreeBSD: head/usr.sbin/makefs/ffs/Makefile.inc 214921 2010-11-07 16:05:04Z cognet $
+#
+
+.PATH:	${.CURDIR}/ffs ${.CURDIR}/../../sys/vfs/ufs
+
+CFLAGS+=	-I${.CURDIR}/../../sys/vfs/ufs
+
+SRCS+=	ffs_alloc.c ffs_balloc.c ffs_bswap.c ffs_subr.c ufs_bmap.c
+SRCS+=	buf.c mkfs.c
diff --git a/usr.sbin/makefs/ffs/buf.c b/usr.sbin/makefs/ffs/buf.c
new file mode 100644
index 0000000000..fac154a99e
--- /dev/null
+++ b/usr.sbin/makefs/ffs/buf.c
@@ -0,0 +1,222 @@
+/*	$NetBSD: buf.c,v 1.12 2004/06/20 22:20:18 jmc Exp $	*/
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/ffs/buf.c 214921 2010-11-07 16:05:04Z cognet $");
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/buf.h"
+#include "ffs/ufs_inode.h"
+
+extern int sectorsize;		/* XXX: from ffs.c & mkfs.c */
+
+TAILQ_HEAD(buftailhead,buf) buftail;
+
+int
+bread(int fd, struct fs *fs, daddr_t blkno, int size, struct buf **bpp)
+{
+	off_t	offset;
+	ssize_t	rv;
+
+	assert (fs != NULL);
+	assert (bpp != NULL);
+
+	if (debug & DEBUG_BUF_BREAD)
+		printf("bread: fs %p blkno %lld size %d\n",
+		    fs, (long long)blkno, size);
+	*bpp = getblk(fd, fs, blkno, size);
+	offset = (*bpp)->b_blkno * sectorsize;	/* XXX */
+	if (debug & DEBUG_BUF_BREAD)
+		printf("bread: bp %p blkno %lld offset %lld bcount %ld\n",
+		    (*bpp), (long long)(*bpp)->b_blkno, (long long) offset,
+		    (*bpp)->b_bcount);
+	if (lseek((*bpp)->b_fd, offset, SEEK_SET) == -1)
+		err(1, "bread: lseek %lld (%lld)",
+		    (long long)(*bpp)->b_blkno, (long long)offset);
+	rv = read((*bpp)->b_fd, (*bpp)->b_data, (*bpp)->b_bcount);
+	if (debug & DEBUG_BUF_BREAD)
+		printf("bread: read %ld (%lld) returned %d\n",
+		    (*bpp)->b_bcount, (long long)offset, (int)rv);
+	if (rv == -1)				/* read error */
+		err(1, "bread: read %ld (%lld) returned %d",
+		    (*bpp)->b_bcount, (long long)offset, (int)rv);
+	else if (rv != (*bpp)->b_bcount)	/* short read */
+		err(1, "bread: read %ld (%lld) returned %d",
+		    (*bpp)->b_bcount, (long long)offset, (int)rv);
+	else
+		return (0);
+}
+
+void
+brelse(struct buf *bp)
+{
+
+	assert (bp != NULL);
+	assert (bp->b_data != NULL);
+
+	if (bp->b_lblkno < 0) {
+		/*
+		 * XXX	don't remove any buffers with negative logical block
+		 *	numbers (lblkno), so that we retain the mapping
+		 *	of negative lblkno -> real blkno that ffs_balloc()
+		 *	sets up.
+		 *
+		 *	if we instead released these buffers, and implemented
+		 *	ufs_strategy() (and ufs_bmaparray()) and called those
+		 *	from bread() and bwrite() to convert the lblkno to
+		 *	a real blkno, we'd add a lot more code & complexity
+		 *	and reading off disk, for little gain, because this
+		 *	simple hack works for our purpose.
+		 */
+		bp->b_bcount = 0;
+		return;
+	}
+
+	TAILQ_REMOVE(&buftail, bp, b_tailq);
+	free(bp->b_data);
+	free(bp);
+}
+
+int
+bwrite(struct buf *bp)
+{
+	off_t	offset;
+	ssize_t	rv;
+
+	assert (bp != NULL);
+	offset = bp->b_blkno * sectorsize;	/* XXX */
+	if (debug & DEBUG_BUF_BWRITE)
+		printf("bwrite: bp %p blkno %lld offset %lld bcount %ld\n",
+		    bp, (long long)bp->b_blkno, (long long) offset,
+		    bp->b_bcount);
+	if (lseek(bp->b_fd, offset, SEEK_SET) == -1)
+		return (errno);
+	rv = write(bp->b_fd, bp->b_data, bp->b_bcount);
+	if (debug & DEBUG_BUF_BWRITE)
+		printf("bwrite: write %ld (offset %lld) returned %lld\n",
+		    bp->b_bcount, (long long)offset, (long long)rv);
+	if (rv == bp->b_bcount)
+		return (0);
+	else if (rv == -1)		/* write error */
+		return (errno);
+	else				/* short write ? */
+		return (EAGAIN);
+}
+
+void
+bcleanup(void)
+{
+	struct buf *bp;
+
+	/*
+	 * XXX	this really shouldn't be necessary, but i'm curious to
+	 *	know why there's still some buffers lying around that
+	 *	aren't brelse()d
+	 */
+
+	if (TAILQ_EMPTY(&buftail))
+		return;
+
+	printf("bcleanup: unflushed buffers:\n");
+	TAILQ_FOREACH(bp, &buftail, b_tailq) {
+		printf("\tlblkno %10lld  blkno %10lld  count %6ld  bufsize %6ld\n",
+		    (long long)bp->b_lblkno, (long long)bp->b_blkno,
+		    bp->b_bcount, bp->b_bufsize);
+	}
+	printf("bcleanup: done\n");
+}
+
+struct buf *
+getblk(int fd, struct fs *fs, daddr_t blkno, int size)
+{
+	static int buftailinitted;
+	struct buf *bp;
+	void *n;
+
+	assert (fs != NULL);
+	if (debug & DEBUG_BUF_GETBLK)
+		printf("getblk: fs %p blkno %lld size %d\n", fs,
+		    (long long)blkno, size);
+
+	bp = NULL;
+	if (!buftailinitted) {
+		if (debug & DEBUG_BUF_GETBLK)
+			printf("getblk: initialising tailq\n");
+		TAILQ_INIT(&buftail);
+		buftailinitted = 1;
+	} else {
+		TAILQ_FOREACH(bp, &buftail, b_tailq) {
+			if (bp->b_lblkno != blkno)
+				continue;
+			break;
+		}
+	}
+	if (bp == NULL) {
+		if ((bp = calloc(1, sizeof(struct buf))) == NULL)
+			err(1, "getblk: calloc");
+
+		bp->b_bufsize = 0;
+		bp->b_blkno = bp->b_lblkno = blkno;
+		bp->b_fd = fd;
+		bp->b_fs = fs;
+		bp->b_data = NULL;
+		TAILQ_INSERT_HEAD(&buftail, bp, b_tailq);
+	}
+	bp->b_bcount = size;
+	if (bp->b_data == NULL || bp->b_bcount > bp->b_bufsize) {
+		n = realloc(bp->b_data, size);
+		if (n == NULL)
+			err(1, "getblk: realloc b_data %ld", bp->b_bcount);
+		bp->b_data = n;
+		bp->b_bufsize = size;
+	}
+
+	return (bp);
+}
diff --git a/usr.sbin/makefs/ffs/buf.h b/usr.sbin/makefs/ffs/buf.h
new file mode 100644
index 0000000000..811813e02e
--- /dev/null
+++ b/usr.sbin/makefs/ffs/buf.h
@@ -0,0 +1,67 @@
+/*	$NetBSD: buf.h,v 1.2 2001/11/02 03:12:49 lukem Exp $	*/
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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: head/usr.sbin/makefs/ffs/buf.h 186334 2008-12-19 18:45:43Z sam $
+ */
+
+#ifndef _FFS_BUF_H
+#define	_FFS_BUF_H
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+struct buf {
+	void *		b_data;
+	long		b_bufsize;
+	long		b_bcount;
+	daddr_t		b_blkno;
+	daddr_t		b_lblkno;
+	int		b_fd;
+	struct fs *	b_fs;
+
+	TAILQ_ENTRY(buf)	b_tailq;
+};
+
+void		bcleanup(void);
+int		bread(int, struct fs *, daddr_t, int, struct buf **);
+void		brelse(struct buf *);
+int		bwrite(struct buf *);
+struct buf *	getblk(int, struct fs *, daddr_t, int);
+
+#define	bdwrite(bp)	bwrite(bp)
+#define	clrbuf(bp)	memset((bp)->b_data, 0, (u_int)(bp)->b_bcount)
+
+#endif	/* _FFS_BUF_H */
diff --git a/usr.sbin/makefs/ffs/ffs_alloc.c b/usr.sbin/makefs/ffs/ffs_alloc.c
new file mode 100644
index 0000000000..e69a96d991
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_alloc.c
@@ -0,0 +1,678 @@
+/*	$NetBSD: ffs_alloc.c,v 1.14 2004/06/20 22:20:18 jmc Exp $	*/
+/* From: NetBSD: ffs_alloc.c,v 1.50 2001/09/06 02:16:01 lukem Exp */
+
+/*
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program
+ *
+ * Copyright (c) 1982, 1986, 1989, 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.
+ *
+ *	@(#)ffs_alloc.c	8.19 (Berkeley) 7/13/95
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/ffs/ffs_alloc.c 241015 2012-09-27 23:31:19Z mdf $");
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <stdint.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/ufs_bswap.h"
+#include "ffs/buf.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+
+static int scanc(u_int, const u_char *, const u_char *, int);
+
+static daddr_t ffs_alloccg(struct inode *, int, daddr_t, int);
+static daddr_t ffs_alloccgblk(struct inode *, struct buf *, daddr_t);
+static daddr_t ffs_hashalloc(struct inode *, int, daddr_t, int,
+		     daddr_t (*)(struct inode *, int, daddr_t, int));
+static int32_t ffs_mapsearch(struct fs *, struct cg *, daddr_t, int);
+
+/*
+ * Allocate a block in the file system.
+ * 
+ * The size of the requested block is given, which must be some
+ * multiple of fs_fsize and <= fs_bsize.
+ * A preference may be optionally specified. If a preference is given
+ * the following hierarchy is used to allocate a block:
+ *   1) allocate the requested block.
+ *   2) allocate a rotationally optimal block in the same cylinder.
+ *   3) allocate a block in the same cylinder group.
+ *   4) quadradically rehash into other cylinder groups, until an
+ *      available block is located.
+ * If no block preference is given the following hierarchy is used
+ * to allocate a block:
+ *   1) allocate a block in the cylinder group that contains the
+ *      inode for the file.
+ *   2) quadradically rehash into other cylinder groups, until an
+ *      available block is located.
+ */
+int
+ffs_alloc(struct inode *ip, daddr_t lbn __unused, daddr_t bpref, int size,
+    daddr_t *bnp)
+{
+	struct fs *fs = ip->i_fs;
+	daddr_t bno;
+	int cg;
+	
+	*bnp = 0;
+	if (size > fs->fs_bsize || fragoff(fs, size) != 0) {
+		errx(1, "ffs_alloc: bad size: bsize %d size %d",
+		    fs->fs_bsize, size);
+	}
+	if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0)
+		goto nospace;
+	if (bpref >= fs->fs_size)
+		bpref = 0;
+	if (bpref == 0)
+		cg = ino_to_cg(fs, ip->i_number);
+	else
+		cg = dtog(fs, bpref);
+	bno = ffs_hashalloc(ip, cg, bpref, size, ffs_alloccg);
+	if (bno > 0) {
+		ip->i_ffs1_blocks += size / DEV_BSIZE;
+		*bnp = bno;
+		return (0);
+	}
+nospace:
+	return (ENOSPC);
+}
+
+/*
+ * Select the desired position for the next block in a file.  The file is
+ * logically divided into sections. The first section is composed of the
+ * direct blocks. Each additional section contains fs_maxbpg blocks.
+ * 
+ * If no blocks have been allocated in the first section, the policy is to
+ * request a block in the same cylinder group as the inode that describes
+ * the file. If no blocks have been allocated in any other section, the
+ * policy is to place the section in a cylinder group with a greater than
+ * average number of free blocks.  An appropriate cylinder group is found
+ * by using a rotor that sweeps the cylinder groups. When a new group of
+ * blocks is needed, the sweep begins in the cylinder group following the
+ * cylinder group from which the previous allocation was made. The sweep
+ * continues until a cylinder group with greater than the average number
+ * of free blocks is found. If the allocation is for the first block in an
+ * indirect block, the information on the previous allocation is unavailable;
+ * here a best guess is made based upon the logical block number being
+ * allocated.
+ * 
+ * If a section is already partially allocated, the policy is to
+ * contiguously allocate fs_maxcontig blocks.  The end of one of these
+ * contiguous blocks and the beginning of the next is physically separated
+ * so that the disk head will be in transit between them for at least
+ * fs_rotdelay milliseconds.  This is to allow time for the processor to
+ * schedule another I/O transfer.
+ */
+/* XXX ondisk32 */
+daddr_t
+ffs_blkpref_ufs1(struct inode *ip, daddr_t lbn, int indx, int32_t *bap)
+{
+	struct fs *fs;
+	int cg;
+	int avgbfree, startcg;
+
+	fs = ip->i_fs;
+	if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) {
+		if (lbn < NDADDR + NINDIR(fs)) {
+			cg = ino_to_cg(fs, ip->i_number);
+			return (fs->fs_fpg * cg + fs->fs_frag);
+		}
+		/*
+		 * Find a cylinder with greater than average number of
+		 * unused data blocks.
+		 */
+		if (indx == 0 || bap[indx - 1] == 0)
+			startcg =
+			    ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg;
+		else
+			startcg = dtog(fs,
+				ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + 1);
+		startcg %= fs->fs_ncg;
+		avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
+		for (cg = startcg; cg < fs->fs_ncg; cg++)
+			if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree)
+				return (fs->fs_fpg * cg + fs->fs_frag);
+		for (cg = 0; cg <= startcg; cg++)
+			if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree)
+				return (fs->fs_fpg * cg + fs->fs_frag);
+		return (0);
+	}
+	/*
+	 * We just always try to lay things out contiguously.
+	 */
+	return ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + fs->fs_frag;
+}
+
+daddr_t
+ffs_blkpref_ufs2(struct inode *ip, daddr_t lbn, int indx, int64_t *bap)
+{
+	struct fs *fs;
+	int cg;
+	int avgbfree, startcg;
+
+	fs = ip->i_fs;
+	if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) {
+		if (lbn < NDADDR + NINDIR(fs)) {
+			cg = ino_to_cg(fs, ip->i_number);
+			return (fs->fs_fpg * cg + fs->fs_frag);
+		}
+		/*
+		 * Find a cylinder with greater than average number of
+		 * unused data blocks.
+		 */
+		if (indx == 0 || bap[indx - 1] == 0)
+			startcg =
+			    ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg;
+		else
+			startcg = dtog(fs,
+				ufs_rw64(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + 1);
+		startcg %= fs->fs_ncg;
+		avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
+		for (cg = startcg; cg < fs->fs_ncg; cg++)
+			if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
+				return (fs->fs_fpg * cg + fs->fs_frag);
+			}
+		for (cg = 0; cg < startcg; cg++)
+			if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
+				return (fs->fs_fpg * cg + fs->fs_frag);
+			}
+		return (0);
+	}
+	/*
+	 * We just always try to lay things out contiguously.
+	 */
+	return ufs_rw64(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + fs->fs_frag;
+}
+
+/*
+ * Implement the cylinder overflow algorithm.
+ *
+ * The policy implemented by this algorithm is:
+ *   1) allocate the block in its requested cylinder group.
+ *   2) quadradically rehash on the cylinder group number.
+ *   3) brute force search for a free block.
+ *
+ * `size':	size for data blocks, mode for inodes
+ */
+/*VARARGS5*/
+static daddr_t
+ffs_hashalloc(struct inode *ip, int cg, daddr_t pref, int size,
+    daddr_t (*allocator)(struct inode *, int, daddr_t, int))
+{
+	struct fs *fs;
+	daddr_t result;
+	int i, icg = cg;
+
+	fs = ip->i_fs;
+	/*
+	 * 1: preferred cylinder group
+	 */
+	result = (*allocator)(ip, cg, pref, size);
+	if (result)
+		return (result);
+	/*
+	 * 2: quadratic rehash
+	 */
+	for (i = 1; i < fs->fs_ncg; i *= 2) {
+		cg += i;
+		if (cg >= fs->fs_ncg)
+			cg -= fs->fs_ncg;
+		result = (*allocator)(ip, cg, 0, size);
+		if (result)
+			return (result);
+	}
+	/*
+	 * 3: brute force search
+	 * Note that we start at i == 2, since 0 was checked initially,
+	 * and 1 is always checked in the quadratic rehash.
+	 */
+	cg = (icg + 2) % fs->fs_ncg;
+	for (i = 2; i < fs->fs_ncg; i++) {
+		result = (*allocator)(ip, cg, 0, size);
+		if (result)
+			return (result);
+		cg++;
+		if (cg == fs->fs_ncg)
+			cg = 0;
+	}
+	return (0);
+}
+
+/*
+ * Determine whether a block can be allocated.
+ *
+ * Check to see if a block of the appropriate size is available,
+ * and if it is, allocate it.
+ */
+static daddr_t
+ffs_alloccg(struct inode *ip, int cg, daddr_t bpref, int size)
+{
+	struct cg *cgp;
+	struct buf *bp;
+	daddr_t bno, blkno;
+	int error, frags, allocsiz, i;
+	struct fs *fs = ip->i_fs;
+	const int needswap = UFS_FSNEEDSWAP(fs);
+
+	if (fs->fs_cs(fs, cg).cs_nbfree == 0 && size == fs->fs_bsize)
+		return (0);
+	error = bread(ip->i_fd, ip->i_fs, fsbtodb(fs, cgtod(fs, cg)),
+		(int)fs->fs_cgsize, &bp);
+	if (error) {
+		brelse(bp);
+		return (0);
+	}
+	cgp = (struct cg *)bp->b_data;
+	if (!cg_chkmagic_swap(cgp, needswap) ||
+	    (cgp->cg_cs.cs_nbfree == 0 && size == fs->fs_bsize)) {
+		brelse(bp);
+		return (0);
+	}
+	if (size == fs->fs_bsize) {
+		bno = ffs_alloccgblk(ip, bp, bpref);
+		bdwrite(bp);
+		return (bno);
+	}
+	/*
+	 * check to see if any fragments are already available
+	 * allocsiz is the size which will be allocated, hacking
+	 * it down to a smaller size if necessary
+	 */
+	frags = numfrags(fs, size);
+	for (allocsiz = frags; allocsiz < fs->fs_frag; allocsiz++)
+		if (cgp->cg_frsum[allocsiz] != 0)
+			break;
+	if (allocsiz == fs->fs_frag) {
+		/*
+		 * no fragments were available, so a block will be 
+		 * allocated, and hacked up
+		 */
+		if (cgp->cg_cs.cs_nbfree == 0) {
+			brelse(bp);
+			return (0);
+		}
+		bno = ffs_alloccgblk(ip, bp, bpref);
+		bpref = dtogd(fs, bno);
+		for (i = frags; i < fs->fs_frag; i++)
+			setbit(cg_blksfree_swap(cgp, needswap), bpref + i);
+		i = fs->fs_frag - frags;
+		ufs_add32(cgp->cg_cs.cs_nffree, i, needswap);
+		fs->fs_cstotal.cs_nffree += i;
+		fs->fs_cs(fs, cg).cs_nffree += i;
+		fs->fs_fmod = 1;
+		ufs_add32(cgp->cg_frsum[i], 1, needswap);
+		bdwrite(bp);
+		return (bno);
+	}
+	bno = ffs_mapsearch(fs, cgp, bpref, allocsiz);
+	for (i = 0; i < frags; i++)
+		clrbit(cg_blksfree_swap(cgp, needswap), bno + i);
+	ufs_add32(cgp->cg_cs.cs_nffree, -frags, needswap);
+	fs->fs_cstotal.cs_nffree -= frags;
+	fs->fs_cs(fs, cg).cs_nffree -= frags;
+	fs->fs_fmod = 1;
+	ufs_add32(cgp->cg_frsum[allocsiz], -1, needswap);
+	if (frags != allocsiz)
+		ufs_add32(cgp->cg_frsum[allocsiz - frags], 1, needswap);
+	blkno = cg * fs->fs_fpg + bno;
+	bdwrite(bp);
+	return blkno;
+}
+
+/*
+ * Allocate a block in a cylinder group.
+ *
+ * This algorithm implements the following policy:
+ *   1) allocate the requested block.
+ *   2) allocate a rotationally optimal block in the same cylinder.
+ *   3) allocate the next available block on the block rotor for the
+ *      specified cylinder group.
+ * Note that this routine only allocates fs_bsize blocks; these
+ * blocks may be fragmented by the routine that allocates them.
+ */
+static daddr_t
+ffs_alloccgblk(struct inode *ip, struct buf *bp, daddr_t bpref)
+{
+	struct cg *cgp;
+	daddr_t blkno;
+	int32_t bno;
+	struct fs *fs = ip->i_fs;
+	const int needswap = UFS_FSNEEDSWAP(fs);
+	u_int8_t *blksfree_swap;
+
+	cgp = (struct cg *)bp->b_data;
+	blksfree_swap = cg_blksfree_swap(cgp, needswap);
+	if (bpref == 0 || (uint32_t)dtog(fs, bpref) != ufs_rw32(cgp->cg_cgx, needswap)) {
+		bpref = ufs_rw32(cgp->cg_rotor, needswap);
+	} else {
+		bpref = blknum(fs, bpref);
+		bno = dtogd(fs, bpref);
+		/*
+		 * if the requested block is available, use it
+		 */
+		if (ffs_isblock(fs, blksfree_swap, fragstoblks(fs, bno)))
+			goto gotit;
+	}
+	/*
+	 * Take the next available one in this cylinder group.
+	 */
+	bno = ffs_mapsearch(fs, cgp, bpref, (int)fs->fs_frag);
+	if (bno < 0)
+		return (0);
+	cgp->cg_rotor = ufs_rw32(bno, needswap);
+gotit:
+	blkno = fragstoblks(fs, bno);
+	ffs_clrblock(fs, blksfree_swap, (long)blkno);
+	ffs_clusteracct(fs, cgp, blkno, -1);
+	ufs_add32(cgp->cg_cs.cs_nbfree, -1, needswap);
+	fs->fs_cstotal.cs_nbfree--;
+	fs->fs_cs(fs, ufs_rw32(cgp->cg_cgx, needswap)).cs_nbfree--;
+	fs->fs_fmod = 1;
+	blkno = ufs_rw32(cgp->cg_cgx, needswap) * fs->fs_fpg + bno;
+	return (blkno);
+}
+
+/*
+ * Free a block or fragment.
+ *
+ * The specified block or fragment is placed back in the
+ * free map. If a fragment is deallocated, a possible 
+ * block reassembly is checked.
+ */
+void
+ffs_blkfree(struct inode *ip, daddr_t bno, long size)
+{
+	struct cg *cgp;
+	struct buf *bp;
+	int32_t fragno, cgbno;
+	int i, error, cg, blk, frags, bbase;
+	struct fs *fs = ip->i_fs;
+	const int needswap = UFS_FSNEEDSWAP(fs);
+
+	if (size > fs->fs_bsize || fragoff(fs, size) != 0 ||
+	    fragnum(fs, bno) + numfrags(fs, size) > fs->fs_frag) {
+		errx(1, "blkfree: bad size: bno %lld bsize %d size %ld",
+		    (long long)bno, fs->fs_bsize, size);
+	}
+	cg = dtog(fs, bno);
+	if (bno >= fs->fs_size) {
+		warnx("bad block %lld, ino %ju", (long long)bno,
+		    (uintmax_t)ip->i_number);
+		return;
+	}
+	error = bread(ip->i_fd, ip->i_fs, fsbtodb(fs, cgtod(fs, cg)),
+		(int)fs->fs_cgsize, &bp);
+	if (error) {
+		brelse(bp);
+		return;
+	}
+	cgp = (struct cg *)bp->b_data;
+	if (!cg_chkmagic_swap(cgp, needswap)) {
+		brelse(bp);
+		return;
+	}
+	cgbno = dtogd(fs, bno);
+	if (size == fs->fs_bsize) {
+		fragno = fragstoblks(fs, cgbno);
+		if (!ffs_isfreeblock(fs, cg_blksfree_swap(cgp, needswap), fragno)) {
+			errx(1, "blkfree: freeing free block %lld",
+			    (long long)bno);
+		}
+		ffs_setblock(fs, cg_blksfree_swap(cgp, needswap), fragno);
+		ffs_clusteracct(fs, cgp, fragno, 1);
+		ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap);
+		fs->fs_cstotal.cs_nbfree++;
+		fs->fs_cs(fs, cg).cs_nbfree++;
+	} else {
+		bbase = cgbno - fragnum(fs, cgbno);
+		/*
+		 * decrement the counts associated with the old frags
+		 */
+		blk = blkmap(fs, cg_blksfree_swap(cgp, needswap), bbase);
+		ffs_fragacct_swap(fs, blk, cgp->cg_frsum, -1, needswap);
+		/*
+		 * deallocate the fragment
+		 */
+		frags = numfrags(fs, size);
+		for (i = 0; i < frags; i++) {
+			if (isset(cg_blksfree_swap(cgp, needswap), cgbno + i)) {
+				errx(1, "blkfree: freeing free frag: block %lld",
+				    (long long)(cgbno + i));
+			}
+			setbit(cg_blksfree_swap(cgp, needswap), cgbno + i);
+		}
+		ufs_add32(cgp->cg_cs.cs_nffree, i, needswap);
+		fs->fs_cstotal.cs_nffree += i;
+		fs->fs_cs(fs, cg).cs_nffree += i;
+		/*
+		 * add back in counts associated with the new frags
+		 */
+		blk = blkmap(fs, cg_blksfree_swap(cgp, needswap), bbase);
+		ffs_fragacct_swap(fs, blk, cgp->cg_frsum, 1, needswap);
+		/*
+		 * if a complete block has been reassembled, account for it
+		 */
+		fragno = fragstoblks(fs, bbase);
+		if (ffs_isblock(fs, cg_blksfree_swap(cgp, needswap), fragno)) {
+			ufs_add32(cgp->cg_cs.cs_nffree, -fs->fs_frag, needswap);
+			fs->fs_cstotal.cs_nffree -= fs->fs_frag;
+			fs->fs_cs(fs, cg).cs_nffree -= fs->fs_frag;
+			ffs_clusteracct(fs, cgp, fragno, 1);
+			ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap);
+			fs->fs_cstotal.cs_nbfree++;
+			fs->fs_cs(fs, cg).cs_nbfree++;
+		}
+	}
+	fs->fs_fmod = 1;
+	bdwrite(bp);
+}
+
+
+static int
+scanc(u_int size, const u_char *cp, const u_char table[], int mask)
+{
+	const u_char *end = &cp[size];
+
+	while (cp < end && (table[*cp] & mask) == 0)
+		cp++;
+	return (end - cp);
+}
+
+/*
+ * Find a block of the specified size in the specified cylinder group.
+ *
+ * It is a panic if a request is made to find a block if none are
+ * available.
+ */
+static int32_t
+ffs_mapsearch(struct fs *fs, struct cg *cgp, daddr_t bpref, int allocsiz)
+{
+	int32_t bno;
+	int start, len, loc, i;
+	int blk, field, subfield, pos;
+	int ostart, olen;
+	const int needswap = UFS_FSNEEDSWAP(fs);
+
+	/*
+	 * find the fragment by searching through the free block
+	 * map for an appropriate bit pattern
+	 */
+	if (bpref)
+		start = dtogd(fs, bpref) / NBBY;
+	else
+		start = ufs_rw32(cgp->cg_frotor, needswap) / NBBY;
+	len = howmany(fs->fs_fpg, NBBY) - start;
+	ostart = start;
+	olen = len;
+	loc = scanc((u_int)len,
+		(const u_char *)&cg_blksfree_swap(cgp, needswap)[start],
+		(const u_char *)fragtbl[fs->fs_frag],
+		(1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
+	if (loc == 0) {
+		len = start + 1;
+		start = 0;
+		loc = scanc((u_int)len,
+			(const u_char *)&cg_blksfree_swap(cgp, needswap)[0],
+			(const u_char *)fragtbl[fs->fs_frag],
+			(1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
+		if (loc == 0) {
+			errx(1,
+    "ffs_alloccg: map corrupted: start %d len %d offset %d %ld",
+				ostart, olen,
+				ufs_rw32(cgp->cg_freeoff, needswap),
+				(long)cg_blksfree_swap(cgp, needswap) - (long)cgp);
+			/* NOTREACHED */
+		}
+	}
+	bno = (start + len - loc) * NBBY;
+	cgp->cg_frotor = ufs_rw32(bno, needswap);
+	/*
+	 * found the byte in the map
+	 * sift through the bits to find the selected frag
+	 */
+	for (i = bno + NBBY; bno < i; bno += fs->fs_frag) {
+		blk = blkmap(fs, cg_blksfree_swap(cgp, needswap), bno);
+		blk <<= 1;
+		field = around[allocsiz];
+		subfield = inside[allocsiz];
+		for (pos = 0; pos <= fs->fs_frag - allocsiz; pos++) {
+			if ((blk & field) == subfield)
+				return (bno + pos);
+			field <<= 1;
+			subfield <<= 1;
+		}
+	}
+	errx(1, "ffs_alloccg: block not in map: bno %lld", (long long)bno);
+	return (-1);
+}
+
+/*
+ * Update the cluster map because of an allocation or free.
+ *
+ * Cnt == 1 means free; cnt == -1 means allocating.
+ */
+void
+ffs_clusteracct(struct fs *fs, struct cg *cgp, int32_t blkno, int cnt)
+{
+	int32_t *sump;
+	int32_t *lp;
+	u_char *freemapp, *mapp;
+	int i, start, end, forw, back, map, bit;
+	const int needswap = UFS_FSNEEDSWAP(fs);
+
+	if (fs->fs_contigsumsize <= 0)
+		return;
+	freemapp = cg_clustersfree_swap(cgp, needswap);
+	sump = cg_clustersum_swap(cgp, needswap);
+	/*
+	 * Allocate or clear the actual block.
+	 */
+	if (cnt > 0)
+		setbit(freemapp, blkno);
+	else
+		clrbit(freemapp, blkno);
+	/*
+	 * Find the size of the cluster going forward.
+	 */
+	start = blkno + 1;
+	end = start + fs->fs_contigsumsize;
+	if ((unsigned)end >= ufs_rw32(cgp->cg_nclusterblks, needswap))
+		end = ufs_rw32(cgp->cg_nclusterblks, needswap);
+	mapp = &freemapp[start / NBBY];
+	map = *mapp++;
+	bit = 1 << (start % NBBY);
+	for (i = start; i < end; i++) {
+		if ((map & bit) == 0)
+			break;
+		if ((i & (NBBY - 1)) != (NBBY - 1)) {
+			bit <<= 1;
+		} else {
+			map = *mapp++;
+			bit = 1;
+		}
+	}
+	forw = i - start;
+	/*
+	 * Find the size of the cluster going backward.
+	 */
+	start = blkno - 1;
+	end = start - fs->fs_contigsumsize;
+	if (end < 0)
+		end = -1;
+	mapp = &freemapp[start / NBBY];
+	map = *mapp--;
+	bit = 1 << (start % NBBY);
+	for (i = start; i > end; i--) {
+		if ((map & bit) == 0)
+			break;
+		if ((i & (NBBY - 1)) != 0) {
+			bit >>= 1;
+		} else {
+			map = *mapp--;
+			bit = 1 << (NBBY - 1);
+		}
+	}
+	back = start - i;
+	/*
+	 * Account for old cluster and the possibly new forward and
+	 * back clusters.
+	 */
+	i = back + forw + 1;
+	if (i > fs->fs_contigsumsize)
+		i = fs->fs_contigsumsize;
+	ufs_add32(sump[i], cnt, needswap);
+	if (back > 0)
+		ufs_add32(sump[back], -cnt, needswap);
+	if (forw > 0)
+		ufs_add32(sump[forw], -cnt, needswap);
+
+	/*
+	 * Update cluster summary information.
+	 */
+	lp = &sump[fs->fs_contigsumsize];
+	for (i = fs->fs_contigsumsize; i > 0; i--)
+		if (ufs_rw32(*lp--, needswap) > 0)
+			break;
+	fs->fs_maxcluster[ufs_rw32(cgp->cg_cgx, needswap)] = i;
+}
diff --git a/usr.sbin/makefs/ffs/ffs_balloc.c b/usr.sbin/makefs/ffs/ffs_balloc.c
new file mode 100644
index 0000000000..25d7668234
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_balloc.c
@@ -0,0 +1,323 @@
+/*	$NetBSD: ffs_balloc.c,v 1.13 2004/06/20 22:20:18 jmc Exp $	*/
+/* From NetBSD: ffs_balloc.c,v 1.25 2001/08/08 08:36:36 lukem Exp */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 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.
+ *
+ *	@(#)ffs_balloc.c	8.8 (Berkeley) 6/16/95
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/ffs/ffs_balloc.c 186334 2008-12-19 18:45:43Z sam $");
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/ufs_bswap.h"
+#include "ffs/buf.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+
+static int ffs_balloc_ufs1(struct inode *, off_t, int, struct buf **);
+
+/*
+ * Balloc defines the structure of file system storage
+ * by allocating the physical blocks on a device given
+ * the inode and the logical block number in a file.
+ *
+ * Assume: flags == B_SYNC | B_CLRBUF
+ */
+
+int
+ffs_balloc(struct inode *ip, off_t offset, int bufsize, struct buf **bpp)
+{
+	return ffs_balloc_ufs1(ip, offset, bufsize, bpp);
+}
+
+static int
+ffs_balloc_ufs1(struct inode *ip, off_t offset, int bufsize, struct buf **bpp)
+{
+	daddr_t lbn, lastlbn;
+	int size;
+	int32_t nb;
+	struct buf *bp, *nbp;
+	struct fs *fs = ip->i_fs;
+	struct indir indirs[NIADDR + 2];
+	daddr_t newb, pref;
+	int32_t *bap;
+	int osize, nsize, num, i, error;
+	int32_t *allocblk, allociblk[NIADDR + 1];
+	int32_t *allocib;
+	const int needswap = UFS_FSNEEDSWAP(fs);
+
+	lbn = lblkno(fs, offset);
+	size = blkoff(fs, offset) + bufsize;
+	if (bpp != NULL) {
+		*bpp = NULL;
+	}
+
+	assert(size <= fs->fs_bsize);
+	if (lbn < 0)
+		return (EFBIG);
+
+	/*
+	 * If the next write will extend the file into a new block,
+	 * and the file is currently composed of a fragment
+	 * this fragment has to be extended to be a full block.
+	 */
+
+	lastlbn = lblkno(fs, ip->i_ffs1_size);
+	if (lastlbn < NDADDR && lastlbn < lbn) {
+		nb = lastlbn;
+		osize = blksize(fs, ip, nb);
+		if (osize < fs->fs_bsize && osize > 0) {
+			warnx("need to ffs_realloccg; not supported!");
+			abort();
+		}
+	}
+
+	/*
+	 * The first NDADDR blocks are direct blocks
+	 */
+
+	if (lbn < NDADDR) {
+		nb = ufs_rw32(ip->i_ffs1_db[lbn], needswap);
+		if (nb != 0 && ip->i_ffs1_size >= lblktosize(fs, lbn + 1)) {
+
+			/*
+			 * The block is an already-allocated direct block
+			 * and the file already extends past this block,
+			 * thus this must be a whole block.
+			 * Just read the block (if requested).
+			 */
+
+			if (bpp != NULL) {
+				error = bread(ip->i_fd, ip->i_fs, lbn,
+				    fs->fs_bsize, bpp);
+				if (error) {
+					brelse(*bpp);
+					return (error);
+				}
+			}
+			return (0);
+		}
+		if (nb != 0) {
+
+			/*
+			 * Consider need to reallocate a fragment.
+			 */
+
+			osize = fragroundup(fs, blkoff(fs, ip->i_ffs1_size));
+			nsize = fragroundup(fs, size);
+			if (nsize <= osize) {
+
+				/*
+				 * The existing block is already
+				 * at least as big as we want.
+				 * Just read the block (if requested).
+				 */
+
+				if (bpp != NULL) {
+					error = bread(ip->i_fd, ip->i_fs, lbn,
+					    osize, bpp);
+					if (error) {
+						brelse(*bpp);
+						return (error);
+					}
+				}
+				return 0;
+			} else {
+				warnx("need to ffs_realloccg; not supported!");
+				abort();
+			}
+		} else {
+
+			/*
+			 * the block was not previously allocated,
+			 * allocate a new block or fragment.
+			 */
+
+			if (ip->i_ffs1_size < lblktosize(fs, lbn + 1))
+				nsize = fragroundup(fs, size);
+			else
+				nsize = fs->fs_bsize;
+			error = ffs_alloc(ip, lbn,
+			    ffs_blkpref_ufs1(ip, lbn, (int)lbn,
+				&ip->i_ffs1_db[0]),
+				nsize, &newb);
+			if (error)
+				return (error);
+			if (bpp != NULL) {
+				bp = getblk(ip->i_fd, ip->i_fs, lbn, nsize);
+				bp->b_blkno = fsbtodb(fs, newb);
+				clrbuf(bp);
+				*bpp = bp;
+			}
+		}
+		ip->i_ffs1_db[lbn] = ufs_rw32((int32_t)newb, needswap);
+		return (0);
+	}
+
+	/*
+	 * Determine the number of levels of indirection.
+	 */
+
+	pref = 0;
+	if ((error = ufs_getlbns(ip, lbn, indirs, &num)) != 0)
+		return (error);
+
+	if (num < 1) {
+		warnx("ffs_balloc: ufs_getlbns returned indirect block");
+		abort();
+	}
+
+	/*
+	 * Fetch the first indirect block allocating if necessary.
+	 */
+
+	--num;
+	nb = ufs_rw32(ip->i_ffs1_ib[indirs[0].in_off], needswap);
+	allocib = NULL;
+	allocblk = allociblk;
+	if (nb == 0) {
+		pref = ffs_blkpref_ufs1(ip, lbn, 0, (int32_t *)0);
+		error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+		if (error)
+			return error;
+		nb = newb;
+		*allocblk++ = nb;
+		bp = getblk(ip->i_fd, ip->i_fs, indirs[1].in_lbn, fs->fs_bsize);
+		bp->b_blkno = fsbtodb(fs, nb);
+		clrbuf(bp);
+		/*
+		 * Write synchronously so that indirect blocks
+		 * never point at garbage.
+		 */
+		if ((error = bwrite(bp)) != 0)
+			return error;
+		allocib = &ip->i_ffs1_ib[indirs[0].in_off];
+		*allocib = ufs_rw32((int32_t)nb, needswap);
+	}
+
+	/*
+	 * Fetch through the indirect blocks, allocating as necessary.
+	 */
+
+	for (i = 1;;) {
+		error = bread(ip->i_fd, ip->i_fs, indirs[i].in_lbn, 
+		    fs->fs_bsize, &bp);
+		if (error) {
+			brelse(bp);
+			return error;
+		}
+		bap = (int32_t *)bp->b_data;
+		nb = ufs_rw32(bap[indirs[i].in_off], needswap);
+		if (i == num)
+			break;
+		i++;
+		if (nb != 0) {
+			brelse(bp);
+			continue;
+		}
+		if (pref == 0)
+			pref = ffs_blkpref_ufs1(ip, lbn, 0, (int32_t *)0);
+		error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+		if (error) {
+			brelse(bp);
+			return error;
+		}
+		nb = newb;
+		*allocblk++ = nb;
+		nbp = getblk(ip->i_fd, ip->i_fs, indirs[i].in_lbn,
+		    fs->fs_bsize);
+		nbp->b_blkno = fsbtodb(fs, nb);
+		clrbuf(nbp);
+		/*
+		 * Write synchronously so that indirect blocks
+		 * never point at garbage.
+		 */
+
+		if ((error = bwrite(nbp)) != 0) {
+			brelse(bp);
+			return error;
+		}
+		bap[indirs[i - 1].in_off] = ufs_rw32(nb, needswap);
+
+		bwrite(bp);
+	}
+
+	/*
+	 * Get the data block, allocating if necessary.
+	 */
+
+	if (nb == 0) {
+		pref = ffs_blkpref_ufs1(ip, lbn, indirs[num].in_off, &bap[0]);
+		error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+		if (error) {
+			brelse(bp);
+			return error;
+		}
+		nb = newb;
+		*allocblk++ = nb;
+		if (bpp != NULL) {
+			nbp = getblk(ip->i_fd, ip->i_fs, lbn, fs->fs_bsize);
+			nbp->b_blkno = fsbtodb(fs, nb);
+			clrbuf(nbp);
+			*bpp = nbp;
+		}
+		bap[indirs[num].in_off] = ufs_rw32(nb, needswap);
+
+		/*
+		 * If required, write synchronously, otherwise use
+		 * delayed write.
+		 */
+		bwrite(bp);
+		return (0);
+	}
+	brelse(bp);
+	if (bpp != NULL) {
+		error = bread(ip->i_fd, ip->i_fs, lbn, (int)fs->fs_bsize, &nbp);
+		if (error) {
+			brelse(nbp);
+			return error;
+		}
+		*bpp = nbp;
+	}
+	return (0);
+}
diff --git a/usr.sbin/makefs/ffs/ffs_bswap.c b/usr.sbin/makefs/ffs/ffs_bswap.c
new file mode 100644
index 0000000000..373703f228
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_bswap.c
@@ -0,0 +1,221 @@
+/*	$NetBSD: ffs_bswap.c,v 1.28 2004/05/25 14:54:59 hannken Exp $	*/
+
+/*
+ * Copyright (c) 1998 Manuel Bouyer.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by Manuel Bouyer.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * 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.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/ffs/ffs_bswap.c 307927 2016-10-25 16:29:15Z marcel $");
+
+#include <sys/param.h>
+#if defined(_KERNEL)
+#include <sys/systm.h>
+#endif
+
+#if !defined(_KERNEL)
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define panic(x)	printf("%s\n", (x)), abort()
+#endif
+
+#include <ufs/ufs/dinode.h>
+#include "ffs/ufs_bswap.h"
+#include <ufs/ffs/fs.h>
+
+#define	FS_42POSTBLFMT		-1	/* 4.2BSD rotational table format */
+#define	FS_DYNAMICPOSTBLFMT	1	/* dynamic rotational table format */
+
+void ffs_csum_swap(struct csum *o, struct csum *n, int size);
+void ffs_csumtotal_swap(struct csum *o, struct csum *n);
+
+void
+ffs_sb_swap(struct fs *o, struct fs *n)
+{
+	size_t i;
+	u_int32_t *o32, *n32;
+
+	/*
+	 * In order to avoid a lot of lines, as the first N fields (52)
+	 * of the superblock up to fs_fmod are u_int32_t, we just loop
+	 * here to convert them.
+	 */
+	o32 = (u_int32_t *)o;
+	n32 = (u_int32_t *)n;
+	for (i = 0; i < offsetof(struct fs, fs_fmod) / sizeof(u_int32_t); i++)
+		n32[i] = bswap32(o32[i]);
+
+	n->fs_swuid = bswap64(o->fs_swuid);
+	n->fs_cgrotor = bswap32(o->fs_cgrotor); /* Unused */
+	n->fs_cpc = bswap32(o->fs_cpc);
+
+	/* These fields overlap with a possible location for the
+	 * historic FS_DYNAMICPOSTBLFMT postbl table, and with the
+	 * first half of the historic FS_42POSTBLFMT postbl table.
+	 */
+	ffs_csumtotal_swap(&o->fs_cstotal, &n->fs_cstotal);
+	n->fs_time = bswap64(o->fs_time);
+	n->fs_size = bswap64(o->fs_size);
+	n->fs_dsize = bswap64(o->fs_dsize);
+	n->fs_csaddr = bswap64(o->fs_csaddr);
+	n->fs_pendingblocks = bswap64(o->fs_pendingblocks);
+	n->fs_pendinginodes = bswap32(o->fs_pendinginodes);
+
+	/* These fields overlap with the second half of the
+	 * historic FS_42POSTBLFMT postbl table
+	 */
+	for (i = 0; i < FSMAXSNAP; i++)
+		n->fs_snapinum[i] = bswap32(o->fs_snapinum[i]);
+	n->fs_avgfilesize = bswap32(o->fs_avgfilesize);
+	n->fs_avgfpdir = bswap32(o->fs_avgfpdir);
+	/* fs_sparecon[28] - ignore for now */
+	n->fs_flags = bswap32(o->fs_flags);
+	n->fs_contigsumsize = bswap32(o->fs_contigsumsize);
+	n->fs_maxsymlinklen = bswap32(o->fs_maxsymlinklen);
+	n->fs_inodefmt = bswap32(o->fs_inodefmt);
+	n->fs_maxfilesize = bswap64(o->fs_maxfilesize);
+	n->fs_qbmask = bswap64(o->fs_qbmask);
+	n->fs_qfmask = bswap64(o->fs_qfmask);
+	n->fs_state = bswap32(o->fs_state);
+	n->fs_postblformat = bswap32(o->fs_postblformat);
+	n->fs_nrpos = bswap32(o->fs_nrpos);
+	n->fs_postbloff = bswap32(o->fs_postbloff);
+	n->fs_rotbloff = bswap32(o->fs_rotbloff);
+
+	n->fs_magic = bswap32(o->fs_magic);
+}
+
+void
+ffs_dinode1_swap(struct ufs1_dinode *o, struct ufs1_dinode *n)
+{
+
+	n->di_mode = bswap16(o->di_mode);
+	n->di_nlink = bswap16(o->di_nlink);
+	n->di_size = bswap64(o->di_size);
+	n->di_atime = bswap32(o->di_atime);
+	n->di_atimensec = bswap32(o->di_atimensec);
+	n->di_mtime = bswap32(o->di_mtime);
+	n->di_mtimensec = bswap32(o->di_mtimensec);
+	n->di_ctime = bswap32(o->di_ctime);
+	n->di_ctimensec = bswap32(o->di_ctimensec);
+	memcpy(n->di_db, o->di_db, sizeof(n->di_db));
+	memcpy(n->di_ib, o->di_ib, sizeof(n->di_ib));
+	n->di_flags = bswap32(o->di_flags);
+	n->di_blocks = bswap32(o->di_blocks);
+	n->di_gen = bswap32(o->di_gen);
+	n->di_uid = bswap32(o->di_uid);
+	n->di_gid = bswap32(o->di_gid);
+}
+
+void
+ffs_csum_swap(struct csum *o, struct csum *n, int size)
+{
+	size_t i;
+	u_int32_t *oint, *nint;
+
+	oint = (u_int32_t*)o;
+	nint = (u_int32_t*)n;
+
+	for (i = 0; i < size / sizeof(u_int32_t); i++)
+		nint[i] = bswap32(oint[i]);
+}
+
+void
+ffs_csumtotal_swap(struct csum *o, struct csum *n)
+{
+	n->cs_ndir = bswap64(o->cs_ndir);
+	n->cs_nbfree = bswap64(o->cs_nbfree);
+	n->cs_nifree = bswap64(o->cs_nifree);
+	n->cs_nffree = bswap64(o->cs_nffree);
+}
+
+/*
+ * Note that ffs_cg_swap may be called with o == n.
+ */
+void
+ffs_cg_swap(struct cg *o, struct cg *n, struct fs *fs)
+{
+	int i;
+	u_int32_t *n32, *o32;
+	u_int16_t *n16, *o16;
+	int32_t btotoff, boff, clustersumoff;
+
+	n->cg_firstfield = bswap32(o->cg_firstfield);
+	n->cg_magic = bswap32(o->cg_magic);
+	n->cg_cgx = bswap32(o->cg_cgx);
+	n->cg_ncyl = bswap16(o->cg_ncyl);
+	n->cg_niblk = bswap16(o->cg_niblk);
+	n->cg_ndblk = bswap32(o->cg_ndblk);
+	n->cg_cs.cs_ndir = bswap32(o->cg_cs.cs_ndir);
+	n->cg_cs.cs_nbfree = bswap32(o->cg_cs.cs_nbfree);
+	n->cg_cs.cs_nifree = bswap32(o->cg_cs.cs_nifree);
+	n->cg_cs.cs_nffree = bswap32(o->cg_cs.cs_nffree);
+	n->cg_rotor = bswap32(o->cg_rotor);
+	n->cg_frotor = bswap32(o->cg_frotor);
+	n->cg_irotor = bswap32(o->cg_irotor);
+	for (i = 0; i < MAXFRAG; i++)
+		n->cg_frsum[i] = bswap32(o->cg_frsum[i]);
+	
+	n->cg_btotoff = bswap32(o->cg_btotoff);
+	n->cg_boff = bswap32(o->cg_boff);
+	n->cg_iusedoff = bswap32(o->cg_iusedoff);
+	n->cg_freeoff = bswap32(o->cg_freeoff);
+	n->cg_nextfreeoff = bswap32(o->cg_nextfreeoff);
+	n->cg_clustersumoff = bswap32(o->cg_clustersumoff);
+	n->cg_clusteroff = bswap32(o->cg_clusteroff);
+	n->cg_nclusterblks = bswap32(o->cg_nclusterblks);
+	n->cg_time = bswap64(o->cg_time);
+
+	if (n->cg_magic == CG_MAGIC) {
+		btotoff = n->cg_btotoff;
+		boff = n->cg_boff;
+		clustersumoff = n->cg_clustersumoff;
+	} else {
+		btotoff = bswap32(n->cg_btotoff);
+		boff = bswap32(n->cg_boff);
+		clustersumoff = bswap32(n->cg_clustersumoff);
+	}
+	n32 = (u_int32_t *)((u_int8_t *)n + btotoff);
+	o32 = (u_int32_t *)((u_int8_t *)o + btotoff);
+	n16 = (u_int16_t *)((u_int8_t *)n + boff);
+	o16 = (u_int16_t *)((u_int8_t *)o + boff);
+
+	for (i = 0; i < fs->fs_cpg; i++)
+		n32[i] = bswap32(o32[i]);
+	
+	for (i = 0; i < fs->fs_cpg * fs->fs_nrpos; i++)
+		n16[i] = bswap16(o16[i]);
+
+	n32 = (u_int32_t *)((u_int8_t *)n + clustersumoff);
+	o32 = (u_int32_t *)((u_int8_t *)o + clustersumoff);
+	for (i = 1; i < fs->fs_contigsumsize + 1; i++)
+		n32[i] = bswap32(o32[i]);
+}
diff --git a/usr.sbin/makefs/ffs/ffs_extern.h b/usr.sbin/makefs/ffs/ffs_extern.h
new file mode 100644
index 0000000000..d648f421cb
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_extern.h
@@ -0,0 +1,75 @@
+/*	$NetBSD: ffs_extern.h,v 1.6 2003/08/07 11:25:33 agc Exp $	*/
+/* From: NetBSD: ffs_extern.h,v 1.19 2001/08/17 02:18:48 lukem Exp */
+
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ *	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.
+ *
+ *	@(#)ffs_extern.h	8.6 (Berkeley) 3/30/95
+ * $FreeBSD: head/usr.sbin/makefs/ffs/ffs_extern.h 239359 2012-08-17 17:45:27Z mjg $
+ */
+
+#include "ffs/buf.h"
+
+struct inode;
+
+/*
+ * Structure used to pass around logical block paths generated by
+ * ufs_getlbns and used by truncate and bmap code.
+ */
+struct indir {
+	daddr_t in_lbn;		/* Logical block number. */
+	int	in_off;			/* Offset in buffer. */
+};
+
+	/* ffs.c */
+_Noreturn void panic(const char *, ...) __printflike(1, 2);
+
+	/* ffs_alloc.c */
+int ffs_alloc(struct inode *, daddr_t, daddr_t, int, daddr_t *);
+daddr_t ffs_blkpref_ufs1(struct inode *, daddr_t, int, int32_t *);
+void ffs_blkfree(struct inode *, daddr_t, long);
+void ffs_clusteracct(struct fs *, struct cg *, int32_t, int);
+
+	/* ffs_balloc.c */
+int ffs_balloc(struct inode *, off_t, int, struct buf **);
+
+	/* ffs_bswap.c */
+void ffs_sb_swap(struct fs*, struct fs *);
+void ffs_dinode1_swap(struct ufs1_dinode *, struct ufs1_dinode *);
+void ffs_csum_swap(struct csum *, struct csum *, int);
+void ffs_cg_swap(struct cg *, struct cg *, struct fs *);
+
+	/* ffs_subr.c */
+void ffs_fragacct(struct fs *, int, int32_t[], int, int);
+int ffs_isblock(struct fs *, u_char *, int32_t);
+int ffs_isfreeblock(struct fs *, u_char *, int32_t);
+void ffs_clrblock(struct fs *, u_char *, int32_t);
+void ffs_setblock(struct fs *, u_char *, int32_t);
+
+	/* ufs_bmap.c */
+int ufs_getlbns(struct inode *, daddr_t, struct indir *, int *);
diff --git a/usr.sbin/makefs/ffs/ffs_subr.c b/usr.sbin/makefs/ffs/ffs_subr.c
new file mode 100644
index 0000000000..cb5e362c0b
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_subr.c
@@ -0,0 +1,194 @@
+/*	$NetBSD: ffs_subr.c,v 1.32 2003/12/30 12:33:24 pk Exp $	*/
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 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.
+ *
+ *	@(#)ffs_subr.c	8.5 (Berkeley) 3/21/95
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/ffs/ffs_subr.c 307927 2016-10-25 16:29:15Z marcel $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+#include "ffs/ffs_extern.h"
+#include "ffs/ufs_bswap.h"
+
+/*
+ * Update the frsum fields to reflect addition or deletion 
+ * of some frags.
+ */
+void
+ffs_fragacct_swap(struct fs *fs, int fragmap, int32_t fraglist[], int cnt, int needswap)
+{
+	int inblk;
+	int field, subfield;
+	int siz, pos;
+
+	inblk = (int)(fragtbl[fs->fs_frag][fragmap]) << 1;
+	fragmap <<= 1;
+	for (siz = 1; siz < fs->fs_frag; siz++) {
+		if ((inblk & (1 << (siz + (fs->fs_frag & (NBBY - 1))))) == 0)
+			continue;
+		field = around[siz];
+		subfield = inside[siz];
+		for (pos = siz; pos <= fs->fs_frag; pos++) {
+			if ((fragmap & field) == subfield) {
+				fraglist[siz] = ufs_rw32(
+				    ufs_rw32(fraglist[siz], needswap) + cnt,
+				    needswap);
+				pos += siz;
+				field <<= siz;
+				subfield <<= siz;
+			}
+			field <<= 1;
+			subfield <<= 1;
+		}
+	}
+}
+
+/*
+ * block operations
+ *
+ * check if a block is available
+ *  returns true if all the corresponding bits in the free map are 1
+ *  returns false if any corresponding bit in the free map is 0
+ */
+int
+ffs_isblock(fs, cp, h)
+	struct fs *fs;
+	u_char *cp;
+	int32_t h;
+{
+	u_char mask;
+
+	switch ((int)fs->fs_fragshift) {
+	case 3:
+		return (cp[h] == 0xff);
+	case 2:
+		mask = 0x0f << ((h & 0x1) << 2);
+		return ((cp[h >> 1] & mask) == mask);
+	case 1:
+		mask = 0x03 << ((h & 0x3) << 1);
+		return ((cp[h >> 2] & mask) == mask);
+	case 0:
+		mask = 0x01 << (h & 0x7);
+		return ((cp[h >> 3] & mask) == mask);
+	default:
+		panic("ffs_isblock: unknown fs_fragshift %d",
+		    (int)fs->fs_fragshift);
+	}
+}
+
+/*
+ * check if a block is completely allocated
+ *  returns true if all the corresponding bits in the free map are 0
+ *  returns false if any corresponding bit in the free map is 1
+ */
+int
+ffs_isfreeblock(fs, cp, h)
+	struct fs *fs;
+	u_char *cp;
+	int32_t h;
+{
+
+	switch ((int)fs->fs_fragshift) {
+	case 3:
+		return (cp[h] == 0);
+	case 2:
+		return ((cp[h >> 1] & (0x0f << ((h & 0x1) << 2))) == 0);
+	case 1:
+		return ((cp[h >> 2] & (0x03 << ((h & 0x3) << 1))) == 0);
+	case 0:
+		return ((cp[h >> 3] & (0x01 << (h & 0x7))) == 0);
+	default:
+		panic("ffs_isfreeblock: unknown fs_fragshift %d",
+		    (int)fs->fs_fragshift);
+	}
+}
+
+/*
+ * take a block out of the map
+ */
+void
+ffs_clrblock(fs, cp, h)
+	struct fs *fs;
+	u_char *cp;
+	int32_t h;
+{
+
+	switch ((int)fs->fs_fragshift) {
+	case 3:
+		cp[h] = 0;
+		return;
+	case 2:
+		cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2));
+		return;
+	case 1:
+		cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1));
+		return;
+	case 0:
+		cp[h >> 3] &= ~(0x01 << (h & 0x7));
+		return;
+	default:
+		panic("ffs_clrblock: unknown fs_fragshift %d",
+		    (int)fs->fs_fragshift);
+	}
+}
+
+/*
+ * put a block into the map
+ */
+void
+ffs_setblock(fs, cp, h)
+	struct fs *fs;
+	u_char *cp;
+	int32_t h;
+{
+
+	switch ((int)fs->fs_fragshift) {
+	case 3:
+		cp[h] = 0xff;
+		return;
+	case 2:
+		cp[h >> 1] |= (0x0f << ((h & 0x1) << 2));
+		return;
+	case 1:
+		cp[h >> 2] |= (0x03 << ((h & 0x3) << 1));
+		return;
+	case 0:
+		cp[h >> 3] |= (0x01 << (h & 0x7));
+		return;
+	default:
+		panic("ffs_setblock: unknown fs_fragshift %d",
+		    (int)fs->fs_fragshift);
+	}
+}
diff --git a/usr.sbin/makefs/ffs/mkfs.c b/usr.sbin/makefs/ffs/mkfs.c
new file mode 100644
index 0000000000..042790b3e0
--- /dev/null
+++ b/usr.sbin/makefs/ffs/mkfs.c
@@ -0,0 +1,764 @@
+/*	$NetBSD: mkfs.c,v 1.22 2011/10/09 22:30:13 christos Exp $	*/
+
+/*
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program
+ *
+ * Copyright (c) 1980, 1989, 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>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/ffs/mkfs.c 303038 2016-07-19 18:40:54Z emaste $");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "makefs.h"
+#include "ffs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/ufs_bswap.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+#include "ffs/newfs_extern.h"
+
+#ifndef BBSIZE
+#define	BBSIZE	8192			/* size of boot area, with label */
+#endif
+
+static void initcg(int, time_t, const fsinfo_t *);
+static int ilog2(int);
+
+static int count_digits(int);
+
+/*
+ * make file system for cylinder-group style file systems
+ */
+#define	UMASK		0755
+#define	POWEROF2(num)	(((num) & ((num) - 1)) == 0)
+
+union {
+	struct fs fs;
+	char pad[SBSIZE];
+} fsun;
+#define	sblock	fsun.fs
+struct	csum *fscs;
+
+union {
+	struct cg cg;
+	char pad[FFS_MAXBSIZE];
+} cgun;
+#define	acg	cgun.cg
+
+char *iobuf;
+int iobufsize;
+
+char writebuf[FFS_MAXBSIZE];
+
+static int64_t fssize;	   /* file system size */
+static int     sectorsize;	   /* bytes/sector */
+static int     fsize;	   /* fragment size */
+static int     bsize;	   /* block size */
+static int     maxblkspercg;
+static int     minfree;	   /* free space threshold */
+static int     opt;		   /* optimization preference (space or time) */
+static int     density;	   /* number of bytes per inode */
+static int     maxcontig;	   /* max contiguous blocks to allocate */
+static int     maxbpg;	   /* maximum blocks per file in a cyl group */
+static int     bbsize;	   /* boot block size */
+static int     sbsize;	   /* superblock size */
+static int     avgfilesize;	   /* expected average file size */
+static int     avgfpdir;	   /* expected number of files per directory */
+
+struct fs *
+ffs_mkfs(const char *fsys, const fsinfo_t *fsopts, time_t tstamp)
+{
+	int fragsperinode, optimalfpg, origdensity, minfpg, lastminfpg;
+	int32_t cylno, i, csfrags;
+	long long sizepb;
+	void *space;
+	int size, blks;
+	int nprintcols, printcolwidth;
+	ffs_opt_t	*ffs_opts = fsopts->fs_specific;
+
+	fssize =        fsopts->size / fsopts->sectorsize;
+	sectorsize =    fsopts->sectorsize;
+	fsize =         ffs_opts->fsize;
+	bsize =         ffs_opts->bsize;
+	maxblkspercg =  ffs_opts->maxblkspercg;
+	minfree =       ffs_opts->minfree;
+	opt =           ffs_opts->optimization;
+	density =       ffs_opts->density;
+	maxcontig =     ffs_opts->maxcontig;
+	maxbpg =        ffs_opts->maxbpg;
+	avgfilesize =   ffs_opts->avgfilesize;
+	avgfpdir =      ffs_opts->avgfpdir;
+	bbsize =        BBSIZE;
+	sbsize =        SBSIZE;
+
+	strlcpy(sblock.fs_volname, ffs_opts->label, sizeof(sblock.fs_volname));
+
+	sblock.fs_inodefmt = FS_44INODEFMT;
+	sblock.fs_maxsymlinklen = MAXSYMLINKLEN_UFS1;
+	sblock.fs_flags = 0;
+	/*
+	 * Validate the given file system size.
+	 * Verify that its last block can actually be accessed.
+	 * Convert to file system fragment sized units.
+	 */
+	if (fssize <= 0) {
+		printf("preposterous size %lld\n", (long long)fssize);
+		exit(13);
+	}
+	ffs_wtfs(fssize - 1, sectorsize, (char *)&sblock, fsopts);
+
+	/*
+	 * collect and verify the filesystem density info
+	 */
+	sblock.fs_avgfilesize = avgfilesize;
+	sblock.fs_avgfpdir = avgfpdir;
+	if (sblock.fs_avgfilesize <= 0)
+		printf("illegal expected average file size %d\n",
+		    sblock.fs_avgfilesize), exit(14);
+	if (sblock.fs_avgfpdir <= 0)
+		printf("illegal expected number of files per directory %d\n",
+		    sblock.fs_avgfpdir), exit(15);
+	/*
+	 * collect and verify the block and fragment sizes
+	 */
+	sblock.fs_bsize = bsize;
+	sblock.fs_fsize = fsize;
+	if (!POWEROF2(sblock.fs_bsize)) {
+		printf("block size must be a power of 2, not %d\n",
+		    sblock.fs_bsize);
+		exit(16);
+	}
+	if (!POWEROF2(sblock.fs_fsize)) {
+		printf("fragment size must be a power of 2, not %d\n",
+		    sblock.fs_fsize);
+		exit(17);
+	}
+	if (sblock.fs_fsize < sectorsize) {
+		printf("fragment size %d is too small, minimum is %d\n",
+		    sblock.fs_fsize, sectorsize);
+		exit(18);
+	}
+	if (sblock.fs_bsize < MINBSIZE) {
+		printf("block size %d is too small, minimum is %d\n",
+		    sblock.fs_bsize, MINBSIZE);
+		exit(19);
+	}
+	if (sblock.fs_bsize > FFS_MAXBSIZE) {
+		printf("block size %d is too large, maximum is %d\n",
+		    sblock.fs_bsize, FFS_MAXBSIZE);
+		exit(19);
+	}
+	if (sblock.fs_bsize < sblock.fs_fsize) {
+		printf("block size (%d) cannot be smaller than fragment size (%d)\n",
+		    sblock.fs_bsize, sblock.fs_fsize);
+		exit(20);
+	}
+
+	sblock.fs_maxcontig = maxcontig;
+
+	if (sblock.fs_maxcontig > 1)
+		sblock.fs_contigsumsize = MIN(sblock.fs_maxcontig,FS_MAXCONTIG);
+
+	sblock.fs_bmask = ~(sblock.fs_bsize - 1);
+	sblock.fs_fmask = ~(sblock.fs_fsize - 1);
+	sblock.fs_qbmask = ~sblock.fs_bmask;
+	sblock.fs_qfmask = ~sblock.fs_fmask;
+	for (sblock.fs_bshift = 0, i = sblock.fs_bsize; i > 1; i >>= 1)
+		sblock.fs_bshift++;
+	for (sblock.fs_fshift = 0, i = sblock.fs_fsize; i > 1; i >>= 1)
+		sblock.fs_fshift++;
+	sblock.fs_frag = numfrags(&sblock, sblock.fs_bsize);
+	for (sblock.fs_fragshift = 0, i = sblock.fs_frag; i > 1; i >>= 1)
+		sblock.fs_fragshift++;
+	if (sblock.fs_frag > MAXFRAG) {
+		printf("fragment size %d is too small, "
+			"minimum with block size %d is %d\n",
+		    sblock.fs_fsize, sblock.fs_bsize,
+		    sblock.fs_bsize / MAXFRAG);
+		exit(21);
+	}
+	sblock.fs_fsbtodb = ilog2(sblock.fs_fsize / sectorsize);
+	sblock.fs_size = fssize = dbtofsb(&sblock, fssize);
+
+	sblock.fs_magic = FS_MAGIC;
+	sblock.fs_nindir = sblock.fs_bsize / sizeof(ufs_daddr_t);
+	sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs1_dinode);
+	sblock.fs_maxsymlinklen = ((NDADDR + NIADDR) * sizeof (ufs_daddr_t));
+	sblock.fs_inodefmt = FS_44INODEFMT;
+	sblock.fs_cgoffset = 0;
+	sblock.fs_cgmask = 0xffffffff;
+	sblock.fs_size = sblock.fs_size;
+	sblock.fs_rotdelay = 0;
+	sblock.fs_rps = 60;
+	sblock.fs_nspf = sblock.fs_fsize / sectorsize;
+	sblock.fs_cpg = 1;
+	sblock.fs_interleave = 1;
+	sblock.fs_trackskew = 0;
+	sblock.fs_cpc = 0;
+	sblock.fs_postblformat = 1;
+	sblock.fs_nrpos = 1;
+
+	sblock.fs_sblkno =
+	    roundup(howmany(8192 + SBSIZE, sblock.fs_fsize),
+		sblock.fs_frag);
+	sblock.fs_cblkno = (daddr_t)(sblock.fs_sblkno +
+	    roundup(howmany(SBSIZE, sblock.fs_fsize), sblock.fs_frag));
+	sblock.fs_iblkno = sblock.fs_cblkno + sblock.fs_frag;
+	sblock.fs_maxfilesize = sblock.fs_bsize * NDADDR - 1;
+	for (sizepb = sblock.fs_bsize, i = 0; i < NIADDR; i++) {
+		sizepb *= NINDIR(&sblock);
+		sblock.fs_maxfilesize += sizepb;
+	}
+
+	/*
+	 * Calculate the number of blocks to put into each cylinder group.
+	 *
+	 * This algorithm selects the number of blocks per cylinder
+	 * group. The first goal is to have at least enough data blocks
+	 * in each cylinder group to meet the density requirement. Once
+	 * this goal is achieved we try to expand to have at least
+	 * 1 cylinder group. Once this goal is achieved, we pack as
+	 * many blocks into each cylinder group map as will fit.
+	 *
+	 * We start by calculating the smallest number of blocks that we
+	 * can put into each cylinder group. If this is too big, we reduce
+	 * the density until it fits.
+	 */
+	origdensity = density;
+	for (;;) {
+		fragsperinode = MAX(numfrags(&sblock, density), 1);
+		minfpg = fragsperinode * INOPB(&sblock);
+		if (minfpg > sblock.fs_size)
+			minfpg = sblock.fs_size;
+		sblock.fs_ipg = INOPB(&sblock);
+		sblock.fs_fpg = roundup(sblock.fs_iblkno +
+		    sblock.fs_ipg / INOPF(&sblock), sblock.fs_frag);
+		if (sblock.fs_fpg < minfpg)
+			sblock.fs_fpg = minfpg;
+		sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+		    INOPB(&sblock));
+		sblock.fs_fpg = roundup(sblock.fs_iblkno +
+		    sblock.fs_ipg / INOPF(&sblock), sblock.fs_frag);
+		if (sblock.fs_fpg < minfpg)
+			sblock.fs_fpg = minfpg;
+		sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+		    INOPB(&sblock));
+		if (CGSIZE(&sblock) < (unsigned long)sblock.fs_bsize)
+			break;
+		density -= sblock.fs_fsize;
+	}
+	if (density != origdensity)
+		printf("density reduced from %d to %d\n", origdensity, density);
+
+	if (maxblkspercg <= 0 || maxblkspercg >= fssize)
+		maxblkspercg = fssize - 1;
+	/*
+	 * Start packing more blocks into the cylinder group until
+	 * it cannot grow any larger, the number of cylinder groups
+	 * drops below 1, or we reach the size requested.
+	 */
+	for ( ; sblock.fs_fpg < maxblkspercg; sblock.fs_fpg += sblock.fs_frag) {
+		sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+		    INOPB(&sblock));
+		if (sblock.fs_size / sblock.fs_fpg < 1)
+			break;
+		if (CGSIZE(&sblock) < (unsigned long)sblock.fs_bsize)
+			continue;
+		if (CGSIZE(&sblock) == (unsigned long)sblock.fs_bsize)
+			break;
+		sblock.fs_fpg -= sblock.fs_frag;
+		sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+		    INOPB(&sblock));
+		break;
+	}
+	/*
+	 * Check to be sure that the last cylinder group has enough blocks
+	 * to be viable. If it is too small, reduce the number of blocks
+	 * per cylinder group which will have the effect of moving more
+	 * blocks into the last cylinder group.
+	 */
+	optimalfpg = sblock.fs_fpg;
+	for (;;) {
+		sblock.fs_ncg = howmany(sblock.fs_size, sblock.fs_fpg);
+		lastminfpg = roundup(sblock.fs_iblkno +
+		    sblock.fs_ipg / INOPF(&sblock), sblock.fs_frag);
+		if (sblock.fs_size < lastminfpg) {
+			printf("Filesystem size %lld < minimum size of %d\n",
+			    (long long)sblock.fs_size, lastminfpg);
+			exit(28);
+		}
+		if (sblock.fs_size % sblock.fs_fpg >= lastminfpg ||
+		    sblock.fs_size % sblock.fs_fpg == 0)
+			break;
+		sblock.fs_fpg -= sblock.fs_frag;
+		sblock.fs_ipg = roundup(howmany(sblock.fs_fpg, fragsperinode),
+		    INOPB(&sblock));
+	}
+	if (optimalfpg != sblock.fs_fpg)
+		printf("Reduced frags per cylinder group from %d to %d %s\n",
+		   optimalfpg, sblock.fs_fpg, "to enlarge last cyl group");
+	sblock.fs_cgsize = fragroundup(&sblock, CGSIZE(&sblock));
+	sblock.fs_dblkno = sblock.fs_iblkno + sblock.fs_ipg / INOPF(&sblock);
+	sblock.fs_spc = sblock.fs_fpg * sblock.fs_nspf;
+	sblock.fs_nsect = sblock.fs_spc;
+	sblock.fs_npsect = sblock.fs_spc;
+	sblock.fs_ncyl = sblock.fs_ncg;
+
+	/*
+	 * fill in remaining fields of the super block
+	 */
+	sblock.fs_csaddr = cgdmin(&sblock, 0);
+	sblock.fs_cssize =
+	    fragroundup(&sblock, sblock.fs_ncg * sizeof(struct csum));
+
+	/*
+	 * Setup memory for temporary in-core cylgroup summaries.
+	 * Cribbed from ffs_mountfs().
+	 */
+	size = sblock.fs_cssize;
+	blks = howmany(size, sblock.fs_fsize);
+	if (sblock.fs_contigsumsize > 0)
+		size += sblock.fs_ncg * sizeof(int32_t);
+	if ((space = (char *)calloc(1, size)) == NULL)
+		err(1, "memory allocation error for cg summaries");
+	sblock.fs_csp = space;
+	space = (char *)space + sblock.fs_cssize;
+	if (sblock.fs_contigsumsize > 0) {
+		int32_t *lp;
+
+		sblock.fs_maxcluster = lp = space;
+		for (i = 0; i < sblock.fs_ncg; i++)
+		*lp++ = sblock.fs_contigsumsize;
+	}
+
+	sblock.fs_sbsize = fragroundup(&sblock, sizeof(struct fs));
+	if (sblock.fs_sbsize > SBSIZE)
+		sblock.fs_sbsize = SBSIZE;
+	sblock.fs_minfree = minfree;
+	sblock.fs_maxcontig = maxcontig;
+	sblock.fs_maxbpg = maxbpg;
+	sblock.fs_optim = opt;
+	sblock.fs_cgrotor = 0;
+	sblock.fs_pendingblocks = 0;
+	sblock.fs_pendinginodes = 0;
+	sblock.fs_cstotal.cs_ndir = 0;
+	sblock.fs_cstotal.cs_nbfree = 0;
+	sblock.fs_cstotal.cs_nifree = 0;
+	sblock.fs_cstotal.cs_nffree = 0;
+	sblock.fs_fmod = 0;
+	sblock.fs_ronly = 0;
+	sblock.fs_state = 0;
+	sblock.fs_clean = FS_ISCLEAN;
+	sblock.fs_ronly = 0;
+	sblock.fs_id[0] = tstamp;
+	sblock.fs_id[1] = random();
+	sblock.fs_fsmnt[0] = '\0';
+	csfrags = howmany(sblock.fs_cssize, sblock.fs_fsize);
+	sblock.fs_dsize = sblock.fs_size - sblock.fs_sblkno -
+	    sblock.fs_ncg * (sblock.fs_dblkno - sblock.fs_sblkno);
+	sblock.fs_cstotal.cs_nbfree =
+	    fragstoblks(&sblock, sblock.fs_dsize) -
+	    howmany(csfrags, sblock.fs_frag);
+	sblock.fs_cstotal.cs_nffree =
+	    fragnum(&sblock, sblock.fs_size) +
+	    (fragnum(&sblock, csfrags) > 0 ?
+	    sblock.fs_frag - fragnum(&sblock, csfrags) : 0);
+	sblock.fs_cstotal.cs_nifree = sblock.fs_ncg * sblock.fs_ipg - ROOTINO;
+	sblock.fs_cstotal.cs_ndir = 0;
+	sblock.fs_dsize -= csfrags;
+	sblock.fs_time = tstamp;
+	/*
+	 * Dump out summary information about file system.
+	 */
+#define	B2MBFACTOR (1 / (1024.0 * 1024.0))
+	printf("%s: %.1fMB (%lld sectors) block size %d, "
+	       "fragment size %d\n",
+	    fsys, (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR,
+	    (long long)fsbtodb(&sblock, sblock.fs_size),
+	    sblock.fs_bsize, sblock.fs_fsize);
+	printf("\tusing %d cylinder groups of %.2fMB, %d blks, "
+	       "%d inodes.\n",
+	    sblock.fs_ncg,
+	    (float)sblock.fs_fpg * sblock.fs_fsize * B2MBFACTOR,
+	    sblock.fs_fpg / sblock.fs_frag, sblock.fs_ipg);
+#undef B2MBFACTOR
+	/*
+	 * Now determine how wide each column will be, and calculate how
+	 * many columns will fit in a 76 char line. 76 is the width of the
+	 * subwindows in sysinst.
+	 */
+	printcolwidth = count_digits(
+			fsbtodb(&sblock, cgsblock(&sblock, sblock.fs_ncg -1)));
+	nprintcols = 76 / (printcolwidth + 2);
+
+	/*
+	 * allocate space for superblock, cylinder group map, and
+	 * two sets of inode blocks.
+	 */
+	if (sblock.fs_bsize < SBSIZE)
+		iobufsize = SBSIZE + 3 * sblock.fs_bsize;
+	else
+		iobufsize = 4 * sblock.fs_bsize;
+	if ((iobuf = malloc(iobufsize)) == NULL) {
+		printf("Cannot allocate I/O buffer\n");
+		exit(38);
+	}
+	memset(iobuf, 0, iobufsize);
+	/*
+	 * Make a copy of the superblock into the buffer that we will be
+	 * writing out in each cylinder group.
+	 */
+	memcpy(writebuf, &sblock, sbsize);
+	if (fsopts->needswap)
+		ffs_sb_swap(&sblock, (struct fs*)writebuf);
+	memcpy(iobuf, writebuf, SBSIZE);
+
+	printf("super-block backups (for fsck -b #) at:");
+	for (cylno = 0; cylno < sblock.fs_ncg; cylno++) {
+		initcg(cylno, tstamp, fsopts);
+		if (cylno % nprintcols == 0)
+			printf("\n");
+		printf(" %*lld,", printcolwidth,
+			(long long)fsbtodb(&sblock, cgsblock(&sblock, cylno)));
+		fflush(stdout);
+	}
+	printf("\n");
+
+	/*
+	 * Now construct the initial file system,
+	 * then write out the super-block.
+	 */
+	sblock.fs_time = tstamp;
+	if (fsopts->needswap)
+		sblock.fs_flags |= FS_SWAPPED;
+	ffs_write_superblock(&sblock, fsopts);
+	return (&sblock);
+}
+
+/*
+ * Write out the superblock and its duplicates,
+ * and the cylinder group summaries
+ */
+void
+ffs_write_superblock(struct fs *fs, const fsinfo_t *fsopts)
+{
+	int cylno, size, blks, i, saveflag;
+	void *space;
+	char *wrbuf;
+
+	saveflag = fs->fs_flags & FS_INTERNAL;
+	fs->fs_flags &= ~FS_INTERNAL;
+
+        memcpy(writebuf, &sblock, sbsize);
+	if (fsopts->needswap)
+		ffs_sb_swap(fs, (struct fs*)writebuf);
+	ffs_wtfs(8192 / sectorsize, sbsize, writebuf, fsopts);
+
+	/* Write out the duplicate super blocks */
+	for (cylno = 0; cylno < fs->fs_ncg; cylno++)
+		ffs_wtfs(fsbtodb(fs, cgsblock(fs, cylno)),
+		    sbsize, writebuf, fsopts);
+
+	/* Write out the cylinder group summaries */
+	size = fs->fs_cssize;
+	blks = howmany(size, fs->fs_fsize);
+	space = (void *)fs->fs_csp;
+	if ((wrbuf = malloc(size)) == NULL)
+		err(1, "ffs_write_superblock: malloc %d", size);
+	for (i = 0; i < blks; i+= fs->fs_frag) {
+		size = fs->fs_bsize;
+		if (i + fs->fs_frag > blks)
+			size = (blks - i) * fs->fs_fsize;
+		if (fsopts->needswap)
+			ffs_csum_swap((struct csum *)space,
+			    (struct csum *)wrbuf, size);
+		else
+			memcpy(wrbuf, space, (u_int)size);
+		ffs_wtfs(fsbtodb(fs, fs->fs_csaddr + i), size, wrbuf, fsopts);
+		space = (char *)space + size;
+	}
+	free(wrbuf);
+	fs->fs_flags |= saveflag;
+}
+
+/*
+ * Initialize a cylinder group.
+ */
+static void
+initcg(int cylno, time_t utime, const fsinfo_t *fsopts)
+{
+	daddr_t cbase, dmax;
+	int32_t i, j, d, dlower, dupper, blkno;
+	struct ufs1_dinode *dp1;
+	struct ufs2_dinode *dp2;
+	int start;
+
+	/*
+	 * Determine block bounds for cylinder group.
+	 * Allow space for super block summary information in first
+	 * cylinder group.
+	 */
+	cbase = cgbase(&sblock, cylno);
+	dmax = cbase + sblock.fs_fpg;
+	if (dmax > sblock.fs_size)
+		dmax = sblock.fs_size;
+	dlower = cgsblock(&sblock, cylno) - cbase;
+	dupper = cgdmin(&sblock, cylno) - cbase;
+	if (cylno == 0)
+		dupper += howmany(sblock.fs_cssize, sblock.fs_fsize);
+	memset(&acg, 0, sblock.fs_cgsize);
+	acg.cg_time = utime;
+	acg.cg_magic = CG_MAGIC;
+	acg.cg_cgx = cylno;
+	acg.cg_niblk = sblock.fs_ipg;
+	acg.cg_ndblk = dmax - cbase;
+	if (sblock.fs_contigsumsize > 0)
+		acg.cg_nclusterblks = acg.cg_ndblk >> sblock.fs_fragshift;
+	start = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield);
+	if (cylno == sblock.fs_ncg - 1)
+		acg.cg_ncyl = howmany(acg.cg_ndblk,
+		    sblock.fs_fpg / sblock.fs_cpg);
+	else
+		acg.cg_ncyl = sblock.fs_cpg;
+	acg.cg_time = 0;
+	acg.cg_niblk = 0;
+	acg.cg_btotoff = start;
+	acg.cg_boff = acg.cg_btotoff +
+	    sblock.fs_cpg * sizeof(int32_t);
+	acg.cg_iusedoff = acg.cg_boff +
+	    sblock.fs_cpg * sizeof(u_int16_t);
+	acg.cg_freeoff = acg.cg_iusedoff + howmany(sblock.fs_ipg, CHAR_BIT);
+	if (sblock.fs_contigsumsize <= 0) {
+		acg.cg_nextfreeoff = acg.cg_freeoff +
+		   howmany(sblock.fs_fpg, CHAR_BIT);
+	} else {
+		acg.cg_clustersumoff = acg.cg_freeoff +
+		    howmany(sblock.fs_fpg, CHAR_BIT) - sizeof(int32_t);
+		acg.cg_clustersumoff =
+		    roundup(acg.cg_clustersumoff, sizeof(int32_t));
+		acg.cg_clusteroff = acg.cg_clustersumoff +
+		    (sblock.fs_contigsumsize + 1) * sizeof(int32_t);
+		acg.cg_nextfreeoff = acg.cg_clusteroff +
+		    howmany(fragstoblks(&sblock, sblock.fs_fpg), CHAR_BIT);
+	}
+	if (acg.cg_nextfreeoff > sblock.fs_cgsize) {
+		printf("Panic: cylinder group too big\n");
+		exit(37);
+	}
+	acg.cg_cs.cs_nifree += sblock.fs_ipg;
+	if (cylno == 0)
+		for (i = 0; i < ROOTINO; i++) {
+			setbit(cg_inosused_swap(&acg, 0), i);
+			acg.cg_cs.cs_nifree--;
+		}
+	if (cylno > 0) {
+		/*
+		 * In cylno 0, beginning space is reserved
+		 * for boot and super blocks.
+		 */
+		for (d = 0, blkno = 0; d < dlower;) {
+			ffs_setblock(&sblock, cg_blksfree_swap(&acg, 0), blkno);
+			if (sblock.fs_contigsumsize > 0)
+				setbit(cg_clustersfree_swap(&acg, 0), blkno);
+			acg.cg_cs.cs_nbfree++;
+			d += sblock.fs_frag;
+			blkno++;
+		}
+	}
+	if ((i = (dupper & (sblock.fs_frag - 1))) != 0) {
+		acg.cg_frsum[sblock.fs_frag - i]++;
+		for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) {
+			setbit(cg_blksfree_swap(&acg, 0), dupper);
+			acg.cg_cs.cs_nffree++;
+		}
+	}
+	for (d = dupper, blkno = dupper >> sblock.fs_fragshift;
+	     d + sblock.fs_frag <= acg.cg_ndblk; ) {
+		ffs_setblock(&sblock, cg_blksfree_swap(&acg, 0), blkno);
+		if (sblock.fs_contigsumsize > 0)
+			setbit(cg_clustersfree_swap(&acg, 0), blkno);
+		acg.cg_cs.cs_nbfree++;
+		d += sblock.fs_frag;
+		blkno++;
+	}
+	if (d < acg.cg_ndblk) {
+		acg.cg_frsum[acg.cg_ndblk - d]++;
+		for (; d < acg.cg_ndblk; d++) {
+			setbit(cg_blksfree_swap(&acg, 0), d);
+			acg.cg_cs.cs_nffree++;
+		}
+	}
+	if (sblock.fs_contigsumsize > 0) {
+		int32_t *sump = cg_clustersum_swap(&acg, 0);
+		u_char *mapp = cg_clustersfree_swap(&acg, 0);
+		int map = *mapp++;
+		int bit = 1;
+		int run = 0;
+
+		for (i = 0; i < acg.cg_nclusterblks; i++) {
+			if ((map & bit) != 0) {
+				run++;
+			} else if (run != 0) {
+				if (run > sblock.fs_contigsumsize)
+					run = sblock.fs_contigsumsize;
+				sump[run]++;
+				run = 0;
+			}
+			if ((i & (CHAR_BIT - 1)) != (CHAR_BIT - 1)) {
+				bit <<= 1;
+			} else {
+				map = *mapp++;
+				bit = 1;
+			}
+		}
+		if (run != 0) {
+			if (run > sblock.fs_contigsumsize)
+				run = sblock.fs_contigsumsize;
+			sump[run]++;
+		}
+	}
+	sblock.fs_cs(&sblock, cylno) = acg.cg_cs;
+	/*
+	 * Write out the duplicate super block, the cylinder group map
+	 * and two blocks worth of inodes in a single write.
+	 */
+	start = MAX(sblock.fs_bsize, SBSIZE);
+	memcpy(&iobuf[start], &acg, sblock.fs_cgsize);
+	if (fsopts->needswap)
+		ffs_cg_swap(&acg, (struct cg*)&iobuf[start], &sblock);
+	start += sblock.fs_bsize;
+	dp1 = (struct ufs1_dinode *)(&iobuf[start]);
+	dp2 = (struct ufs2_dinode *)(&iobuf[start]);
+	ffs_wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)), iobufsize, iobuf,
+	    fsopts);
+	/*
+	 * Initialize all the inodes.
+	 */
+	for (i = 2 * sblock.fs_frag;
+	     i < sblock.fs_ipg / INOPF(&sblock);
+	     i += sblock.fs_frag) {
+		dp1 = (struct ufs1_dinode *)(&iobuf[start]);
+		for (j = 0; j < INOPB(&sblock); j++) {
+			dp1->di_gen = random();
+			dp1++;
+		}
+		ffs_wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i),
+		    sblock.fs_bsize, &iobuf[start], fsopts);
+	}
+}
+
+/*
+ * read a block from the file system
+ */
+void
+ffs_rdfs(daddr_t bno, int size, void *bf, const fsinfo_t *fsopts)
+{
+	int n;
+	off_t offset;
+
+	offset = bno;
+	offset *= fsopts->sectorsize;
+	if (lseek(fsopts->fd, offset, SEEK_SET) < 0)
+		err(1, "ffs_rdfs: seek error for sector %lld: %s\n",
+		    (long long)bno, strerror(errno));
+	n = read(fsopts->fd, bf, size);
+	if (n == -1) {
+		abort();
+		err(1, "ffs_rdfs: read error bno %lld size %d", (long long)bno,
+		    size);
+	}
+	else if (n != size)
+		errx(1, "ffs_rdfs: read error for sector %lld: %s\n",
+		    (long long)bno, strerror(errno));
+}
+
+/*
+ * write a block to the file system
+ */
+void
+ffs_wtfs(daddr_t bno, int size, void *bf, const fsinfo_t *fsopts)
+{
+	int n;
+	off_t offset;
+
+	offset = bno;
+	offset *= fsopts->sectorsize;
+	if (lseek(fsopts->fd, offset, SEEK_SET) < 0)
+		err(1, "wtfs: seek error for sector %lld: %s\n",
+		    (long long)bno, strerror(errno));
+	n = write(fsopts->fd, bf, size);
+	if (n == -1)
+		err(1, "wtfs: write error for sector %lld: %s\n",
+		    (long long)bno, strerror(errno));
+	else if (n != size)
+		errx(1, "wtfs: write error for sector %lld: %s\n",
+		    (long long)bno, strerror(errno));
+}
+
+
+/* Determine how many digits are needed to print a given integer */
+static int
+count_digits(int num)
+{
+	int ndig;
+
+	for(ndig = 1; num > 9; num /=10, ndig++);
+
+	return (ndig);
+}
+
+static int
+ilog2(int val)
+{
+	u_int n;
+
+	for (n = 0; n < sizeof(n) * CHAR_BIT; n++)
+		if (1 << n == val)
+			return (n);
+	errx(1, "ilog2: %d is not a power of 2\n", val);
+}
diff --git a/usr.sbin/makefs/ffs/newfs_extern.h b/usr.sbin/makefs/ffs/newfs_extern.h
new file mode 100644
index 0000000000..6393ce91a3
--- /dev/null
+++ b/usr.sbin/makefs/ffs/newfs_extern.h
@@ -0,0 +1,36 @@
+/*	$NetBSD: newfs_extern.h,v 1.4 2015/12/21 00:58:08 christos Exp $	*/
+/* From: NetBSD: extern.h,v 1.3 2000/12/01 12:03:27 simonb Exp $ */
+
+/*
+ * Copyright (c) 1997 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: head/usr.sbin/makefs/ffs/newfs_extern.h 301879 2016-06-14 14:03:28Z emaste $
+ */
+
+/* prototypes */
+struct fs	*ffs_mkfs(const char *, const fsinfo_t *, time_t);
+void		ffs_write_superblock(struct fs *, const fsinfo_t *);
+void		ffs_rdfs(daddr_t, int, void *, const fsinfo_t *);
+void		ffs_wtfs(daddr_t, int, void *, const fsinfo_t *);
+
+#define	FFS_MAXBSIZE	65536
diff --git a/usr.sbin/makefs/ffs/ufs_bmap.c b/usr.sbin/makefs/ffs/ufs_bmap.c
new file mode 100644
index 0000000000..5d46f41cc7
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ufs_bmap.c
@@ -0,0 +1,140 @@
+/*	$NetBSD: ufs_bmap.c,v 1.14 2004/06/20 22:20:18 jmc Exp $	*/
+/* From: NetBSD: ufs_bmap.c,v 1.14 2001/11/08 05:00:51 chs Exp */
+
+/*
+ * Copyright (c) 1989, 1991, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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.
+ *
+ *	@(#)ufs_bmap.c	8.8 (Berkeley) 8/11/95
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/ffs/ufs_bmap.c 239359 2012-08-17 17:45:27Z mjg $");
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <strings.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/ufs_bswap.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+
+/*
+ * Create an array of logical block number/offset pairs which represent the
+ * path of indirect blocks required to access a data block.  The first "pair"
+ * contains the logical block number of the appropriate single, double or
+ * triple indirect block and the offset into the inode indirect block array.
+ * Note, the logical block number of the inode single/double/triple indirect
+ * block appears twice in the array, once with the offset into the i_ffs_ib and
+ * once with the offset into the page itself.
+ */
+int
+ufs_getlbns(struct inode *ip, daddr_t bn, struct indir *ap, int *nump)
+{
+	daddr_t metalbn, realbn;
+	int64_t blockcnt;
+	int lbc;
+	int i, numlevels, off;
+	u_long lognindir;
+
+	lognindir = ffs(NINDIR(ip->i_fs)) - 1;
+	if (nump)
+		*nump = 0;
+	numlevels = 0;
+	realbn = bn;
+	if ((long)bn < 0)
+		bn = -(long)bn;
+
+	assert (bn >= NDADDR);
+
+	/* 
+	 * Determine the number of levels of indirection.  After this loop
+	 * is done, blockcnt indicates the number of data blocks possible
+	 * at the given level of indirection, and NIADDR - i is the number
+	 * of levels of indirection needed to locate the requested block.
+	 */
+
+	bn -= NDADDR;
+	for (lbc = 0, i = NIADDR;; i--, bn -= blockcnt) {
+		if (i == 0)
+			return (EFBIG);
+
+		lbc += lognindir;
+		blockcnt = (int64_t)1 << lbc;
+
+		if (bn < blockcnt)
+			break;
+	}
+
+	/* Calculate the address of the first meta-block. */
+	if (realbn >= 0)
+		metalbn = -(realbn - bn + NIADDR - i);
+	else
+		metalbn = -(-realbn - bn + NIADDR - i);
+
+	/* 
+	 * At each iteration, off is the offset into the bap array which is
+	 * an array of disk addresses at the current level of indirection.
+	 * The logical block number and the offset in that block are stored
+	 * into the argument array.
+	 */
+	ap->in_lbn = metalbn;
+	ap->in_off = off = NIADDR - i;
+	ap++;
+	for (++numlevels; i <= NIADDR; i++) {
+		/* If searching for a meta-data block, quit when found. */
+		if (metalbn == realbn)
+			break;
+
+		lbc -= lognindir;
+		blockcnt = (int64_t)1 << lbc;
+		off = (bn >> lbc) & (NINDIR(ip->i_fs) - 1);
+
+		++numlevels;
+		ap->in_lbn = metalbn;
+		ap->in_off = off;
+		++ap;
+
+		metalbn -= -1 + (off << lbc);
+	}
+	if (nump)
+		*nump = numlevels;
+	return (0);
+}
diff --git a/usr.sbin/makefs/ffs/ufs_bswap.h b/usr.sbin/makefs/ffs/ufs_bswap.h
new file mode 100644
index 0000000000..7c68aee04a
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ufs_bswap.h
@@ -0,0 +1,90 @@
+/*	$NetBSD: ufs_bswap.h,v 1.13 2003/10/05 17:48:50 bouyer Exp $	*/
+
+/*
+ * Copyright (c) 1998 Manuel Bouyer.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by Manuel Bouyer.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * 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: head/usr.sbin/makefs/ffs/ufs_bswap.h 253675 2013-07-26 14:22:03Z marius $
+ */
+
+#ifndef _UFS_UFS_BSWAP_H_
+#define _UFS_UFS_BSWAP_H_
+
+#if defined(_KERNEL_OPT)
+#include "opt_ffs.h"
+#endif
+
+#include <sys/endian.h>
+
+#include "makefs.h"
+
+/* Macros to access UFS flags */
+#ifdef FFS_EI
+#define	UFS_MPNEEDSWAP(mp)	(VFSTOUFS(mp)->um_flags & UFS_NEEDSWAP)
+#define UFS_FSNEEDSWAP(fs)	((fs)->fs_flags & FS_SWAPPED)
+#define	UFS_IPNEEDSWAP(ip)	UFS_MPNEEDSWAP(ITOV(ip)->v_mount)
+#else
+#define	UFS_MPNEEDSWAP(mp) (0)
+#define UFS_FSNEEDSWAP(fs) (0)
+#define	UFS_IPNEEDSWAP(ip) (0)
+#endif
+
+#if !defined(_KERNEL) || defined(FFS_EI)
+/* inlines for access to swapped data */
+static __inline u_int16_t ufs_rw16 __P((u_int16_t, int));
+static __inline u_int32_t ufs_rw32 __P((u_int32_t, int));
+static __inline u_int64_t ufs_rw64 __P((u_int64_t, int));
+
+static __inline u_int16_t
+ufs_rw16(u_int16_t a, int ns)
+{
+	return ((ns) ?  bswap16(a) : (a));
+}
+static __inline u_int32_t
+ufs_rw32(u_int32_t a, int ns)
+{
+	return ((ns) ?  bswap32(a) : (a));
+}
+static __inline u_int64_t
+ufs_rw64(u_int64_t a, int ns)
+{
+	return ((ns) ?  bswap64(a) : (a));
+}
+#else
+#define ufs_rw16(a, ns) ((uint16_t)(a))
+#define ufs_rw32(a, ns) ((uint32_t)(a))
+#define ufs_rw64(a, ns) ((uint64_t)(a))
+#endif
+
+#define ufs_add16(a, b, ns) \
+	(a) = ufs_rw16(ufs_rw16((a), (ns)) + (b), (ns))
+#define ufs_add32(a, b, ns) \
+	(a) = ufs_rw32(ufs_rw32((a), (ns)) + (b), (ns))
+#define ufs_add64(a, b, ns) \
+	(a) = ufs_rw64(ufs_rw64((a), (ns)) + (b), (ns))
+
+#endif /* !_UFS_UFS_BSWAP_H_ */
diff --git a/usr.sbin/makefs/ffs/ufs_inode.h b/usr.sbin/makefs/ffs/ufs_inode.h
new file mode 100644
index 0000000000..9c6e5cd5c0
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ufs_inode.h
@@ -0,0 +1,73 @@
+/*	$NetBSD: ufs_inode.h,v 1.3 2003/08/07 11:25:34 agc Exp $	*/
+/* From:  NetBSD: inode.h,v 1.27 2001/12/18 10:57:23 fvdl Exp $ */
+
+/*
+ * Copyright (c) 1982, 1989, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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.
+ *
+ *	@(#)inode.h	8.9 (Berkeley) 5/14/95
+ * $FreeBSD: head/usr.sbin/makefs/ffs/ufs_inode.h 186334 2008-12-19 18:45:43Z sam $
+ */
+
+union dinode {
+	struct ufs1_dinode ffs1_din;
+};
+
+struct inode {
+	ino_t	  	i_number;	/* The identity of the inode. */
+	struct fs	*i_fs;		/* File system */
+	union dinode	i_din;
+	int		i_fd;		/* File descriptor */
+	uint64_t	i_size;
+};
+
+#define	i_ffs1_atime		i_din.ffs1_din.di_atime
+#define	i_ffs1_atimensec	i_din.ffs1_din.di_atimensec
+#define	i_ffs1_blocks		i_din.ffs1_din.di_blocks
+#define	i_ffs1_ctime		i_din.ffs1_din.di_ctime
+#define	i_ffs1_ctimensec	i_din.ffs1_din.di_ctimensec
+#define	i_ffs1_db		i_din.ffs1_din.di_db
+#define	i_ffs1_flags		i_din.ffs1_din.di_flags
+#define	i_ffs1_gen		i_din.ffs1_din.di_gen
+#define	i_ffs11_gid		i_din.ffs1_din.di_gid
+#define	i_ffs1_ib		i_din.ffs1_din.di_ib
+#define	i_ffs1_mode		i_din.ffs1_din.di_mode
+#define	i_ffs1_mtime		i_din.ffs1_din.di_mtime
+#define	i_ffs1_mtimensec	i_din.ffs1_din.di_mtimensec
+#define	i_ffs1_nlink		i_din.ffs1_din.di_nlink
+#define	i_ffs1_rdev		i_din.ffs1_din.di_rdev
+#define	i_ffs1_shortlink	i_din.ffs1_din.db
+#define	i_ffs1_size		i_din.ffs1_din.di_size
+#define	i_ffs1_uid		i_din.ffs1_din.di_uid
+
+#undef DIP
+#define DIP(ip, field) (ip)->i_ffs1_##field
diff --git a/usr.sbin/makefs/makefs.8 b/usr.sbin/makefs/makefs.8
new file mode 100644
index 0000000000..416f6d1918
--- /dev/null
+++ b/usr.sbin/makefs/makefs.8
@@ -0,0 +1,411 @@
+.\"	$NetBSD: makefs.8,v 1.33 2011/05/22 21:51:39 christos Exp $
+.\"
+.\" Copyright (c) 2001-2003 Wasabi Systems, Inc.
+.\" All rights reserved.
+.\"
+.\" Written by Luke Mewburn for Wasabi Systems, Inc.
+.\"
+.\" 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. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"      This product includes software developed for the NetBSD Project by
+.\"      Wasabi Systems, Inc.
+.\" 4. The name of Wasabi Systems, Inc. may not be used to endorse
+.\"    or promote products derived from this software without specific prior
+.\"    written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+.\" 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: head/usr.sbin/makefs/makefs.8 303038 2016-07-19 18:40:54Z emaste $
+.\"
+.Dd September 4, 2016
+.Dt MAKEFS 8
+.Os
+.Sh NAME
+.Nm makefs
+.Nd create a file system image from a directory tree or a mtree manifest
+.Sh SYNOPSIS
+.Nm
+.Op Fl DxZ
+.Op Fl B Ar endian
+.Op Fl b Ar free-blocks
+.Op Fl d Ar debug-mask
+.Op Fl F Ar mtree-specfile
+.Op Fl f Ar free-files
+.Op Fl M Ar minimum-size
+.Op Fl m Ar maximum-size
+.Op Fl N Ar userdb-dir
+.Op Fl o Ar fs-options
+.Op Fl R Ar roundup-size
+.Op Fl S Ar sector-size
+.Op Fl s Ar image-size
+.Op Fl T Ar timestamp
+.Op Fl t Ar fs-type
+.Ar image-file
+.Ar directory | manifest
+.Op Ar extra-directory ...
+.Sh DESCRIPTION
+The utility
+.Nm
+creates a file system image into
+.Ar image-file
+from the directory tree
+.Ar directory
+or from the mtree manifest
+.Ar manifest .
+If any optional directory trees are passed in the
+.Ar extra-directory
+arguments, then the directory tree of each argument will be merged
+into the
+.Ar directory
+or
+.Ar manifest
+first before creating
+.Ar image-file .
+No special devices or privileges are required to perform this task.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl B Ar endian
+Set the byte order of the image to
+.Ar endian .
+Valid byte orders are
+.Ql 4321 ,
+.Ql big ,
+or
+.Ql be
+for big endian, and
+.Ql 1234 ,
+.Ql little ,
+or
+.Ql le
+for little endian.
+Some file systems may have a fixed byte order; in those cases this
+argument will be ignored.
+.It Fl b Ar free-blocks
+Ensure that a minimum of
+.Ar free-blocks
+free blocks exist in the image.
+An optional
+.Ql %
+suffix may be provided to indicate that
+.Ar free-blocks
+indicates a percentage of the calculated image size.
+.It Fl D
+Treat duplicate paths in an mtree manifest as warnings not error.
+.It Fl d Ar debug-mask
+Enable various levels of debugging, depending upon which bits are
+set in
+.Ar debug-mask .
+XXX: document these
+.It Fl F Ar mtree-specfile
+Use
+.Ar mtree-specfile
+as an
+.Xr mtree 8
+.Sq specfile
+specification.
+This option has no effect when the image is created from a mtree manifest
+rather than a directory.
+.Pp
+If a specfile entry exists in the underlying file system, its
+permissions and modification time will be used unless specifically
+overridden by the specfile.
+An error will be raised if the type of entry in the specfile
+conflicts with that of an existing entry.
+.Pp
+In the opposite case (where a specfile entry does not have an entry
+in the underlying file system) the following occurs:
+If the specfile entry is marked
+.Sy optional ,
+the specfile entry is ignored.
+Otherwise, the entry will be created in the image, and it is
+necessary to specify at least the following parameters in the
+specfile:
+.Sy type ,
+.Sy mode ,
+.Sy gname ,
+or
+.Sy gid ,
+and
+.Sy uname
+or
+.Sy uid ,
+and
+.Sy link
+(in the case of symbolic links).
+If
+.Sy time
+isn't provided, the current time will be used.
+If
+.Sy flags
+isn't provided, the current file flags will be used.
+Missing regular file entries will be created as zero-length files.
+.It Fl f Ar free-files
+Ensure that a minimum of
+.Ar free-files
+free files (inodes) exist in the image.
+An optional
+.Ql %
+suffix may be provided to indicate that
+.Ar free-files
+indicates a percentage of the calculated image size.
+.It Fl M Ar minimum-size
+Set the minimum size of the file system image to
+.Ar minimum-size .
+.It Fl m Ar maximum-size
+Set the maximum size of the file system image to
+.Ar maximum-size .
+An error will be raised if the target file system needs to be larger
+than this to accommodate the provided directory tree.
+.It Fl N Ar userdb-dir
+Use the user database text file
+.Pa master.passwd
+and group database text file
+.Pa group
+from
+.Ar userdb-dir ,
+rather than using the results from the system's
+.Xr getpwnam 3
+and
+.Xr getgrnam 3
+(and related) library calls.
+.It Fl o Ar fs-options
+Set file system specific options.
+.Ar fs-options
+is a comma separated list of options.
+Valid file system specific options are detailed below.
+.It Fl p
+Deprecated.
+See the
+.Fl Z
+flag.
+.It Fl R Ar roundup-size
+Round the image up to
+.Ar roundup-size .
+.Ar roundup-size
+should be a multiple of the file system block size.
+This option only applies to the
+.Sy ffs
+file system type.
+.It Fl S Ar sector-size
+Set the file system sector size to
+.Ar sector-size .
+.\" XXX: next line also true for cd9660?
+Defaults to 512.
+.It Fl s Ar image-size
+Set the size of the file system image to
+.Ar image-size .
+.It Fl T Ar timestamp
+Specify a timestamp to be set for all filesystem files and directories
+created so that repeatable builds are possible.
+The
+.Ar timestamp
+can be a
+.Pa pathname ,
+where the timestamps are derived from that file, or an integer
+value interpreted as the number of seconds from the Epoch.
+Note that timestamps specified in an
+.Xr mtree 5
+spec file, override the default timestamp.
+.It Fl t Ar fs-type
+Create an
+.Ar fs-type
+file system image.
+The following file system types are supported:
+.Bl -tag -width cd9660 -offset indent
+.It Sy ffs
+BSD fast file system (default).
+.It Sy cd9660
+ISO 9660 file system.
+.El
+.It Fl x
+Exclude file system nodes not explicitly listed in the specfile.
+.It Fl Z
+Create a sparse file for
+.Sy ffs .
+This is useful for virtual machine images.
+.El
+.Pp
+Where sizes are specified, a decimal number of bytes is expected.
+Two or more numbers may be separated by an
+.Dq x
+to indicate a product.
+Each number may have one of the following optional suffixes:
+.Bl -tag -width 3n -offset indent -compact
+.It b
+Block; multiply by 512
+.It k
+Kibi; multiply by 1024 (1 KiB)
+.It m
+Mebi; multiply by 1048576 (1 MiB)
+.It g
+Gibi; multiply by 1073741824 (1 GiB)
+.It t
+Tebi; multiply by 1099511627776 (1 TiB)
+.It w
+Word; multiply by the number of bytes in an integer
+.El
+.\"
+.\"
+.Ss FFS-specific options
+.Sy ffs
+images have ffs-specific optional parameters that may be provided.
+Each of the options consists of a keyword, an equal sign
+.Pq Ql = ,
+and a value.
+The following keywords are supported:
+.Pp
+.Bl -tag -width optimization -offset indent -compact
+.It Sy avgfilesize
+Expected average file size.
+.It Sy avgfpdir
+Expected number of files per directory.
+.It Sy bsize
+Block size.
+.It Sy density
+Bytes per inode.
+.It Sy fsize
+Fragment size.
+.It Sy label
+Label name of the image.
+.It Sy maxbpg
+Maximum blocks per file in a cylinder group.
+.It Sy minfree
+Minimum % free.
+.It Sy optimization
+Optimization preference; one of
+.Ql space
+or
+.Ql time .
+.It Sy maxbpcg
+Maximum total number of blocks in a cylinder group.
+.It Sy version
+UFS version.
+1 for FFS (default)
+.\", 2 for UFS2.
+.El
+.Ss CD9660-specific options
+.Sy cd9660
+images have ISO9660-specific optional parameters that may be
+provided.
+The arguments consist of a keyword and, optionally, an equal sign
+.Pq Ql = ,
+and a value.
+The following keywords are supported:
+.Pp
+.Bl -tag -width omit-trailing-period -offset indent -compact
+.It Sy allow-deep-trees
+Allow the directory structure to exceed the maximum specified in
+the spec.
+.It Sy allow-illegal-chars
+Allow illegal characters in filenames. This option is not implemented.
+.It Sy allow-lowercase
+Allow lowercase characters in filenames. This option is not implemented.
+.It Sy allow-max-name
+Allow 37 instead of 33 characters for filenames by omitting the
+version id.
+.It Sy allow-multidot
+Allow multiple dots in a filename.
+.It Sy applicationid
+Application ID of the image.
+.It Sy archimedes
+Use the
+.Ql ARCHIMEDES
+extension to encode
+.Tn RISC OS
+metadata.
+.It Sy bootimagedir
+Boot image directory. This option is not implemented.
+.It Sy chrp-boot
+Write an MBR partition table to the image to allow older CHRP hardware to
+boot.
+.It Sy boot-load-segment
+Set load segment for the boot image.
+.It Sy bootimage
+Filename of a boot image in the format
+.Dq sysid;filename ,
+where
+.Dq sysid
+is one of
+.Ql i386 ,
+.Ql mac68k ,
+.Ql macppc ,
+or
+.Ql powerpc .
+.It Sy generic-bootimage
+Load a generic boot image into the first 32K of the cd9660 image.
+.It Sy hard-disk-boot
+Boot image is a hard disk image.
+.It Sy isolevel
+An integer representing the ISO 9660 interchange level where
+.Dq level
+is either
+.Ql 1
+or
+.Ql 2 .
+.Dq level
+.Ql 3
+is not implemented.
+.It Sy keep-bad-images
+Do not discard images whose write was aborted due to an error.
+For debugging purposes.
+.It Sy label
+Label name of the image.
+.It Sy no-boot
+Boot image is not bootable.
+.It Sy no-emul-boot
+Boot image is a
+.Dq no emulation
+ElTorito image.
+.It Sy no-trailing-padding
+Do not pad the image (apparently Linux needs the padding).
+.It Sy omit-trailing-period
+Omit trailing periods in filenames.
+.It Sy preparer
+Preparer ID of the image.
+.It Sy publisher
+Publisher ID of the image.
+.It Sy rockridge
+Use RockRidge extensions (for longer filenames, etc.).
+.It Sy verbose
+Turns on verbose output.
+.It Sy volumeid
+Volume set identifier of the image.
+.El
+.Sh SEE ALSO
+.Xr mtree 5 ,
+.Xr mtree 8 ,
+.Xr newfs 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Nx 1.6 .
+.Sh AUTHORS
+.An Luke Mewburn
+.Aq Mt lukem@NetBSD.org
+(original program),
+.An Daniel Watt ,
+.An Walter Deignan ,
+.An Ryan Gabrys ,
+.An Alan Perez-Rathke ,
+.An Ram Vedam
+(cd9660 support)
diff --git a/usr.sbin/makefs/makefs.c b/usr.sbin/makefs/makefs.c
new file mode 100644
index 0000000000..2f67002818
--- /dev/null
+++ b/usr.sbin/makefs/makefs.c
@@ -0,0 +1,414 @@
+/*	$NetBSD: makefs.c,v 1.26 2006/10/22 21:11:56 christos Exp $	*/
+
+/*
+ * Copyright (c) 2001-2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/makefs.c 303036 2016-07-19 18:15:22Z emaste $");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "makefs.h"
+//#include "mtree.h"
+
+/*
+ * list of supported file systems and dispatch functions
+ */
+typedef struct {
+	const char	*type;
+	void		(*prepare_options)(fsinfo_t *);
+	int		(*parse_options)(const char *, fsinfo_t *);
+	void		(*cleanup_options)(fsinfo_t *);
+	void		(*make_fs)(const char *, const char *, fsnode *,
+				fsinfo_t *);
+} fstype_t;
+
+static fstype_t fstypes[] = {
+#define ENTRY(name) { \
+	# name, name ## _prep_opts, name ## _parse_opts, \
+	name ## _cleanup_opts, name ## _makefs  \
+}
+	ENTRY(ffs),
+	ENTRY(cd9660),
+	{ .type = NULL	},
+};
+
+u_int		debug;
+int		dupsok;
+struct timespec	start_time;
+struct stat	stampst;
+
+static	fstype_t *get_fstype(const char *);
+static	int	get_tstamp(const char *, struct stat *);
+static	void	usage(void);
+int		main(int, char *[]);
+
+int
+main(int argc, char *argv[])
+{
+	struct stat	 sb;
+	struct timeval	 start;
+	fstype_t	*fstype;
+	fsinfo_t	 fsoptions;
+	fsnode		*root;
+	int	 	 ch, i, len;
+	char		*subtree;
+	char		*specfile;
+
+	setprogname(argv[0]);
+
+	debug = 0;
+	if ((fstype = get_fstype(DEFAULT_FSTYPE)) == NULL)
+		errx(1, "Unknown default fs type `%s'.", DEFAULT_FSTYPE);
+
+		/* set default fsoptions */
+	(void)memset(&fsoptions, 0, sizeof(fsoptions));
+	fsoptions.fd = -1;
+	fsoptions.sectorsize = -1;
+
+	if (fstype->prepare_options)
+		fstype->prepare_options(&fsoptions);
+
+	specfile = NULL;
+	ch = gettimeofday(&start, NULL);
+
+	start_time.tv_sec = start.tv_sec;
+	start_time.tv_nsec = start.tv_usec * 1000;
+
+	if (ch == -1)
+		err(1, "Unable to get system time");
+
+
+	while ((ch = getopt(argc, argv, "B:b:Dd:f:F:M:m:N:o:pR:s:S:t:T:xZ")) != -1) {
+		switch (ch) {
+
+		case 'B':
+			if (strcmp(optarg, "be") == 0 ||
+			    strcmp(optarg, "4321") == 0 ||
+			    strcmp(optarg, "big") == 0) {
+#if BYTE_ORDER == LITTLE_ENDIAN
+				fsoptions.needswap = 1;
+#endif
+			} else if (strcmp(optarg, "le") == 0 ||
+			    strcmp(optarg, "1234") == 0 ||
+			    strcmp(optarg, "little") == 0) {
+#if BYTE_ORDER == BIG_ENDIAN
+				fsoptions.needswap = 1;
+#endif
+			} else {
+				warnx("Invalid endian `%s'.", optarg);
+				usage();
+			}
+			break;
+
+		case 'b':
+			len = strlen(optarg) - 1;
+			if (optarg[len] == '%') {
+				optarg[len] = '\0';
+				fsoptions.freeblockpc =
+				    strsuftoll("free block percentage",
+					optarg, 0, 99);
+			} else {
+				fsoptions.freeblocks =
+				    strsuftoll("free blocks",
+					optarg, 0, LLONG_MAX);
+			}
+			break;
+
+		case 'D':
+			dupsok = 1;
+			break;
+
+		case 'd':
+			debug = strtoll(optarg, NULL, 0);
+			break;
+
+		case 'f':
+			len = strlen(optarg) - 1;
+			if (optarg[len] == '%') {
+				optarg[len] = '\0';
+				fsoptions.freefilepc =
+				    strsuftoll("free file percentage",
+					optarg, 0, 99);
+			} else {
+				fsoptions.freefiles =
+				    strsuftoll("free files",
+					optarg, 0, LLONG_MAX);
+			}
+			break;
+
+//		case 'F':
+//			specfile = optarg;
+//			break;
+
+		case 'M':
+			fsoptions.minsize =
+			    strsuftoll("minimum size", optarg, 1LL, LLONG_MAX);
+			break;
+
+//		case 'N':
+//			if (! setup_getid(optarg))
+//				errx(1,
+//			    "Unable to use user and group databases in `%s'",
+//				    optarg);
+//			break;
+
+		case 'm':
+			fsoptions.maxsize =
+			    strsuftoll("maximum size", optarg, 1LL, LLONG_MAX);
+			break;
+			
+		case 'o':
+		{
+			char *p;
+
+			while ((p = strsep(&optarg, ",")) != NULL) {
+				if (*p == '\0')
+					errx(1, "Empty option");
+				if (! fstype->parse_options(p, &fsoptions))
+					usage();
+			}
+			break;
+		}
+		case 'p':
+			/* Deprecated in favor of 'Z' */
+			fsoptions.sparse = 1;
+			break;
+
+		case 'R':
+			/* Round image size up to specified block size */
+			fsoptions.roundup =
+			    strsuftoll("roundup-size", optarg, 0, LLONG_MAX);
+			break;
+
+		case 's':
+			fsoptions.minsize = fsoptions.maxsize =
+			    strsuftoll("size", optarg, 1LL, LLONG_MAX);
+			break;
+
+		case 'S':
+			fsoptions.sectorsize =
+			    (int)strsuftoll("sector size", optarg,
+				1LL, INT_MAX);
+			break;
+
+		case 't':
+			/* Check current one and cleanup if necessary. */
+			if (fstype->cleanup_options)
+				fstype->cleanup_options(&fsoptions);
+			fsoptions.fs_specific = NULL;
+			if ((fstype = get_fstype(optarg)) == NULL)
+				errx(1, "Unknown fs type `%s'.", optarg);
+			fstype->prepare_options(&fsoptions);
+			break;
+
+		case 'T':
+			if (get_tstamp(optarg, &stampst) == -1)
+				errx(1, "Cannot get timestamp from `%s'",
+				    optarg);
+			break;
+
+		case 'x':
+			fsoptions.onlyspec = 1;
+			break;
+
+		case 'Z':
+			/* Superscedes 'p' for compatibility with NetBSD makefs(8) */
+			fsoptions.sparse = 1;
+			break;
+
+		case '?':
+		default:
+			usage();
+			/* NOTREACHED */
+
+		}
+	}
+	if (debug) {
+		printf("debug mask: 0x%08x\n", debug);
+		printf("start time: %ld.%ld, %s",
+		    (long)start_time.tv_sec, (long)start_time.tv_nsec,
+		    ctime(&start_time.tv_sec));
+	}
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 2)
+		usage();
+
+//	/* -x must be accompanied by -F */
+//	if (fsoptions.onlyspec != 0 && specfile == NULL)
+//		errx(1, "-x requires -F mtree-specfile.");
+
+	/* Accept '-' as meaning "read from standard input". */
+	if (strcmp(argv[1], "-") == 0)
+		sb.st_mode = S_IFREG;
+	else {
+		if (stat(argv[1], &sb) == -1)
+			err(1, "Can't stat `%s'", argv[1]);
+	}
+
+	switch (sb.st_mode & S_IFMT) {
+	case S_IFDIR:		/* walk the tree */
+		subtree = argv[1];
+		TIMER_START(start);
+		root = walk_dir(subtree, ".", NULL, NULL);
+		TIMER_RESULTS(start, "walk_dir");
+		break;
+//	case S_IFREG:		/* read the manifest file */
+//		subtree = ".";
+//		TIMER_START(start);
+//		root = read_mtree(argv[1], NULL);
+//		TIMER_RESULTS(start, "manifest");
+//		break;
+	default:
+		errx(1, "%s: not a file or directory", argv[1]);
+		/* NOTREACHED */
+	}
+
+	/* append extra directory */
+	for (i = 2; i < argc; i++) {
+		if (stat(argv[i], &sb) == -1)
+			err(1, "Can't stat `%s'", argv[i]);
+		if (!S_ISDIR(sb.st_mode))
+			errx(1, "%s: not a directory", argv[i]);
+		TIMER_START(start);
+		root = walk_dir(argv[i], ".", NULL, root);
+		TIMER_RESULTS(start, "walk_dir2");
+	}
+
+//	if (specfile) {		/* apply a specfile */
+//		TIMER_START(start);
+//		apply_specfile(specfile, subtree, root, fsoptions.onlyspec);
+//		TIMER_RESULTS(start, "apply_specfile");
+//	}
+
+	if (debug & DEBUG_DUMP_FSNODES) {
+		printf("\nparent: %s\n", subtree);
+		dump_fsnodes(root);
+		putchar('\n');
+	}
+
+				/* build the file system */
+	TIMER_START(start);
+	fstype->make_fs(argv[0], subtree, root, &fsoptions);
+	TIMER_RESULTS(start, "make_fs");
+
+	free_fsnodes(root);
+
+	exit(0);
+	/* NOTREACHED */
+}
+
+
+int
+set_option(option_t *options, const char *var, const char *val)
+{
+	int	i;
+
+	for (i = 0; options[i].name != NULL; i++) {
+		if (strcmp(options[i].name, var) != 0)
+			continue;
+		*options[i].value = (int)strsuftoll(options[i].desc, val,
+		    options[i].minimum, options[i].maximum);
+		return (1);
+	}
+	warnx("Unknown option `%s'", var);
+	return (0);
+}
+
+
+static fstype_t *
+get_fstype(const char *type)
+{
+	int i;
+	
+	for (i = 0; fstypes[i].type != NULL; i++)
+		if (strcmp(fstypes[i].type, type) == 0)
+			return (&fstypes[i]);
+	return (NULL);
+}
+
+static int
+get_tstamp(const char *b, struct stat *st)
+{
+	time_t when;
+	char *eb;
+	long long l;
+
+	if (stat(b, st) != -1)
+		return 0;
+
+	{
+		errno = 0;
+		l = strtoll(b, &eb, 0);
+		if (b == eb || *eb || errno)
+			return -1;
+		when = (time_t)l;
+	}
+
+	st->st_ino = 1;
+#ifdef HAVE_STRUCT_STAT_BIRTHTIME
+	st->st_birthtime =
+#endif
+	st->st_mtime = st->st_ctime = st->st_atime = when;
+	return 0;
+}
+
+static void
+usage(void)
+{
+	const char *prog;
+
+	prog = getprogname();
+	fprintf(stderr,
+"usage: %s [-xZ] [-B endian] [-b free-blocks] [-d debug-mask]\n"
+"\t[-F mtree-specfile] [-f free-files] [-M minimum-size] [-m maximum-size]\n"
+"\t[-N userdb-dir] [-o fs-options] [-R roundup-size] [-S sector-size]\n"
+"\t[-s image-size] [-T <timestamp/file>] [-t fs-type]\n"
+"\timage-file directory | manifest [extra-directory ...]\n",
+	    prog);
+	exit(1);
+}
diff --git a/usr.sbin/makefs/makefs.h b/usr.sbin/makefs/makefs.h
new file mode 100644
index 0000000000..3845540ed4
--- /dev/null
+++ b/usr.sbin/makefs/makefs.h
@@ -0,0 +1,285 @@
+/*	$NetBSD: makefs.h,v 1.20 2008/12/28 21:51:46 christos Exp $	*/
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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: head/usr.sbin/makefs/makefs.h 301879 2016-06-14 14:03:28Z emaste $
+ */
+
+#ifndef	_MAKEFS_H
+#define	_MAKEFS_H
+
+#include <sys/stat.h>
+#include <err.h>
+
+/*
+ * fsnode -
+ *	a component of the tree; contains a filename, a pointer to
+ *	fsinode, optional symlink name, and tree pointers
+ *
+ * fsinode - 
+ *	equivalent to an inode, containing target file system inode number,
+ *	refcount (nlink), and stat buffer
+ *
+ * A tree of fsnodes looks like this:
+ *
+ *	name	"."		"bin"		"netbsd"
+ *	type	S_IFDIR		S_IFDIR		S_IFREG
+ *	next 	  >		  >		NULL
+ *	parent	NULL		NULL		NULL
+ *	child	NULL		  v
+ *
+ *	name			"."		"ls"
+ *	type			S_IFDIR		S_IFREG
+ *	next			  >		NULL
+ *	parent			  ^		^ (to "bin")
+ *	child			NULL		NULL
+ *
+ * Notes:
+ *	-   first always points to first entry, at current level, which
+ *	    must be "." when the tree has been built; during build it may
+ *	    not be if "." hasn't yet been found by readdir(2).
+ */
+
+enum fi_flags {
+	FI_SIZED =	1<<0,		/* inode sized */
+	FI_ALLOCATED =	1<<1,		/* fsinode->ino allocated */
+	FI_WRITTEN =	1<<2,		/* inode written */
+};
+
+typedef struct {
+	uint32_t	 ino;		/* inode number used on target fs */
+	uint32_t	 nlink;		/* number of links to this entry */
+	enum fi_flags	 flags;		/* flags used by fs specific code */
+	struct stat	 st;		/* stat entry */
+} fsinode;
+
+typedef struct _fsnode {
+	struct _fsnode	*parent;	/* parent (NULL if root) */
+	struct _fsnode	*child;		/* child (if type == S_IFDIR) */
+	struct _fsnode	*next;		/* next */
+	struct _fsnode	*first;		/* first node of current level (".") */
+	uint32_t	 type;		/* type of entry */
+	fsinode		*inode;		/* actual inode data */
+	char		*symlink;	/* symlink target */
+	char		*contents;	/* file to provide contents */
+	const char	*root;		/* root path */
+	char		*path;		/* directory name */
+	char		*name;		/* file name */
+	int		flags;		/* misc flags */
+} fsnode;
+
+#define	FSNODE_F_HASSPEC	0x01	/* fsnode has a spec entry */
+#define	FSNODE_F_OPTIONAL	0x02	/* fsnode is optional */
+
+/*
+ * fsinfo_t - contains various settings and parameters pertaining to
+ * the image, including current settings, global options, and fs
+ * specific options
+ */
+typedef struct {
+		/* current settings */
+	off_t	size;		/* total size */
+	off_t	inodes;		/* number of inodes */
+	uint32_t curinode;	/* current inode */
+
+		/* image settings */
+	int	fd;		/* file descriptor of image */
+	void	*superblock;	/* superblock */
+	int	onlyspec;	/* only add entries in specfile */
+
+
+		/* global options */
+	off_t	minsize;	/* minimum size image should be */
+	off_t	maxsize;	/* maximum size image can be */
+	off_t	freefiles;	/* free file entries to leave */
+	int	freefilepc;	/* free file % */
+	off_t	freeblocks;	/* free blocks to leave */
+	int	freeblockpc;	/* free block % */
+	int	needswap;	/* non-zero if byte swapping needed */
+	int	sectorsize;	/* sector size */
+	int	sparse;		/* sparse image, don't fill it with zeros */
+	off_t	roundup;	/* round image size up to this value */
+
+	void	*fs_specific;	/* File system specific additions. */
+} fsinfo_t;
+
+
+/*
+ * option_t - contains option name, description, pointer to location to store
+ * result, and range checks for the result. Used to simplify fs specific
+ * option setting
+ */
+typedef struct {
+	const char	*name;		/* option name */
+	int		*value;		/* where to stuff the value */
+	int		minimum;	/* minimum for value */
+	int		maximum;	/* maximum for value */
+	const char	*desc;		/* option description */
+} option_t;
+
+
+void		apply_specfile(const char *, const char *, fsnode *, int);
+void		dump_fsnodes(fsnode *);
+const char *	inode_type(mode_t);
+fsnode *	read_mtree(const char *, fsnode *);
+int		set_option(option_t *, const char *, const char *);
+fsnode *	walk_dir(const char *, const char *, fsnode *, fsnode *);
+void		free_fsnodes(fsnode *);
+
+void		ffs_prep_opts(fsinfo_t *);
+int		ffs_parse_opts(const char *, fsinfo_t *);
+void		ffs_cleanup_opts(fsinfo_t *);
+void		ffs_makefs(const char *, const char *, fsnode *, fsinfo_t *);
+
+void		cd9660_prep_opts(fsinfo_t *);
+int		cd9660_parse_opts(const char *, fsinfo_t *);
+void		cd9660_cleanup_opts(fsinfo_t *);
+void		cd9660_makefs(const char *, const char *, fsnode *, fsinfo_t *);
+
+
+extern	u_int		debug;
+extern	int		dupsok;
+extern	struct timespec	start_time;
+extern struct stat stampst;
+
+/*
+ * If -x is specified, we want to exclude nodes which do not appear
+ * in the spec file.
+ */
+#define	FSNODE_EXCLUDE_P(opts, fsnode)	\
+	((opts)->onlyspec != 0 && ((fsnode)->flags & FSNODE_F_HASSPEC) == 0)
+
+#define	DEBUG_TIME			0x00000001
+		/* debug bits 1..3 unused at this time */
+#define	DEBUG_WALK_DIR			0x00000010
+#define	DEBUG_WALK_DIR_NODE		0x00000020
+#define	DEBUG_WALK_DIR_LINKCHECK	0x00000040
+#define	DEBUG_DUMP_FSNODES		0x00000080
+#define	DEBUG_DUMP_FSNODES_VERBOSE	0x00000100
+#define	DEBUG_FS_PARSE_OPTS		0x00000200
+#define	DEBUG_FS_MAKEFS			0x00000400
+#define	DEBUG_FS_VALIDATE		0x00000800
+#define	DEBUG_FS_CREATE_IMAGE		0x00001000
+#define	DEBUG_FS_SIZE_DIR		0x00002000
+#define	DEBUG_FS_SIZE_DIR_NODE		0x00004000
+#define	DEBUG_FS_SIZE_DIR_ADD_DIRENT	0x00008000
+#define	DEBUG_FS_POPULATE		0x00010000
+#define	DEBUG_FS_POPULATE_DIRBUF	0x00020000
+#define	DEBUG_FS_POPULATE_NODE		0x00040000
+#define	DEBUG_FS_WRITE_FILE		0x00080000
+#define	DEBUG_FS_WRITE_FILE_BLOCK	0x00100000
+#define	DEBUG_FS_MAKE_DIRBUF		0x00200000
+#define	DEBUG_FS_WRITE_INODE		0x00400000
+#define	DEBUG_BUF_BREAD			0x00800000
+#define	DEBUG_BUF_BWRITE		0x01000000
+#define	DEBUG_BUF_GETBLK		0x02000000
+#define	DEBUG_APPLY_SPECFILE		0x04000000
+#define	DEBUG_APPLY_SPECENTRY		0x08000000
+#define	DEBUG_APPLY_SPECONLY		0x10000000
+
+
+#define	TIMER_START(x)				\
+	if (debug & DEBUG_TIME)			\
+		gettimeofday(&(x), NULL)
+
+#define	TIMER_RESULTS(x,d)				\
+	if (debug & DEBUG_TIME) {			\
+		struct timeval end, td;			\
+		gettimeofday(&end, NULL);		\
+		timersub(&end, &(x), &td);		\
+		printf("%s took %lld.%06ld seconds\n",	\
+		    (d), (long long)td.tv_sec,		\
+		    (long)td.tv_usec);			\
+	}
+
+
+#ifndef	DEFAULT_FSTYPE
+#define	DEFAULT_FSTYPE	"ffs"
+#endif
+
+
+/*
+ *	ffs specific settings
+ *	---------------------
+ */
+
+#define	FFS_EI		/* for opposite endian support in ffs headers */
+
+/*
+ * Write-arounds/compat shims for endian-agnostic support.
+ * These belong in the kernel if/when it's possible to mount
+ * filesystems w/ either byte order.
+ */
+
+/*
+ * File system internal flags, also in fs_flags.
+ * (Pick highest number to avoid conflicts with others)
+ */
+#define        FS_SWAPPED      0x80000000      /* file system is endian swapped */
+#define        FS_INTERNAL     0x80000000      /* mask for internal flags */
+
+#define        FS_ISCLEAN      1
+
+#define        DINODE1_SIZE    (sizeof(struct ufs1_dinode))
+
+#define MAXSYMLINKLEN_UFS1     ((NDADDR + NIADDR) * sizeof(ufs_daddr_t))
+
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+#define DIRSIZ_SWAP(oldfmt, dp, needswap)      \
+    (((oldfmt) && !(needswap)) ?       \
+    DIRECTSIZ((dp)->d_type) : DIRECTSIZ((dp)->d_namlen))
+#else
+#define DIRSIZ_SWAP(oldfmt, dp, needswap)      \
+    (((oldfmt) && (needswap)) ?                \
+    DIRECTSIZ((dp)->d_type) : DIRECTSIZ((dp)->d_namlen))
+#endif
+
+#define        cg_chkmagic_swap(cgp, ns) \
+    (ufs_rw32((cgp)->cg_magic, (ns)) == CG_MAGIC)
+#define        cg_inosused_swap(cgp, ns) \
+    ((u_int8_t *)((u_int8_t *)(cgp) + ufs_rw32((cgp)->cg_iusedoff, (ns))))
+#define        cg_blksfree_swap(cgp, ns) \
+    ((u_int8_t *)((u_int8_t *)(cgp) + ufs_rw32((cgp)->cg_freeoff, (ns))))
+#define        cg_clustersfree_swap(cgp, ns) \
+    ((u_int8_t *)((u_int8_t *)(cgp) + ufs_rw32((cgp)->cg_clusteroff, (ns))))
+#define        cg_clustersum_swap(cgp, ns) \
+    ((int32_t *)((uintptr_t)(cgp) + ufs_rw32((cgp)->cg_clustersumoff, ns)))
+
+struct fs;
+void   ffs_fragacct_swap(struct fs *, int, int32_t [], int, int);
+
+fsinode *link_check(fsinode *);
+
+#endif	/* _MAKEFS_H */
diff --git a/usr.sbin/makefs/mtree.c b/usr.sbin/makefs/mtree.c
new file mode 100644
index 0000000000..997ab12cdd
--- /dev/null
+++ b/usr.sbin/makefs/mtree.c
@@ -0,0 +1,1137 @@
+/*-
+ * Copyright (c) 2011 Marcel Moolenaar
+ * 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(S) ``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(S) 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>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/mtree.c 307927 2016-10-25 16:29:15Z marcel $");
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/sbuf.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <inttypes.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "makefs.h"
+
+#ifndef ENOATTR
+#define	ENOATTR	ENOMSG
+#endif
+
+#define	IS_DOT(nm)	((nm)[0] == '.' && (nm)[1] == '\0')
+#define	IS_DOTDOT(nm)	((nm)[0] == '.' && (nm)[1] == '.' && (nm)[2] == '\0')
+
+struct mtree_fileinfo {
+	SLIST_ENTRY(mtree_fileinfo) next;
+	FILE *fp;
+	const char *name;
+	u_int line;
+};
+
+/* Global state used while parsing. */
+static SLIST_HEAD(, mtree_fileinfo) mtree_fileinfo =
+    SLIST_HEAD_INITIALIZER(mtree_fileinfo);
+static fsnode *mtree_root;
+static fsnode *mtree_current;
+static fsnode mtree_global;
+static fsinode mtree_global_inode;
+static u_int errors, warnings;
+
+static void mtree_error(const char *, ...) __printflike(1, 2);
+static void mtree_warning(const char *, ...) __printflike(1, 2);
+
+static int
+mtree_file_push(const char *name, FILE *fp)
+{
+	struct mtree_fileinfo *fi;
+
+	fi = malloc(sizeof(*fi));
+	if (fi == NULL)
+		return (ENOMEM);
+
+	if (strcmp(name, "-") == 0)
+		fi->name = strdup("(stdin)");
+	else
+		fi->name = strdup(name);
+	if (fi->name == NULL) {
+		free(fi);
+		return (ENOMEM);
+	}
+
+	fi->fp = fp;
+	fi->line = 0;
+
+	SLIST_INSERT_HEAD(&mtree_fileinfo, fi, next);
+	return (0);
+}
+
+static void
+mtree_print(const char *msgtype, const char *fmt, va_list ap)
+{
+	struct mtree_fileinfo *fi;
+
+	if (msgtype != NULL) {
+		fi = SLIST_FIRST(&mtree_fileinfo);
+		if (fi != NULL)
+			fprintf(stderr, "%s:%u: ", fi->name, fi->line);
+		fprintf(stderr, "%s: ", msgtype);
+	}
+	vfprintf(stderr, fmt, ap);
+}
+
+static void
+mtree_error(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	mtree_print("error", fmt, ap);
+	va_end(ap);
+
+	errors++;
+	fputc('\n', stderr);
+}
+
+static void
+mtree_warning(const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	mtree_print("warning", fmt, ap);
+	va_end(ap);
+
+	warnings++;
+	fputc('\n', stderr);
+}
+
+#ifndef MAKEFS_MAX_TREE_DEPTH
+# define MAKEFS_MAX_TREE_DEPTH (MAXPATHLEN/2)
+#endif
+
+/* construct path to node->name */
+static char *
+mtree_file_path(fsnode *node)
+{
+	fsnode *pnode;
+	struct sbuf *sb;
+	char *res, *rp[MAKEFS_MAX_TREE_DEPTH];
+	int depth;
+
+	depth = 0;
+	rp[depth] = node->name;
+	for (pnode = node->parent; pnode && depth < MAKEFS_MAX_TREE_DEPTH - 1;
+	     pnode = pnode->parent) {
+		if (strcmp(pnode->name, ".") == 0)
+			break;
+		rp[++depth] = pnode->name;
+	}
+	
+	sb = sbuf_new_auto();
+	if (sb == NULL) {
+		errno = ENOMEM;
+		return (NULL);
+	}
+	while (depth > 0) {
+		sbuf_cat(sb, rp[depth--]);
+		sbuf_putc(sb, '/');
+	}
+	sbuf_cat(sb, rp[depth]);
+	sbuf_finish(sb);
+	res = strdup(sbuf_data(sb));
+	sbuf_delete(sb);
+	if (res == NULL)
+		errno = ENOMEM;
+	return res;
+
+}
+
+/* mtree_resolve() sets errno to indicate why NULL was returned. */
+static char *
+mtree_resolve(const char *spec, int *istemp)
+{
+	struct sbuf *sb;
+	char *res, *var = NULL;
+	const char *base, *p, *v;
+	size_t len;
+	int c, error, quoted, subst;
+
+	len = strlen(spec);
+	if (len == 0) {
+		errno = EINVAL;
+		return (NULL);
+	}
+
+	c = (len > 1) ? (spec[0] == spec[len - 1]) ? spec[0] : 0 : 0;
+	*istemp = (c == '`') ? 1 : 0;
+	subst = (c == '`' || c == '"') ? 1 : 0;
+	quoted = (subst || c == '\'') ? 1 : 0;
+
+	if (!subst) {
+		res = strdup(spec + quoted);
+		if (res != NULL && quoted)
+			res[len - 2] = '\0';
+		return (res);
+	}
+
+	sb = sbuf_new_auto();
+	if (sb == NULL) {
+		errno = ENOMEM;
+		return (NULL);
+	}
+
+	base = spec + 1;
+	len -= 2;
+	error = 0;
+	while (len > 0) {
+		p = strchr(base, '$');
+		if (p == NULL) {
+			sbuf_bcat(sb, base, len);
+			base += len;
+			len = 0;
+			continue;
+		}
+		/* The following is safe. spec always starts with a quote. */
+		if (p[-1] == '\\')
+			p--;
+		if (base != p) {
+			sbuf_bcat(sb, base, p - base);
+			len -= p - base;
+			base = p;
+		}
+		if (*p == '\\') {
+			sbuf_putc(sb, '$');
+			base += 2;
+			len -= 2;
+			continue;
+		}
+		/* Skip the '$'. */
+		base++;
+		len--;
+		/* Handle ${X} vs $X. */
+		v = base;
+		if (*base == '{') {
+			p = strchr(v, '}');
+			if (p == NULL)
+				p = v;
+		} else
+			p = v;
+		len -= (p + 1) - base;
+		base = p + 1;
+
+		if (v == p) {
+			sbuf_putc(sb, *v);
+			continue;
+		}
+
+		error = ENOMEM;
+		var = calloc(p - v, 1);
+		if (var == NULL)
+			break;
+
+		memcpy(var, v + 1, p - v - 1);
+		if (strcmp(var, ".CURDIR") == 0) {
+			res = getcwd(NULL, 0);
+			if (res == NULL)
+				break;
+		} else if (strcmp(var, ".PROG") == 0) {
+			res = strdup(getprogname());
+			if (res == NULL)
+				break;
+		} else {
+			v = getenv(var);
+			if (v != NULL) {
+				res = strdup(v);
+				if (res == NULL)
+					break;
+			} else
+				res = NULL;
+		}
+		error = 0;
+
+		if (res != NULL) {
+			sbuf_cat(sb, res);
+			free(res);
+		}
+		free(var);
+		var = NULL;
+	}
+
+	free(var);
+	sbuf_finish(sb);
+	res = (error == 0) ? strdup(sbuf_data(sb)) : NULL;
+	sbuf_delete(sb);
+	if (res == NULL)
+		errno = ENOMEM;
+	return (res);
+}
+
+static int
+skip_over(FILE *fp, const char *cs)
+{
+	int c;
+
+	c = getc(fp);
+	while (c != EOF && strchr(cs, c) != NULL)
+		c = getc(fp);
+	if (c != EOF) {
+		ungetc(c, fp);
+		return (0);
+	}
+	return (ferror(fp) ? errno : -1);
+}
+
+static int
+skip_to(FILE *fp, const char *cs)
+{
+	int c;
+
+	c = getc(fp);
+	while (c != EOF && strchr(cs, c) == NULL)
+		c = getc(fp);
+	if (c != EOF) {
+		ungetc(c, fp);
+		return (0);
+	}
+	return (ferror(fp) ? errno : -1);
+}
+
+static int
+read_word(FILE *fp, char *buf, size_t bufsz)
+{
+	struct mtree_fileinfo *fi;
+	size_t idx, qidx;
+	int c, done, error, esc, qlvl;
+
+	if (bufsz == 0)
+		return (EINVAL);
+
+	done = 0;
+	esc = 0;
+	idx = 0;
+	qidx = -1;
+	qlvl = 0;
+	do {
+		c = getc(fp);
+		switch (c) {
+		case EOF:
+			buf[idx] = '\0';
+			error = ferror(fp) ? errno : -1;
+			if (error == -1)
+				mtree_error("unexpected end of file");
+			return (error);
+		case '#':		/* comment -- skip to end of line. */
+			if (!esc) {
+				error = skip_to(fp, "\n");
+				if (!error)
+					continue;
+			}
+			break;
+		case '\\':
+			esc++;
+			if (esc == 1)
+				continue;
+			break;
+		case '`':
+		case '\'':
+		case '"':
+			if (esc)
+				break;
+			if (qlvl == 0) {
+				qlvl++;
+				qidx = idx;
+			} else if (c == buf[qidx]) {
+				qlvl--;
+				if (qlvl > 0) {
+					do {
+						qidx--;
+					} while (buf[qidx] != '`' &&
+					    buf[qidx] != '\'' &&
+					    buf[qidx] != '"');
+				} else
+					qidx = -1;
+			} else {
+				qlvl++;
+				qidx = idx;
+			}
+			break;
+		case ' ':
+		case '\t':
+		case '\n':
+			if (!esc && qlvl == 0) {
+				ungetc(c, fp);
+				c = '\0';
+				done = 1;
+				break;
+			}
+			if (c == '\n') {
+				/*
+				 * We going to eat the newline ourselves.
+				 */
+				if (qlvl > 0)
+					mtree_warning("quoted word straddles "
+					    "onto next line.");
+				fi = SLIST_FIRST(&mtree_fileinfo);
+				fi->line++;
+			}
+			break;
+		case 'a':
+			if (esc)
+				c = '\a';
+			break;
+		case 'b':
+			if (esc)
+				c = '\b';
+			break;
+		case 'f':
+			if (esc)
+				c = '\f';
+			break;
+		case 'n':
+			if (esc)
+				c = '\n';
+			break;
+		case 'r':
+			if (esc)
+				c = '\r';
+			break;
+		case 't':
+			if (esc)
+				c = '\t';
+			break;
+		case 'v':
+			if (esc)
+				c = '\v';
+			break;
+		}
+		buf[idx++] = c;
+		esc = 0;
+	} while (idx < bufsz && !done);
+
+	if (idx >= bufsz) {
+		mtree_error("word too long to fit buffer (max %zu characters)",
+		    bufsz);
+		skip_to(fp, " \t\n");
+	}
+	return (0);
+}
+
+static fsnode *
+create_node(const char *name, u_int type, fsnode *parent, fsnode *global)
+{
+	fsnode *n;
+
+	n = calloc(1, sizeof(*n));
+	if (n == NULL)
+		return (NULL);
+
+	n->name = strdup(name);
+	if (n->name == NULL) {
+		free(n);
+		return (NULL);
+	}
+
+	n->type = (type == 0) ? global->type : type;
+	n->parent = parent;
+
+	n->inode = calloc(1, sizeof(*n->inode));
+	if (n->inode == NULL) {
+		free(n->name);
+		free(n);
+		return (NULL);
+	}
+
+	/* Assign global options/defaults. */
+	bcopy(global->inode, n->inode, sizeof(*n->inode));
+	n->inode->st.st_mode = (n->inode->st.st_mode & ~S_IFMT) | n->type;
+
+	if (n->type == S_IFLNK)
+		n->symlink = global->symlink;
+	else if (n->type == S_IFREG)
+		n->contents = global->contents;
+
+	return (n);
+}
+
+static void
+destroy_node(fsnode *n)
+{
+
+	assert(n != NULL);
+	assert(n->name != NULL);
+	assert(n->inode != NULL);
+
+	free(n->inode);
+	free(n->name);
+	free(n);
+}
+
+static int
+read_number(const char *tok, u_int base, intmax_t *res, intmax_t min,
+    intmax_t max)
+{
+	char *end;
+	intmax_t val;
+
+	val = strtoimax(tok, &end, base);
+	if (end == tok || end[0] != '\0')
+		return (EINVAL);
+	if (val < min || val > max)
+		return (EDOM);
+	*res = val;
+	return (0);
+}
+
+static int
+read_mtree_keywords(FILE *fp, fsnode *node)
+{
+	char keyword[PATH_MAX];
+	char *name, *p, *value;
+	gid_t gid;
+	uid_t uid;
+	struct stat *st, sb;
+	intmax_t num;
+	u_long flset, flclr;
+	int error, istemp, type;
+
+	st = &node->inode->st;
+	do {
+		error = skip_over(fp, " \t");
+		if (error)
+			break;
+
+		error = read_word(fp, keyword, sizeof(keyword));
+		if (error)
+			break;
+
+		if (keyword[0] == '\0')
+			break;
+
+		value = strchr(keyword, '=');
+		if (value != NULL)
+			*value++ = '\0';
+
+		/*
+		 * We use EINVAL, ENOATTR, ENOSYS and ENXIO to signal
+		 * certain conditions:
+		 *   EINVAL -	Value provided for a keyword that does
+		 *		not take a value. The value is ignored.
+		 *   ENOATTR -	Value missing for a keyword that needs
+		 *		a value. The keyword is ignored.
+		 *   ENOSYS -	Unsupported keyword encountered. The
+		 *		keyword is ignored.
+		 *   ENXIO -	Value provided for a keyword that does
+		 *		not take a value. The value is ignored.
+		 */
+		switch (keyword[0]) {
+		case 'c':
+			if (strcmp(keyword, "contents") == 0) {
+				if (value == NULL) {
+					error = ENOATTR;
+					break;
+				}
+				node->contents = strdup(value);
+			} else
+				error = ENOSYS;
+			break;
+		case 'f':
+			if (strcmp(keyword, "flags") == 0) {
+				if (value == NULL) {
+					error = ENOATTR;
+					break;
+				}
+				flset = flclr = 0;
+				if (!strtofflags(&value, &flset, &flclr)) {
+					st->st_flags &= ~flclr;
+					st->st_flags |= flset;
+				} else
+					error = errno;
+			} else
+				error = ENOSYS;
+			break;
+		case 'g':
+			if (strcmp(keyword, "gid") == 0) {
+				if (value == NULL) {
+					error = ENOATTR;
+					break;
+				}
+				error = read_number(value, 10, &num,
+				    0, UINT_MAX);
+				if (!error)
+					st->st_gid = num;
+			} else if (strcmp(keyword, "gname") == 0) {
+				if (value == NULL) {
+					error = ENOATTR;
+					break;
+				}
+				if (gid_from_group(value, &gid) == 0)
+					st->st_gid = gid;
+				else
+					error = EINVAL;
+			} else
+				error = ENOSYS;
+			break;
+		case 'l':
+			if (strcmp(keyword, "link") == 0) {
+				if (value == NULL) {
+					error = ENOATTR;
+					break;
+				}
+				node->symlink = strdup(value);
+			} else
+				error = ENOSYS;
+			break;
+		case 'm':
+			if (strcmp(keyword, "mode") == 0) {
+				if (value == NULL) {
+					error = ENOATTR;
+					break;
+				}
+				if (value[0] >= '0' && value[0] <= '9') {
+					error = read_number(value, 8, &num,
+					    0, 07777);
+					if (!error) {
+						st->st_mode &= S_IFMT;
+						st->st_mode |= num;
+					}
+				} else {
+					/* Symbolic mode not supported. */
+					error = EINVAL;
+					break;
+				}
+			} else
+				error = ENOSYS;
+			break;
+		case 'o':
+			if (strcmp(keyword, "optional") == 0) {
+				if (value != NULL)
+					error = ENXIO;
+				node->flags |= FSNODE_F_OPTIONAL;
+			} else
+				error = ENOSYS;
+			break;
+		case 's':
+			if (strcmp(keyword, "size") == 0) {
+				if (value == NULL) {
+					error = ENOATTR;
+					break;
+				}
+				error = read_number(value, 10, &num,
+				    0, INTMAX_MAX);
+				if (!error)
+					st->st_size = num;
+			} else
+				error = ENOSYS;
+			break;
+		case 't':
+			if (strcmp(keyword, "time") == 0) {
+				if (value == NULL) {
+					error = ENOATTR;
+					break;
+				}
+				p = strchr(value, '.');
+				if (p != NULL)
+					*p++ = '\0';
+				error = read_number(value, 10, &num, 0,
+				    INTMAX_MAX);
+				if (error)
+					break;
+				st->st_atime = num;
+				st->st_ctime = num;
+				st->st_mtime = num;
+				if (p == NULL)
+					break;
+				error = read_number(p, 10, &num, 0,
+				    INTMAX_MAX);
+				if (error)
+					break;
+				if (num != 0)
+					error = EINVAL;
+			} else if (strcmp(keyword, "type") == 0) {
+				if (value == NULL) {
+					error = ENOATTR;
+					break;
+				}
+				if (strcmp(value, "dir") == 0)
+					node->type = S_IFDIR;
+				else if (strcmp(value, "file") == 0)
+					node->type = S_IFREG;
+				else if (strcmp(value, "link") == 0)
+					node->type = S_IFLNK;
+				else
+					error = EINVAL;
+			} else
+				error = ENOSYS;
+			break;
+		case 'u':
+			if (strcmp(keyword, "uid") == 0) {
+				if (value == NULL) {
+					error = ENOATTR;
+					break;
+				}
+				error = read_number(value, 10, &num,
+				    0, UINT_MAX);
+				if (!error)
+					st->st_uid = num;
+			} else if (strcmp(keyword, "uname") == 0) {
+				if (value == NULL) {
+					error = ENOATTR;
+					break;
+				}
+				if (uid_from_user(value, &uid) == 0)
+					st->st_uid = uid;
+				else
+					error = EINVAL;
+			} else
+				error = ENOSYS;
+			break;
+		default:
+			error = ENOSYS;
+			break;
+		}
+
+		switch (error) {
+		case EINVAL:
+			mtree_error("%s: invalid value '%s'", keyword, value);
+			break;
+		case ENOATTR:
+			mtree_error("%s: keyword needs a value", keyword);
+			break;
+		case ENOSYS:
+			mtree_warning("%s: unsupported keyword", keyword);
+			break;
+		case ENXIO:
+			mtree_error("%s: keyword does not take a value",
+			    keyword);
+			break;
+		}
+	} while (1);
+
+	if (error)
+		return (error);
+
+	st->st_mode = (st->st_mode & ~S_IFMT) | node->type;
+
+	/* Nothing more to do for the global defaults. */
+	if (node->name == NULL)
+		return (0);
+
+	/*
+	 * Be intelligent about the file type.
+	 */
+	if (node->contents != NULL) {
+		if (node->symlink != NULL) {
+			mtree_error("%s: both link and contents keywords "
+			    "defined", node->name);
+			return (0);
+		}
+		type = S_IFREG;
+	} else if (node->type != 0) {
+		type = node->type;
+		if (type == S_IFREG) {
+			/* the named path is the default contents */
+			node->contents = mtree_file_path(node);
+		}
+	} else
+		type = (node->symlink != NULL) ? S_IFLNK : S_IFDIR;
+
+	if (node->type == 0)
+		node->type = type;
+
+	if (node->type != type) {
+		mtree_error("%s: file type and defined keywords to not match",
+		    node->name);
+		return (0);
+	}
+
+	st->st_mode = (st->st_mode & ~S_IFMT) | node->type;
+
+	if (node->contents == NULL)
+		return (0);
+
+	name = mtree_resolve(node->contents, &istemp);
+	if (name == NULL)
+		return (errno);
+
+	if (stat(name, &sb) != 0) {
+		mtree_error("%s: contents file '%s' not found", node->name,
+		    name);
+		free(name);
+		return (0);
+	}
+
+	/*
+         * Check for hardlinks. If the contents key is used, then the check
+         * will only trigger if the contents file is a link even if it is used
+         * by more than one file
+	 */
+	if (sb.st_nlink > 1) {
+		fsinode *curino;
+
+		st->st_ino = sb.st_ino;
+		st->st_dev = sb.st_dev;
+		curino = link_check(node->inode);
+		if (curino != NULL) {
+			free(node->inode);
+			node->inode = curino;
+			node->inode->nlink++;
+		}
+	}
+
+	free(node->contents);
+	node->contents = name;
+	st->st_size = sb.st_size;
+	return (0);
+}
+
+static int
+read_mtree_command(FILE *fp)
+{
+	char cmd[10];
+	int error;
+
+	error = read_word(fp, cmd, sizeof(cmd));
+	if (error)
+		goto out;
+
+	error = read_mtree_keywords(fp, &mtree_global);
+
+ out:
+	skip_to(fp, "\n");
+	(void)getc(fp);
+	return (error);
+}
+
+static int
+read_mtree_spec1(FILE *fp, bool def, const char *name)
+{
+	fsnode *last, *node, *parent;
+	u_int type;
+	int error;
+
+	assert(name[0] != '\0');
+
+	/*
+	 * Treat '..' specially, because it only changes our current
+	 * directory. We don't create a node for it. We simply ignore
+	 * any keywords that may appear on the line as well.
+	 * Going up a directory is a little non-obvious. A directory
+	 * node has a corresponding '.' child. The parent of '.' is
+	 * not the '.' node of the parent directory, but the directory
+	 * node within the parent to which the child relates. However,
+	 * going up a directory means we need to find the '.' node to
+	 * which the directoy node is linked.  This we can do via the
+	 * first * pointer, because '.' is always the first entry in a
+	 * directory.
+	 */
+	if (IS_DOTDOT(name)) {
+		/* This deals with NULL pointers as well. */
+		if (mtree_current == mtree_root) {
+			mtree_warning("ignoring .. in root directory");
+			return (0);
+		}
+
+		node = mtree_current;
+
+		assert(node != NULL);
+		assert(IS_DOT(node->name));
+		assert(node->first == node);
+
+		/* Get the corresponding directory node in the parent. */
+		node = mtree_current->parent;
+
+		assert(node != NULL);
+		assert(!IS_DOT(node->name));
+
+		node = node->first;
+
+		assert(node != NULL);
+		assert(IS_DOT(node->name));
+		assert(node->first == node);
+
+		mtree_current = node;
+		return (0);
+	}
+
+	/*
+	 * If we don't have a current directory and the first specification
+	 * (either implicit or defined) is not '.', then we need to create
+	 * a '.' node first (using a recursive call).
+	 */
+	if (!IS_DOT(name) && mtree_current == NULL) {
+		error = read_mtree_spec1(fp, false, ".");
+		if (error)
+			return (error);
+	}
+
+	/*
+	 * Lookup the name in the current directory (if we have a current
+	 * directory) to make sure we do not create multiple nodes for the
+	 * same component. For non-definitions, if we find a node with the
+	 * same name, simply change the current directory. For definitions
+	 * more happens.
+	 */
+	last = NULL;
+	node = mtree_current;
+	while (node != NULL) {
+		assert(node->first == mtree_current);
+
+		if (strcmp(name, node->name) == 0) {
+			if (def == true) {
+				if (!dupsok)
+					mtree_error(
+					    "duplicate definition of %s",
+					    name);
+				else
+					mtree_warning(
+					    "duplicate definition of %s",
+					    name);
+				return (0);
+			}
+
+			if (node->type != S_IFDIR) {
+				mtree_error("%s is not a directory", name);
+				return (0);
+			}
+
+			assert(!IS_DOT(name));
+
+			node = node->child;
+
+			assert(node != NULL);
+			assert(IS_DOT(node->name));
+
+			mtree_current = node;
+			return (0);
+		}
+
+		last = node;
+		node = last->next;
+	}
+
+	parent = (mtree_current != NULL) ? mtree_current->parent : NULL;
+	type = (def == false || IS_DOT(name)) ? S_IFDIR : 0;
+	node = create_node(name, type, parent, &mtree_global);
+	if (node == NULL)
+		return (ENOMEM);
+
+	if (def == true) {
+		error = read_mtree_keywords(fp, node);
+		if (error) {
+			destroy_node(node);
+			return (error);
+		}
+	}
+
+	node->first = (mtree_current != NULL) ? mtree_current : node;
+
+	if (last != NULL)
+		last->next = node;
+
+	if (node->type != S_IFDIR)
+		return (0);
+
+	if (!IS_DOT(node->name)) {
+		parent = node;
+		node = create_node(".", S_IFDIR, parent, parent);
+		if (node == NULL) {
+			last->next = NULL;
+			destroy_node(parent);
+			return (ENOMEM);
+		}
+		parent->child = node;
+		node->first = node;
+	}
+
+	assert(node != NULL);
+	assert(IS_DOT(node->name));
+	assert(node->first == node);
+
+	mtree_current = node;
+	if (mtree_root == NULL)
+		mtree_root = node;
+
+	return (0);
+}
+
+static int
+read_mtree_spec(FILE *fp)
+{
+	char pathspec[PATH_MAX];
+	char *cp;
+	int error;
+
+	error = read_word(fp, pathspec, sizeof(pathspec));
+	if (error)
+		goto out;
+
+	cp = strchr(pathspec, '/');
+	if (cp != NULL) {
+		/* Absolute pathname */
+		mtree_current = mtree_root;
+
+		do {
+			*cp++ = '\0';
+
+			/* Disallow '..' as a component. */
+			if (IS_DOTDOT(pathspec)) {
+				mtree_error("absolute path cannot contain "
+				    ".. component");
+				goto out;
+			}
+
+			/* Ignore multiple adjacent slashes and '.'. */
+			if (pathspec[0] != '\0' && !IS_DOT(pathspec))
+				error = read_mtree_spec1(fp, false, pathspec);
+			memmove(pathspec, cp, strlen(cp) + 1);
+			cp = strchr(pathspec, '/');
+		} while (!error && cp != NULL);
+
+		/* Disallow '.' and '..' as the last component. */
+		if (!error && (IS_DOT(pathspec) || IS_DOTDOT(pathspec))) {
+			mtree_error("absolute path cannot contain . or .. "
+			    "components");
+			goto out;
+		}
+	}
+
+	/* Ignore absolute specfications that end with a slash. */
+	if (!error && pathspec[0] != '\0')
+		error = read_mtree_spec1(fp, true, pathspec);
+
+ out:
+	skip_to(fp, "\n");
+	(void)getc(fp);
+	return (error);
+}
+
+fsnode *
+read_mtree(const char *fname, fsnode *node)
+{
+	struct mtree_fileinfo *fi;
+	FILE *fp;
+	int c, error;
+
+	/* We do not yet support nesting... */
+	assert(node == NULL);
+
+	if (strcmp(fname, "-") == 0)
+		fp = stdin;
+	else {
+		fp = fopen(fname, "r");
+		if (fp == NULL)
+			err(1, "Can't open `%s'", fname);
+	}
+
+	error = mtree_file_push(fname, fp);
+	if (error)
+		goto out;
+
+	bzero(&mtree_global, sizeof(mtree_global));
+	bzero(&mtree_global_inode, sizeof(mtree_global_inode));
+	mtree_global.inode = &mtree_global_inode;
+	mtree_global_inode.nlink = 1;
+	mtree_global_inode.st.st_nlink = 1;
+	mtree_global_inode.st.st_atime = mtree_global_inode.st.st_ctime =
+	    mtree_global_inode.st.st_mtime = time(NULL);
+	errors = warnings = 0;
+
+	setgroupent(1);
+	setpassent(1);
+
+	mtree_root = node;
+	mtree_current = node;
+	do {
+		/* Start of a new line... */
+		fi = SLIST_FIRST(&mtree_fileinfo);
+		fi->line++;
+
+		error = skip_over(fp, " \t");
+		if (error)
+			break;
+
+		c = getc(fp);
+		if (c == EOF) {
+			error = ferror(fp) ? errno : -1;
+			break;
+		}
+
+		switch (c) {
+		case '\n':		/* empty line */
+			error = 0;
+			break;
+		case '#':		/* comment -- skip to end of line. */
+			error = skip_to(fp, "\n");
+			if (!error)
+				(void)getc(fp);
+			break;
+		case '/':		/* special commands */
+			error = read_mtree_command(fp);
+			break;
+		default:		/* specification */
+			ungetc(c, fp);
+			error = read_mtree_spec(fp);
+			break;
+		}
+	} while (!error);
+
+	endpwent();
+	endgrent();
+
+	if (error <= 0 && (errors || warnings)) {
+		warnx("%u error(s) and %u warning(s) in mtree manifest",
+		    errors, warnings);
+		if (errors)
+			exit(1);
+	}
+
+ out:
+	if (error > 0)
+		errc(1, error, "Error reading mtree file");
+
+	if (fp != stdin)
+		fclose(fp);
+
+	if (mtree_root != NULL)
+		return (mtree_root);
+
+	/* Handle empty specifications. */
+	node = create_node(".", S_IFDIR, NULL, &mtree_global);
+	node->first = node;
+	return (node);
+}
diff --git a/usr.sbin/makefs/tests/Makefile b/usr.sbin/makefs/tests/Makefile
new file mode 100644
index 0000000000..273048da3f
--- /dev/null
+++ b/usr.sbin/makefs/tests/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD: head/usr.sbin/makefs/tests/Makefile 299094 2016-05-04 23:20:53Z ngie $
+
+ATF_TESTS_SH+=	makefs_cd9660_tests
+ATF_TESTS_SH+=	makefs_ffs_tests
+
+BINDIR=		${TESTSDIR}
+
+# XXX: PACKAGE support for SCRIPTS
+SCRIPTS+=	makefs_tests_common.sh
+SCRIPTSNAME_makefs_tests_common.sh=	makefs_tests_common.sh
+
+TEST_METADATA.makefs_cd9660_tests+=	required_files="/sbin/mount_cd9660"
+
+.for t in ${ATF_TESTS_SH}
+TEST_METADATA.$t+=	required_user="root"
+.endfor
+
+.include <bsd.test.mk>
diff --git a/usr.sbin/makefs/tests/Makefile.depend b/usr.sbin/makefs/tests/Makefile.depend
new file mode 100644
index 0000000000..11d1e6e5b5
--- /dev/null
+++ b/usr.sbin/makefs/tests/Makefile.depend
@@ -0,0 +1,11 @@
+# $FreeBSD: head/usr.sbin/makefs/tests/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/usr.sbin/makefs/tests/makefs_cd9660_tests.sh b/usr.sbin/makefs/tests/makefs_cd9660_tests.sh
new file mode 100755
index 0000000000..1cc5ce3828
--- /dev/null
+++ b/usr.sbin/makefs/tests/makefs_cd9660_tests.sh
@@ -0,0 +1,373 @@
+#
+# Copyright 2015 EMC Corp.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# * 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+# OWNER 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.
+#
+# $FreeBSD: head/usr.sbin/makefs/tests/makefs_cd9660_tests.sh 290267 2015-11-02 10:08:00Z ngie $
+#
+
+# A note on specs:
+# - A copy of the ISO-9660 spec can be found here:
+#   http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf
+# - Any references to `rockridge` are referring to the `Rock Ridge` extensions
+#   of the ISO-9660 spec. A copy of the draft `IEEE-P1282` spec can be found
+#   here:
+#   http://www.ymi.com/ymi/sites/default/files/pdf/Rockridge.pdf
+
+MAKEFS="makefs -t cd9660"
+MOUNT="mount_cd9660"
+
+. "$(dirname "$0")/makefs_tests_common.sh"
+
+common_cleanup()
+{
+	if ! test_md_device=$(cat $TEST_MD_DEVICE_FILE); then
+		echo "$TEST_MD_DEVICE_FILE could not be opened; has an md(4) device been attached?"
+		return
+	fi
+
+	umount -f /dev/$test_md_device || :
+	mdconfig -d -u $test_md_device || :
+}
+
+check_base_iso9660_image_contents()
+{
+	# Symlinks are treated like files when rockridge support isn't
+	# specified
+	check_image_contents "$@" -X c
+
+	atf_check -e empty -o empty -s exit:0 test -L $TEST_INPUTS_DIR/c
+	atf_check -e empty -o empty -s exit:0 test -f $TEST_MOUNT_DIR/c
+}
+
+atf_test_case D_flag cleanup
+D_flag_body()
+{
+	atf_skip "makefs crashes with SIGBUS with dupe mtree entries; see FreeBSD bug # 192839"
+
+	create_test_inputs
+
+	atf_check -e empty -o save:$TEST_SPEC_FILE -s exit:0 \
+	    mtree -cp $TEST_INPUTS_DIR
+	atf_check -e empty -o not-empty -s exit:0 \
+	    $MAKEFS -F $TEST_SPEC_FILE -M 1m $TEST_IMAGE $TEST_INPUTS_DIR
+
+	atf_check -e empty -o empty -s exit:0 \
+	    cp $TEST_SPEC_FILE spec2.mtree
+	atf_check -e empty -o save:dupe_$TEST_SPEC_FILE -s exit:0 \
+	    cat $TEST_SPEC_FILE spec2.mtree
+
+	atf_check -e empty -o not-empty -s not-exit:0 \
+	    $MAKEFS -F dupe_$TEST_SPEC_FILE -M 1m $TEST_IMAGE $TEST_INPUTS_DIR
+	atf_check -e empty -o not-empty -s exit:0 \
+	    $MAKEFS -D -F dupe_$TEST_SPEC_FILE -M 1m $TEST_IMAGE $TEST_INPUTS_DIR
+}
+D_flag_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case F_flag cleanup
+F_flag_body()
+{
+	create_test_inputs
+
+	atf_check -e empty -o save:$TEST_SPEC_FILE -s exit:0 \
+	    mtree -cp $TEST_INPUTS_DIR
+
+	atf_check -e empty -o empty -s exit:0 \
+	    $MAKEFS -F $TEST_SPEC_FILE -M 1m $TEST_IMAGE $TEST_INPUTS_DIR
+
+	mount_image
+	check_base_iso9660_image_contents
+}
+F_flag_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case from_mtree_spec_file cleanup
+from_mtree_spec_file_body()
+{
+	create_test_inputs
+
+	atf_check -e empty -o save:$TEST_SPEC_FILE -s exit:0 \
+	    mtree -c -k "$DEFAULT_MTREE_KEYWORDS" -p $TEST_INPUTS_DIR
+	cd $TEST_INPUTS_DIR
+	atf_check -e empty -o empty -s exit:0 \
+	    $MAKEFS $TEST_IMAGE $TEST_SPEC_FILE
+	cd -
+
+	mount_image
+	check_base_iso9660_image_contents
+}
+from_mtree_spec_file_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case from_multiple_dirs cleanup
+from_multiple_dirs_body()
+{
+	test_inputs_dir2=$TMPDIR/inputs2
+
+	create_test_inputs
+
+	atf_check -e empty -o empty -s exit:0 mkdir -p $test_inputs_dir2
+	atf_check -e empty -o empty -s exit:0 \
+	    touch $test_inputs_dir2/multiple_dirs_test_file
+
+	atf_check -e empty -o empty -s exit:0 \
+	    $MAKEFS $TEST_IMAGE $TEST_INPUTS_DIR $test_inputs_dir2
+
+	mount_image
+	check_base_iso9660_image_contents -d $test_inputs_dir2
+}
+from_multiple_dirs_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case from_single_dir cleanup
+from_single_dir_body()
+{
+	create_test_inputs
+
+	atf_check -e empty -o empty -s exit:0 \
+	    $MAKEFS $TEST_IMAGE $TEST_INPUTS_DIR
+
+	mount_image
+	check_base_iso9660_image_contents
+}
+from_single_dir_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case o_flag_allow_deep_trees cleanup
+o_flag_allow_deep_trees_body()
+{
+	create_test_inputs
+
+	# Make sure the "more than 8 levels deep" requirement is met.
+	atf_check -e empty -o empty -s exit:0 \
+	    mkdir -p $TEST_INPUTS_DIR/a/b/c/d/e/f/g/h/i/j
+
+	atf_check -e empty -o empty -s exit:0 \
+	    $MAKEFS -o allow-deep-trees $TEST_IMAGE $TEST_INPUTS_DIR
+
+	mount_image
+	check_base_iso9660_image_contents
+}
+o_flag_allow_deep_trees_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case o_flag_allow_max_name cleanup
+o_flag_allow_max_name_body()
+{
+	atf_expect_fail "-o allow-max-name doesn't appear to be implemented on FreeBSD's copy of makefs [yet]"
+
+	create_test_inputs
+
+	long_path=$TEST_INPUTS_DIR/$(jot -s '' -b 0 37)
+
+	# Make sure the "37 char name" limit requirement is met.
+	atf_check -e empty -o empty -s exit:0 touch $long_path
+
+	atf_check -e empty -o empty -s exit:0 \
+	    $MAKEFS -o allow-max-name $TEST_IMAGE $TEST_INPUTS_DIR
+
+	mount_image
+	check_base_iso9660_image_contents
+}
+o_flag_allow_max_name_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case o_flag_isolevel_1 cleanup
+o_flag_isolevel_1_body()
+{
+	atf_expect_fail "this testcase needs work; the filenames generated seem incorrect/corrupt"
+
+	create_test_inputs
+
+	atf_check -e empty -o empty -s exit:0 \
+	    $MAKEFS -o isolevel=1 $TEST_IMAGE $TEST_INPUTS_DIR
+
+	mount_image
+	check_base_iso9660_image_contents
+}
+o_flag_isolevel_1_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case o_flag_isolevel_2 cleanup
+o_flag_isolevel_2_body()
+{
+	create_test_inputs
+
+	atf_check -e empty -o empty -s exit:0 \
+	    $MAKEFS -o isolevel=2 $TEST_IMAGE $TEST_INPUTS_DIR
+
+	mount_image
+	check_base_iso9660_image_contents
+}
+o_flag_isolevel_2_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case o_flag_isolevel_3 cleanup
+o_flag_isolevel_3_body()
+{
+	create_test_inputs
+
+	# XXX: isolevel=3 isn't implemented yet. See FreeBSD bug # 203645
+	if true; then
+	atf_check -e match:'makefs: ISO Level 3 is greater than 2\.' -o empty -s not-exit:0 \
+	    $MAKEFS -o isolevel=3 $TEST_IMAGE $TEST_INPUTS_DIR
+	else
+	atf_check -e empty -o empty -s exit:0 \
+	    $MAKEFS -o isolevel=3 $TEST_IMAGE $TEST_INPUTS_DIR
+	mount_image
+	check_base_iso9660_image_contents
+	fi
+}
+o_flag_isolevel_3_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case o_flag_preparer
+o_flag_preparer_body()
+{
+	create_test_dirs
+
+	preparer='My Very First ISO'
+	preparer_uppercase="$(echo $preparer | tr '[[:lower:]]' '[[:upper:]]')"
+
+	atf_check -e empty -o empty -s exit:0 touch $TEST_INPUTS_DIR/dummy_file
+	atf_check -e empty -o empty -s exit:0 \
+	    $MAKEFS -o preparer="$preparer" $TEST_IMAGE $TEST_INPUTS_DIR
+	atf_check -e empty -o match:"$preparer_uppercase" -s exit:0 \
+	    strings $TEST_IMAGE
+}
+
+atf_test_case o_flag_publisher
+o_flag_publisher_body()
+{
+	create_test_dirs
+
+	publisher='My Super Awesome Publishing Company LTD'
+	publisher_uppercase="$(echo $publisher | tr '[[:lower:]]' '[[:upper:]]')"
+
+	atf_check -e empty -o empty -s exit:0 touch $TEST_INPUTS_DIR/dummy_file
+	atf_check -e empty -o empty -s exit:0 \
+	    $MAKEFS -o publisher="$publisher" $TEST_IMAGE $TEST_INPUTS_DIR
+	atf_check -e empty -o match:"$publisher_uppercase" -s exit:0 \
+	    strings $TEST_IMAGE
+}
+
+atf_test_case o_flag_rockridge cleanup
+o_flag_rockridge_body()
+{
+	create_test_dirs
+
+	# Make sure the "more than 8 levels deep" requirement is met.
+	atf_check -e empty -o empty -s exit:0 \
+	    mkdir -p $TEST_INPUTS_DIR/a/b/c/d/e/f/g/h/i/j
+
+	# Make sure the "pathname larger than 255 chars" requirement is met.
+	#
+	# $long_path's needs to be nested in a directory, as creating it
+	# outright as a 256 char filename via touch will fail with ENAMETOOLONG
+	long_path=$TEST_INPUTS_DIR/$(jot -s '/' -b "$(jot -s '' -b 0 64)" 4)
+	atf_check -e empty -o empty -s exit:0 mkdir -p "$(dirname $long_path)"
+	atf_check -e empty -o empty -s exit:0 touch "$long_path"
+
+	atf_check -e empty -o empty -s exit:0 \
+	    $MAKEFS -o rockridge $TEST_IMAGE $TEST_INPUTS_DIR
+
+	mount_image
+	check_image_contents -X .rr_moved
+
+	# .rr_moved is a special directory created when you have deep directory
+	# trees with rock ridge extensions on
+	atf_check -e empty -o empty -s exit:0 \
+	    test -d $TEST_MOUNT_DIR/.rr_moved
+}
+o_flag_rockridge_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case o_flag_rockridge_dev_nodes cleanup
+o_flag_rockridge_dev_nodes_head()
+{
+	atf_set "descr" "Functional tests to ensure that dev nodes are handled properly with rockridge extensions (NetBSD kern/48852; FreeBSD bug 203648)"
+}
+o_flag_rockridge_dev_nodes_body()
+{
+	create_test_dirs
+
+	(tar -cvf - -C /dev null && touch .tar_ok) | \
+	atf_check -e not-empty -o empty -s exit:0 tar -xvf - -C "$TEST_INPUTS_DIR"
+
+	atf_check -e empty -o empty -s exit:0 test -c $TEST_INPUTS_DIR/null
+	atf_check -e empty -o empty -s exit:0 test -f .tar_ok
+
+	atf_check -e empty -o empty -s exit:0 \
+	    $MAKEFS -o rockridge $TEST_IMAGE $TEST_INPUTS_DIR
+
+	mount_image
+	check_image_contents
+}
+o_flag_rockridge_dev_nodes_cleanup()
+{
+	common_cleanup
+}
+
+atf_init_test_cases()
+{
+	atf_add_test_case D_flag
+	atf_add_test_case F_flag
+
+	atf_add_test_case from_mtree_spec_file
+	atf_add_test_case from_multiple_dirs
+	atf_add_test_case from_single_dir
+
+	atf_add_test_case o_flag_allow_deep_trees
+	atf_add_test_case o_flag_allow_max_name
+	atf_add_test_case o_flag_isolevel_1
+	atf_add_test_case o_flag_isolevel_2
+	atf_add_test_case o_flag_isolevel_3
+	atf_add_test_case o_flag_preparer
+	atf_add_test_case o_flag_publisher
+	atf_add_test_case o_flag_rockridge
+	atf_add_test_case o_flag_rockridge_dev_nodes
+}
diff --git a/usr.sbin/makefs/tests/makefs_ffs_tests.sh b/usr.sbin/makefs/tests/makefs_ffs_tests.sh
new file mode 100755
index 0000000000..0c04b508d0
--- /dev/null
+++ b/usr.sbin/makefs/tests/makefs_ffs_tests.sh
@@ -0,0 +1,237 @@
+#
+# Copyright 2015 EMC Corp.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# * 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+# OWNER 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.
+#
+# $FreeBSD: head/usr.sbin/makefs/tests/makefs_ffs_tests.sh 290270 2015-11-02 11:06:51Z ngie $
+#
+
+MAKEFS="makefs -t ffs"
+MOUNT="mount"
+
+. "$(dirname "$0")/makefs_tests_common.sh"
+
+TEST_TUNEFS_OUTPUT=$TMPDIR/tunefs.output
+
+common_cleanup()
+{
+	if ! test_md_device=$(cat $TEST_MD_DEVICE_FILE); then
+		echo "$TEST_MD_DEVICE_FILE could not be opened; has an md(4) device been attached?"
+		return
+	fi
+
+	umount -f /dev/$test_md_device || :
+	mdconfig -d -u $test_md_device || :
+}
+
+check_ffs_image_contents()
+{
+	atf_check -e save:$TEST_TUNEFS_OUTPUT -o empty -s exit:0 \
+	    tunefs -p /dev/$(cat $TEST_MD_DEVICE_FILE)
+
+	check_image_contents "$@"
+}
+
+atf_test_case D_flag cleanup
+D_flag_body()
+{
+	atf_skip "makefs crashes with SIGBUS with dupe mtree entries; see FreeBSD bug # 192839"
+
+	create_test_inputs
+
+	atf_check -e empty -o save:$TEST_SPEC_FILE -s exit:0 \
+	    mtree -cp $TEST_INPUTS_DIR
+	atf_check -e empty -o not-empty -s exit:0 \
+	    $MAKEFS -F $TEST_SPEC_FILE -M 1m $TEST_IMAGE $TEST_INPUTS_DIR
+
+	atf_check -e empty -o empty -s exit:0 \
+	    cp $TEST_SPEC_FILE spec2.mtree
+	atf_check -e empty -o save:dupe_$TEST_SPEC_FILE -s exit:0 \
+	    cat $TEST_SPEC_FILE spec2.mtree
+
+	atf_check -e empty -o not-empty -s not-exit:0 \
+	    $MAKEFS -F dupe_$TEST_SPEC_FILE -M 1m $TEST_IMAGE $TEST_INPUTS_DIR
+	atf_check -e empty -o not-empty -s exit:0 \
+	    $MAKEFS -D -F dupe_$TEST_SPEC_FILE -M 1m $TEST_IMAGE $TEST_INPUTS_DIR
+}
+D_flag_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case F_flag cleanup
+F_flag_body()
+{
+	create_test_inputs
+
+	atf_check -e empty -o save:$TEST_SPEC_FILE -s exit:0 \
+	    mtree -cp $TEST_INPUTS_DIR
+
+	atf_check -e empty -o not-empty -s exit:0 \
+	    $MAKEFS -F $TEST_SPEC_FILE -M 1m $TEST_IMAGE $TEST_INPUTS_DIR
+
+	mount_image
+	check_ffs_image_contents
+}
+F_flag_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case from_mtree_spec_file cleanup
+from_mtree_spec_file_body()
+{
+	create_test_inputs
+
+	atf_check -e empty -o save:$TEST_SPEC_FILE -s exit:0 \
+	    mtree -c -k "$DEFAULT_MTREE_KEYWORDS" -p $TEST_INPUTS_DIR
+
+	cd $TEST_INPUTS_DIR
+	atf_check -e empty -o not-empty -s exit:0 \
+	    $MAKEFS $TEST_IMAGE $TEST_SPEC_FILE
+	cd -
+
+	mount_image
+	check_ffs_image_contents
+}
+from_mtree_spec_file_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case from_multiple_dirs cleanup
+from_multiple_dirs_body()
+{
+	test_inputs_dir2=$TMPDIR/inputs2
+
+	create_test_inputs
+
+	atf_check -e empty -o empty -s exit:0 mkdir -p $test_inputs_dir2
+	atf_check -e empty -o empty -s exit:0 \
+	    touch $test_inputs_dir2/multiple_dirs_test_file
+
+	atf_check -e empty -o not-empty -s exit:0 \
+	    $MAKEFS $TEST_IMAGE $TEST_INPUTS_DIR $test_inputs_dir2
+
+	mount_image
+	check_image_contents -d $test_inputs_dir2
+}
+from_multiple_dirs_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case from_single_dir cleanup
+from_single_dir_body()
+{
+	create_test_inputs
+
+	atf_check -e empty -o not-empty -s exit:0 \
+	    $MAKEFS -M 1m $TEST_IMAGE $TEST_INPUTS_DIR
+
+	mount_image
+	check_ffs_image_contents
+}
+from_single_dir_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case o_flag_version_1 cleanup
+o_flag_version_1_body()
+{
+	ffs_version=1
+
+	platform=$(uname)
+	case "$platform" in
+	FreeBSD)
+		ffs_label=UFS${ffs_version}
+		;;
+	NetBSD)
+		ffs_label=FFSv${ffs_version}
+		;;
+	*)
+		atf_skip "Unsupported platform"
+		;;
+	esac
+
+	create_test_inputs
+
+	atf_check -e empty -o not-empty -s exit:0 \
+	    $MAKEFS -M 1m -o version=$ffs_version $TEST_IMAGE $TEST_INPUTS_DIR
+
+	mount_image
+	atf_check -e empty -o match:"$ffs_label" dumpfs $TEST_MOUNT_DIR
+	check_ffs_image_contents
+}
+o_flag_version_1_cleanup()
+{
+	common_cleanup
+}
+
+atf_test_case o_flag_version_2 cleanup
+o_flag_version_2_body()
+{
+	ffs_version=2
+
+	platform=$(uname)
+	case "$platform" in
+	FreeBSD)
+		ffs_label=UFS${ffs_version}
+		;;
+	NetBSD)
+		ffs_label=FFSv${ffs_version}
+		;;
+	*)
+		atf_skip "Unsupported platform"
+		;;
+	esac
+
+	create_test_inputs
+
+	atf_check -e empty -o not-empty -s exit:0 \
+	    $MAKEFS -M 1m -o version=$ffs_version $TEST_IMAGE $TEST_INPUTS_DIR
+
+	mount_image
+	atf_check -e empty -o match:"$ffs_label" dumpfs $TEST_MOUNT_DIR
+	check_ffs_image_contents
+}
+o_flag_version_2_cleanup()
+{
+	common_cleanup
+}
+
+atf_init_test_cases()
+{
+
+	atf_add_test_case D_flag
+	atf_add_test_case F_flag
+
+	atf_add_test_case from_mtree_spec_file
+	atf_add_test_case from_multiple_dirs
+	atf_add_test_case from_single_dir
+
+	atf_add_test_case o_flag_version_1
+	atf_add_test_case o_flag_version_2
+}
diff --git a/usr.sbin/makefs/tests/makefs_tests_common.sh b/usr.sbin/makefs/tests/makefs_tests_common.sh
new file mode 100755
index 0000000000..987c3f85c3
--- /dev/null
+++ b/usr.sbin/makefs/tests/makefs_tests_common.sh
@@ -0,0 +1,152 @@
+#
+# Copyright 2015 EMC Corp.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# * 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+# OWNER 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.
+#
+# $FreeBSD: head/usr.sbin/makefs/tests/makefs_tests_common.sh 290267 2015-11-02 10:08:00Z ngie $
+#
+
+KB=1024
+: ${TMPDIR=/tmp}
+# TODO: add mtree `time` support; get a lot of errors like this right now when
+# passing generating disk images with keyword mtree support, like:
+#
+# `[...]/mtree.spec:8: error: time: invalid value '1446458503'`
+#
+#DEFAULT_MTREE_KEYWORDS="type,mode,gid,uid,size,link,time"
+DEFAULT_MTREE_KEYWORDS="type,mode,gid,uid,size,link"
+TEST_IMAGE="$TMPDIR/test.img"
+TEST_INPUTS_DIR="$TMPDIR/inputs"
+TEST_MD_DEVICE_FILE="$TMPDIR/md.output"
+TEST_MOUNT_DIR="$TMPDIR/mnt"
+TEST_SPEC_FILE="$TMPDIR/mtree.spec"
+
+check_image_contents()
+{
+	local directories=$TEST_INPUTS_DIR
+	local excludes mtree_excludes_arg mtree_file
+	local mtree_keywords="$DEFAULT_MTREE_KEYWORDS"
+
+	while getopts "d:f:m:X:" flag; do
+		case "$flag" in
+		d)
+			directories="$directories $OPTARG"
+			;;
+		f)
+			mtree_file=$OPTARG
+			;;
+		m)
+			mtree_keywords=$OPTARG
+			;;
+		X)
+			excludes="$excludes $OPTARG"
+			;;
+		*)
+			echo "usage: check_image_contents [-d directory ...] [-f mtree-file] [-m mtree-keywords] [-X exclude]"
+			atf_fail "unhandled option: $flag"
+			;;
+		esac
+	done
+
+	if [ -n "$excludes" ]; then
+		echo "$excludes" | tr ' ' '\n' > excludes.txt
+		mtree_excludes_arg="-X excludes.txt"
+	fi
+
+	if [ -z "$mtree_file" ]; then
+		mtree_file=input_spec.mtree
+		for directory in $directories; do
+			mtree -c -k $mtree_keywords -p $directory $mtree_excludes_arg
+		done > $mtree_file
+	fi
+
+	echo "<---- Input spec BEGIN ---->"
+	cat $mtree_file
+	echo "<---- Input spec END ---->"
+	atf_check -e empty -o empty -s exit:0 \
+	    mtree -UW -f $mtree_file \
+		-p $TEST_MOUNT_DIR \
+		$mtree_excludes_arg
+}
+
+create_test_dirs()
+{
+	atf_check -e empty -s exit:0 mkdir -m 0777 -p $TEST_MOUNT_DIR
+	atf_check -e empty -s exit:0 mkdir -m 0777 -p $TEST_INPUTS_DIR
+}
+
+create_test_inputs()
+{
+	create_test_dirs
+
+	cd $TEST_INPUTS_DIR
+
+	atf_check -e empty -s exit:0 mkdir -m 0755 -p a/b/1
+	atf_check -e empty -s exit:0 ln -s a/b c
+	atf_check -e empty -s exit:0 touch d
+	atf_check -e empty -s exit:0 ln d e
+	atf_check -e empty -s exit:0 touch .f
+	atf_check -e empty -s exit:0 mkdir .g
+	# XXX: fifos on the filesystem don't match fifos created by makefs for
+	# some odd reason.
+	#atf_check -e empty -s exit:0 mkfifo h
+	atf_check -e ignore -s exit:0 dd if=/dev/zero of=i count=1000 bs=1
+	atf_check -e empty -s exit:0 touch klmn
+	atf_check -e empty -s exit:0 touch opqr
+	atf_check -e empty -s exit:0 touch stuv
+	atf_check -e empty -s exit:0 install -m 0755 /dev/null wxyz
+	atf_check -e empty -s exit:0 touch 0b00000001
+	atf_check -e empty -s exit:0 touch 0b00000010
+	atf_check -e empty -s exit:0 touch 0b00000011
+	atf_check -e empty -s exit:0 touch 0b00000100
+	atf_check -e empty -s exit:0 touch 0b00000101
+	atf_check -e empty -s exit:0 touch 0b00000110
+	atf_check -e empty -s exit:0 touch 0b00000111
+	atf_check -e empty -s exit:0 touch 0b00001000
+	atf_check -e empty -s exit:0 touch 0b00001001
+	atf_check -e empty -s exit:0 touch 0b00001010
+	atf_check -e empty -s exit:0 touch 0b00001011
+	atf_check -e empty -s exit:0 touch 0b00001100
+	atf_check -e empty -s exit:0 touch 0b00001101
+	atf_check -e empty -s exit:0 touch 0b00001110
+
+	for filesize in 1 512 $(( 2 * $KB )) $(( 10 * $KB )) $(( 512 * $KB )); \
+	do
+		atf_check -e ignore -o empty -s exit:0 \
+		    dd if=/dev/zero of=${filesize}.file bs=1 \
+		    count=1 oseek=${filesize} conv=sparse
+		files="${files} ${filesize}.file"
+	done
+
+	cd -
+}
+
+mount_image()
+{
+	atf_check -e empty -o save:$TEST_MD_DEVICE_FILE -s exit:0 \
+	    mdconfig -a -f $TEST_IMAGE
+	atf_check -e empty -o empty -s exit:0 \
+	    $MOUNT /dev/$(cat $TEST_MD_DEVICE_FILE) $TEST_MOUNT_DIR
+}
+
diff --git a/usr.sbin/makefs/walk.c b/usr.sbin/makefs/walk.c
new file mode 100644
index 0000000000..b6496ad5df
--- /dev/null
+++ b/usr.sbin/makefs/walk.c
@@ -0,0 +1,696 @@
+/*	$NetBSD: walk.c,v 1.24 2008/12/28 21:51:46 christos Exp $	*/
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn for Wasabi Systems, Inc.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed for the NetBSD Project by
+ *      Wasabi Systems, Inc.
+ * 4. The name of Wasabi Systems, Inc. may not be used to endorse
+ *    or promote products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC
+ * 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>
+__FBSDID("$FreeBSD: head/usr.sbin/makefs/walk.c 307927 2016-10-25 16:29:15Z marcel $");
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "makefs.h"
+//#include "mtree.h"
+//#include "extern.h"
+
+#if 0
+static	void	 apply_specdir(const char *, NODE *, fsnode *, int);
+static	void	 apply_specentry(const char *, NODE *, fsnode *);
+#endif
+static	fsnode	*create_fsnode(const char *, const char *, const char *,
+			       struct stat *);
+
+
+/*
+ * walk_dir --
+ *	build a tree of fsnodes from `root' and `dir', with a parent
+ *	fsnode of `parent' (which may be NULL for the root of the tree).
+ *	append the tree to a fsnode of `join' if it is not NULL.
+ *	each "level" is a directory, with the "." entry guaranteed to be
+ *	at the start of the list, and without ".." entries.
+ */
+fsnode *
+walk_dir(const char *root, const char *dir, fsnode *parent, fsnode *join)
+{
+	fsnode		*first, *cur, *prev, *last;
+	DIR		*dirp;
+	struct dirent	*dent;
+	char		path[MAXPATHLEN + 1];
+	struct stat	stbuf;
+	char		*name, *rp;
+	int		dot, len;
+
+	assert(root != NULL);
+	assert(dir != NULL);
+
+	len = snprintf(path, sizeof(path), "%s/%s", root, dir);
+	if (len >= (int)sizeof(path))
+		errx(1, "Pathname too long.");
+	if (debug & DEBUG_WALK_DIR)
+		printf("walk_dir: %s %p\n", path, parent);
+	if ((dirp = opendir(path)) == NULL)
+		err(1, "Can't opendir `%s'", path);
+	rp = path + strlen(root) + 1;
+	if (join != NULL) {
+		first = cur = join;
+		while (cur->next != NULL)
+			cur = cur->next;
+		prev = cur;
+	} else
+		first = prev = NULL;
+	last = prev;
+	while ((dent = readdir(dirp)) != NULL) {
+		name = dent->d_name;
+		dot = 0;
+		if (name[0] == '.')
+			switch (name[1]) {
+			case '\0':	/* "." */
+				if (join != NULL)
+					continue;
+				dot = 1;
+				break;
+			case '.':	/* ".." */
+				if (name[2] == '\0')
+					continue;
+				/* FALLTHROUGH */
+			default:
+				dot = 0;
+			}
+		if (debug & DEBUG_WALK_DIR_NODE)
+			printf("scanning %s/%s/%s\n", root, dir, name);
+		if (snprintf(path + len, sizeof(path) - len, "/%s", name) >=
+		    (int)sizeof(path) - len)
+			errx(1, "Pathname too long.");
+		if (lstat(path, &stbuf) == -1)
+			err(1, "Can't lstat `%s'", path);
+#ifdef S_ISSOCK
+		if (S_ISSOCK(stbuf.st_mode & S_IFMT)) {
+			if (debug & DEBUG_WALK_DIR_NODE)
+				printf("  skipping socket %s\n", path);
+			continue;
+		}
+#endif
+
+		if (join != NULL) {
+			cur = join->next;
+			for (;;) {
+				if (cur == NULL || strcmp(cur->name, name) == 0)
+					break;
+				if (cur == last) {
+					cur = NULL;
+					break;
+				}
+				cur = cur->next;
+			}
+			if (cur != NULL) {
+				if (S_ISDIR(cur->type) &&
+				    S_ISDIR(stbuf.st_mode)) {
+					if (debug & DEBUG_WALK_DIR_NODE)
+						printf("merging %s with %p\n",
+						    path, cur->child);
+					cur->child = walk_dir(root, rp, cur,
+					    cur->child);
+					continue;
+				}
+				errx(1, "Can't merge %s `%s' with existing %s",
+				    inode_type(stbuf.st_mode), path,
+				    inode_type(cur->type));
+			}
+		}
+
+		cur = create_fsnode(root, dir, name, &stbuf);
+		cur->parent = parent;
+		if (dot) {
+				/* ensure "." is at the start of the list */
+			cur->next = first;
+			first = cur;
+			if (! prev)
+				prev = cur;
+			cur->first = first;
+		} else {			/* not "." */
+			if (prev)
+				prev->next = cur;
+			prev = cur;
+			if (!first)
+				first = cur;
+			cur->first = first;
+			if (S_ISDIR(cur->type)) {
+				cur->child = walk_dir(root, rp, cur, NULL);
+				continue;
+			}
+		}
+		if (stbuf.st_nlink > 1) {
+			fsinode	*curino;
+
+			curino = link_check(cur->inode);
+			if (curino != NULL) {
+				free(cur->inode);
+				cur->inode = curino;
+				cur->inode->nlink++;
+				if (debug & DEBUG_WALK_DIR_LINKCHECK)
+					printf("link_check: found [%llu, %llu]\n",
+					    (unsigned long long)curino->st.st_dev,
+					    (unsigned long long)curino->st.st_ino);
+			}
+		}
+		if (S_ISLNK(cur->type)) {
+			char	slink[PATH_MAX+1];
+			int	llen;
+
+			llen = readlink(path, slink, sizeof(slink) - 1);
+			if (llen == -1)
+				err(1, "Readlink `%s'", path);
+			slink[llen] = '\0';
+			if ((cur->symlink = strdup(slink)) == NULL)
+				err(1, "Memory allocation error");
+		}
+	}
+	assert(first != NULL);
+	if (join == NULL)
+		for (cur = first->next; cur != NULL; cur = cur->next)
+			cur->first = first;
+	if (closedir(dirp) == -1)
+		err(1, "Can't closedir `%s/%s'", root, dir);
+	return (first);
+}
+
+static fsnode *
+create_fsnode(const char *root, const char *path, const char *name,
+    struct stat *stbuf)
+{
+	fsnode *cur;
+
+	if ((cur = calloc(1, sizeof(fsnode))) == NULL ||
+	    (cur->path = strdup(path)) == NULL ||
+	    (cur->name = strdup(name)) == NULL ||
+	    (cur->inode = calloc(1, sizeof(fsinode))) == NULL)
+		err(1, "Memory allocation error");
+	cur->root = root;
+	cur->type = stbuf->st_mode & S_IFMT;
+	cur->inode->nlink = 1;
+	cur->inode->st = *stbuf;
+	if (stampst.st_ino) {
+		cur->inode->st.st_atime = stampst.st_atime;
+		cur->inode->st.st_mtime = stampst.st_mtime;
+		cur->inode->st.st_ctime = stampst.st_ctime;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+		cur->inode->st.st_atimensec = stampst.st_atimensec;
+		cur->inode->st.st_mtimensec = stampst.st_mtimensec;
+		cur->inode->st.st_ctimensec = stampst.st_ctimensec;
+#endif
+#if HAVE_STRUCT_STAT_BIRTHTIME
+		cur->inode->st.st_birthtime = stampst.st_birthtime;
+		cur->inode->st.st_birthtimensec = stampst.st_birthtimensec;
+#endif
+	}
+	return (cur);
+}
+
+/*
+ * free_fsnodes --
+ *	Removes node from tree and frees it and all of
+ *   its descendants.
+ */
+void
+free_fsnodes(fsnode *node)
+{
+	fsnode	*cur, *next;
+
+	assert(node != NULL);
+
+	/* for ".", start with actual parent node */
+	if (node->first == node) {
+		assert(node->name[0] == '.' && node->name[1] == '\0');
+		if (node->parent) {
+			assert(node->parent->child == node);
+			node = node->parent;
+		}
+	}
+
+	/* Find ourselves in our sibling list and unlink */
+	if (node->first != node) {
+		for (cur = node->first; cur->next; cur = cur->next) {
+			if (cur->next == node) {
+				cur->next = node->next;
+				node->next = NULL;
+				break;
+			}
+		}
+	}
+
+	for (cur = node; cur != NULL; cur = next) {
+		next = cur->next;
+		if (cur->child) {
+			cur->child->parent = NULL;
+			free_fsnodes(cur->child);
+		}
+		if (cur->inode->nlink-- == 1)
+			free(cur->inode);
+		if (cur->symlink)
+			free(cur->symlink);
+		free(cur->path);
+		free(cur->name);
+		free(cur);
+	}
+}
+
+#if 0
+/*
+ * apply_specfile --
+ *	read in the mtree(8) specfile, and apply it to the tree
+ *	at dir,parent. parameters in parent on equivalent types
+ *	will be changed to those found in specfile, and missing
+ *	entries will be added.
+ */
+void
+apply_specfile(const char *specfile, const char *dir, fsnode *parent, int speconly)
+{
+	struct timeval	 start;
+	FILE	*fp;
+	NODE	*root;
+
+	assert(specfile != NULL);
+	assert(parent != NULL);
+
+	if (debug & DEBUG_APPLY_SPECFILE)
+		printf("apply_specfile: %s, %s %p\n", specfile, dir, parent);
+
+				/* read in the specfile */
+	if ((fp = fopen(specfile, "r")) == NULL)
+		err(1, "Can't open `%s'", specfile);
+	TIMER_START(start);
+	root = spec(fp);
+	TIMER_RESULTS(start, "spec");
+	if (fclose(fp) == EOF)
+		err(1, "Can't close `%s'", specfile);
+
+				/* perform some sanity checks */
+	if (root == NULL)
+		errx(1, "Specfile `%s' did not contain a tree", specfile);
+	assert(strcmp(root->name, ".") == 0);
+	assert(root->type == F_DIR);
+
+				/* merge in the changes */
+	apply_specdir(dir, root, parent, speconly);
+
+}
+
+static void
+apply_specdir(const char *dir, NODE *specnode, fsnode *dirnode, int speconly)
+{
+	char	 path[MAXPATHLEN + 1];
+	NODE	*curnode;
+	fsnode	*curfsnode;
+
+	assert(specnode != NULL);
+	assert(dirnode != NULL);
+
+	if (debug & DEBUG_APPLY_SPECFILE)
+		printf("apply_specdir: %s %p %p\n", dir, specnode, dirnode);
+
+	if (specnode->type != F_DIR)
+		errx(1, "Specfile node `%s/%s' is not a directory",
+		    dir, specnode->name);
+	if (dirnode->type != S_IFDIR)
+		errx(1, "Directory node `%s/%s' is not a directory",
+		    dir, dirnode->name);
+
+	apply_specentry(dir, specnode, dirnode);
+
+	/* Remove any filesystem nodes not found in specfile */
+	/* XXX inefficient.  This is O^2 in each dir and it would
+	 * have been better never to have walked this part of the tree
+	 * to begin with
+	 */
+	if (speconly) {
+		fsnode *next;
+		assert(dirnode->name[0] == '.' && dirnode->name[1] == '\0');
+		for (curfsnode = dirnode->next; curfsnode != NULL; curfsnode = next) {
+			next = curfsnode->next;
+			for (curnode = specnode->child; curnode != NULL;
+			     curnode = curnode->next) {
+				if (strcmp(curnode->name, curfsnode->name) == 0)
+					break;
+			}
+			if (curnode == NULL) {
+				if (debug & DEBUG_APPLY_SPECONLY) {
+					printf("apply_specdir: trimming %s/%s %p\n", dir, curfsnode->name, curfsnode);
+				}
+				free_fsnodes(curfsnode);
+			}
+		}
+	}
+
+			/* now walk specnode->child matching up with dirnode */
+	for (curnode = specnode->child; curnode != NULL;
+	    curnode = curnode->next) {
+		if (debug & DEBUG_APPLY_SPECENTRY)
+			printf("apply_specdir:  spec %s\n",
+			    curnode->name);
+		for (curfsnode = dirnode->next; curfsnode != NULL;
+		    curfsnode = curfsnode->next) {
+#if 0	/* too verbose for now */
+			if (debug & DEBUG_APPLY_SPECENTRY)
+				printf("apply_specdir:  dirent %s\n",
+				    curfsnode->name);
+#endif
+			if (strcmp(curnode->name, curfsnode->name) == 0)
+				break;
+		}
+		if (snprintf(path, sizeof(path), "%s/%s",
+		    dir, curnode->name) >= sizeof(path))
+			errx(1, "Pathname too long.");
+		if (curfsnode == NULL) {	/* need new entry */
+			struct stat	stbuf;
+					/* check that enough info is provided */
+#define NODETEST(t, m)							\
+			if (!(t))					\
+				errx(1, "`%s': %s not provided", path, m)
+			NODETEST(curnode->flags & F_TYPE, "type");
+			NODETEST(curnode->flags & F_MODE, "mode");
+				/* XXX: require F_TIME ? */
+			NODETEST(curnode->flags & F_GID ||
+			    curnode->flags & F_GNAME, "group");
+			NODETEST(curnode->flags & F_UID ||
+			    curnode->flags & F_UNAME, "user");
+/*			if (curnode->type == F_BLOCK || curnode->type == F_CHAR)
+				NODETEST(curnode->flags & F_DEV,
+				    "device number");*/
+#undef NODETEST
+
+			if (debug & DEBUG_APPLY_SPECFILE)
+				printf("apply_specdir: adding %s\n",
+				    curnode->name);
+					/* build minimal fsnode */
+			memset(&stbuf, 0, sizeof(stbuf));
+			stbuf.st_mode = nodetoino(curnode->type);
+			stbuf.st_nlink = 1;
+			stbuf.st_mtime = stbuf.st_atime =
+			    stbuf.st_ctime = start_time.tv_sec;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+			stbuf.st_mtimensec = stbuf.st_atimensec =
+			    stbuf.st_ctimensec = start_time.tv_nsec;
+#endif
+			curfsnode = create_fsnode(".", ".", curnode->name,
+			    &stbuf);
+			curfsnode->parent = dirnode->parent;
+			curfsnode->first = dirnode;
+			curfsnode->next = dirnode->next;
+			dirnode->next = curfsnode;
+			if (curfsnode->type == S_IFDIR) {
+					/* for dirs, make "." entry as well */
+				curfsnode->child = create_fsnode(".", ".", ".",
+				    &stbuf);
+				curfsnode->child->parent = curfsnode;
+				curfsnode->child->first = curfsnode->child;
+			}
+			if (curfsnode->type == S_IFLNK) {
+				assert(curnode->slink != NULL);
+					/* for symlinks, copy the target */
+				if ((curfsnode->symlink =
+				    strdup(curnode->slink)) == NULL)
+					err(1, "Memory allocation error");
+			}
+		}
+		apply_specentry(dir, curnode, curfsnode);
+		if (curnode->type == F_DIR) {
+			if (curfsnode->type != S_IFDIR)
+				errx(1, "`%s' is not a directory", path);
+			assert (curfsnode->child != NULL);
+			apply_specdir(path, curnode, curfsnode->child, speconly);
+		}
+	}
+}
+
+static void
+apply_specentry(const char *dir, NODE *specnode, fsnode *dirnode)
+{
+
+	assert(specnode != NULL);
+	assert(dirnode != NULL);
+
+	if (nodetoino(specnode->type) != dirnode->type)
+		errx(1, "`%s/%s' type mismatch: specfile %s, tree %s",
+		    dir, specnode->name, inode_type(nodetoino(specnode->type)),
+		    inode_type(dirnode->type));
+
+	if (debug & DEBUG_APPLY_SPECENTRY)
+		printf("apply_specentry: %s/%s\n", dir, dirnode->name);
+
+#define ASEPRINT(t, b, o, n) \
+		if (debug & DEBUG_APPLY_SPECENTRY) \
+			printf("\t\t\tchanging %s from " b " to " b "\n", \
+			    t, o, n)
+
+	if (specnode->flags & (F_GID | F_GNAME)) {
+		ASEPRINT("gid", "%d",
+		    dirnode->inode->st.st_gid, specnode->st_gid);
+		dirnode->inode->st.st_gid = specnode->st_gid;
+	}
+	if (specnode->flags & F_MODE) {
+		ASEPRINT("mode", "%#o",
+		    dirnode->inode->st.st_mode & ALLPERMS, specnode->st_mode);
+		dirnode->inode->st.st_mode &= ~ALLPERMS;
+		dirnode->inode->st.st_mode |= (specnode->st_mode & ALLPERMS);
+	}
+		/* XXX: ignoring F_NLINK for now */
+	if (specnode->flags & F_SIZE) {
+		ASEPRINT("size", "%lld",
+		    (long long)dirnode->inode->st.st_size,
+		    (long long)specnode->st_size);
+		dirnode->inode->st.st_size = specnode->st_size;
+	}
+	if (specnode->flags & F_SLINK) {
+		assert(dirnode->symlink != NULL);
+		assert(specnode->slink != NULL);
+		ASEPRINT("symlink", "%s", dirnode->symlink, specnode->slink);
+		free(dirnode->symlink);
+		if ((dirnode->symlink = strdup(specnode->slink)) == NULL)
+			err(1, "Memory allocation error");
+	}
+	if (specnode->flags & F_TIME) {
+		ASEPRINT("time", "%ld",
+		    (long)dirnode->inode->st.st_mtime,
+		    (long)specnode->st_mtimespec.tv_sec);
+		dirnode->inode->st.st_mtime =		specnode->st_mtimespec.tv_sec;
+		dirnode->inode->st.st_atime =		specnode->st_mtimespec.tv_sec;
+		dirnode->inode->st.st_ctime =		start_time.tv_sec;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+		dirnode->inode->st.st_mtimensec =	specnode->st_mtimespec.tv_nsec;
+		dirnode->inode->st.st_atimensec =	specnode->st_mtimespec.tv_nsec;
+		dirnode->inode->st.st_ctimensec =	start_time.tv_nsec;
+#endif
+	}
+	if (specnode->flags & (F_UID | F_UNAME)) {
+		ASEPRINT("uid", "%d",
+		    dirnode->inode->st.st_uid, specnode->st_uid);
+		dirnode->inode->st.st_uid = specnode->st_uid;
+	}
+#if HAVE_STRUCT_STAT_ST_FLAGS
+	if (specnode->flags & F_FLAGS) {
+		ASEPRINT("flags", "%#lX",
+		    (unsigned long)dirnode->inode->st.st_flags,
+		    (unsigned long)specnode->st_flags);
+		dirnode->inode->st.st_flags = specnode->st_flags;
+	}
+#endif
+/*	if (specnode->flags & F_DEV) {
+		ASEPRINT("rdev", "%#llx",
+		    (unsigned long long)dirnode->inode->st.st_rdev,
+		    (unsigned long long)specnode->st_rdev);
+		dirnode->inode->st.st_rdev = specnode->st_rdev;
+	}*/
+#undef ASEPRINT
+
+	dirnode->flags |= FSNODE_F_HASSPEC;
+}
+#endif
+
+/*
+ * dump_fsnodes --
+ *	dump the fsnodes from `cur'
+ */
+void
+dump_fsnodes(fsnode *root)
+{
+	fsnode	*cur;
+	char	path[MAXPATHLEN + 1];
+
+	printf("dump_fsnodes: %s %p\n", root->path, root);
+	for (cur = root; cur != NULL; cur = cur->next) {
+		if (snprintf(path, sizeof(path), "%s/%s", cur->path,
+		    cur->name) >= (int)sizeof(path))
+			errx(1, "Pathname too long.");
+
+		if (debug & DEBUG_DUMP_FSNODES_VERBOSE)
+			printf("cur=%8p parent=%8p first=%8p ",
+			    cur, cur->parent, cur->first);
+		printf("%7s: %s", inode_type(cur->type), path);
+		if (S_ISLNK(cur->type)) {
+			assert(cur->symlink != NULL);
+			printf(" -> %s", cur->symlink);
+		} else {
+			assert (cur->symlink == NULL);
+		}
+		if (cur->inode->nlink > 1)
+			printf(", nlinks=%d", cur->inode->nlink);
+		putchar('\n');
+
+		if (cur->child) {
+			assert (cur->type == S_IFDIR);
+			dump_fsnodes(cur->child);
+		}
+	}
+	printf("dump_fsnodes: finished %s/%s\n", root->path, root->name);
+}
+
+
+/*
+ * inode_type --
+ *	for a given inode type `mode', return a descriptive string.
+ *	for most cases, uses inotype() from mtree/misc.c
+ */
+const char *
+inode_type(mode_t mode)
+{
+	if (S_ISREG(mode))
+		return ("file");
+	if (S_ISLNK(mode))
+		return ("symlink");
+	if (S_ISDIR(mode))
+		return ("dir");
+	if (S_ISLNK(mode))
+		return ("link");
+	if (S_ISFIFO(mode))
+		return ("fifo");
+	if (S_ISSOCK(mode))
+		return ("socket");
+	/* XXX should not happen but handle them */
+	if (S_ISCHR(mode))
+		return ("char");
+	if (S_ISBLK(mode))
+		return ("block");
+	return ("unknown");
+}
+
+
+/*
+ * link_check --
+ *	return pointer to fsinode matching `entry's st_ino & st_dev if it exists,
+ *	otherwise add `entry' to table and return NULL
+ */
+/* This was borrowed from du.c and tweaked to keep an fsnode 
+ * pointer instead. -- dbj@netbsd.org
+ */
+fsinode *
+link_check(fsinode *entry)
+{
+	static struct entry {
+		fsinode *data;
+	} *htable;
+	static int htshift;  /* log(allocated size) */
+	static int htmask;   /* allocated size - 1 */
+	static int htused;   /* 2*number of insertions */
+	int h, h2;
+	uint64_t tmp;
+	/* this constant is (1<<64)/((1+sqrt(5))/2)
+	 * aka (word size)/(golden ratio)
+	 */
+	const uint64_t HTCONST = 11400714819323198485ULL;
+	const int HTBITS = 64;
+	
+	/* Never store zero in hashtable */
+	assert(entry);
+
+	/* Extend hash table if necessary, keep load under 0.5 */
+	if (htused<<1 >= htmask) {
+		struct entry *ohtable;
+
+		if (!htable)
+			htshift = 10;   /* starting hashtable size */
+		else
+			htshift++;   /* exponential hashtable growth */
+
+		htmask  = (1 << htshift) - 1;
+		htused = 0;
+
+		ohtable = htable;
+		htable = calloc(htmask+1, sizeof(*htable));
+		if (!htable)
+			err(1, "Memory allocation error");
+
+		/* populate newly allocated hashtable */
+		if (ohtable) {
+			int i;
+			for (i = 0; i <= htmask>>1; i++)
+				if (ohtable[i].data)
+					link_check(ohtable[i].data);
+			free(ohtable);
+		}
+	}
+
+	/* multiplicative hashing */
+	tmp = entry->st.st_dev;
+	tmp <<= HTBITS>>1;
+	tmp |=  entry->st.st_ino;
+	tmp *= HTCONST;
+	h  = tmp >> (HTBITS - htshift);
+	h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */
+	
+	/* open address hashtable search with double hash probing */
+	while (htable[h].data) {
+		if ((htable[h].data->st.st_ino == entry->st.st_ino) &&
+		    (htable[h].data->st.st_dev == entry->st.st_dev)) {
+			return htable[h].data;
+		}
+		h = (h + h2) & htmask;
+	}
+
+	/* Insert the current entry into hashtable */
+	htable[h].data = entry;
+	htused++;
+	return NULL;
+}
-- 
2.16.2

