summaryrefslogtreecommitdiffstats
path: root/usr.sbin/makefs
diff options
context:
space:
mode:
authorsam <sam@FreeBSD.org>2008-12-19 18:47:46 +0000
committersam <sam@FreeBSD.org>2008-12-19 18:47:46 +0000
commit04b1d503e162172ac1f99cab401b24577008d8a5 (patch)
tree5b7fad36ebd9c20647ab964d225fd877ac7e71bb /usr.sbin/makefs
parent02652b132c30b431a86bae5535d22c29f316e7dc (diff)
parentc7e77b59099de503f20d8b8fd7e07548aef51b0e (diff)
downloadFreeBSD-src-04b1d503e162172ac1f99cab401b24577008d8a5.zip
FreeBSD-src-04b1d503e162172ac1f99cab401b24577008d8a5.tar.gz
import netbsd makefs tool
Diffstat (limited to 'usr.sbin/makefs')
-rw-r--r--usr.sbin/makefs/Makefile28
-rw-r--r--usr.sbin/makefs/compat/pwcache.c623
-rw-r--r--usr.sbin/makefs/compat/pwcache.h73
-rw-r--r--usr.sbin/makefs/compat/strsuftoll.c229
-rw-r--r--usr.sbin/makefs/ffs.c1093
-rw-r--r--usr.sbin/makefs/ffs/buf.c222
-rw-r--r--usr.sbin/makefs/ffs/buf.h67
-rw-r--r--usr.sbin/makefs/ffs/ffs_alloc.c683
-rw-r--r--usr.sbin/makefs/ffs/ffs_balloc.c578
-rw-r--r--usr.sbin/makefs/ffs/ffs_bswap.c270
-rw-r--r--usr.sbin/makefs/ffs/ffs_extern.h77
-rw-r--r--usr.sbin/makefs/ffs/ffs_subr.c202
-rw-r--r--usr.sbin/makefs/ffs/mkfs.c832
-rw-r--r--usr.sbin/makefs/ffs/newfs_extern.h41
-rw-r--r--usr.sbin/makefs/ffs/ufs_bmap.c142
-rw-r--r--usr.sbin/makefs/ffs/ufs_bswap.h94
-rw-r--r--usr.sbin/makefs/ffs/ufs_inode.h97
-rw-r--r--usr.sbin/makefs/getid.c436
-rw-r--r--usr.sbin/makefs/makefs.8288
-rw-r--r--usr.sbin/makefs/makefs.c314
-rw-r--r--usr.sbin/makefs/makefs.h305
-rw-r--r--usr.sbin/makefs/walk.c568
22 files changed, 7262 insertions, 0 deletions
diff --git a/usr.sbin/makefs/Makefile b/usr.sbin/makefs/Makefile
new file mode 100644
index 0000000..8626dd0
--- /dev/null
+++ b/usr.sbin/makefs/Makefile
@@ -0,0 +1,28 @@
+# $FreeBSD$
+
+PROG= makefs
+MAN= makefs.8
+
+WARNS?= 2
+
+CFLAGS+=-I.
+SRCS= ffs.c getid.c makefs.c walk.c
+
+.PATH: ${.CURDIR}/ffs
+CFLAGS+=-Iffs
+CFLAGS+=-DHAVE_STRUCT_STAT_ST_FLAGS=1
+CFLAGS+=-DHAVE_STRUCT_STAT_ST_GEN=1
+SRCS+= buf.c ffs_alloc.c ffs_balloc.c ffs_bswap.c ffs_subr.c mkfs.c ufs_bmap.c
+
+.PATH: ${.CURDIR}/compat
+CFLAGS+=-Icompat
+SRCS+= pwcache.c strsuftoll.c
+
+.PATH: ${.CURDIR}/../mtree
+CFLAGS+=-I../mtree
+SRCS+= misc.c spec.c
+
+.PATH: ${.CURDIR}/../../sys/ufs/ffs
+SRCS+= ffs_tables.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/makefs/compat/pwcache.c b/usr.sbin/makefs/compat/pwcache.c
new file mode 100644
index 0000000..0579d26
--- /dev/null
+++ b/usr.sbin/makefs/compat/pwcache.c
@@ -0,0 +1,623 @@
+/* $NetBSD: pwcache.c,v 1.29 2004/06/20 22:20:14 jmc Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * 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.
+ *
+ * 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.
+ *
+ * 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 the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <assert.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __weak_alias
+__weak_alias(user_from_uid,_user_from_uid)
+__weak_alias(group_from_gid,_group_from_gid)
+__weak_alias(pwcache_userdb,_pwcache_userdb)
+__weak_alias(pwcache_groupdb,_pwcache_groupdb)
+#endif
+
+#include "pwcache.h"
+
+/*
+ * routines that control user, group, uid and gid caches (for the archive
+ * member print routine).
+ * IMPORTANT:
+ * these routines cache BOTH hits and misses, a major performance improvement
+ */
+
+/*
+ * function pointers to various name lookup routines.
+ * these may be changed as necessary.
+ */
+static int (*_pwcache_setgroupent)(int) = setgroupent;
+static void (*_pwcache_endgrent)(void) = endgrent;
+static struct group * (*_pwcache_getgrnam)(const char *) = getgrnam;
+static struct group * (*_pwcache_getgrgid)(gid_t) = getgrgid;
+static int (*_pwcache_setpassent)(int) = setpassent;
+static void (*_pwcache_endpwent)(void) = endpwent;
+static struct passwd * (*_pwcache_getpwnam)(const char *) = getpwnam;
+static struct passwd * (*_pwcache_getpwuid)(uid_t) = getpwuid;
+
+/*
+ * internal state
+ */
+static int pwopn; /* is password file open */
+static int gropn; /* is group file open */
+static UIDC **uidtb; /* uid to name cache */
+static GIDC **gidtb; /* gid to name cache */
+static UIDC **usrtb; /* user name to uid cache */
+static GIDC **grptb; /* group name to gid cache */
+
+static int uidtb_fail; /* uidtb_start() failed ? */
+static int gidtb_fail; /* gidtb_start() failed ? */
+static int usrtb_fail; /* usrtb_start() failed ? */
+static int grptb_fail; /* grptb_start() failed ? */
+
+
+static u_int st_hash(const char *, size_t, int);
+static int uidtb_start(void);
+static int gidtb_start(void);
+static int usrtb_start(void);
+static int grptb_start(void);
+
+
+static u_int
+st_hash(const char *name, size_t len, int tabsz)
+{
+ u_int key = 0;
+
+ while (len--) {
+ key += *name++;
+ key = (key << 8) | (key >> 24);
+ }
+
+ return (key % tabsz);
+}
+
+/*
+ * uidtb_start
+ * creates an an empty uidtb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+static int
+uidtb_start(void)
+{
+
+ if (uidtb != NULL)
+ return (0);
+ if (uidtb_fail)
+ return (-1);
+ if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
+ ++uidtb_fail;
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * gidtb_start
+ * creates an an empty gidtb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+static int
+gidtb_start(void)
+{
+
+ if (gidtb != NULL)
+ return (0);
+ if (gidtb_fail)
+ return (-1);
+ if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
+ ++gidtb_fail;
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * usrtb_start
+ * creates an an empty usrtb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+static int
+usrtb_start(void)
+{
+
+ if (usrtb != NULL)
+ return (0);
+ if (usrtb_fail)
+ return (-1);
+ if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
+ ++usrtb_fail;
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * grptb_start
+ * creates an an empty grptb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+static int
+grptb_start(void)
+{
+
+ if (grptb != NULL)
+ return (0);
+ if (grptb_fail)
+ return (-1);
+ if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
+ ++grptb_fail;
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * user_from_uid()
+ * caches the name (if any) for the uid. If noname clear, we always
+ * return the stored name (if valid or invalid match).
+ * We use a simple hash table.
+ * Return
+ * Pointer to stored name (or a empty string)
+ */
+const char *
+user_from_uid(uid_t uid, int noname)
+{
+ struct passwd *pw;
+ UIDC *ptr, **pptr;
+
+ if ((uidtb == NULL) && (uidtb_start() < 0))
+ return (NULL);
+
+ /*
+ * see if we have this uid cached
+ */
+ pptr = uidtb + (uid % UID_SZ);
+ ptr = *pptr;
+
+ if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
+ /*
+ * have an entry for this uid
+ */
+ if (!noname || (ptr->valid == VALID))
+ return (ptr->name);
+ return (NULL);
+ }
+
+ /*
+ * No entry for this uid, we will add it
+ */
+ if (!pwopn) {
+ if (_pwcache_setpassent != NULL)
+ (*_pwcache_setpassent)(1);
+ ++pwopn;
+ }
+
+ if (ptr == NULL)
+ *pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
+
+ if ((pw = (*_pwcache_getpwuid)(uid)) == NULL) {
+ /*
+ * no match for this uid in the local password file
+ * a string that is the uid in numeric format
+ */
+ if (ptr == NULL)
+ return (NULL);
+ ptr->uid = uid;
+ (void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid);
+ ptr->valid = INVALID;
+ if (noname)
+ return (NULL);
+ } else {
+ /*
+ * there is an entry for this uid in the password file
+ */
+ if (ptr == NULL)
+ return (pw->pw_name);
+ ptr->uid = uid;
+ (void)strlcpy(ptr->name, pw->pw_name, UNMLEN);
+ ptr->valid = VALID;
+ }
+ return (ptr->name);
+}
+
+/*
+ * group_from_gid()
+ * caches the name (if any) for the gid. If noname clear, we always
+ * return the stored name (if valid or invalid match).
+ * We use a simple hash table.
+ * Return
+ * Pointer to stored name (or a empty string)
+ */
+const char *
+group_from_gid(gid_t gid, int noname)
+{
+ struct group *gr;
+ GIDC *ptr, **pptr;
+
+ if ((gidtb == NULL) && (gidtb_start() < 0))
+ return (NULL);
+
+ /*
+ * see if we have this gid cached
+ */
+ pptr = gidtb + (gid % GID_SZ);
+ ptr = *pptr;
+
+ if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
+ /*
+ * have an entry for this gid
+ */
+ if (!noname || (ptr->valid == VALID))
+ return (ptr->name);
+ return (NULL);
+ }
+
+ /*
+ * No entry for this gid, we will add it
+ */
+ if (!gropn) {
+ if (_pwcache_setgroupent != NULL)
+ (*_pwcache_setgroupent)(1);
+ ++gropn;
+ }
+
+ if (ptr == NULL)
+ *pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
+
+ if ((gr = (*_pwcache_getgrgid)(gid)) == NULL) {
+ /*
+ * no match for this gid in the local group file, put in
+ * a string that is the gid in numberic format
+ */
+ if (ptr == NULL)
+ return (NULL);
+ ptr->gid = gid;
+ (void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid);
+ ptr->valid = INVALID;
+ if (noname)
+ return (NULL);
+ } else {
+ /*
+ * there is an entry for this group in the group file
+ */
+ if (ptr == NULL)
+ return (gr->gr_name);
+ ptr->gid = gid;
+ (void)strlcpy(ptr->name, gr->gr_name, GNMLEN);
+ ptr->valid = VALID;
+ }
+ return (ptr->name);
+}
+
+/*
+ * uid_from_user()
+ * caches the uid for a given user name. We use a simple hash table.
+ * Return
+ * the uid (if any) for a user name, or a -1 if no match can be found
+ */
+int
+uid_from_user(const char *name, uid_t *uid)
+{
+ struct passwd *pw;
+ UIDC *ptr, **pptr;
+ size_t namelen;
+
+ /*
+ * return -1 for mangled names
+ */
+ if (name == NULL || ((namelen = strlen(name)) == 0))
+ return (-1);
+ if ((usrtb == NULL) && (usrtb_start() < 0))
+ return (-1);
+
+ /*
+ * look up in hash table, if found and valid return the uid,
+ * if found and invalid, return a -1
+ */
+ pptr = usrtb + st_hash(name, namelen, UNM_SZ);
+ ptr = *pptr;
+
+ if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
+ if (ptr->valid == INVALID)
+ return (-1);
+ *uid = ptr->uid;
+ return (0);
+ }
+
+ if (!pwopn) {
+ if (_pwcache_setpassent != NULL)
+ (*_pwcache_setpassent)(1);
+ ++pwopn;
+ }
+
+ if (ptr == NULL)
+ *pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
+
+ /*
+ * no match, look it up, if no match store it as an invalid entry,
+ * or store the matching uid
+ */
+ if (ptr == NULL) {
+ if ((pw = (*_pwcache_getpwnam)(name)) == NULL)
+ return (-1);
+ *uid = pw->pw_uid;
+ return (0);
+ }
+ (void)strlcpy(ptr->name, name, UNMLEN);
+ if ((pw = (*_pwcache_getpwnam)(name)) == NULL) {
+ ptr->valid = INVALID;
+ return (-1);
+ }
+ ptr->valid = VALID;
+ *uid = ptr->uid = pw->pw_uid;
+ return (0);
+}
+
+/*
+ * gid_from_group()
+ * caches the gid for a given group name. We use a simple hash table.
+ * Return
+ * the gid (if any) for a group name, or a -1 if no match can be found
+ */
+int
+gid_from_group(const char *name, gid_t *gid)
+{
+ struct group *gr;
+ GIDC *ptr, **pptr;
+ size_t namelen;
+
+ /*
+ * return -1 for mangled names
+ */
+ if (name == NULL || ((namelen = strlen(name)) == 0))
+ return (-1);
+ if ((grptb == NULL) && (grptb_start() < 0))
+ return (-1);
+
+ /*
+ * look up in hash table, if found and valid return the uid,
+ * if found and invalid, return a -1
+ */
+ pptr = grptb + st_hash(name, namelen, GID_SZ);
+ ptr = *pptr;
+
+ if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
+ if (ptr->valid == INVALID)
+ return (-1);
+ *gid = ptr->gid;
+ return (0);
+ }
+
+ if (!gropn) {
+ if (_pwcache_setgroupent != NULL)
+ (*_pwcache_setgroupent)(1);
+ ++gropn;
+ }
+
+ if (ptr == NULL)
+ *pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
+
+ /*
+ * no match, look it up, if no match store it as an invalid entry,
+ * or store the matching gid
+ */
+ if (ptr == NULL) {
+ if ((gr = (*_pwcache_getgrnam)(name)) == NULL)
+ return (-1);
+ *gid = gr->gr_gid;
+ return (0);
+ }
+
+ (void)strlcpy(ptr->name, name, GNMLEN);
+ if ((gr = (*_pwcache_getgrnam)(name)) == NULL) {
+ ptr->valid = INVALID;
+ return (-1);
+ }
+ ptr->valid = VALID;
+ *gid = ptr->gid = gr->gr_gid;
+ return (0);
+}
+
+#define FLUSHTB(arr, len, fail) \
+ do { \
+ if (arr != NULL) { \
+ for (i = 0; i < len; i++) \
+ if (arr[i] != NULL) \
+ free(arr[i]); \
+ arr = NULL; \
+ } \
+ fail = 0; \
+ } while (/* CONSTCOND */0);
+
+int
+pwcache_userdb(
+ int (*a_setpassent)(int),
+ void (*a_endpwent)(void),
+ struct passwd * (*a_getpwnam)(const char *),
+ struct passwd * (*a_getpwuid)(uid_t))
+{
+ int i;
+
+ /* a_setpassent and a_endpwent may be NULL */
+ if (a_getpwnam == NULL || a_getpwuid == NULL)
+ return (-1);
+
+ if (_pwcache_endpwent != NULL)
+ (*_pwcache_endpwent)();
+ FLUSHTB(uidtb, UID_SZ, uidtb_fail);
+ FLUSHTB(usrtb, UNM_SZ, usrtb_fail);
+ pwopn = 0;
+ _pwcache_setpassent = a_setpassent;
+ _pwcache_endpwent = a_endpwent;
+ _pwcache_getpwnam = a_getpwnam;
+ _pwcache_getpwuid = a_getpwuid;
+
+ return (0);
+}
+
+int
+pwcache_groupdb(
+ int (*a_setgroupent)(int),
+ void (*a_endgrent)(void),
+ struct group * (*a_getgrnam)(const char *),
+ struct group * (*a_getgrgid)(gid_t))
+{
+ int i;
+
+ /* a_setgroupent and a_endgrent may be NULL */
+ if (a_getgrnam == NULL || a_getgrgid == NULL)
+ return (-1);
+
+ if (_pwcache_endgrent != NULL)
+ (*_pwcache_endgrent)();
+ FLUSHTB(gidtb, GID_SZ, gidtb_fail);
+ FLUSHTB(grptb, GNM_SZ, grptb_fail);
+ gropn = 0;
+ _pwcache_setgroupent = a_setgroupent;
+ _pwcache_endgrent = a_endgrent;
+ _pwcache_getgrnam = a_getgrnam;
+ _pwcache_getgrgid = a_getgrgid;
+
+ return (0);
+}
+
+
+#ifdef TEST_PWCACHE
+
+struct passwd *
+test_getpwnam(const char *name)
+{
+ static struct passwd foo;
+
+ memset(&foo, 0, sizeof(foo));
+ if (strcmp(name, "toor") == 0) {
+ foo.pw_uid = 666;
+ return &foo;
+ }
+ return (getpwnam(name));
+}
+
+int
+main(int argc, char *argv[])
+{
+ uid_t u;
+ int r, i;
+
+ printf("pass 1 (default userdb)\n");
+ for (i = 1; i < argc; i++) {
+ printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
+ i, pwopn, usrtb_fail, usrtb);
+ r = uid_from_user(argv[i], &u);
+ if (r == -1)
+ printf(" uid_from_user %s: failed\n", argv[i]);
+ else
+ printf(" uid_from_user %s: %d\n", argv[i], u);
+ }
+ printf("pass 1 finish: pwopn %d usrtb_fail %d usrtb %p\n",
+ pwopn, usrtb_fail, usrtb);
+
+ puts("");
+ printf("pass 2 (replacement userdb)\n");
+ printf("pwcache_userdb returned %d\n",
+ pwcache_userdb(setpassent, test_getpwnam, getpwuid));
+ printf("pwopn %d usrtb_fail %d usrtb %p\n", pwopn, usrtb_fail, usrtb);
+
+ for (i = 1; i < argc; i++) {
+ printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
+ i, pwopn, usrtb_fail, usrtb);
+ u = -1;
+ r = uid_from_user(argv[i], &u);
+ if (r == -1)
+ printf(" uid_from_user %s: failed\n", argv[i]);
+ else
+ printf(" uid_from_user %s: %d\n", argv[i], u);
+ }
+ printf("pass 2 finish: pwopn %d usrtb_fail %d usrtb %p\n",
+ pwopn, usrtb_fail, usrtb);
+
+ puts("");
+ printf("pass 3 (null pointers)\n");
+ printf("pwcache_userdb returned %d\n",
+ pwcache_userdb(NULL, NULL, NULL));
+
+ return (0);
+}
+#endif /* TEST_PWCACHE */
diff --git a/usr.sbin/makefs/compat/pwcache.h b/usr.sbin/makefs/compat/pwcache.h
new file mode 100644
index 0000000..1708951
--- /dev/null
+++ b/usr.sbin/makefs/compat/pwcache.h
@@ -0,0 +1,73 @@
+/* $NetBSD: pwcache.h,v 1.5 2003/11/10 08:51:51 wiz Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * 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.
+ *
+ * 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.
+ *
+ * @(#)cache.h 8.1 (Berkeley) 5/31/93
+ * $FreeBSD$
+ */
+
+/*
+ * Constants and data structures used to implement group and password file
+ * caches. Traditional passwd/group cache routines perform quite poorly with
+ * archives. The chances of hitting a valid lookup with an archive is quite a
+ * bit worse than with files already resident on the file system. These misses
+ * create a MAJOR performance cost. To address this problem, these routines
+ * cache both hits and misses.
+ *
+ * NOTE: name lengths must be as large as those stored in ANY PROTOCOL and
+ * as stored in the passwd and group files. CACHE SIZES MUST BE PRIME
+ */
+#define UNMLEN 32 /* >= user name found in any protocol */
+#define GNMLEN 32 /* >= group name found in any protocol */
+#define UID_SZ 317 /* size of uid to user_name cache */
+#define UNM_SZ 317 /* size of user_name to uid cache */
+#define GID_SZ 251 /* size of gid to group_name cache */
+#define GNM_SZ 251 /* size of group_name to gid cache */
+#define VALID 1 /* entry and name are valid */
+#define INVALID 2 /* entry valid, name NOT valid */
+
+/*
+ * Node structures used in the user, group, uid, and gid caches.
+ */
+
+typedef struct uidc {
+ int valid; /* is this a valid or a miss entry */
+ char name[UNMLEN]; /* uid name */
+ uid_t uid; /* cached uid */
+} UIDC;
+
+typedef struct gidc {
+ int valid; /* is this a valid or a miss entry */
+ char name[GNMLEN]; /* gid name */
+ gid_t gid; /* cached gid */
+} GIDC;
diff --git a/usr.sbin/makefs/compat/strsuftoll.c b/usr.sbin/makefs/compat/strsuftoll.c
new file mode 100644
index 0000000..f73e3ad
--- /dev/null
+++ b/usr.sbin/makefs/compat/strsuftoll.c
@@ -0,0 +1,229 @@
+/* $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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. 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.
+ */
+/*-
+ * 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$");
+
+#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/usr.sbin/makefs/ffs.c b/usr.sbin/makefs/ffs.c
new file mode 100644
index 0000000..c9f4fb5
--- /dev/null
+++ b/usr.sbin/makefs/ffs.c
@@ -0,0 +1,1093 @@
+/* $NetBSD: ffs.c,v 1.30 2004/06/24 22:30:13 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.
+ */
+/*
+ * 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$");
+
+#include <sys/param.h>
+
+#include <sys/mount.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 "makefs.h"
+
+#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) \
+ ((fsopts->version == 1) ? \
+ (dp)->ffs1_din.di_##field : (dp)->ffs2_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 *);
+static void *ffs_build_dinode2(struct ufs2_dinode *, dirbuf_t *, fsnode *,
+ fsnode *, fsinfo_t *);
+
+
+
+int sectorsize; /* XXX: for buf.c::getblk() */
+
+ /* publically visible functions */
+
+int
+ffs_parse_opts(const char *option, fsinfo_t *fsopts)
+{
+ option_t ffs_options[] = {
+ { "bsize", &fsopts->bsize, 1, INT_MAX,
+ "block size" },
+ { "fsize", &fsopts->fsize, 1, INT_MAX,
+ "fragment size" },
+ { "density", &fsopts->density, 1, INT_MAX,
+ "bytes per inode" },
+ { "minfree", &fsopts->minfree, 0, 99,
+ "minfree" },
+ { "maxbpf", &fsopts->maxbpg, 1, INT_MAX,
+ "max blocks per file in a cg" },
+ { "avgfilesize", &fsopts->avgfilesize, 1, INT_MAX,
+ "expected average file size" },
+ { "avgfpdir", &fsopts->avgfpdir, 1, INT_MAX,
+ "expected # of files per directory" },
+ { "extent", &fsopts->maxbsize, 1, INT_MAX,
+ "maximum # extent size" },
+ { "maxbpcg", &fsopts->maxblkspercg, 1, INT_MAX,
+ "max # of blocks per group" },
+ { "version", &fsopts->version, 1, 2,
+ "UFS version" },
+ { NULL }
+ };
+
+ char *var, *val;
+ int rv;
+
+ (void)&ffs_options;
+ assert(option != NULL);
+ assert(fsopts != 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) {
+ fsopts->optimization = FS_OPTTIME;
+ } else if (strcmp(val, "space") == 0) {
+ fsopts->optimization = FS_OPTSPACE;
+ } else {
+ warnx("Invalid optimization `%s'", val);
+ goto leave_ffs_parse_opts;
+ }
+ 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;
+ superblock->fs_old_cstotal.cs_ndir = superblock->fs_cstotal.cs_ndir;
+ superblock->fs_old_cstotal.cs_nbfree = superblock->fs_cstotal.cs_nbfree;
+ superblock->fs_old_cstotal.cs_nifree = superblock->fs_cstotal.cs_nifree;
+ superblock->fs_old_cstotal.cs_nffree = superblock->fs_cstotal.cs_nffree;
+
+ /* 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
+ off_t size;
+
+ assert(dir != NULL);
+ assert(root != NULL);
+ assert(fsopts != 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 (fsopts->fsize == -1)
+ fsopts->fsize = MAX(DFL_FRAGSIZE, fsopts->sectorsize);
+ if (fsopts->bsize == -1)
+ fsopts->bsize = MIN(DFL_BLKSIZE, 8 * fsopts->fsize);
+ if (fsopts->cpg == -1)
+ fsopts->cpg = DFL_CYLSPERGROUP;
+ else
+ fsopts->cpgflg = 1;
+ /* fsopts->density is set below */
+ if (fsopts->nsectors == -1)
+ fsopts->nsectors = DFL_NSECTORS;
+ if (fsopts->minfree == -1)
+ fsopts->minfree = MINFREE;
+ if (fsopts->optimization == -1)
+ fsopts->optimization = DEFAULTOPT;
+ if (fsopts->maxcontig == -1)
+ fsopts->maxcontig =
+ MAX(1, MIN(MAXPHYS, FFS_MAXBSIZE) / fsopts->bsize);
+ /* XXX ondisk32 */
+ if (fsopts->maxbpg == -1)
+ fsopts->maxbpg = fsopts->bsize / sizeof(int32_t);
+ if (fsopts->avgfilesize == -1)
+ fsopts->avgfilesize = AVFILESIZ;
+ if (fsopts->avgfpdir == -1)
+ fsopts->avgfpdir = AFPDIR;
+
+ /* 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 += (SBLOCK_UFS1 + SBLOCKSIZE) * ncg;
+ /* add space needed to store inodes, x3 for blockmaps, etc */
+ if (fsopts->version == 1)
+ fsopts->size += ncg * DINODE1_SIZE *
+ roundup(fsopts->inodes / ncg, fsopts->bsize / DINODE1_SIZE);
+ else
+ fsopts->size += ncg * DINODE2_SIZE *
+ roundup(fsopts->inodes / ncg, fsopts->bsize / DINODE2_SIZE);
+
+ /* add minfree */
+ if (fsopts->minfree > 0)
+ fsopts->size =
+ fsopts->size * (100 + fsopts->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 */
+ size = roundup(fsopts->size, fsopts->bsize);
+
+ /* now check calculated sizes vs requested sizes */
+ if (fsopts->maxsize > 0 && size > fsopts->maxsize) {
+ if (debug & DEBUG_FS_VALIDATE) {
+ printf("%s: `%s' size of %lld is larger than the "
+ "maxsize of %lld; rounding down to %lld.",
+ __func__, dir, (long long)size,
+ (long long)fsopts->maxsize,
+ rounddown(fsopts->size, fsopts->bsize));
+ }
+ size = rounddown(fsopts->size, fsopts->bsize);
+ }
+ fsopts->size = size;
+
+ /* calculate density if necessary */
+ if (fsopts->density == -1)
+ fsopts->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 */
+}
+
+
+static void
+ffs_dump_fsinfo(fsinfo_t *f)
+{
+
+ 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",
+ f->bsize, f->fsize, f->cpg, f->density);
+ printf("\tnsectors %d, rpm %d, minfree %d\n",
+ f->nsectors, f->rpm, f->minfree);
+ printf("\tmaxcontig %d, maxbpg %d\n",
+ f->maxcontig, f->maxbpg);
+ printf("\toptimization %s\n",
+ f->optimization == FS_OPTSPACE ? "space" : "time");
+}
+
+
+static int
+ffs_create_image(const char *image, fsinfo_t *fsopts)
+{
+#if HAVE_STRUCT_STATVFS_F_IOSIZE
+ struct statvfs sfs;
+#endif
+ struct fs *fs;
+ char *buf;
+ int i, bufsize;
+ off_t bufrem;
+
+ assert (image != NULL);
+ assert (fsopts != NULL);
+
+ /* create image */
+ if ((fsopts->fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0777))
+ == -1) {
+ warn("Can't open `%s' for writing", image);
+ return (-1);
+ }
+
+ /* zero image */
+#if HAVE_STRUCT_STATVFS_F_IOSIZE
+ if (fstatvfs(fsopts->fd, &sfs) == -1) {
+#endif
+ bufsize = 8192;
+#if HAVE_STRUCT_STATVFS_F_IOSIZE
+ warn("can't fstatvfs `%s', using default %d byte chunk",
+ image, bufsize);
+ } else
+ bufsize = sfs.f_iosize;
+#endif
+ bufrem = fsopts->size;
+ 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);
+ return (-1);
+ }
+ bufrem -= i;
+ }
+
+ /* make the file system */
+ if (debug & DEBUG_FS_CREATE_IMAGE)
+ printf("calling mkfs(\"%s\", ...)\n", image);
+ fs = ffs_mkfs(image, fsopts);
+ 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;
+
+ /* node may be NULL (empty directory) */
+ assert(fsopts != 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), fsopts->fsize); \
+} while (0);
+
+ curdirsize = 0;
+ for (node = root; node != NULL; node = node->next) {
+ ADDDIRENT(node->name);
+ if (FSNODE_EXCLUDE_P(fsopts, node))
+ continue;
+ 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 >= (fsopts->version == 1 ?
+ MAXSYMLINKLEN_UFS1 :
+ MAXSYMLINKLEN_UFS2))
+ 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;
+
+ 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;
+ dinp->di_atime = cur->inode->st.st_atime;
+ dinp->di_mtime = cur->inode->st.st_mtime;
+ dinp->di_ctime = cur->inode->st.st_ctime;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+ dinp->di_atimensec = cur->inode->st.st_atimensec;
+ dinp->di_mtimensec = cur->inode->st.st_mtimensec;
+ dinp->di_ctimensec = cur->inode->st.st_ctimensec;
+#endif
+#if HAVE_STRUCT_STAT_ST_FLAGS
+ dinp->di_flags = cur->inode->st.st_flags;
+#endif
+#if HAVE_STRUCT_STAT_ST_GEN
+ dinp->di_gen = cur->inode->st.st_gen;
+#endif
+ dinp->di_uid = cur->inode->st.st_uid;
+ dinp->di_gid = cur->inode->st.st_gid;
+ /* 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 void *
+ffs_build_dinode2(struct ufs2_dinode *dinp, dirbuf_t *dbufp, fsnode *cur,
+ fsnode *root, fsinfo_t *fsopts)
+{
+ int slen;
+ void *membuf;
+
+ 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;
+ dinp->di_atime = cur->inode->st.st_atime;
+ dinp->di_mtime = cur->inode->st.st_mtime;
+ dinp->di_ctime = cur->inode->st.st_ctime;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+ dinp->di_atimensec = cur->inode->st.st_atimensec;
+ dinp->di_mtimensec = cur->inode->st.st_mtimensec;
+ dinp->di_ctimensec = cur->inode->st.st_ctimensec;
+#endif
+#if HAVE_STRUCT_STAT_ST_FLAGS
+ dinp->di_flags = cur->inode->st.st_flags;
+#endif
+#if HAVE_STRUCT_STAT_ST_GEN
+ dinp->di_gen = cur->inode->st.st_gen;
+#endif
+#if HAVE_STRUCT_STAT_BIRTHTIME
+ dinp->di_birthtime = cur->inode->st.st_birthtime;
+ dinp->di_birthnsec = cur->inode->st.st_birthtimensec;
+#endif
+ dinp->di_uid = cur->inode->st.st_uid;
+ dinp->di_gid = cur->inode->st.st_gid;
+ /* 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_rw64(cur->inode->st.st_rdev, fsopts->needswap);
+ } else if (S_ISLNK(cur->type)) { /* symlink */
+ slen = strlen(cur->symlink);
+ if (slen < MAXSYMLINKLEN_UFS2) { /* 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];
+
+ assert(dir != NULL);
+ assert(root != NULL);
+ assert(fsopts != 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 (FSNODE_EXCLUDE_P(fsopts, cur))
+ continue;
+ 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 (FSNODE_EXCLUDE_P(fsopts, cur))
+ continue;
+ if (cur->inode->flags & FI_WRITTEN)
+ continue; /* skip hard-linked entries */
+ cur->inode->flags |= FI_WRITTEN;
+
+ if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name)
+ >= sizeof(path))
+ errx(1, "Pathname too long.");
+
+ if (cur->child != NULL)
+ continue; /* child creates own inode */
+
+ /* build on-disk inode */
+ if (fsopts->version == 1)
+ membuf = ffs_build_dinode1(&din.ffs1_din, &dirbuf, cur,
+ root, fsopts);
+ else
+ membuf = ffs_build_dinode2(&din.ffs2_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, 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 (FSNODE_EXCLUDE_P(fsopts, cur))
+ continue;
+ 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;
+ struct inode in;
+ struct buf * bp;
+
+ assert (din != NULL);
+ assert (buf != NULL);
+ assert (fsopts != NULL);
+
+ isfile = S_ISREG(DIP(din, mode));
+ fbuf = NULL;
+ ffd = -1;
+
+ 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);
+ if (fsopts->version == 1)
+ memcpy(&in.i_din.ffs1_din, &din->ffs1_din,
+ sizeof(in.i_din.ffs1_din));
+ else
+ memcpy(&in.i_din.ffs2_din, &din->ffs2_din,
+ sizeof(in.i_din.ffs2_din));
+ in.i_fd = fsopts->fd;
+
+ if (DIP(din, size) == 0)
+ goto write_inode_and_leave; /* mmm, cheating */
+
+ if (isfile) {
+ if ((fbuf = malloc(fsopts->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, fsopts->bsize);
+ if (isfile) {
+ if (read(ffd, fbuf, chunk) != chunk)
+ err(1, "Reading `%s', %lld bytes to go",
+ (char *)buf, (long long)bufleft);
+ 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;
+ 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 { /* 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 ufs2_dinode *dp2, *dip;
+ struct cg *cgp;
+ struct fs *fs;
+ int cg, cgino, i;
+ daddr_t d;
+ char sbbuf[FFS_MAXBSIZE];
+ int32_t initediblk;
+
+ assert (dp != NULL);
+ assert (ino > 0);
+ assert (fsopts != 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;
+ dp2 = (struct ufs2_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++;
+ }
+
+ /*
+ * Initialize inode blocks on the fly for UFS2.
+ */
+ initediblk = ufs_rw32(cgp->cg_initediblk, fsopts->needswap);
+ if (fsopts->version == 2 && cgino + INOPB(fs) > initediblk &&
+ initediblk < ufs_rw32(cgp->cg_niblk, fsopts->needswap)) {
+ memset(buf, 0, fs->fs_bsize);
+ dip = (struct ufs2_dinode *)buf;
+ srandom(time(NULL));
+ for (i = 0; i < INOPB(fs); i++) {
+ dip->di_gen = random() / 2 + 1;
+ dip++;
+ }
+ ffs_wtfs(fsbtodb(fs, ino_to_fsba(fs,
+ cg * fs->fs_ipg + initediblk)),
+ fs->fs_bsize, buf, fsopts);
+ initediblk += INOPB(fs);
+ cgp->cg_initediblk = ufs_rw32(initediblk, fsopts->needswap);
+ }
+
+
+ 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) {
+ if (fsopts->version == 1)
+ ffs_dinode1_swap(&dp->ffs1_din,
+ &dp1[ino_to_fsbo(fs, ino)]);
+ else
+ ffs_dinode2_swap(&dp->ffs2_din,
+ &dp2[ino_to_fsbo(fs, ino)]);
+ } else {
+ if (fsopts->version == 1)
+ dp1[ino_to_fsbo(fs, ino)] = dp->ffs1_din;
+ else
+ dp2[ino_to_fsbo(fs, ino)] = dp->ffs2_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/buf.c b/usr.sbin/makefs/ffs/buf.c
new file mode 100644
index 0000000..08fb627
--- /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$");
+
+#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 0000000..02c6713
--- /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$
+ */
+
+#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 0000000..0fe65e6
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_alloc.c
@@ -0,0 +1,683 @@
+/* $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$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <errno.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, daddr_t bpref, int size,
+ daddr_t *bnp)
+{
+ struct fs *fs = ip->i_fs;
+ daddr_t bno;
+ int cg;
+
+ *bnp = 0;
+ if ((u_int)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) {
+ if (ip->i_fs->fs_magic == FS_UFS1_MAGIC)
+ ip->i_ffs1_blocks += size / DEV_BSIZE;
+ else
+ ip->i_ffs2_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(ip, lbn, indx, bap)
+ 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;
+
+ cgp = (struct cg *)bp->b_data;
+ blksfree = cg_blksfree_swap(cgp, needswap);
+ if (bpref == 0 || 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, 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, (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 ((u_int)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 %d", (long long)bno, 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 (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 0000000..0a048ad
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_balloc.c
@@ -0,0 +1,578 @@
+/* $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$");
+
+#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 **);
+static int ffs_balloc_ufs2(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)
+{
+ if (ip->i_fs->fs_magic == FS_UFS2_MAGIC)
+ return ffs_balloc_ufs2(ip, offset, bufsize, bpp);
+ else
+ 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);
+}
+
+static int
+ffs_balloc_ufs2(struct inode *ip, off_t offset, int bufsize, struct buf **bpp)
+{
+ daddr_t lbn, lastlbn;
+ int size;
+ struct buf *bp, *nbp;
+ struct fs *fs = ip->i_fs;
+ struct indir indirs[NIADDR + 2];
+ daddr_t newb, pref, nb;
+ int64_t *bap;
+ int osize, nsize, num, i, error;
+ int64_t *allocblk, allociblk[NIADDR + 1];
+ int64_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_ffs2_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_rw64(ip->i_ffs2_db[lbn], needswap);
+ if (nb != 0 && ip->i_ffs2_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_ffs2_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_ffs2_size < lblktosize(fs, lbn + 1))
+ nsize = fragroundup(fs, size);
+ else
+ nsize = fs->fs_bsize;
+ error = ffs_alloc(ip, lbn,
+ ffs_blkpref_ufs2(ip, lbn, (int)lbn,
+ &ip->i_ffs2_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_ffs2_db[lbn] = ufs_rw64(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_rw64(ip->i_ffs2_ib[indirs[0].in_off], needswap);
+ allocib = NULL;
+ allocblk = allociblk;
+ if (nb == 0) {
+ pref = ffs_blkpref_ufs2(ip, lbn, 0, (int64_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_ffs2_ib[indirs[0].in_off];
+ *allocib = ufs_rw64(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 = (int64_t *)bp->b_data;
+ nb = ufs_rw64(bap[indirs[i].in_off], needswap);
+ if (i == num)
+ break;
+ i++;
+ if (nb != 0) {
+ brelse(bp);
+ continue;
+ }
+ if (pref == 0)
+ pref = ffs_blkpref_ufs2(ip, lbn, 0, (int64_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_rw64(nb, needswap);
+
+ bwrite(bp);
+ }
+
+ /*
+ * Get the data block, allocating if necessary.
+ */
+
+ if (nb == 0) {
+ pref = ffs_blkpref_ufs2(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_rw64(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 0000000..d560884
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_bswap.c
@@ -0,0 +1,270 @@
+/* $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$");
+
+#include <sys/param.h>
+#if defined(_KERNEL)
+#include <sys/systm.h>
+#endif
+
+#include <ufs/ufs/dinode.h>
+#include "ffs/ufs_bswap.h"
+#include <ufs/ffs/fs.h>
+/* XXX temporary */
+struct ufsmount;
+struct bufobj;
+struct mount;
+struct vnode;
+typedef int vfs_vget_t(struct mount *mp, ino_t ino, int flags,
+ struct vnode **vpp);
+#include <ufs/ffs/ffs_extern.h>
+
+#if !defined(_KERNEL)
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define panic(x) printf("%s\n", (x)), abort()
+#endif
+
+#define fs_old_postbloff fs_spare5[0]
+#define fs_old_rotbloff fs_spare5[1]
+#define fs_old_postbl_start fs_maxbsize
+#define fs_old_headswitch fs_id[0]
+#define fs_old_trkseek fs_id[1]
+#define fs_old_csmask fs_spare1[0]
+#define fs_old_csshift fs_spare1[1]
+
+#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_total *o, struct csum_total *n);
+
+void
+ffs_sb_swap(struct fs *o, struct fs *n)
+{
+ int 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_old_cpc = bswap32(o->fs_old_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.
+ */
+ n->fs_maxbsize = bswap32(o->fs_maxbsize);
+ n->fs_sblockloc = bswap64(o->fs_sblockloc);
+ 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_old_inodefmt = bswap32(o->fs_old_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_old_postblformat = bswap32(o->fs_old_postblformat);
+ n->fs_old_nrpos = bswap32(o->fs_old_nrpos);
+ n->fs_old_postbloff = bswap32(o->fs_old_postbloff);
+ n->fs_old_rotbloff = bswap32(o->fs_old_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_u.oldids[0] = bswap16(o->di_u.oldids[0]);
+ n->di_u.oldids[1] = bswap16(o->di_u.oldids[1]);
+ 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, (NDADDR + NIADDR) * sizeof(u_int32_t));
+ 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_dinode2_swap(struct ufs2_dinode *o, struct ufs2_dinode *n)
+{
+ n->di_mode = bswap16(o->di_mode);
+ n->di_nlink = bswap16(o->di_nlink);
+ n->di_uid = bswap32(o->di_uid);
+ n->di_gid = bswap32(o->di_gid);
+ n->di_blksize = bswap32(o->di_blksize);
+ n->di_size = bswap64(o->di_size);
+ n->di_blocks = bswap64(o->di_blocks);
+ n->di_atime = bswap64(o->di_atime);
+ n->di_atimensec = bswap32(o->di_atimensec);
+ n->di_mtime = bswap64(o->di_mtime);
+ n->di_mtimensec = bswap32(o->di_mtimensec);
+ n->di_ctime = bswap64(o->di_ctime);
+ n->di_ctimensec = bswap32(o->di_ctimensec);
+ n->di_birthtime = bswap64(o->di_ctime);
+ n->di_birthnsec = bswap32(o->di_ctimensec);
+ n->di_gen = bswap32(o->di_gen);
+ n->di_kernflags = bswap32(o->di_kernflags);
+ n->di_flags = bswap32(o->di_flags);
+ n->di_extsize = bswap32(o->di_extsize);
+ memcpy(n->di_extb, o->di_extb, (NXADDR + NDADDR + NIADDR) * 8);
+}
+
+void
+ffs_csum_swap(struct csum *o, struct csum *n, int size)
+{
+ int 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_total *o, struct csum_total *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_old_time = bswap32(o->cg_old_time);
+ n->cg_cgx = bswap32(o->cg_cgx);
+ n->cg_old_ncyl = bswap16(o->cg_old_ncyl);
+ n->cg_old_niblk = bswap16(o->cg_old_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_old_btotoff = bswap32(o->cg_old_btotoff);
+ n->cg_old_boff = bswap32(o->cg_old_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_niblk = bswap32(o->cg_niblk);
+ n->cg_initediblk = bswap32(o->cg_initediblk);
+ n->cg_time = bswap64(o->cg_time);
+
+ if (fs->fs_magic == FS_UFS2_MAGIC)
+ return;
+
+ if (n->cg_magic == CG_MAGIC) {
+ btotoff = n->cg_old_btotoff;
+ boff = n->cg_old_boff;
+ clustersumoff = n->cg_clustersumoff;
+ } else {
+ btotoff = bswap32(n->cg_old_btotoff);
+ boff = bswap32(n->cg_old_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_old_cpg; i++)
+ n32[i] = bswap32(o32[i]);
+
+ for (i = 0; i < fs->fs_old_cpg * fs->fs_old_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 0000000..d95e69b
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_extern.h
@@ -0,0 +1,77 @@
+/* $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$
+ */
+
+#include "ffs/buf.h"
+
+/*
+ * 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. */
+ int in_exists; /* Flag if the block exists. */
+};
+
+ /* ffs.c */
+void panic(const char *, ...)
+ __attribute__((__noreturn__,__format__(__printf__,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 *);
+daddr_t ffs_blkpref_ufs2(struct inode *, daddr_t, int, int64_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_dinode2_swap(struct ufs2_dinode *, struct ufs2_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 0000000..5f9b6f2
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_subr.c
@@ -0,0 +1,202 @@
+/* $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$");
+
+#include <sys/param.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+/* XXX temporary */
+struct ufsmount;
+struct bufobj;
+struct mount;
+struct vnode;
+typedef int vfs_vget_t(struct mount *mp, ino_t ino, int flags,
+ struct vnode **vpp);
+#include <ufs/ffs/ffs_extern.h>
+#include "ffs/ufs_bswap.h"
+void panic __P((const char *, ...))
+ __attribute__((__noreturn__,__format__(__printf__,1,2)));
+
+/*
+ * 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 correponding 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 0000000..ce9ec59
--- /dev/null
+++ b/usr.sbin/makefs/ffs/mkfs.c
@@ -0,0 +1,832 @@
+/* $NetBSD: mkfs.c,v 1.20 2004/06/24 22:30:13 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) 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$");
+
+#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 <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"
+
+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[SBLOCKSIZE];
+} 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 int Oflag; /* format as an 4.3BSD file system */
+static int64_t fssize; /* file system size */
+static int sectorsize; /* bytes/sector */
+static int fsize; /* fragment size */
+static int bsize; /* block size */
+static int maxbsize; /* maximum clustering */
+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 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)
+{
+ int fragsperinode, optimalfpg, origdensity, minfpg, lastminfpg;
+ int32_t cylno, i, csfrags;
+ long long sizepb;
+ void *space;
+ int size, blks;
+ int nprintcols, printcolwidth;
+
+ Oflag = fsopts->version;
+ fssize = fsopts->size / fsopts->sectorsize;
+ sectorsize = fsopts->sectorsize;
+ fsize = fsopts->fsize;
+ bsize = fsopts->bsize;
+ maxbsize = fsopts->maxbsize;
+ maxblkspercg = fsopts->maxblkspercg;
+ minfree = fsopts->minfree;
+ opt = fsopts->optimization;
+ density = fsopts->density;
+ maxcontig = fsopts->maxcontig;
+ maxbpg = fsopts->maxbpg;
+ avgfilesize = fsopts->avgfilesize;
+ avgfpdir = fsopts->avgfpdir;
+ sbsize = SBLOCKSIZE;
+
+ if (Oflag == 0) {
+ sblock.fs_old_inodefmt = FS_42INODEFMT;
+ sblock.fs_maxsymlinklen = 0;
+ sblock.fs_old_flags = 0;
+ } else {
+ sblock.fs_old_inodefmt = FS_44INODEFMT;
+ sblock.fs_maxsymlinklen = (Oflag == 1 ? MAXSYMLINKLEN_UFS1 :
+ MAXSYMLINKLEN_UFS2);
+ sblock.fs_old_flags = FS_FLAGS_UPDATED;
+ 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);
+ }
+
+ if (maxbsize < bsize || !POWEROF2(maxbsize)) {
+ sblock.fs_maxbsize = sblock.fs_bsize;
+ printf("Extent size set to %d\n", sblock.fs_maxbsize);
+ } else if (sblock.fs_maxbsize > FS_MAXCONTIG * sblock.fs_bsize) {
+ sblock.fs_maxbsize = FS_MAXCONTIG * sblock.fs_bsize;
+ printf("Extent size reduced to %d\n", sblock.fs_maxbsize);
+ } else {
+ sblock.fs_maxbsize = maxbsize;
+ }
+ sblock.fs_maxcontig = maxcontig;
+ if (sblock.fs_maxcontig < sblock.fs_maxbsize / sblock.fs_bsize) {
+ sblock.fs_maxcontig = sblock.fs_maxbsize / sblock.fs_bsize;
+ printf("Maxcontig raised to %d\n", sblock.fs_maxbsize);
+ }
+
+ 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);
+
+ if (Oflag <= 1) {
+ sblock.fs_magic = FS_UFS1_MAGIC;
+ sblock.fs_sblockloc = SBLOCK_UFS1;
+ sblock.fs_nindir = sblock.fs_bsize / sizeof(int32_t);
+ sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs1_dinode);
+ sblock.fs_maxsymlinklen = ((NDADDR + NIADDR) *
+ sizeof (int32_t));
+ sblock.fs_old_inodefmt = FS_44INODEFMT;
+ sblock.fs_old_cgoffset = 0;
+ sblock.fs_old_cgmask = 0xffffffff;
+ sblock.fs_old_size = sblock.fs_size;
+ sblock.fs_old_rotdelay = 0;
+ sblock.fs_old_rps = 60;
+ sblock.fs_old_nspf = sblock.fs_fsize / sectorsize;
+ sblock.fs_old_cpg = 1;
+ sblock.fs_old_interleave = 1;
+ sblock.fs_old_trackskew = 0;
+ sblock.fs_old_cpc = 0;
+ sblock.fs_old_postblformat = 1;
+ sblock.fs_old_nrpos = 1;
+ } else {
+ sblock.fs_magic = FS_UFS2_MAGIC;
+#if 0 /* XXX makefs is used for small filesystems. */
+ sblock.fs_sblockloc = SBLOCK_UFS2;
+#else
+ sblock.fs_sblockloc = SBLOCK_UFS1;
+#endif
+ sblock.fs_nindir = sblock.fs_bsize / sizeof(int64_t);
+ sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs2_dinode);
+ sblock.fs_maxsymlinklen = ((NDADDR + NIADDR) *
+ sizeof (int64_t));
+ }
+
+ sblock.fs_sblkno =
+ roundup(howmany(sblock.fs_sblockloc + SBLOCKSIZE, sblock.fs_fsize),
+ sblock.fs_frag);
+ sblock.fs_cblkno = (daddr_t)(sblock.fs_sblkno +
+ roundup(howmany(SBLOCKSIZE, 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);
+ if (Oflag <= 1) {
+ sblock.fs_old_spc = sblock.fs_fpg * sblock.fs_old_nspf;
+ sblock.fs_old_nsect = sblock.fs_old_spc;
+ sblock.fs_old_npsect = sblock.fs_old_spc;
+ sblock.fs_old_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 > SBLOCKSIZE)
+ sblock.fs_sbsize = SBLOCKSIZE;
+ 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] = start_time.tv_sec;
+ 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 = start_time.tv_sec;
+ if (Oflag <= 1) {
+ sblock.fs_old_time = start_time.tv_sec;
+ sblock.fs_old_dsize = sblock.fs_dsize;
+ sblock.fs_old_csaddr = sblock.fs_csaddr;
+ sblock.fs_old_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir;
+ sblock.fs_old_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree;
+ sblock.fs_old_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree;
+ sblock.fs_old_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree;
+ }
+ /*
+ * 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 < SBLOCKSIZE)
+ iobufsize = SBLOCKSIZE + 3 * sblock.fs_bsize;
+ else
+ iobufsize = 4 * sblock.fs_bsize;
+ if ((iobuf = malloc(iobufsize)) == 0) {
+ 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, SBLOCKSIZE);
+
+ printf("super-block backups (for fsck -b #) at:");
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++) {
+ initcg(cylno, start_time.tv_sec, 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 = start_time.tv_sec;
+ if (Oflag <= 1) {
+ sblock.fs_old_cstotal.cs_ndir = sblock.fs_cstotal.cs_ndir;
+ sblock.fs_old_cstotal.cs_nbfree = sblock.fs_cstotal.cs_nbfree;
+ sblock.fs_old_cstotal.cs_nifree = sblock.fs_cstotal.cs_nifree;
+ sblock.fs_old_cstotal.cs_nffree = sblock.fs_cstotal.cs_nffree;
+ }
+ 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(fs->fs_sblockloc / 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_initediblk = sblock.fs_ipg < 2 * INOPB(&sblock) ?
+ sblock.fs_ipg : 2 * INOPB(&sblock);
+ 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 (Oflag == 2) {
+ acg.cg_iusedoff = start;
+ } else {
+ if (cylno == sblock.fs_ncg - 1)
+ acg.cg_old_ncyl = howmany(acg.cg_ndblk,
+ sblock.fs_fpg / sblock.fs_old_cpg);
+ else
+ acg.cg_old_ncyl = sblock.fs_old_cpg;
+ acg.cg_old_time = acg.cg_time;
+ acg.cg_time = 0;
+ acg.cg_old_niblk = acg.cg_niblk;
+ acg.cg_niblk = 0;
+ acg.cg_initediblk = 0;
+ acg.cg_old_btotoff = start;
+ acg.cg_old_boff = acg.cg_old_btotoff +
+ sblock.fs_old_cpg * sizeof(int32_t);
+ acg.cg_iusedoff = acg.cg_old_boff +
+ sblock.fs_old_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 = sblock.fs_bsize > SBLOCKSIZE ? sblock.fs_bsize : SBLOCKSIZE;
+ 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]);
+ for (i = 0; i < acg.cg_initediblk; i++) {
+ if (sblock.fs_magic == FS_UFS1_MAGIC) {
+ /* No need to swap, it'll stay random */
+ dp1->di_gen = random();
+ dp1++;
+ } else {
+ dp2->di_gen = random();
+ dp2++;
+ }
+ }
+ ffs_wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)), iobufsize, iobuf,
+ fsopts);
+ /*
+ * For the old file system, we have to initialize all the inodes.
+ */
+ if (Oflag <= 1) {
+ 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 0000000..88559b6
--- /dev/null
+++ b/usr.sbin/makefs/ffs/newfs_extern.h
@@ -0,0 +1,41 @@
+/* $NetBSD: newfs_extern.h,v 1.2 2004/06/24 22:30:13 lukem 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christos Zoulas.
+ * 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$
+ */
+
+/* prototypes */
+struct fs *ffs_mkfs(const char *, const fsinfo_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 0000000..85c1d87
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ufs_bmap.c
@@ -0,0 +1,142 @@
+/* $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$");
+
+#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->in_exists = 0;
+ 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->in_exists = 0;
+ ++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 0000000..a6b1270
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ufs_bswap.h
@@ -0,0 +1,94 @@
+/* $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$
+ */
+
+#ifndef _UFS_UFS_BSWAP_H_
+#define _UFS_UFS_BSWAP_H_
+
+#if defined(_KERNEL_OPT)
+#include "opt_ffs.h"
+#endif
+
+#include <sys/endian.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(a, ns)
+ u_int16_t a;
+ int ns;
+{
+ return ((ns) ? bswap16(a) : (a));
+}
+static __inline u_int32_t
+ufs_rw32(a, ns)
+ u_int32_t a;
+ int ns;
+{
+ return ((ns) ? bswap32(a) : (a));
+}
+static __inline u_int64_t
+ufs_rw64(a, ns)
+ 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 0000000..8286f86
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ufs_inode.h
@@ -0,0 +1,97 @@
+/* $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$
+ */
+
+union dinode {
+ struct ufs1_dinode ffs1_din;
+ struct ufs2_dinode ffs2_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
+
+#define i_ffs2_atime i_din.ffs2_din.di_atime
+#define i_ffs2_atimensec i_din.ffs2_din.di_atimensec
+#define i_ffs2_blocks i_din.ffs2_din.di_blocks
+#define i_ffs2_ctime i_din.ffs2_din.di_ctime
+#define i_ffs2_ctimensec i_din.ffs2_din.di_ctimensec
+#define i_ffs2_birthtime i_din.ffs2_din.di_birthtime
+#define i_ffs2_birthnsec i_din.ffs2_din.di_birthnsec
+#define i_ffs2_db i_din.ffs2_din.di_db
+#define i_ffs2_flags i_din.ffs2_din.di_flags
+#define i_ffs2_gen i_din.ffs2_din.di_gen
+#define i_ffs21_gid i_din.ffs2_din.di_gid
+#define i_ffs2_ib i_din.ffs2_din.di_ib
+#define i_ffs2_mode i_din.ffs2_din.di_mode
+#define i_ffs2_mtime i_din.ffs2_din.di_mtime
+#define i_ffs2_mtimensec i_din.ffs2_din.di_mtimensec
+#define i_ffs2_nlink i_din.ffs2_din.di_nlink
+#define i_ffs2_rdev i_din.ffs2_din.di_rdev
+#define i_ffs2_shortlink i_din.ffs2_din.db
+#define i_ffs2_size i_din.ffs2_din.di_size
+#define i_ffs2_uid i_din.ffs2_din.di_uid
+
+#undef DIP
+#define DIP(ip, field) \
+ (((ip)->i_fs->fs_magic == FS_UFS1_MAGIC) ? \
+ (ip)->i_ffs1_##field : (ip)->i_ffs2_##field)
diff --git a/usr.sbin/makefs/getid.c b/usr.sbin/makefs/getid.c
new file mode 100644
index 0000000..ca52fa3
--- /dev/null
+++ b/usr.sbin/makefs/getid.c
@@ -0,0 +1,436 @@
+/* $NetBSD: getid.c,v 1.5 2004/06/20 22:20:18 jmc 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#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 "makefs.h"
+
+#include "mtree.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;
+ return (_gr_fp = fopen(grfile, "r")) ? 1 : 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;
+ }
+ 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;
+ return (_pw_fp = fopen(pwfile, "r")) ? 1 : 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;
+ }
+ 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/usr.sbin/makefs/makefs.8 b/usr.sbin/makefs/makefs.8
new file mode 100644
index 0000000..101366a
--- /dev/null
+++ b/usr.sbin/makefs/makefs.8
@@ -0,0 +1,288 @@
+.\" $NetBSD: makefs.8,v 1.13 2004/02/13 17:56:18 wiz 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$
+.\"
+.Dd March 30, 2003
+.Dt MAKEFS 8
+.Os
+.Sh NAME
+.Nm makefs
+.Nd create a file system image from a directory tree
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Fl t Ar fs-type
+.Ek
+.Bk -words
+.Op Fl o Ar fs-options
+.Ek
+.Bk -words
+.Op Fl d Ar debug-mask
+.Ek
+.Bk -words
+.Op Fl B Ar byte-order
+.Ek
+.Bk -words
+.Op Fl S Ar sector-size
+.Ek
+.Bk -words
+.Op Fl M Ar minimum-size
+.Ek
+.Bk -words
+.Op Fl m Ar maximum-size
+.Ek
+.Bk -words
+.Op Fl s Ar image-size
+.Ek
+.Bk -words
+.Op Fl b Ar free-blocks
+.Ek
+.Bk -words
+.Op Fl f Ar free-files
+.Ek
+.Bk -words
+.Op Fl F Ar specfile
+.Ek
+.Op Fl x
+.Bk -words
+.Op Fl N Ar userdb-dir
+.Ek
+.Ar image-file
+.Ar directory
+.Sh DESCRIPTION
+The utility
+.Nm
+creates a file system image into
+.Ar image-file
+from the directory tree
+.Ar directory .
+No special devices or privileges are required to perform this task.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl t Ar fs-type
+Create an
+.Ar fs-type
+file system image.
+The following file system types are supported:
+.Bl -tag -width ffs -offset indent
+.It Sy ffs
+BSD fast file system (default).
+.El
+.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 d Ar debug-mask
+Enable various levels of debugging, depending upon which bits are set
+in
+.Ar debug-mask .
+XXX: document these
+.It Fl B Ar byte-order
+Set the byte order of the image to
+.Ar byte-order .
+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 S Ar sector-size
+Set the file system sector size to
+.Ar sector-size .
+Defaults to 512.
+.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 s Ar image-size
+Set the size of the file system image to
+.Ar image-size .
+.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 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-blocks
+indicates a percentage of the calculated image size
+.It Fl F Ar specfile
+Use
+.Ar specfile
+as an
+.Xr mtree 8
+.Sq specfile
+specification.
+.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 ,
+.Sy device
+(in the case of block or character devices), 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 x
+Exclude file system nodes not explcitly listed in the specfile.
+.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.
+.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
+Kilo; multiply by 1024 (1 KB)
+.It m
+Mega; multiply by 1048576 (1 MB)
+.It g
+Giga; multiply by 1073741824 (1 GB)
+.It t
+Tera; multiply by 1099511627776 (1 TB)
+.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 equals 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 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 extent
+Maximum extent size
+.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
+.Sh SEE ALSO
+.Xr mtree 8 ,
+.Xr newfs 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Nx 1.6 .
+.Sh AUTHORS
+.An Luke Mewburn
+.Aq lukem@NetBSD.org .
diff --git a/usr.sbin/makefs/makefs.c b/usr.sbin/makefs/makefs.c
new file mode 100644
index 0000000..c3e4bd2
--- /dev/null
+++ b/usr.sbin/makefs/makefs.c
@@ -0,0 +1,314 @@
+/* $NetBSD: makefs.c,v 1.20 2004/06/20 22:20:18 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "makefs.h"
+#include "mtree.h"
+
+/*
+ * list of supported file systems and dispatch functions
+ */
+typedef struct {
+ const char *type;
+ int (*parse_options)(const char *, fsinfo_t *);
+ void (*make_fs)(const char *, const char *, fsnode *,
+ fsinfo_t *);
+} fstype_t;
+
+static fstype_t fstypes[] = {
+ { "ffs", ffs_parse_opts, ffs_makefs },
+ { NULL },
+};
+
+u_int debug;
+struct timespec start_time;
+
+static fstype_t *get_fstype(const char *);
+static void usage(void);
+int main(int, char *[]);
+
+int
+main(int argc, char *argv[])
+{
+ struct timeval start;
+ fstype_t *fstype;
+ fsinfo_t fsoptions;
+ fsnode *root;
+ int ch, len;
+ 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;
+ fsoptions.bsize= -1;
+ fsoptions.fsize= -1;
+ fsoptions.cpg= -1;
+ fsoptions.density= -1;
+ fsoptions.minfree= -1;
+ fsoptions.optimization= -1;
+ fsoptions.maxcontig= -1;
+ fsoptions.maxbpg= -1;
+ fsoptions.avgfilesize= -1;
+ fsoptions.avgfpdir= -1;
+ fsoptions.version = 1;
+
+ specfile = NULL;
+ if (gettimeofday(&start, NULL) == -1)
+ err(1, "Unable to get system time");
+
+ start_time.tv_sec = start.tv_sec;
+ start_time.tv_nsec = start.tv_usec * 1000;
+
+ while ((ch = getopt(argc, argv, "B:b:d:f:F:M:m:N:o:s:S:t:x")) != -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':
+ debug =
+ (int)strsuftoll("debug mask", optarg, 0, UINT_MAX);
+ 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 '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':
+ if ((fstype = get_fstype(optarg)) == NULL)
+ errx(1, "Unknown fs type `%s'.", optarg);
+ break;
+
+ case 'x':
+ fsoptions.onlyspec = 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.");
+
+ /* walk the tree */
+ TIMER_START(start);
+ root = walk_dir(argv[1], NULL);
+ TIMER_RESULTS(start, "walk_dir");
+
+ if (specfile) { /* apply a specfile */
+ TIMER_START(start);
+ apply_specfile(specfile, argv[1], root);
+ TIMER_RESULTS(start, "apply_specfile");
+ }
+
+ if (debug & DEBUG_DUMP_FSNODES) {
+ printf("\nparent: %s\n", argv[1]);
+ dump_fsnodes(".", root);
+ putchar('\n');
+ }
+
+ /* build the file system */
+ TIMER_START(start);
+ fstype->make_fs(argv[0], argv[1], root, &fsoptions);
+ TIMER_RESULTS(start, "make_fs");
+
+ 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 void
+usage(void)
+{
+ const char *prog;
+
+ prog = getprogname();
+ fprintf(stderr,
+"usage: %s [-t fs-type] [-o fs-options] [-d debug-mask] [-B endian]\n"
+"\t[-S sector-size] [-M minimum-size] [-m maximum-size] [-s image-size]\n"
+"\t[-b free-blocks] [-f free-files] [-F mtree-specfile] [-x]\n"
+"\t[-N userdb-dir] image-file directory\n",
+ prog);
+ exit(1);
+}
diff --git a/usr.sbin/makefs/makefs.h b/usr.sbin/makefs/makefs.h
new file mode 100644
index 0000000..ce18bf8
--- /dev/null
+++ b/usr.sbin/makefs/makefs.h
@@ -0,0 +1,305 @@
+/* $NetBSD: makefs.h,v 1.14 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.
+ *
+ * $FreeBSD$
+ */
+
+#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 *name; /* file name */
+ int flags; /* misc flags */
+} fsnode;
+
+#define FSNODE_F_HASSPEC 0x01 /* fsnode has a spec entry */
+
+/*
+ * 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 */
+
+ /* ffs specific options */
+ 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 maxbsize; /* maximum extent size */
+ int maxblkspercg; /* max # of blocks per cylinder group */
+ /* XXX: support `old' file systems ? */
+} 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 *);
+void dump_fsnodes(const char *, fsnode *);
+const char * inode_type(mode_t);
+int set_option(option_t *, const char *, const char *);
+fsnode * walk_dir(const char *, fsnode *);
+
+int ffs_parse_opts(const char *, fsinfo_t *);
+void ffs_makefs(const char *, const char *, fsnode *, fsinfo_t *);
+
+
+
+extern u_int debug;
+extern struct timespec start_time;
+
+/*
+ * 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 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 %ld.%06ld seconds\n", \
+ (d), (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 DINODE2_SIZE (sizeof(struct ufs2_dinode))
+
+#define MAXSYMLINKLEN_UFS1 ((NDADDR + NIADDR) * sizeof(ufs1_daddr_t))
+#define MAXSYMLINKLEN_UFS2 ((NDADDR + NIADDR) * sizeof(ufs2_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);
+
+/*
+ * Declarations for compat routines.
+ */
+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);
+
+struct passwd;
+int uid_from_user(const char *, uid_t *);
+int pwcache_userdb(int (*)(int), void (*)(void),
+ struct passwd * (*)(const char *), struct passwd * (*)(uid_t));
+struct group;
+int gid_from_group(const char *, gid_t *);
+int pwcache_groupdb(int (*)(int), void (*)(void),
+ struct group * (*)(const char *), struct group * (*)(gid_t));
+
+int setup_getid(const char *dir);
+
+#endif /* _MAKEFS_H */
diff --git a/usr.sbin/makefs/walk.c b/usr.sbin/makefs/walk.c
new file mode 100644
index 0000000..e485787
--- /dev/null
+++ b/usr.sbin/makefs/walk.c
@@ -0,0 +1,568 @@
+/* $NetBSD: walk.c,v 1.17 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.
+ */
+
+/*
+ * The function link_check() was inspired from NetBSD's usr.bin/du/du.c,
+ * which has the following copyright notice:
+ *
+ *
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Newcomb.
+ *
+ * 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$");
+
+#include <sys/param.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 "makefs.h"
+
+#include "mtree.h"
+#include "extern.h" /* NB: mtree */
+
+static void apply_specdir(const char *, NODE *, fsnode *);
+static void apply_specentry(const char *, NODE *, fsnode *);
+static fsnode *create_fsnode(const char *, struct stat *);
+static fsinode *link_check(fsinode *);
+
+
+/*
+ * walk_dir --
+ * build a tree of fsnodes from `dir', with a parent fsnode of `parent'
+ * (which may be NULL for the root of the tree).
+ * 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 *dir, fsnode *parent)
+{
+ fsnode *first, *cur, *prev;
+ DIR *dirp;
+ struct dirent *dent;
+ char path[MAXPATHLEN + 1];
+ struct stat stbuf;
+
+ assert(dir != NULL);
+
+ if (debug & DEBUG_WALK_DIR)
+ printf("walk_dir: %s %p\n", dir, parent);
+ if ((dirp = opendir(dir)) == NULL)
+ err(1, "Can't opendir `%s'", dir);
+ first = prev = NULL;
+ while ((dent = readdir(dirp)) != NULL) {
+ if (strcmp(dent->d_name, "..") == 0)
+ continue;
+ if (debug & DEBUG_WALK_DIR_NODE)
+ printf("scanning %s/%s\n", dir, dent->d_name);
+ if (snprintf(path, sizeof(path), "%s/%s", dir, dent->d_name)
+ >= sizeof(path))
+ 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
+
+ cur = create_fsnode(dent->d_name, &stbuf);
+ cur->parent = parent;
+ if (strcmp(dent->d_name, ".") == 0) {
+ /* ensure "." is at the start of the list */
+ cur->next = first;
+ first = cur;
+ if (! prev)
+ prev = cur;
+ } else { /* not "." */
+ if (prev)
+ prev->next = cur;
+ prev = cur;
+ if (!first)
+ first = cur;
+ if (S_ISDIR(cur->type)) {
+ cur->child = walk_dir(path, cur);
+ 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 (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");
+ }
+ }
+ for (cur = first; cur != NULL; cur = cur->next)
+ cur->first = first;
+ if (closedir(dirp) == -1)
+ err(1, "Can't closedir `%s'", dir);
+ return (first);
+}
+
+static fsnode *
+create_fsnode(const char *name, struct stat *stbuf)
+{
+ fsnode *cur;
+
+ if ((cur = calloc(1, sizeof(fsnode))) == NULL ||
+ (cur->name = strdup(name)) == NULL ||
+ (cur->inode = calloc(1, sizeof(fsinode))) == NULL)
+ err(1, "Memory allocation error");
+ cur->type = stbuf->st_mode & S_IFMT;
+ cur->inode->nlink = 1;
+ cur->inode->st = *stbuf;
+ return (cur);
+}
+
+/*
+ * 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)
+{
+ 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 = mtree_readspec(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);
+}
+
+static 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;
+ case F_SOCK:
+ return S_IFSOCK;
+ default:
+ printf("unknown type %d", type);
+ abort();
+ }
+ /* NOTREACHED */
+}
+
+static void
+apply_specdir(const char *dir, NODE *specnode, fsnode *dirnode)
+{
+ 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);
+
+ /* 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;
+
+ /*
+ * don't add optional spec entries
+ * that lack an existing fs entry
+ */
+ if ((curnode->flags & F_OPT) &&
+ lstat(path, &stbuf) == -1)
+ continue;
+
+ /* 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");
+#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);
+ }
+ }
+}
+
+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
+#undef ASEPRINT
+
+ dirnode->flags |= FSNODE_F_HASSPEC;
+}
+
+
+/*
+ * dump_fsnodes --
+ * dump the fsnodes from `cur', based in the directory `dir'
+ */
+void
+dump_fsnodes(const char *dir, fsnode *root)
+{
+ fsnode *cur;
+ char path[MAXPATHLEN + 1];
+
+ assert (dir != NULL);
+ printf("dump_fsnodes: %s %p\n", dir, root);
+ for (cur = root; cur != NULL; cur = cur->next) {
+ if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name)
+ >= 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(path, cur->child);
+ }
+ }
+ printf("dump_fsnodes: finished %s\n", dir);
+}
+
+
+/*
+ * inode_type --
+ * for a given inode type `mode', return a descriptive string.
+ */
+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 fsnode matching `entry's st_ino & st_dev if it exists,
+ * otherwise add `entry' to table and return NULL
+ */
+static fsinode *
+link_check(fsinode *entry)
+{
+ static struct dupnode {
+ uint32_t dev;
+ uint64_t ino;
+ fsinode *dup;
+ } *dups, *newdups;
+ static int ndups, maxdups;
+
+ int i;
+
+ assert (entry != NULL);
+
+ /* XXX; maybe traverse in reverse for speed? */
+ for (i = 0; i < ndups; i++) {
+ if (dups[i].dev == entry->st.st_dev &&
+ dups[i].ino == entry->st.st_ino) {
+ if (debug & DEBUG_WALK_DIR_LINKCHECK)
+ printf("link_check: found [%d,%d]\n",
+ entry->st.st_dev, entry->st.st_ino);
+ return (dups[i].dup);
+ }
+ }
+
+ if (debug & DEBUG_WALK_DIR_LINKCHECK)
+ printf("link_check: no match for [%d, %d]\n",
+ entry->st.st_dev, entry->st.st_ino);
+ if (ndups == maxdups) {
+ if ((newdups = realloc(dups, sizeof(struct dupnode) * (maxdups + 128)))
+ == NULL)
+ err(1, "Memory allocation error");
+ dups = newdups;
+ maxdups += 128;
+ }
+ dups[ndups].dev = entry->st.st_dev;
+ dups[ndups].ino = entry->st.st_ino;
+ dups[ndups].dup = entry;
+ ndups++;
+
+ return (NULL);
+}
OpenPOWER on IntegriCloud