--- /dev/null
+# $NetBSD: Makefile,v 1.32 2009/04/22 15:23:05 lukem Exp $
+# from: @(#)Makefile 8.2 (Berkeley) 4/27/95
+
+.include <bsd.own.mk>
+
+PROG= mtree
+#CPPFLAGS+=-DDEBUG
+CPPFLAGS+= -DMTREE
+MAN= mtree.8
+SRCS= compare.c crc.c create.c excludes.c misc.c mtree.c spec.c verify.c \
+ getid.c pack_dev.c
+.if (${HOSTPROG:U} == "")
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+.endif
+
+CPPFLAGS+= -I${NETBSDSRCDIR}/sbin/mknod
+.PATH: ${NETBSDSRCDIR}/sbin/mknod
+
+.include <bsd.prog.mk>
--- /dev/null
+/* $NetBSD: compare.c,v 1.52 2008/12/28 19:36:30 christos Exp $ */
+
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)compare.c 8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: compare.c,v 1.52 2008/12/28 19:36:30 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef NO_MD5
+#include <md5.h>
+#endif
+#ifndef NO_RMD160
+#include <rmd160.h>
+#endif
+#ifndef NO_SHA1
+#include <sha1.h>
+#endif
+#ifndef NO_SHA2
+#include <sha2.h>
+#endif
+
+#include "extern.h"
+
+#define INDENTNAMELEN 8
+#define MARK \
+do { \
+ len = printf("%s: ", RP(p)); \
+ if (len > INDENTNAMELEN) { \
+ tab = "\t"; \
+ printf("\n"); \
+ } else { \
+ tab = ""; \
+ printf("%*s", INDENTNAMELEN - (int)len, ""); \
+ } \
+} while (0)
+#define LABEL if (!label++) MARK
+
+#if HAVE_STRUCT_STAT_ST_FLAGS
+
+#define CHANGEFLAGS \
+ if (flags != p->fts_statp->st_flags) { \
+ char *sf; \
+ if (!label) { \
+ MARK; \
+ sf = flags_to_string(p->fts_statp->st_flags, "none"); \
+ printf("%sflags (\"%s\"", tab, sf); \
+ free(sf); \
+ } \
+ if (lchflags(p->fts_accpath, flags)) { \
+ label++; \
+ printf(", not modified: %s)\n", \
+ strerror(errno)); \
+ } else { \
+ sf = flags_to_string(flags, "none"); \
+ printf(", modified to \"%s\")\n", sf); \
+ free(sf); \
+ } \
+ }
+
+/* SETFLAGS:
+ * given pflags, additionally set those flags specified in s->st_flags and
+ * selected by mask (the other flags are left unchanged).
+ */
+#define SETFLAGS(pflags, mask) \
+do { \
+ flags = (s->st_flags & (mask)) | (pflags); \
+ CHANGEFLAGS; \
+} while (0)
+
+/* CLEARFLAGS:
+ * given pflags, reset the flags specified in s->st_flags and selected by mask
+ * (the other flags are left unchanged).
+ */
+#define CLEARFLAGS(pflags, mask) \
+do { \
+ flags = (~(s->st_flags & (mask)) & CH_MASK) & (pflags); \
+ CHANGEFLAGS; \
+} while (0)
+#endif /* HAVE_STRUCT_STAT_ST_FLAGS */
+
+int
+compare(NODE *s, FTSENT *p)
+{
+ u_int32_t len, val, flags;
+ int fd, label;
+ const char *cp, *tab;
+#if !defined(NO_MD5) || !defined(NO_RMD160) || !defined(NO_SHA1) || !defined(NO_SHA2)
+ char *digestbuf;
+#endif
+
+ tab = NULL;
+ label = 0;
+ switch(s->type) {
+ case F_BLOCK:
+ if (!S_ISBLK(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_CHAR:
+ if (!S_ISCHR(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_DIR:
+ if (!S_ISDIR(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_FIFO:
+ if (!S_ISFIFO(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_FILE:
+ if (!S_ISREG(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_LINK:
+ if (!S_ISLNK(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+#ifdef S_ISSOCK
+ case F_SOCK:
+ if (!S_ISSOCK(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+#endif
+typeerr: LABEL;
+ printf("\ttype (%s, %s)\n",
+ nodetype(s->type), inotype(p->fts_statp->st_mode));
+ return (label);
+ }
+ if (mtree_Wflag)
+ goto afterpermwhack;
+#if HAVE_STRUCT_STAT_ST_FLAGS && !defined(__minix)
+ if (iflag && !uflag) {
+ if (s->flags & F_FLAGS)
+ SETFLAGS(p->fts_statp->st_flags, SP_FLGS);
+ return (label);
+ }
+ if (mflag && !uflag) {
+ if (s->flags & F_FLAGS)
+ CLEARFLAGS(p->fts_statp->st_flags, SP_FLGS);
+ return (label);
+ }
+#endif
+ if (s->flags & F_DEV &&
+ (s->type == F_BLOCK || s->type == F_CHAR) &&
+ s->st_rdev != p->fts_statp->st_rdev) {
+ LABEL;
+ printf("%sdevice (%#llx, %#llx",
+ tab, (long long)s->st_rdev,
+ (long long)p->fts_statp->st_rdev);
+ if (uflag) {
+#ifndef __minix
+ if ((unlink(p->fts_accpath) == -1) ||
+ (mknod(p->fts_accpath,
+ s->st_mode | nodetoino(s->type),
+ s->st_rdev) == -1) ||
+ (lchown(p->fts_accpath, p->fts_statp->st_uid,
+ p->fts_statp->st_gid) == -1) )
+ printf(", not modified: %s)\n",
+ strerror(errno));
+ else
+ printf(", modified)\n");
+#endif
+ } else
+ printf(")\n");
+ tab = "\t";
+ }
+ /* Set the uid/gid first, then set the mode. */
+ if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
+ LABEL;
+ printf("%suser (%lu, %lu",
+ tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
+ if (uflag) {
+#ifndef __minix
+ if (lchown(p->fts_accpath, s->st_uid, -1))
+ printf(", not modified: %s)\n",
+ strerror(errno));
+ else
+ printf(", modified)\n");
+#endif
+ } else
+ printf(")\n");
+ tab = "\t";
+ }
+ if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
+ LABEL;
+ printf("%sgid (%lu, %lu",
+ tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
+ if (uflag) {
+#ifndef __minix
+ if (lchown(p->fts_accpath, -1, s->st_gid))
+ printf(", not modified: %s)\n",
+ strerror(errno));
+ else
+ printf(", modified)\n");
+#endif
+ }
+ else
+ printf(")\n");
+ tab = "\t";
+ }
+ if (s->flags & F_MODE &&
+ s->st_mode != (p->fts_statp->st_mode & MBITS)) {
+ if (lflag) {
+ mode_t tmode, mode;
+
+ tmode = s->st_mode;
+ mode = p->fts_statp->st_mode & MBITS;
+ /*
+ * if none of the suid/sgid/etc bits are set,
+ * then if the mode is a subset of the target,
+ * skip.
+ */
+ if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) ||
+ (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO))))
+ if ((mode | tmode) == tmode)
+ goto skip;
+ }
+
+ LABEL;
+ printf("%spermissions (%#lo, %#lo",
+ tab, (u_long)s->st_mode,
+ (u_long)p->fts_statp->st_mode & MBITS);
+ if (uflag) {
+#ifndef __minix
+ if (lchmod(p->fts_accpath, s->st_mode))
+ printf(", not modified: %s)\n",
+ strerror(errno));
+ else
+ printf(", modified)\n");
+#endif
+ }
+ else
+ printf(")\n");
+ tab = "\t";
+ skip: ;
+ }
+ if (s->flags & F_NLINK && s->type != F_DIR &&
+ s->st_nlink != p->fts_statp->st_nlink) {
+ LABEL;
+ printf("%slink count (%lu, %lu)\n",
+ tab, (u_long)s->st_nlink, (u_long)p->fts_statp->st_nlink);
+ tab = "\t";
+ }
+ if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) {
+ LABEL;
+ printf("%ssize (%lld, %lld)\n",
+ tab, (long long)s->st_size,
+ (long long)p->fts_statp->st_size);
+ tab = "\t";
+ }
+ /*
+ * XXX
+ * Since utimes(2) only takes a timeval, there's no point in
+ * comparing the low bits of the timespec nanosecond field. This
+ * will only result in mismatches that we can never fix.
+ *
+ * Doesn't display microsecond differences.
+ */
+ if (s->flags & F_TIME) {
+ struct timeval tv[2];
+ struct stat *ps = p->fts_statp;
+ time_t smtime = s->st_mtimespec.tv_sec;
+
+#if defined(BSD4_4) && !defined(HAVE_NBTOOL_CONFIG_H)
+ time_t pmtime = ps->st_mtimespec.tv_sec;
+
+ TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec);
+ TIMESPEC_TO_TIMEVAL(&tv[1], &ps->st_mtimespec);
+#else
+ time_t pmtime = (time_t)ps->st_mtime;
+
+ tv[0].tv_sec = smtime;
+ tv[0].tv_usec = 0;
+ tv[1].tv_sec = pmtime;
+ tv[1].tv_usec = 0;
+#endif
+
+ if (tv[0].tv_sec != tv[1].tv_sec ||
+ tv[0].tv_usec != tv[1].tv_usec) {
+ LABEL;
+ printf("%smodification time (%.24s, ",
+ tab, ctime(&smtime));
+ printf("%.24s", ctime(&pmtime));
+ if (tflag) {
+#ifndef __minix
+ tv[1] = tv[0];
+ if (utimes(p->fts_accpath, tv))
+ printf(", not modified: %s)\n",
+ strerror(errno));
+ else
+ printf(", modified)\n");
+#endif
+ } else
+ printf(")\n");
+ tab = "\t";
+ }
+ }
+#if HAVE_STRUCT_STAT_ST_FLAGS && !defined(__minix)
+ /*
+ * XXX
+ * since lchflags(2) will reset file times, the utimes() above
+ * may have been useless! oh well, we'd rather have correct
+ * flags, rather than times?
+ */
+ if ((s->flags & F_FLAGS) && ((s->st_flags != p->fts_statp->st_flags)
+ || mflag || iflag)) {
+ if (s->st_flags != p->fts_statp->st_flags) {
+ char *f_s;
+ LABEL;
+ f_s = flags_to_string(s->st_flags, "none");
+ printf("%sflags (\"%s\" is not ", tab, f_s);
+ free(f_s);
+ f_s = flags_to_string(p->fts_statp->st_flags, "none");
+ printf("\"%s\"", f_s);
+ free(f_s);
+ }
+ if (uflag) {
+ if (iflag)
+ SETFLAGS(0, CH_MASK);
+ else if (mflag)
+ CLEARFLAGS(0, SP_FLGS);
+ else
+ SETFLAGS(0, (~SP_FLGS & CH_MASK));
+ } else
+ printf(")\n");
+ tab = "\t";
+ }
+#endif /* HAVE_STRUCT_STAT_ST_FLAGS */
+
+ /*
+ * from this point, no more permission checking or whacking
+ * occurs, only checking of stuff like checksums and symlinks.
+ */
+ afterpermwhack:
+ if (s->flags & F_CKSUM) {
+ if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
+ LABEL;
+ printf("%scksum: %s: %s\n",
+ tab, p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else if (crc(fd, &val, &len)) {
+ close(fd);
+ LABEL;
+ printf("%scksum: %s: %s\n",
+ tab, p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else {
+ close(fd);
+ if (s->cksum != val) {
+ LABEL;
+ printf("%scksum (%lu, %lu)\n",
+ tab, s->cksum, (unsigned long)val);
+ }
+ tab = "\t";
+ }
+ }
+#ifndef NO_MD5
+ if (s->flags & F_MD5) {
+ if ((digestbuf = MD5File(p->fts_accpath, NULL)) == NULL) {
+ LABEL;
+ printf("%smd5: %s: %s\n",
+ tab, p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else {
+ if (strcmp(s->md5digest, digestbuf)) {
+ LABEL;
+ printf("%smd5 (0x%s, 0x%s)\n",
+ tab, s->md5digest, digestbuf);
+ }
+ tab = "\t";
+ free(digestbuf);
+ }
+ }
+#endif /* ! NO_MD5 */
+#ifndef NO_RMD160
+ if (s->flags & F_RMD160) {
+ if ((digestbuf = RMD160File(p->fts_accpath, NULL)) == NULL) {
+ LABEL;
+ printf("%srmd160: %s: %s\n",
+ tab, p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else {
+ if (strcmp(s->rmd160digest, digestbuf)) {
+ LABEL;
+ printf("%srmd160 (0x%s, 0x%s)\n",
+ tab, s->rmd160digest, digestbuf);
+ }
+ tab = "\t";
+ free(digestbuf);
+ }
+ }
+#endif /* ! NO_RMD160 */
+#ifndef NO_SHA1
+ if (s->flags & F_SHA1) {
+ if ((digestbuf = SHA1File(p->fts_accpath, NULL)) == NULL) {
+ LABEL;
+ printf("%ssha1: %s: %s\n",
+ tab, p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else {
+ if (strcmp(s->sha1digest, digestbuf)) {
+ LABEL;
+ printf("%ssha1 (0x%s, 0x%s)\n",
+ tab, s->sha1digest, digestbuf);
+ }
+ tab = "\t";
+ free(digestbuf);
+ }
+ }
+#endif /* ! NO_SHA1 */
+#ifndef NO_SHA2
+ if (s->flags & F_SHA256) {
+ if ((digestbuf = SHA256_File(p->fts_accpath, NULL)) == NULL) {
+ LABEL;
+ printf("%ssha256: %s: %s\n",
+ tab, p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else {
+ if (strcmp(s->sha256digest, digestbuf)) {
+ LABEL;
+ printf("%ssha256 (0x%s, 0x%s)\n",
+ tab, s->sha256digest, digestbuf);
+ }
+ tab = "\t";
+ free(digestbuf);
+ }
+ }
+ if (s->flags & F_SHA384) {
+ if ((digestbuf = SHA384_File(p->fts_accpath, NULL)) == NULL) {
+ LABEL;
+ printf("%ssha384: %s: %s\n",
+ tab, p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else {
+ if (strcmp(s->sha384digest, digestbuf)) {
+ LABEL;
+ printf("%ssha384 (0x%s, 0x%s)\n",
+ tab, s->sha384digest, digestbuf);
+ }
+ tab = "\t";
+ free(digestbuf);
+ }
+ }
+ if (s->flags & F_SHA512) {
+ if ((digestbuf = SHA512_File(p->fts_accpath, NULL)) == NULL) {
+ LABEL;
+ printf("%ssha512: %s: %s\n",
+ tab, p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else {
+ if (strcmp(s->sha512digest, digestbuf)) {
+ LABEL;
+ printf("%ssha512 (0x%s, 0x%s)\n",
+ tab, s->sha512digest, digestbuf);
+ }
+ tab = "\t";
+ free(digestbuf);
+ }
+ }
+#endif /* ! NO_SHA2 */
+ if (s->flags & F_SLINK &&
+ strcmp(cp = rlink(p->fts_accpath), s->slink)) {
+ LABEL;
+ printf("%slink ref (%s, %s", tab, cp, s->slink);
+ if (uflag) {
+ if ((unlink(p->fts_accpath) == -1) ||
+ (symlink(s->slink, p->fts_accpath) == -1) )
+ printf(", not modified: %s)\n",
+ strerror(errno));
+ else
+ printf(", modified)\n");
+ } else
+ printf(")\n");
+ }
+ return (label);
+}
+
+const char *
+rlink(const char *name)
+{
+ static char lbuf[MAXPATHLEN];
+ int len;
+
+ if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1)
+ mtree_err("%s: %s", name, strerror(errno));
+ lbuf[len] = '\0';
+ return (lbuf);
+}
--- /dev/null
+/* $NetBSD: crc.c,v 1.8 2005/06/02 06:04:46 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James W. Williams of NASA Goddard Space Flight Center.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)crc.c 8.1 (Berkeley) 6/17/93";
+#else
+__RCSID("$NetBSD: crc.c,v 1.8 2005/06/02 06:04:46 lukem Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static const u_int32_t crctab[] = {
+ 0x0,
+ 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+ 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
+ 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+ 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
+ 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
+ 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
+ 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+ 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
+ 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
+ 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
+ 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+ 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
+ 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+ 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
+ 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+ 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
+ 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
+ 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
+ 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
+ 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
+ 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
+ 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+ 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
+ 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+ 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
+ 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+ 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
+ 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
+ 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
+ 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+ 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
+ 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
+ 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
+ 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+ 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
+ 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+ 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
+ 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+ 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
+ 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
+ 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
+ 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
+ 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
+ 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
+ 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+ 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
+ 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+ 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
+ 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+};
+
+/*
+ * Compute a POSIX 1003.2 checksum. This routine has been broken out so that
+ * other programs can use it. It takes a file descriptor to read from and
+ * locations to store the crc and the number of bytes read. It returns 0 on
+ * success and 1 on failure. Errno is set on failure.
+ */
+extern int sflag;
+u_int32_t crc_total = ~0; /* The crc over a number of files. */
+
+int
+crc(int fd, u_int32_t *cval, u_int32_t *clen)
+{
+ u_char *p;
+ int nr;
+ u_int32_t thecrc, len;
+ u_int32_t crctot;
+ u_char buf[16 * 1024];
+
+#define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
+
+ thecrc = len = crctot = 0;
+ if (sflag)
+ crctot = ~crc_total;
+ while ((nr = read(fd, buf, sizeof(buf))) > 0)
+ if (sflag) {
+ for (len += nr, p = buf; nr--; ++p) {
+ COMPUTE(thecrc, *p);
+ COMPUTE(crctot, *p);
+ }
+ } else {
+ for (len += nr, p = buf; nr--; ++p)
+ COMPUTE(thecrc, *p);
+ }
+ if (nr < 0)
+ return 1;
+
+ *clen = len;
+
+ /* Include the length of the file. */
+ if (sflag) {
+ for (; len != 0; len >>= 8) {
+ COMPUTE(thecrc, len & 0xff);
+ COMPUTE(crctot, len & 0xff);
+ }
+ } else {
+ for (; len != 0; len >>= 8)
+ COMPUTE(thecrc, len & 0xff);
+ }
+
+ *cval = ~thecrc;
+ if (sflag)
+ crc_total = ~crctot;
+ return 0;
+}
--- /dev/null
+/* $NetBSD: create.c,v 1.58 2009/04/03 21:18:59 apb Exp $ */
+
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)create.c 8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: create.c,v 1.58 2009/04/03 21:18:59 apb Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#if ! HAVE_NBTOOL_CONFIG_H
+#include <dirent.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#ifndef NO_MD5
+#include <md5.h>
+#endif
+#ifndef NO_RMD160
+#include <rmd160.h>
+#endif
+#ifndef NO_SHA1
+#include <sha1.h>
+#endif
+#ifndef NO_SHA2
+#include <sha2.h>
+#endif
+
+#include "extern.h"
+
+#define INDENTNAMELEN 15
+#define MAXLINELEN 80
+
+static gid_t gid;
+static uid_t uid;
+static mode_t mode;
+static u_long flags;
+
+static int dcmp(const FTSENT **, const FTSENT **);
+static void output(int *, const char *, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+static int statd(FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, u_long *);
+static void statf(FTSENT *);
+
+void
+cwalk(void)
+{
+ FTS *t;
+ FTSENT *p;
+ time_t clocktime;
+ char host[MAXHOSTNAMELEN + 1];
+ const char *user;
+ char *argv[2];
+ char dot[] = ".";
+
+ argv[0] = dot;
+ argv[1] = NULL;
+
+ time(&clocktime);
+ gethostname(host, sizeof(host));
+ host[sizeof(host) - 1] = '\0';
+ if ((user = getlogin()) == NULL) {
+ struct passwd *pw;
+ user = (pw = getpwuid(getuid())) == NULL ? pw->pw_name :
+ "<unknown>";
+ }
+
+ printf(
+ "#\t user: %s\n#\tmachine: %s\n#\t tree: %s\n#\t date: %s",
+ user, host, fullpath, ctime(&clocktime));
+
+ if ((t = fts_open(argv, ftsoptions, dcmp)) == NULL)
+ mtree_err("fts_open: %s", strerror(errno));
+ while ((p = fts_read(t)) != NULL) {
+ if (check_excludes(p->fts_name, p->fts_path)) {
+ fts_set(t, p, FTS_SKIP);
+ continue;
+ }
+ switch(p->fts_info) {
+ case FTS_D:
+ printf("\n# %s\n", p->fts_path);
+ statd(t, p, &uid, &gid, &mode, &flags);
+ statf(p);
+ break;
+ case FTS_DP:
+ if (p->fts_level > 0)
+ printf("# %s\n..\n\n", p->fts_path);
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ mtree_err("%s: %s",
+ p->fts_path, strerror(p->fts_errno));
+ break;
+ default:
+ if (!dflag)
+ statf(p);
+ break;
+
+ }
+ }
+ fts_close(t);
+ if (sflag && keys & F_CKSUM)
+ mtree_err("%s checksum: %u", fullpath, crc_total);
+}
+
+static void
+statf(FTSENT *p)
+{
+ u_int32_t len, val;
+ int fd, indent;
+ const char *name;
+#if !defined(NO_MD5) || !defined(NO_RMD160) || !defined(NO_SHA1) || !defined(NO_SHA2)
+ char *digestbuf;
+#endif
+
+ indent = printf("%s%s",
+ S_ISDIR(p->fts_statp->st_mode) ? "" : " ", vispath(p->fts_name));
+
+ if (indent > INDENTNAMELEN)
+ indent = MAXLINELEN;
+ else
+ indent += printf("%*s", INDENTNAMELEN - indent, "");
+
+ if (!S_ISREG(p->fts_statp->st_mode))
+ output(&indent, "type=%s", inotype(p->fts_statp->st_mode));
+ if (keys & (F_UID | F_UNAME) && p->fts_statp->st_uid != uid) {
+ if (keys & F_UNAME &&
+ (name = user_from_uid(p->fts_statp->st_uid, 1)) != NULL)
+ output(&indent, "uname=%s", name);
+ else /* if (keys & F_UID) */
+ output(&indent, "uid=%u", p->fts_statp->st_uid);
+ }
+ if (keys & (F_GID | F_GNAME) && p->fts_statp->st_gid != gid) {
+ if (keys & F_GNAME &&
+ (name = group_from_gid(p->fts_statp->st_gid, 1)) != NULL)
+ output(&indent, "gname=%s", name);
+ else /* if (keys & F_GID) */
+ output(&indent, "gid=%u", p->fts_statp->st_gid);
+ }
+ if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode)
+ output(&indent, "mode=%#o", p->fts_statp->st_mode & MBITS);
+ if (keys & F_DEV &&
+ (S_ISBLK(p->fts_statp->st_mode) || S_ISCHR(p->fts_statp->st_mode)))
+ output(&indent, "device=%#llx",
+ (long long)p->fts_statp->st_rdev);
+ if (keys & F_NLINK && p->fts_statp->st_nlink != 1)
+ output(&indent, "nlink=%u", p->fts_statp->st_nlink);
+ if (keys & F_SIZE && S_ISREG(p->fts_statp->st_mode))
+ output(&indent, "size=%lld", (long long)p->fts_statp->st_size);
+ if (keys & F_TIME)
+#if defined(BSD4_4) && !defined(HAVE_NBTOOL_CONFIG_H)
+ output(&indent, "time=%ld.%ld",
+ (long)p->fts_statp->st_mtimespec.tv_sec,
+ p->fts_statp->st_mtimespec.tv_nsec);
+#else
+ output(&indent, "time=%ld.%ld",
+ (long)p->fts_statp->st_mtime, (long)0);
+#endif
+ if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) {
+ if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 ||
+ crc(fd, &val, &len))
+ mtree_err("%s: %s", p->fts_accpath, strerror(errno));
+ close(fd);
+ output(&indent, "cksum=%lu", (long)val);
+ }
+#ifndef NO_MD5
+ if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) {
+ if ((digestbuf = MD5File(p->fts_accpath, NULL)) == NULL)
+ mtree_err("%s: MD5File failed: %s", p->fts_accpath, strerror(errno));
+ output(&indent, "md5=%s", digestbuf);
+ free(digestbuf);
+ }
+#endif /* ! NO_MD5 */
+#ifndef NO_RMD160
+ if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) {
+ if ((digestbuf = RMD160File(p->fts_accpath, NULL)) == NULL)
+ mtree_err("%s: RMD160File failed: %s", p->fts_accpath, strerror(errno));
+ output(&indent, "rmd160=%s", digestbuf);
+ free(digestbuf);
+ }
+#endif /* ! NO_RMD160 */
+#ifndef NO_SHA1
+ if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) {
+ if ((digestbuf = SHA1File(p->fts_accpath, NULL)) == NULL)
+ mtree_err("%s: SHA1File failed: %s", p->fts_accpath, strerror(errno));
+ output(&indent, "sha1=%s", digestbuf);
+ free(digestbuf);
+ }
+#endif /* ! NO_SHA1 */
+#ifndef NO_SHA2
+ if (keys & F_SHA256 && S_ISREG(p->fts_statp->st_mode)) {
+ if ((digestbuf = SHA256_File(p->fts_accpath, NULL)) == NULL)
+ mtree_err("%s: SHA256_File failed: %s", p->fts_accpath, strerror(errno));
+ output(&indent, "sha256=%s", digestbuf);
+ free(digestbuf);
+ }
+ if (keys & F_SHA384 && S_ISREG(p->fts_statp->st_mode)) {
+ if ((digestbuf = SHA384_File(p->fts_accpath, NULL)) == NULL)
+ mtree_err("%s: SHA384_File failed: %s", p->fts_accpath, strerror(errno));
+ output(&indent, "sha384=%s", digestbuf);
+ free(digestbuf);
+ }
+ if (keys & F_SHA512 && S_ISREG(p->fts_statp->st_mode)) {
+ if ((digestbuf = SHA512_File(p->fts_accpath, NULL)) == NULL)
+ mtree_err("%s: SHA512_File failed: %s", p->fts_accpath, strerror(errno));
+ output(&indent, "sha512=%s", digestbuf);
+ free(digestbuf);
+ }
+#endif /* ! NO_SHA2 */
+ if (keys & F_SLINK &&
+ (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE))
+ output(&indent, "link=%s", vispath(rlink(p->fts_accpath)));
+#if HAVE_STRUCT_STAT_ST_FLAGS
+ if (keys & F_FLAGS && p->fts_statp->st_flags != flags)
+ output(&indent, "flags=%s",
+ flags_to_string(p->fts_statp->st_flags, "none"));
+#endif
+ putchar('\n');
+}
+
+/* XXX
+ * FLAGS2INDEX will fail once the user and system settable bits need more
+ * than one byte, respectively.
+ */
+#define FLAGS2INDEX(x) (((x >> 8) & 0x0000ff00) | (x & 0x000000ff))
+
+#define MTREE_MAXGID 5000
+#define MTREE_MAXUID 5000
+#define MTREE_MAXMODE (MBITS + 1)
+#if HAVE_STRUCT_STAT_ST_FLAGS
+#define MTREE_MAXFLAGS (FLAGS2INDEX(CH_MASK) + 1) /* 1808 */
+#else
+#define MTREE_MAXFLAGS 1
+#endif
+#define MTREE_MAXS 16
+
+static int
+statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode,
+ u_long *pflags)
+{
+ FTSENT *p;
+ gid_t sgid;
+ uid_t suid;
+ mode_t smode;
+ u_long sflags = 0;
+ const char *name;
+ gid_t savegid;
+ uid_t saveuid;
+ mode_t savemode;
+ u_long saveflags;
+ u_short maxgid, maxuid, maxmode, maxflags;
+ u_short g[MTREE_MAXGID], u[MTREE_MAXUID],
+ m[MTREE_MAXMODE], f[MTREE_MAXFLAGS];
+ static int first = 1;
+
+ savegid = *pgid;
+ saveuid = *puid;
+ savemode = *pmode;
+ saveflags = *pflags;
+ if ((p = fts_children(t, 0)) == NULL) {
+ if (errno)
+ mtree_err("%s: %s", RP(parent), strerror(errno));
+ return (1);
+ }
+
+ memset(g, 0, sizeof(g));
+ memset(u, 0, sizeof(u));
+ memset(m, 0, sizeof(m));
+ memset(f, 0, sizeof(f));
+
+ maxuid = maxgid = maxmode = maxflags = 0;
+ for (; p; p = p->fts_link) {
+ smode = p->fts_statp->st_mode & MBITS;
+ if (smode < MTREE_MAXMODE && ++m[smode] > maxmode) {
+ savemode = smode;
+ maxmode = m[smode];
+ }
+ sgid = p->fts_statp->st_gid;
+ if (sgid < MTREE_MAXGID && ++g[sgid] > maxgid) {
+ savegid = sgid;
+ maxgid = g[sgid];
+ }
+ suid = p->fts_statp->st_uid;
+ if (suid < MTREE_MAXUID && ++u[suid] > maxuid) {
+ saveuid = suid;
+ maxuid = u[suid];
+ }
+
+#if HAVE_STRUCT_STAT_ST_FLAGS
+ sflags = FLAGS2INDEX(p->fts_statp->st_flags);
+ if (sflags < MTREE_MAXFLAGS && ++f[sflags] > maxflags) {
+ saveflags = p->fts_statp->st_flags;
+ maxflags = f[sflags];
+ }
+#endif
+ }
+ /*
+ * If the /set record is the same as the last one we do not need to
+ * output a new one. So first we check to see if anything changed.
+ * Note that we always output a /set record for the first directory.
+ */
+ if (((keys & (F_UNAME | F_UID)) && (*puid != saveuid)) ||
+ ((keys & (F_GNAME | F_GID)) && (*pgid != savegid)) ||
+ ((keys & F_MODE) && (*pmode != savemode)) ||
+ ((keys & F_FLAGS) && (*pflags != saveflags)) ||
+ first) {
+ first = 0;
+ printf("/set type=file");
+ if (keys & (F_UID | F_UNAME)) {
+ if (keys & F_UNAME &&
+ (name = user_from_uid(saveuid, 1)) != NULL)
+ printf(" uname=%s", name);
+ else /* if (keys & F_UID) */
+ printf(" uid=%lu", (u_long)saveuid);
+ }
+ if (keys & (F_GID | F_GNAME)) {
+ if (keys & F_GNAME &&
+ (name = group_from_gid(savegid, 1)) != NULL)
+ printf(" gname=%s", name);
+ else /* if (keys & F_UID) */
+ printf(" gid=%lu", (u_long)savegid);
+ }
+ if (keys & F_MODE)
+ printf(" mode=%#lo", (u_long)savemode);
+ if (keys & F_NLINK)
+ printf(" nlink=1");
+ if (keys & F_FLAGS)
+ printf(" flags=%s",
+ flags_to_string(saveflags, "none"));
+ printf("\n");
+ *puid = saveuid;
+ *pgid = savegid;
+ *pmode = savemode;
+ *pflags = saveflags;
+ }
+ return (0);
+}
+
+/*
+ * dcmp --
+ * used as a comparison function passed to fts_open() to control
+ * the order in which fts_read() returns results. We make
+ * directories sort after non-directories, but otherwise sort in
+ * strcmp() order.
+ *
+ * Keep this in sync with nodecmp() in spec.c.
+ */
+static int
+dcmp(const FTSENT **a, const FTSENT **b)
+{
+
+ if (S_ISDIR((*a)->fts_statp->st_mode)) {
+ if (!S_ISDIR((*b)->fts_statp->st_mode))
+ return (1);
+ } else if (S_ISDIR((*b)->fts_statp->st_mode))
+ return (-1);
+ return (strcmp((*a)->fts_name, (*b)->fts_name));
+}
+
+void
+output(int *offset, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[1024];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (*offset + strlen(buf) > MAXLINELEN - 3) {
+ printf(" \\\n%*s", INDENTNAMELEN, "");
+ *offset = INDENTNAMELEN;
+ }
+ *offset += printf(" %s", buf) + 1;
+}
--- /dev/null
+/* $NetBSD: excludes.c,v 1.13 2004/06/20 22:20:18 jmc Exp $ */
+
+/*
+ * Copyright 2000 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+
+#if defined(__RCSID) && !defined(lint)
+__RCSID("$NetBSD: excludes.c,v 1.13 2004/06/20 22:20:18 jmc Exp $");
+#endif
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <fnmatch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <util.h>
+
+#include "extern.h"
+
+
+/*
+ * We're assuming that there won't be a whole lot of excludes,
+ * so it's OK to use a stupid algorithm.
+ */
+struct exclude {
+ LIST_ENTRY(exclude) link;
+ const char *glob;
+ int pathname;
+};
+static LIST_HEAD(, exclude) excludes;
+
+
+void
+init_excludes(void)
+{
+
+ LIST_INIT(&excludes);
+}
+
+void
+read_excludes_file(const char *name)
+{
+ FILE *fp;
+ char *line;
+ struct exclude *e;
+
+ fp = fopen(name, "r");
+ if (fp == 0)
+ err(1, "%s", name);
+
+ while ((line = fparseln(fp, NULL, NULL, NULL,
+ FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT | FPARSELN_UNESCESC))
+ != NULL) {
+ if (line[0] == '\0')
+ continue;
+
+ if ((e = malloc(sizeof *e)) == NULL)
+ mtree_err("memory allocation error");
+
+ e->glob = line;
+ if (strchr(e->glob, '/') != NULL)
+ e->pathname = 1;
+ else
+ e->pathname = 0;
+ LIST_INSERT_HEAD(&excludes, e, link);
+ }
+ fclose(fp);
+}
+
+int
+check_excludes(const char *fname, const char *path)
+{
+ struct exclude *e;
+
+ /* fnmatch(3) has a funny return value convention... */
+#define MATCH(g, n) (fnmatch((g), (n), FNM_PATHNAME) == 0)
+
+ e = LIST_FIRST(&excludes);
+ while (e) {
+ if ((e->pathname && MATCH(e->glob, path))
+ || MATCH(e->glob, fname)) {
+ return (1);
+ }
+ e = LIST_NEXT(e, link);
+ }
+ return (0);
+}
--- /dev/null
+/* $NetBSD: extern.h,v 1.32 2011/08/29 20:37:43 joerg Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include "mtree.h"
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#else
+#define HAVE_STRUCT_STAT_ST_FLAGS 1
+#endif
+
+#include <err.h>
+#include <fts.h>
+#include <util.h>
+
+#if HAVE_NETDB_H
+/* For MAXHOSTNAMELEN on some platforms. */
+#include <netdb.h>
+#endif
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+void addtag(slist_t *, char *);
+int check_excludes(const char *, const char *);
+int compare(NODE *, FTSENT *);
+int crc(int, u_int32_t *, u_int32_t *);
+void cwalk(void);
+void dump_nodes(const char *, NODE *, int);
+void init_excludes(void);
+int matchtags(NODE *);
+__dead __printflike(1,2) void mtree_err(const char *, ...);
+const char *nodetype(u_int);
+u_int parsekey(const char *, int *);
+void parsetags(slist_t *, char *);
+u_int parsetype(const char *);
+void read_excludes_file(const char *);
+const char *rlink(const char *);
+int verify(void);
+
+extern int dflag, eflag, iflag, lflag, mflag, rflag, sflag, tflag, uflag;
+extern int mtree_Mflag, mtree_Sflag, mtree_Wflag;
+extern size_t mtree_lineno;
+extern u_int32_t crc_total;
+extern int ftsoptions, keys;
+extern char fullpath[];
+extern slist_t includetags, excludetags;
+
--- /dev/null
+/* $NetBSD: getid.c,v 1.7 2008/04/28 20:24:17 martin Exp $ */
+/* from: NetBSD: getpwent.c,v 1.48 2000/10/03 03:22:26 enami Exp */
+/* from: NetBSD: getgrent.c,v 1.41 2002/01/12 23:51:30 lukem Exp */
+
+/*
+ * Copyright (c) 1987, 1988, 1989, 1993, 1994, 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn of Wasabi Systems.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: getid.c,v 1.7 2008/04/28 20:24:17 martin Exp $");
+
+#include <sys/param.h>
+
+#include <grp.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static struct group * gi_getgrnam(const char *);
+static struct group * gi_getgrgid(gid_t);
+static int gi_setgroupent(int);
+static void gi_endgrent(void);
+static int grstart(void);
+static int grscan(int, gid_t, const char *);
+static int grmatchline(int, gid_t, const char *);
+
+static struct passwd * gi_getpwnam(const char *);
+static struct passwd * gi_getpwuid(uid_t);
+static int gi_setpassent(int);
+static void gi_endpwent(void);
+static int pwstart(void);
+static int pwscan(int, uid_t, const char *);
+static int pwmatchline(int, uid_t, const char *);
+
+#define MAXGRP 200
+#define MAXLINELENGTH 1024
+
+static FILE *_gr_fp;
+static struct group _gr_group;
+static int _gr_stayopen;
+static int _gr_filesdone;
+static FILE *_pw_fp;
+static struct passwd _pw_passwd; /* password structure */
+static int _pw_stayopen; /* keep fd's open */
+static int _pw_filesdone;
+
+static char grfile[MAXPATHLEN];
+static char pwfile[MAXPATHLEN];
+
+static char *members[MAXGRP];
+static char grline[MAXLINELENGTH];
+static char pwline[MAXLINELENGTH];
+
+int
+setup_getid(const char *dir)
+{
+ if (dir == NULL)
+ return (0);
+
+ /* close existing databases */
+ gi_endgrent();
+ gi_endpwent();
+
+ /* build paths to new databases */
+ snprintf(grfile, sizeof(grfile), "%s/group", dir);
+ snprintf(pwfile, sizeof(pwfile), "%s/master.passwd", dir);
+
+ /* try to open new databases */
+ if (!grstart() || !pwstart())
+ return (0);
+
+ /* switch pwcache(3) lookup functions */
+ if (pwcache_groupdb(gi_setgroupent, gi_endgrent,
+ gi_getgrnam, gi_getgrgid) == -1
+ || pwcache_userdb(gi_setpassent, gi_endpwent,
+ gi_getpwnam, gi_getpwuid) == -1)
+ return (0);
+
+ return (1);
+}
+
+
+/*
+ * group lookup functions
+ */
+
+static struct group *
+gi_getgrnam(const char *name)
+{
+ int rval;
+
+ if (!grstart())
+ return NULL;
+ rval = grscan(1, 0, name);
+ if (!_gr_stayopen)
+ endgrent();
+ return (rval) ? &_gr_group : NULL;
+}
+
+static struct group *
+gi_getgrgid(gid_t gid)
+{
+ int rval;
+
+ if (!grstart())
+ return NULL;
+ rval = grscan(1, gid, NULL);
+ if (!_gr_stayopen)
+ endgrent();
+ return (rval) ? &_gr_group : NULL;
+}
+
+static int
+gi_setgroupent(int stayopen)
+{
+
+ if (!grstart())
+ return 0;
+ _gr_stayopen = stayopen;
+ return 1;
+}
+
+static void
+gi_endgrent(void)
+{
+
+ _gr_filesdone = 0;
+ if (_gr_fp) {
+ (void)fclose(_gr_fp);
+ _gr_fp = NULL;
+ }
+}
+
+static int
+grstart(void)
+{
+
+ _gr_filesdone = 0;
+ if (_gr_fp) {
+ rewind(_gr_fp);
+ return 1;
+ }
+ if (grfile[0] == '\0') /* sanity check */
+ return 0;
+ 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;
+}
+
--- /dev/null
+/* $NetBSD: misc.c,v 1.30 2009/01/18 12:09:38 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)misc.c 8.1 (Berkeley) 6/6/93
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(lint)
+__RCSID("$NetBSD: misc.c,v 1.30 2009/01/18 12:09:38 lukem Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "extern.h"
+
+typedef struct _key {
+ const char *name; /* key name */
+ u_int val; /* value */
+
+#define NEEDVALUE 0x01
+ u_int flags;
+} KEY;
+
+/* NB: the following tables must be sorted lexically. */
+static KEY keylist[] = {
+ {"cksum", F_CKSUM, NEEDVALUE},
+ {"device", F_DEV, NEEDVALUE},
+ {"flags", F_FLAGS, NEEDVALUE},
+ {"gid", F_GID, NEEDVALUE},
+ {"gname", F_GNAME, NEEDVALUE},
+ {"ignore", F_IGN, 0},
+ {"link", F_SLINK, NEEDVALUE},
+ {"md5", F_MD5, NEEDVALUE},
+ {"md5digest", F_MD5, NEEDVALUE},
+ {"mode", F_MODE, NEEDVALUE},
+ {"nlink", F_NLINK, NEEDVALUE},
+ {"optional", F_OPT, 0},
+ {"rmd160", F_RMD160, NEEDVALUE},
+ {"rmd160digest",F_RMD160, NEEDVALUE},
+ {"sha1", F_SHA1, NEEDVALUE},
+ {"sha1digest", F_SHA1, NEEDVALUE},
+ {"sha256", F_SHA256, NEEDVALUE},
+ {"sha256digest",F_SHA256, NEEDVALUE},
+ {"sha384", F_SHA384, NEEDVALUE},
+ {"sha384digest",F_SHA384, NEEDVALUE},
+ {"sha512", F_SHA512, NEEDVALUE},
+ {"sha512digest",F_SHA512, NEEDVALUE},
+ {"size", F_SIZE, NEEDVALUE},
+ {"tags", F_TAGS, NEEDVALUE},
+ {"time", F_TIME, NEEDVALUE},
+ {"type", F_TYPE, NEEDVALUE},
+ {"uid", F_UID, NEEDVALUE},
+ {"uname", F_UNAME, NEEDVALUE}
+};
+
+static KEY typelist[] = {
+ {"block", F_BLOCK, 0},
+ {"char", F_CHAR, 0},
+ {"dir", F_DIR, 0},
+#ifdef S_IFDOOR
+ {"door", F_DOOR, 0},
+#endif
+ {"fifo", F_FIFO, 0},
+ {"file", F_FILE, 0},
+ {"link", F_LINK, 0},
+ {"socket", F_SOCK, 0},
+};
+
+slist_t excludetags, includetags;
+int keys = KEYDEFAULT;
+
+
+int keycompare(const void *, const void *);
+
+u_int
+parsekey(const char *name, int *needvaluep)
+{
+ static int allbits;
+ KEY *k, tmp;
+
+ if (allbits == 0) {
+ size_t i;
+
+ for (i = 0; i < sizeof(keylist) / sizeof(KEY); i++)
+ allbits |= keylist[i].val;
+ }
+ tmp.name = name;
+ if (strcmp(name, "all") == 0)
+ return (allbits);
+ k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY),
+ sizeof(KEY), keycompare);
+ if (k == NULL)
+ mtree_err("unknown keyword `%s'", name);
+
+ if (needvaluep)
+ *needvaluep = k->flags & NEEDVALUE ? 1 : 0;
+
+ return (k->val);
+}
+
+u_int
+parsetype(const char *name)
+{
+ KEY *k, tmp;
+
+ tmp.name = name;
+ k = (KEY *)bsearch(&tmp, typelist, sizeof(typelist) / sizeof(KEY),
+ sizeof(KEY), keycompare);
+ if (k == NULL)
+ mtree_err("unknown file type `%s'", name);
+
+ return (k->val);
+}
+
+int
+keycompare(const void *a, const void *b)
+{
+
+ return (strcmp(((const KEY *)a)->name, ((const KEY *)b)->name));
+}
+
+void
+mtree_err(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vwarnx(fmt, ap);
+ va_end(ap);
+ if (mtree_lineno)
+ warnx("failed at line %lu of the specification",
+ (u_long) mtree_lineno);
+ exit(1);
+ /* NOTREACHED */
+}
+
+void
+addtag(slist_t *list, char *elem)
+{
+
+#define TAG_CHUNK 20
+
+ if ((list->count % TAG_CHUNK) == 0) {
+ char **new;
+
+ new = (char **)realloc(list->list, (list->count + TAG_CHUNK)
+ * sizeof(char *));
+ if (new == NULL)
+ mtree_err("memory allocation error");
+ list->list = new;
+ }
+ list->list[list->count] = elem;
+ list->count++;
+}
+
+void
+parsetags(slist_t *list, char *args)
+{
+ char *p, *e;
+ int len;
+
+ if (args == NULL) {
+ addtag(list, NULL);
+ return;
+ }
+ while ((p = strsep(&args, ",")) != NULL) {
+ if (*p == '\0')
+ continue;
+ len = strlen(p) + 3; /* "," + p + ",\0" */
+ if ((e = malloc(len)) == NULL)
+ mtree_err("memory allocation error");
+ snprintf(e, len, ",%s,", p);
+ addtag(list, e);
+ }
+}
+
+/*
+ * matchtags
+ * returns 0 if there's a match from the exclude list in the node's tags,
+ * or there's an include list and no match.
+ * return 1 otherwise.
+ */
+int
+matchtags(NODE *node)
+{
+ int i;
+
+ if (node->tags) {
+ for (i = 0; i < excludetags.count; i++)
+ if (strstr(node->tags, excludetags.list[i]))
+ break;
+ if (i < excludetags.count)
+ return (0);
+
+ for (i = 0; i < includetags.count; i++)
+ if (strstr(node->tags, includetags.list[i]))
+ break;
+ if (i > 0 && i == includetags.count)
+ return (0);
+ } else if (includetags.count > 0) {
+ return (0);
+ }
+ return (1);
+}
+
+u_int
+nodetoino(u_int type)
+{
+
+ switch (type) {
+ case F_BLOCK:
+ return S_IFBLK;
+ case F_CHAR:
+ return S_IFCHR;
+ case F_DIR:
+ return S_IFDIR;
+ case F_FIFO:
+ return S_IFIFO;
+ case F_FILE:
+ return S_IFREG;
+ case F_LINK:
+ return S_IFLNK;
+#ifdef S_IFSOCK
+ case F_SOCK:
+ return S_IFSOCK;
+#endif
+ default:
+ printf("unknown type %d", type);
+ abort();
+ }
+ /* NOTREACHED */
+}
+
+const char *
+nodetype(u_int type)
+{
+
+ return (inotype(nodetoino(type)));
+}
+
+
+const char *
+inotype(u_int type)
+{
+
+ switch (type & S_IFMT) {
+ case S_IFBLK:
+ return ("block");
+ case S_IFCHR:
+ return ("char");
+ case S_IFDIR:
+ return ("dir");
+ case S_IFIFO:
+ return ("fifo");
+ case S_IFREG:
+ return ("file");
+ case S_IFLNK:
+ return ("link");
+#ifdef S_IFSOCK
+ case S_IFSOCK:
+ return ("socket");
+#endif
+#ifdef S_IFDOOR
+ case S_IFDOOR:
+ return ("door");
+#endif
+ default:
+ return ("unknown");
+ }
+ /* NOTREACHED */
+}
--- /dev/null
+.\" $NetBSD: mtree.8,v 1.53 2010/01/20 14:00:48 wiz Exp $
+.\"
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" Copyright (c) 2001-2004 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Luke Mewburn of Wasabi Systems.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" @(#)mtree.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd January 20, 2010
+.Dt MTREE 8
+.Os
+.Sh NAME
+.Nm mtree
+.Nd map a directory hierarchy
+.Sh SYNOPSIS
+.Nm
+.Op Fl CcDdeLlMPrSUuWx
+.Op Fl i | Fl m
+.Op Fl E Ar tags
+.Op Fl f Ar spec
+.Op Fl I Ar tags
+.Op Fl K Ar keywords
+.Op Fl k Ar keywords
+.Op Fl N Ar dbdir
+.Op Fl p Ar path
+.Op Fl R Ar keywords
+.Op Fl s Ar seed
+.Op Fl X Ar exclude-file
+.Sh DESCRIPTION
+The
+.Nm
+utility compares a file hierarchy against a specification,
+creates a specification for a file hierarchy, or modifies
+a specification.
+.Pp
+The default action, if not overridden by command line options,
+is to compare the file hierarchy rooted in the current directory
+against a specification read from the standard input.
+Messages are written to the standard output for any files whose
+characteristics do not match the specification, or which are
+missing from either the file hierarchy or the specification.
+.Pp
+The options are as follows:
+.Bl -tag -width Xxxexcludexfilexx
+.It Fl C
+Convert a specification into
+a format that's easier to parse with various tools.
+The input specification is read from standard input or
+from the file given by
+.Fl f Ar spec .
+In the output, each file or directory is represented using a single line
+(which might be very long).
+The full path name
+(beginning with
+.Dq \&./ )
+is always printed as the first field;
+.Fl k ,
+.Fl K ,
+and
+.Fl R
+can be used to control which other keywords are printed;
+.Fl E
+and
+.Fl I
+can be used to control which files are printed;
+.Fl S
+option can be used to sort the output.
+.It Fl c
+Print a specification for the file hierarchy originating at
+the current working directory (or the directory provided by
+.Fl p Ar path )
+to the standard output.
+The output is in a style using relative path names.
+.It Fl D
+As per
+.Fl C ,
+except that the path name is always printed as the last field instead of
+the first.
+.It Fl d
+Ignore everything except directory type files.
+.It Fl E Ar tags
+Add the comma separated tags to the
+.Dq exclusion
+list.
+Non-directories with tags which are in the exclusion list are not printed with
+.Fl C
+and
+.Fl D .
+.It Fl e
+Don't complain about files that are in the file hierarchy, but not in the
+specification.
+.It Fl f Ar spec
+Read the specification from
+.Ar file ,
+instead of from the standard input.
+.It Fl I Ar tags
+Add the comma separated tags to the
+.Dq inclusion
+list.
+Non-directories with tags which are in the inclusion list are printed with
+.Fl C
+and
+.Fl D .
+If no inclusion list is provided, the default is to display all files.
+.It Fl i
+If specified, set the schg and/or sappnd flags.
+.It Fl K Ar keywords
+Add the specified (whitespace or comma separated) keywords to the current
+set of keywords.
+If
+.Ql all
+is specified, add all of the other keywords.
+.It Fl k Ar keywords
+Use the
+.Sy type
+keyword plus the specified (whitespace or comma separated)
+keywords instead of the current set of keywords.
+If
+.Ql all
+is specified, use all of the other keywords.
+If the
+.Sy type
+keyword is not desired, suppress it with
+.Fl R Ar type .
+.It Fl L
+Follow all symbolic links in the file hierarchy.
+.It Fl l
+Do
+.Dq loose
+permissions checks, in which more stringent permissions
+will match less stringent ones.
+For example, a file marked mode 0444
+will pass a check for mode 0644.
+.Dq Loose
+checks apply only to read, write and execute permissions -- in
+particular, if other bits like the sticky bit or suid/sgid bits are
+set either in the specification or the file, exact checking will be
+performed.
+This option may not be set at the same time as the
+.Fl u
+or
+.Fl U
+option.
+.It Fl M
+Permit merging of specification entries with different types,
+with the last entry take precedence.
+.It Fl m
+If the schg and/or sappnd flags are specified, reset these flags.
+Note that this is only possible with securelevel less than 1 (i.e.,
+in single user mode or while the system is running in insecure
+mode).
+See
+.Xr init 8
+for information on security levels.
+.It Fl N Ar dbdir
+Use the user database text file
+.Pa master.passwd
+and group database text file
+.Pa group
+from
+.Ar dbdir ,
+rather than using the results from the system's
+.Xr getpwnam 3
+and
+.Xr getgrnam 3
+(and related) library calls.
+.It Fl P
+Don't follow symbolic links in the file hierarchy, instead consider
+the symbolic link itself in any comparisons.
+This is the default.
+.It Fl p Ar path
+Use the file hierarchy rooted in
+.Ar path ,
+instead of the current directory.
+.It Fl R Ar keywords
+Remove the specified (whitespace or comma separated) keywords from the current
+set of keywords.
+If
+.Ql all
+is specified, remove all of the other keywords.
+.It Fl r
+Remove any files in the file hierarchy that are not described in the
+specification.
+.It Fl S
+When reading a specification into an internal data structure,
+sort the entries.
+Sorting will affect the order of the output produced by the
+.Fl C
+or
+.Fl D
+options, and will also affect the order in which
+missing entries are created or reported when a directory tree is checked
+against a specification.
+.Pp
+The sort order is the same as that used by the
+.Fl c
+option, which is that entries within the same directory are
+sorted in the order used by
+.Xr strcmp 3 ,
+except that entries for subdirectories sort after other entries.
+By default, if the
+.Fl S
+option is not used, entries within the same directory are collected
+together (separated from entries for other directories), but not sorted.
+.It Fl s Ar seed
+Display a single checksum to the standard error output that represents all
+of the files for which the keyword
+.Sy cksum
+was specified.
+The checksum is seeded with the specified value.
+.It Fl t
+Modify the modified time of existing files, the device type of devices, and
+symbolic link targets, to match the specification.
+.It Fl U
+Same as
+.Fl u
+except that a mismatch is not considered to be an error if it was corrected.
+.It Fl u
+Modify the owner, group, permissions, and flags of existing files,
+the device type of devices, and symbolic link targets,
+to match the specification.
+Create any missing directories, devices or symbolic links.
+User, group, and permissions must all be specified for missing directories
+to be created.
+Note that unless the
+.Fl i
+option is given, the schg and sappnd flags will not be set, even if
+specified.
+If
+.Fl m
+is given, these flags will be reset.
+Exit with a status of 0 on success,
+2 if the file hierarchy did not match the specification, and
+1 if any other error occurred.
+.It Fl W
+Don't attempt to set various file attributes such as the
+ownership, mode, flags, or time
+when creating new directories or changing existing entries.
+This option will be most useful when used in conjunction with
+.Fl u
+or
+.Fl U .
+.It Fl X Ar exclude-file
+The specified file contains
+.Xr fnmatch 3
+patterns matching files to be excluded from
+the specification, one to a line.
+If the pattern contains a
+.Ql \&/
+character, it will be matched against entire pathnames (relative to
+the starting directory); otherwise,
+it will be matched against basenames only.
+Comments are permitted in
+the
+.Ar exclude-list
+file.
+.It Fl x
+Don't descend below mount points in the file hierarchy.
+.El
+.Pp
+Specifications are mostly composed of
+.Dq keywords ,
+i.e. strings that
+that specify values relating to files.
+No keywords have default values, and if a keyword has no value set, no
+checks based on it are performed.
+.Pp
+Currently supported keywords are as follows:
+.Bl -tag -width sha384digestxx
+.It Sy cksum
+The checksum of the file using the default algorithm specified by
+the
+.Xr cksum 1
+utility.
+.It Sy device
+The device number to use for
+.Sy block
+or
+.Sy char
+file types.
+The argument must be one of the following forms:
+.Pp
+.Bl -tag -width 4n
+.It Ar format , Ns Ar major , Ns Ar minor
+A device with
+.Ar major
+and
+.Ar minor
+fields, for an operating system specified with
+.Ar format .
+See below for valid formats.
+.It Ar format , Ns Ar major , Ns Ar unit , Ns Ar subunit
+A device with
+.Ar major ,
+.Ar unit ,
+and
+.Ar subunit
+fields, for an operating system specified with
+.Ar format .
+(Currently this is only supported by the
+.Sy bsdos
+format.)
+.It Ar number
+Opaque number (as stored on the file system).
+.El
+.Pp
+The following values for
+.Ar format
+are recognized:
+.Sy native ,
+.Sy 386bsd ,
+.Sy 4bsd ,
+.Sy bsdos ,
+.Sy freebsd ,
+.Sy hpux ,
+.Sy isc ,
+.Sy linux ,
+.Sy netbsd ,
+.Sy osf1 ,
+.Sy sco ,
+.Sy solaris ,
+.Sy sunos ,
+.Sy svr3 ,
+.Sy svr4 ,
+and
+.Sy ultrix .
+.Pp
+See
+.Xr mknod 8
+for more details.
+.It Sy flags
+The file flags as a symbolic name.
+See
+.Xr chflags 1
+for information on these names.
+If no flags are to be set the string
+.Ql none
+may be used to override the current default.
+Note that the schg and sappnd flags are treated specially (see the
+.Fl i
+and
+.Fl m
+options).
+.It Sy ignore
+Ignore any file hierarchy below this file.
+.It Sy gid
+The file group as a numeric value.
+.It Sy gname
+The file group as a symbolic name.
+.It Sy link
+The file the symbolic link is expected to reference.
+.It Sy md5
+The
+.Tn MD5
+cryptographic message digest of the file.
+.It Sy md5digest
+Synonym for
+.Sy md5 .
+.It Sy mode
+The current file's permissions as a numeric (octal) or symbolic
+value.
+.It Sy nlink
+The number of hard links the file is expected to have.
+.It Sy optional
+The file is optional; don't complain about the file if it's
+not in the file hierarchy.
+.It Sy rmd160
+The
+.Tn RMD-160
+cryptographic message digest of the file.
+.It Sy rmd160digest
+Synonym for
+.Sy rmd160 .
+.It Sy sha1
+The
+.Tn SHA-1
+cryptographic message digest of the file.
+.It Sy sha1digest
+Synonym for
+.Sy sha1 .
+.It Sy sha256
+The 256-bits
+.Tn SHA-2
+cryptographic message digest of the file.
+.It Sy sha256digest
+Synonym for
+.Sy sha256 .
+.It Sy sha384
+The 384-bits
+.Tn SHA-2
+cryptographic message digest of the file.
+.It Sy sha384digest
+Synonym for
+.Sy sha384 .
+.It Sy sha512
+The 512-bits
+.Tn SHA-2
+cryptographic message digest of the file.
+.It Sy sha512digest
+Synonym for
+.Sy sha512 .
+.It Sy size
+The size, in bytes, of the file.
+.It Sy tags
+Comma delimited tags to be matched with
+.Fl E
+and
+.Fl I .
+These may be specified without leading or trailing commas, but will be
+stored internally with them.
+.It Sy time
+The last modification time of the file.
+.It Sy type
+The type of the file; may be set to any one of the following:
+.Pp
+.Bl -tag -width Sy -compact
+.It Sy block
+block special device
+.It Sy char
+character special device
+.It Sy dir
+directory
+.It Sy fifo
+fifo
+.It Sy file
+regular file
+.It Sy link
+symbolic link
+.It Sy socket
+socket
+.El
+.It Sy uid
+The file owner as a numeric value.
+.It Sy uname
+The file owner as a symbolic name.
+.El
+.Pp
+The default set of keywords are
+.Sy flags ,
+.Sy gid ,
+.Sy link ,
+.Sy mode ,
+.Sy nlink ,
+.Sy size ,
+.Sy time ,
+.Sy type ,
+and
+.Sy uid .
+.Pp
+There are four types of lines in a specification:
+.Pp
+.Bl -enum
+.It
+Set global values for a keyword.
+This consists of the string
+.Ql /set
+followed by whitespace, followed by sets of keyword/value
+pairs, separated by whitespace.
+Keyword/value pairs consist of a keyword, followed by an equals sign
+.Pq Ql = ,
+followed by a value, without whitespace characters.
+Once a keyword has been set, its value remains unchanged until either
+reset or unset.
+.It
+Unset global values for a keyword.
+This consists of the string
+.Ql /unset ,
+followed by whitespace, followed by one or more keywords,
+separated by whitespace.
+If
+.Ql all
+is specified, unset all of the keywords.
+.It
+A file specification, consisting of a path name, followed by whitespace,
+followed by zero or more whitespace separated keyword/value pairs.
+.Pp
+The path name may be preceded by whitespace characters.
+The path name may contain any of the standard path name matching
+characters
+.Po
+.Ql \&[ ,
+.Ql \&] ,
+.Ql \&?
+or
+.Ql *
+.Pc ,
+in which case files
+in the hierarchy will be associated with the first pattern that
+they match.
+.Nm
+uses
+.Xr strsvis 3
+(in VIS_CSTYLE format) to encode path names containing
+non-printable characters.
+Whitespace characters are encoded as
+.Ql \es
+(space),
+.Ql \et
+(tab), and
+.Ql \en
+(new line).
+.Ql #
+characters in path names are escaped by a preceding backslash
+.Ql \e
+to distinguish them from comments.
+.Pp
+Each of the keyword/value pairs consist of a keyword, followed by an
+equals sign
+.Pq Ql = ,
+followed by the keyword's value, without
+whitespace characters.
+These values override, without changing, the global value of the
+corresponding keyword.
+.Pp
+The first path name entry listed must be a directory named
+.Ql \&. ,
+as this ensures that intermixing full and relative path names will
+work consistently and correctly.
+Multiple entries for a directory named
+.Ql \&.
+are permitted; the settings for the last such entry override those
+of the existing entry.
+.Pp
+A path name that contains a slash
+.Pq Ql /
+that is not the first character will be treated as a full path
+(relative to the root of the tree).
+All parent directories referenced in the path name must exist.
+The current directory path used by relative path names will be updated
+appropriately.
+Multiple entries for the same full path are permitted if the types
+are the same (unless
+.Fl M
+is given, and then the types may differ);
+in this case the settings for the last entry take precedence.
+.Pp
+A path name that does not contain a slash will be treated as a relative path.
+Specifying a directory will cause subsequent files to be searched
+for in that directory hierarchy.
+.It
+A line containing only the string
+.Ql \&..
+which causes the current directory path (used by relative paths)
+to ascend one level.
+.El
+.Pp
+Empty lines and lines whose first non-whitespace character is a hash
+mark
+.Pq Ql #
+are ignored.
+.Pp
+The
+.Nm
+utility exits with a status of 0 on success, 1 if any error occurred,
+and 2 if the file hierarchy did not match the specification.
+.Sh FILES
+.Bl -tag -width /etc/mtree -compact
+.It Pa /etc/mtree
+system specification directory
+.El
+.Sh EXAMPLES
+To detect system binaries that have been
+.Dq trojan horsed ,
+it is recommended that
+.Nm
+be run on the file systems, and a copy of the results stored on a different
+machine, or, at least, in encrypted form.
+The seed for the
+.Fl s
+option should not be an obvious value and the final checksum should not be
+stored on-line under any circumstances!
+Then, periodically,
+.Nm
+should be run against the on-line specifications and the final checksum
+compared with the previous value.
+While it is possible for the bad guys to change the on-line specifications
+to conform to their modified binaries, it shouldn't be possible for them
+to make it produce the same final checksum value.
+If the final checksum value changes, the off-line copies of the specification
+can be used to detect which of the binaries have actually been modified.
+.Pp
+The
+.Fl d
+and
+.Fl u
+options can be used in combination to create directory hierarchies
+for distributions and other such things.
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr chgrp 1 ,
+.Xr chmod 1 ,
+.Xr cksum 1 ,
+.Xr stat 2 ,
+.Xr fnmatch 3 ,
+.Xr fts 3 ,
+.Xr strsvis 3 ,
+.Xr chown 8 ,
+.Xr mknod 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 Reno .
+The
+.Sy optional
+keyword appeared in
+.Nx 1.2 .
+The
+.Fl U
+option appeared in
+.Nx 1.3 .
+The
+.Sy flags
+and
+.Sy md5
+keywords, and
+.Fl i
+and
+.Fl m
+options
+appeared in
+.Nx 1.4 .
+The
+.Sy device ,
+.Sy rmd160 ,
+.Sy sha1 ,
+.Sy tags ,
+and
+.Sy all
+keywords,
+.Fl D ,
+.Fl E ,
+.Fl I ,
+.Fl l ,
+.Fl L ,
+.Fl N ,
+.Fl P ,
+.Fl R ,
+.Fl W ,
+and
+.Fl X
+options, and support for full paths appeared in
+.Nx 1.6 .
+The
+.Sy sha256 ,
+.Sy sha384 ,
+and
+.Sy sha512
+keywords appeared in
+.Nx 3.0 .
+The
+.Fl S
+option appeared in
+.Nx 6.0 .
--- /dev/null
+/* $NetBSD: mtree.c,v 1.37 2011/08/29 20:37:43 joerg Exp $ */
+
+/*-
+ * Copyright (c) 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__COPYRIGHT) && !defined(lint)
+__COPYRIGHT("@(#) Copyright (c) 1989, 1990, 1993\
+ The Regents of the University of California. All rights reserved.");
+#endif /* not lint */
+
+#if defined(__RCSID) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)mtree.c 8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: mtree.c,v 1.37 2011/08/29 20:37:43 joerg Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+int ftsoptions = FTS_PHYSICAL;
+int cflag, Cflag, dflag, Dflag, eflag, iflag, lflag, mflag,
+ rflag, sflag, tflag, uflag, Uflag;
+char fullpath[MAXPATHLEN];
+
+__dead static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ int ch, status;
+ char *dir, *p;
+
+ setprogname(argv[0]);
+
+ dir = NULL;
+ init_excludes();
+
+ while ((ch = getopt(argc, argv,
+ "cCdDeE:f:I:ik:K:lLmMN:p:PrR:s:StuUWxX:"))
+ != -1) {
+ switch((char)ch) {
+ case 'c':
+ cflag = 1;
+ break;
+ case 'C':
+ Cflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'D':
+ Dflag = 1;
+ break;
+ case 'E':
+ parsetags(&excludetags, optarg);
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case 'f':
+ if (!(freopen(optarg, "r", stdin)))
+ mtree_err("%s: %s", optarg, strerror(errno));
+ break;
+ case 'i':
+ iflag = 1;
+ break;
+ case 'I':
+ parsetags(&includetags, optarg);
+ break;
+ case 'k':
+ keys = F_TYPE;
+ while ((p = strsep(&optarg, " \t,")) != NULL)
+ if (*p != '\0')
+ keys |= parsekey(p, NULL);
+ break;
+ case 'K':
+ while ((p = strsep(&optarg, " \t,")) != NULL)
+ if (*p != '\0')
+ keys |= parsekey(p, NULL);
+ break;
+ case 'l':
+ lflag = 1;
+ break;
+ case 'L':
+ ftsoptions &= ~FTS_PHYSICAL;
+ ftsoptions |= FTS_LOGICAL;
+ break;
+ case 'm':
+ mflag = 1;
+ break;
+ case 'M':
+ mtree_Mflag = 1;
+ break;
+ case 'N':
+ if (! setup_getid(optarg))
+ mtree_err(
+ "Unable to use user and group databases in `%s'",
+ optarg);
+ break;
+ case 'p':
+ dir = optarg;
+ break;
+ case 'P':
+ ftsoptions &= ~FTS_LOGICAL;
+ ftsoptions |= FTS_PHYSICAL;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 'R':
+ while ((p = strsep(&optarg, " \t,")) != NULL)
+ if (*p != '\0')
+ keys &= ~parsekey(p, NULL);
+ break;
+ case 's':
+ sflag = 1;
+ crc_total = ~strtol(optarg, &p, 0);
+ if (*p)
+ mtree_err("illegal seed value -- %s", optarg);
+ break;
+ case 'S':
+ mtree_Sflag = 1;
+ break;
+ case 't':
+ mtree_err("Minix does not support utimes(2)");
+ tflag = 1;
+ break;
+ case 'u':
+ mtree_err("Minix does not support lchmod(3)");
+ uflag = 1;
+ break;
+ case 'U':
+ Uflag = uflag = 1;
+ break;
+ case 'W':
+ mtree_Wflag = 1;
+ break;
+ case 'x':
+ ftsoptions |= FTS_XDEV;
+ break;
+ case 'X':
+ read_excludes_file(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc)
+ usage();
+
+ if (dir && chdir(dir))
+ mtree_err("%s: %s", dir, strerror(errno));
+
+ if ((cflag || sflag) && !getcwd(fullpath, sizeof(fullpath)))
+ mtree_err("%s", strerror(errno));
+
+ if ((cflag && Cflag) || (cflag && Dflag) || (Cflag && Dflag))
+ mtree_err("-c, -C and -D flags are mutually exclusive");
+
+ if (iflag && mflag)
+ mtree_err("-i and -m flags are mutually exclusive");
+
+ if (lflag && uflag)
+ mtree_err("-l and -u flags are mutually exclusive");
+
+ if (cflag) {
+ cwalk();
+ exit(0);
+ }
+ if (Cflag || Dflag) {
+ dump_nodes("", spec(stdin), Dflag);
+ exit(0);
+ }
+ status = verify();
+ if (Uflag && (status == MISMATCHEXIT))
+ status = 0;
+ exit(status);
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr,
+ "usage: %s [-CcDdeLlMPrSUuWx] [-i|-m] [-E tags] [-f spec]\n"
+ "\t\t[-I tags] [-K keywords] [-k keywords] [-N dbdir] [-p path]\n"
+ "\t\t[-R keywords] [-s seed] [-X exclude-file]\n",
+ getprogname());
+ exit(1);
+}
--- /dev/null
+/* $NetBSD: mtree.h,v 1.27 2009/04/04 21:49:49 apb Exp $ */
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mtree.h 8.1 (Berkeley) 6/6/93
+ */
+
+#ifndef _MTREE_H_
+#define _MTREE_H_
+
+#define KEYDEFAULT (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | \
+ F_TIME | F_TYPE | F_UID | F_FLAGS)
+
+#define MISMATCHEXIT 2
+
+typedef struct _node {
+ struct _node *parent, *child; /* up, down */
+ struct _node *prev, *next; /* left, right */
+ off_t st_size; /* size */
+ struct timespec st_mtimespec; /* last modification time */
+ char *slink; /* symbolic link reference */
+ uid_t st_uid; /* uid */
+ gid_t st_gid; /* gid */
+#define MBITS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
+ mode_t st_mode; /* mode */
+ dev_t st_rdev; /* device type */
+ u_long st_flags; /* flags */
+ nlink_t st_nlink; /* link count */
+ u_long cksum; /* check sum */
+ char *md5digest; /* MD5 digest */
+ char *rmd160digest; /* RMD-160 digest */
+ char *sha1digest; /* SHA1 digest */
+ char *sha256digest; /* SHA256 digest */
+ char *sha384digest; /* SHA384 digest */
+ char *sha512digest; /* SHA512 digest */
+ char *tags; /* tags, comma delimited,
+ * also with leading and
+ * trailing commas */
+ size_t lineno; /* line # entry came from */
+
+#define F_CKSUM 0x00000001 /* cksum(1) check sum */
+#define F_DEV 0x00000002 /* device type */
+#define F_DONE 0x00000004 /* directory done */
+#define F_FLAGS 0x00000008 /* file flags */
+#define F_GID 0x00000010 /* gid */
+#define F_GNAME 0x00000020 /* group name */
+#define F_IGN 0x00000040 /* ignore */
+#define F_MAGIC 0x00000080 /* name has magic chars */
+#define F_MD5 0x00000100 /* MD5 digest */
+#define F_MODE 0x00000200 /* mode */
+#define F_NLINK 0x00000400 /* number of links */
+#define F_OPT 0x00000800 /* existence optional */
+#define F_RMD160 0x00001000 /* RMD-160 digest */
+#define F_SHA1 0x00002000 /* SHA1 digest */
+#define F_SIZE 0x00004000 /* size */
+#define F_SLINK 0x00008000 /* symbolic link */
+#define F_TAGS 0x00010000 /* tags */
+#define F_TIME 0x00020000 /* modification time */
+#define F_TYPE 0x00040000 /* file type */
+#define F_UID 0x00080000 /* uid */
+#define F_UNAME 0x00100000 /* user name */
+#define F_VISIT 0x00200000 /* file visited */
+#define F_SHA256 0x00800000 /* SHA256 digest */
+#define F_SHA384 0x01000000 /* SHA384 digest */
+#define F_SHA512 0x02000000 /* SHA512 digest */
+
+ int flags; /* items set */
+
+#define F_BLOCK 0x001 /* block special */
+#define F_CHAR 0x002 /* char special */
+#define F_DIR 0x004 /* directory */
+#define F_FIFO 0x008 /* fifo */
+#define F_FILE 0x010 /* regular file */
+#define F_LINK 0x020 /* symbolic link */
+#define F_SOCK 0x040 /* socket */
+#define F_DOOR 0x080 /* door */
+ int type; /* file type */
+
+ char name[1]; /* file name (must be last) */
+} NODE;
+
+
+typedef struct {
+ char **list;
+ int count;
+} slist_t;
+
+
+/*
+ * prototypes for functions published to other programs which want to use
+ * the specfile parser but don't want to pull in all of "extern.h"
+ */
+const char *inotype(u_int);
+u_int nodetoino(u_int);
+int setup_getid(const char *);
+NODE *spec(FILE *);
+void free_nodes(NODE *);
+char *vispath(const char *);
+
+
+#define RP(p) \
+ ((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \
+ (p)->fts_path + 2 : (p)->fts_path)
+
+#define UF_MASK ((UF_NODUMP | UF_IMMUTABLE | \
+ UF_APPEND | UF_OPAQUE) \
+ & UF_SETTABLE) /* user settable flags */
+#define SF_MASK ((SF_ARCHIVED | SF_IMMUTABLE | \
+ SF_APPEND) & SF_SETTABLE) /* root settable flags */
+#define CH_MASK (UF_MASK | SF_MASK) /* all settable flags */
+#define SP_FLGS (SF_IMMUTABLE | SF_APPEND) /* special flags */
+
+#endif /* _MTREE_H_ */
--- /dev/null
+/* $NetBSD: spec.c,v 1.80 2012/03/15 02:02:24 joerg Exp $ */
+
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2001-2004 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn of Wasabi Systems.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)spec.c 8.2 (Berkeley) 4/28/95";
+#else
+__RCSID("$NetBSD: spec.c,v 1.80 2012/03/15 02:02:24 joerg Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <vis.h>
+#include <util.h>
+
+#include "extern.h"
+#include "pack_dev.h"
+
+size_t mtree_lineno; /* Current spec line number */
+int mtree_Mflag; /* Merge duplicate entries */
+int mtree_Wflag; /* Don't "whack" permissions */
+int mtree_Sflag; /* Sort entries */
+
+static dev_t parsedev(char *);
+static void replacenode(NODE *, NODE *);
+static void set(char *, NODE *);
+static void unset(char *, NODE *);
+static void addchild(NODE *, NODE *);
+static int nodecmp(const NODE *, const NODE *);
+static int appendfield(int, const char *, ...) __printflike(2, 3);
+
+#define REPLACEPTR(x,v) do { if ((x)) free((x)); (x) = (v); } while (0)
+
+NODE *
+spec(FILE *fp)
+{
+ NODE *centry, *last, *pathparent, *cur;
+ char *p, *e, *next;
+ NODE ginfo, *root;
+ char *buf, *tname, *ntname;
+ size_t tnamelen, plen;
+
+ root = NULL;
+ centry = last = NULL;
+ tname = NULL;
+ tnamelen = 0;
+ memset(&ginfo, 0, sizeof(ginfo));
+ for (mtree_lineno = 0;
+ (buf = fparseln(fp, NULL, &mtree_lineno, NULL,
+ FPARSELN_UNESCCOMM));
+ free(buf)) {
+ /* Skip leading whitespace. */
+ for (p = buf; *p && isspace((unsigned char)*p); ++p)
+ continue;
+
+ /* If nothing but whitespace, continue. */
+ if (!*p)
+ continue;
+
+#ifdef DEBUG
+ fprintf(stderr, "line %lu: {%s}\n",
+ (u_long)mtree_lineno, p);
+#endif
+ /* Grab file name, "$", "set", or "unset". */
+ next = buf;
+ while ((p = strsep(&next, " \t")) != NULL && *p == '\0')
+ continue;
+ if (p == NULL)
+ mtree_err("missing field");
+
+ if (p[0] == '/') {
+ if (strcmp(p + 1, "set") == 0)
+ set(next, &ginfo);
+ else if (strcmp(p + 1, "unset") == 0)
+ unset(next, &ginfo);
+ else
+ mtree_err("invalid specification `%s'", p);
+ continue;
+ }
+
+ if (strcmp(p, "..") == 0) {
+ /* Don't go up, if haven't gone down. */
+ if (root == NULL)
+ goto noparent;
+ if (last->type != F_DIR || last->flags & F_DONE) {
+ if (last == root)
+ goto noparent;
+ last = last->parent;
+ }
+ last->flags |= F_DONE;
+ continue;
+
+noparent: mtree_err("no parent node");
+ }
+
+ plen = strlen(p) + 1;
+ if (plen > tnamelen) {
+ if ((ntname = realloc(tname, plen)) == NULL)
+ mtree_err("realloc: %s", strerror(errno));
+ tname = ntname;
+ tnamelen = plen;
+ }
+ if (strunvis(tname, p) == -1)
+ mtree_err("strunvis failed on `%s'", p);
+ p = tname;
+
+ pathparent = NULL;
+ if (strchr(p, '/') != NULL) {
+ cur = root;
+ for (; (e = strchr(p, '/')) != NULL; p = e+1) {
+ if (p == e)
+ continue; /* handle // */
+ *e = '\0';
+ if (strcmp(p, ".") != 0) {
+ while (cur &&
+ strcmp(cur->name, p) != 0) {
+ cur = cur->next;
+ }
+ }
+ if (cur == NULL || cur->type != F_DIR) {
+ mtree_err("%s: %s", tname,
+ "missing directory in specification");
+ }
+ *e = '/';
+ pathparent = cur;
+ cur = cur->child;
+ }
+ if (*p == '\0')
+ mtree_err("%s: empty leaf element", tname);
+ }
+
+ if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
+ mtree_err("%s", strerror(errno));
+ *centry = ginfo;
+ centry->lineno = mtree_lineno;
+ strcpy(centry->name, p);
+#define MAGIC "?*["
+ if (strpbrk(p, MAGIC))
+ centry->flags |= F_MAGIC;
+ set(next, centry);
+
+ if (root == NULL) {
+ /*
+ * empty tree
+ */
+ if (strcmp(centry->name, ".") != 0 ||
+ centry->type != F_DIR)
+ mtree_err(
+ "root node must be the directory `.'");
+ last = root = centry;
+ root->parent = root;
+ } else if (pathparent != NULL) {
+ /*
+ * full path entry; add or replace
+ */
+ centry->parent = pathparent;
+ addchild(pathparent, centry);
+ last = centry;
+ } else if (strcmp(centry->name, ".") == 0) {
+ /*
+ * duplicate "." entry; always replace
+ */
+ replacenode(root, centry);
+ } else if (last->type == F_DIR && !(last->flags & F_DONE)) {
+ /*
+ * new relative child in current dir;
+ * add or replace
+ */
+ centry->parent = last;
+ addchild(last, centry);
+ last = centry;
+ } else {
+ /*
+ * new relative child in parent dir
+ * (after encountering ".." entry);
+ * add or replace
+ */
+ centry->parent = last->parent;
+ addchild(last->parent, centry);
+ last = centry;
+ }
+ }
+ return (root);
+}
+
+void
+free_nodes(NODE *root)
+{
+ NODE *cur, *next;
+
+ if (root == NULL)
+ return;
+
+ next = NULL;
+ for (cur = root; cur != NULL; cur = next) {
+ next = cur->next;
+ free_nodes(cur->child);
+ REPLACEPTR(cur->slink, NULL);
+ REPLACEPTR(cur->md5digest, NULL);
+ REPLACEPTR(cur->rmd160digest, NULL);
+ REPLACEPTR(cur->sha1digest, NULL);
+ REPLACEPTR(cur->sha256digest, NULL);
+ REPLACEPTR(cur->sha384digest, NULL);
+ REPLACEPTR(cur->sha512digest, NULL);
+ REPLACEPTR(cur->tags, NULL);
+ REPLACEPTR(cur, NULL);
+ }
+}
+
+/*
+ * appendfield --
+ * Like printf(), but output a space either before or after
+ * the regular output, according to the pathlast flag.
+ */
+static int
+appendfield(int pathlast, const char *fmt, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start(ap, fmt);
+ if (!pathlast)
+ printf(" ");
+ result = vprintf(fmt, ap);
+ if (pathlast)
+ printf(" ");
+ va_end(ap);
+ return result;
+}
+
+/*
+ * dump_nodes --
+ * dump the NODEs from `cur', based in the directory `dir'.
+ * if pathlast is none zero, print the path last, otherwise print
+ * it first.
+ */
+void
+dump_nodes(const char *dir, NODE *root, int pathlast)
+{
+ NODE *cur;
+ char path[MAXPATHLEN];
+ const char *name;
+ char *str;
+ char *p, *q;
+
+ for (cur = root; cur != NULL; cur = cur->next) {
+ if (cur->type != F_DIR && !matchtags(cur))
+ continue;
+
+ if (snprintf(path, sizeof(path), "%s%s%s",
+ dir, *dir ? "/" : "", cur->name)
+ >= (int)sizeof(path))
+ mtree_err("Pathname too long.");
+
+ if (!pathlast)
+ printf("%s", vispath(path));
+
+#define MATCHFLAG(f) ((keys & (f)) && (cur->flags & (f)))
+ if (MATCHFLAG(F_TYPE))
+ appendfield(pathlast, "type=%s", nodetype(cur->type));
+ if (MATCHFLAG(F_UID | F_UNAME)) {
+ if (keys & F_UNAME &&
+ (name = user_from_uid(cur->st_uid, 1)) != NULL)
+ appendfield(pathlast, "uname=%s", name);
+ else
+ appendfield(pathlast, "uid=%u", cur->st_uid);
+ }
+ if (MATCHFLAG(F_GID | F_GNAME)) {
+ if (keys & F_GNAME &&
+ (name = group_from_gid(cur->st_gid, 1)) != NULL)
+ appendfield(pathlast, "gname=%s", name);
+ else
+ appendfield(pathlast, "gid=%u", cur->st_gid);
+ }
+ if (MATCHFLAG(F_MODE))
+ appendfield(pathlast, "mode=%#o", cur->st_mode);
+ if (MATCHFLAG(F_DEV) &&
+ (cur->type == F_BLOCK || cur->type == F_CHAR))
+ appendfield(pathlast, "device=%#llx", (long long)cur->st_rdev);
+ if (MATCHFLAG(F_NLINK))
+ appendfield(pathlast, "nlink=%d", cur->st_nlink);
+ if (MATCHFLAG(F_SLINK))
+ appendfield(pathlast, "link=%s", vispath(cur->slink));
+ if (MATCHFLAG(F_SIZE))
+ appendfield(pathlast, "size=%lld", (long long)cur->st_size);
+ if (MATCHFLAG(F_TIME))
+ appendfield(pathlast, "time=%lld.%ld",
+ (long long)cur->st_mtimespec.tv_sec,
+ cur->st_mtimespec.tv_nsec);
+ if (MATCHFLAG(F_CKSUM))
+ appendfield(pathlast, "cksum=%lu", cur->cksum);
+ if (MATCHFLAG(F_MD5))
+ appendfield(pathlast, "md5=%s", cur->md5digest);
+ if (MATCHFLAG(F_RMD160))
+ appendfield(pathlast, "rmd160=%s", cur->rmd160digest);
+ if (MATCHFLAG(F_SHA1))
+ appendfield(pathlast, "sha1=%s", cur->sha1digest);
+ if (MATCHFLAG(F_SHA256))
+ appendfield(pathlast, "sha256=%s", cur->sha256digest);
+ if (MATCHFLAG(F_SHA384))
+ appendfield(pathlast, "sha384=%s", cur->sha384digest);
+ if (MATCHFLAG(F_SHA512))
+ appendfield(pathlast, "sha512=%s", cur->sha512digest);
+ if (MATCHFLAG(F_FLAGS)) {
+ str = flags_to_string(cur->st_flags, "none");
+ appendfield(pathlast, "flags=%s", str);
+ free(str);
+ }
+ if (MATCHFLAG(F_IGN))
+ appendfield(pathlast, "ignore");
+ if (MATCHFLAG(F_OPT))
+ appendfield(pathlast, "optional");
+ if (MATCHFLAG(F_TAGS)) {
+ /* don't output leading or trailing commas */
+ p = cur->tags;
+ while (*p == ',')
+ p++;
+ q = p + strlen(p);
+ while(q > p && q[-1] == ',')
+ q--;
+ appendfield(pathlast, "tags=%.*s", (int)(q - p), p);
+ }
+ puts(pathlast ? vispath(path) : "");
+
+ if (cur->child)
+ dump_nodes(path, cur->child, pathlast);
+ }
+}
+
+/*
+ * vispath --
+ * strsvis(3) encodes path, which must not be longer than MAXPATHLEN
+ * characters long, and returns a pointer to a static buffer containing
+ * the result.
+ */
+char *
+vispath(const char *path)
+{
+ const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' };
+ static char pathbuf[4*MAXPATHLEN + 1];
+
+ strsvis(pathbuf, path, VIS_CSTYLE, extra);
+ return(pathbuf);
+}
+
+
+static dev_t
+parsedev(char *arg)
+{
+#define MAX_PACK_ARGS 3
+ u_long numbers[MAX_PACK_ARGS];
+ char *p, *ep, *dev;
+ int argc;
+ pack_t *pack;
+ dev_t result;
+ const char *error = NULL;
+
+ if ((dev = strchr(arg, ',')) != NULL) {
+ *dev++='\0';
+ if ((pack = pack_find(arg)) == NULL)
+ mtree_err("unknown format `%s'", arg);
+ argc = 0;
+ while ((p = strsep(&dev, ",")) != NULL) {
+ if (*p == '\0')
+ mtree_err("missing number");
+ numbers[argc++] = strtoul(p, &ep, 0);
+ if (*ep != '\0')
+ mtree_err("invalid number `%s'",
+ p);
+ if (argc > MAX_PACK_ARGS)
+ mtree_err("too many arguments");
+ }
+ if (argc < 2)
+ mtree_err("not enough arguments");
+ result = (*pack)(argc, numbers, &error);
+ if (error != NULL)
+ mtree_err("%s", error);
+ } else {
+ result = (dev_t)strtoul(arg, &ep, 0);
+ if (*ep != '\0')
+ mtree_err("invalid device `%s'", arg);
+ }
+ return (result);
+}
+
+static void
+replacenode(NODE *cur, NODE *new)
+{
+
+#define REPLACE(x) cur->x = new->x
+#define REPLACESTR(x) REPLACEPTR(cur->x,new->x)
+
+ if (cur->type != new->type) {
+ if (mtree_Mflag) {
+ /*
+ * merge entries with different types; we
+ * don't want children retained in this case.
+ */
+ REPLACE(type);
+ free_nodes(cur->child);
+ cur->child = NULL;
+ } else {
+ mtree_err(
+ "existing entry for `%s', type `%s'"
+ " does not match type `%s'",
+ cur->name, nodetype(cur->type),
+ nodetype(new->type));
+ }
+ }
+
+ REPLACE(st_size);
+ REPLACE(st_mtimespec);
+ REPLACESTR(slink);
+ if (cur->slink != NULL) {
+ if ((cur->slink = strdup(new->slink)) == NULL)
+ mtree_err("memory allocation error");
+ if (strunvis(cur->slink, new->slink) == -1)
+ mtree_err("strunvis failed on `%s'", new->slink);
+ free(new->slink);
+ }
+ REPLACE(st_uid);
+ REPLACE(st_gid);
+ REPLACE(st_mode);
+ REPLACE(st_rdev);
+ REPLACE(st_flags);
+ REPLACE(st_nlink);
+ REPLACE(cksum);
+ REPLACESTR(md5digest);
+ REPLACESTR(rmd160digest);
+ REPLACESTR(sha1digest);
+ REPLACESTR(sha256digest);
+ REPLACESTR(sha384digest);
+ REPLACESTR(sha512digest);
+ REPLACESTR(tags);
+ REPLACE(lineno);
+ REPLACE(flags);
+ free(new);
+}
+
+static void
+set(char *t, NODE *ip)
+{
+ int type, value, len;
+ gid_t gid;
+ uid_t uid;
+ char *kw, *val, *md, *ep;
+ void *m;
+
+ while ((kw = strsep(&t, "= \t")) != NULL) {
+ if (*kw == '\0')
+ continue;
+ if (strcmp(kw, "all") == 0)
+ mtree_err("invalid keyword `all'");
+ ip->flags |= type = parsekey(kw, &value);
+ if (!value)
+ /* Just set flag bit (F_IGN and F_OPT) */
+ continue;
+ while ((val = strsep(&t, " \t")) != NULL && *val == '\0')
+ continue;
+ if (val == NULL)
+ mtree_err("missing value");
+ switch (type) {
+ case F_CKSUM:
+ ip->cksum = strtoul(val, &ep, 10);
+ if (*ep)
+ mtree_err("invalid checksum `%s'", val);
+ break;
+ case F_DEV:
+ ip->st_rdev = parsedev(val);
+ break;
+ case F_FLAGS:
+ if (strcmp("none", val) == 0)
+ ip->st_flags = 0;
+ else if (string_to_flags(&val, &ip->st_flags, NULL)
+ != 0)
+ mtree_err("invalid flag `%s'", val);
+ break;
+ case F_GID:
+ ip->st_gid = (gid_t)strtoul(val, &ep, 10);
+ if (*ep)
+ mtree_err("invalid gid `%s'", val);
+ break;
+ case F_GNAME:
+ if (mtree_Wflag) /* don't parse if whacking */
+ break;
+ if (gid_from_group(val, &gid) == -1)
+ mtree_err("unknown group `%s'", val);
+ ip->st_gid = gid;
+ break;
+ case F_MD5:
+ if (val[0]=='0' && val[1]=='x')
+ md=&val[2];
+ else
+ md=val;
+ if ((ip->md5digest = strdup(md)) == NULL)
+ mtree_err("memory allocation error");
+ break;
+ case F_MODE:
+ if ((m = setmode(val)) == NULL)
+ mtree_err("cannot set file mode `%s' (%s)",
+ val, strerror(errno));
+ ip->st_mode = getmode(m, 0);
+ free(m);
+ break;
+ case F_NLINK:
+ ip->st_nlink = (nlink_t)strtoul(val, &ep, 10);
+ if (*ep)
+ mtree_err("invalid link count `%s'", val);
+ break;
+ case F_RMD160:
+ if (val[0]=='0' && val[1]=='x')
+ md=&val[2];
+ else
+ md=val;
+ if ((ip->rmd160digest = strdup(md)) == NULL)
+ mtree_err("memory allocation error");
+ break;
+ case F_SHA1:
+ if (val[0]=='0' && val[1]=='x')
+ md=&val[2];
+ else
+ md=val;
+ if ((ip->sha1digest = strdup(md)) == NULL)
+ mtree_err("memory allocation error");
+ break;
+ case F_SIZE:
+ ip->st_size = (off_t)strtoll(val, &ep, 10);
+ if (*ep)
+ mtree_err("invalid size `%s'", val);
+ break;
+ case F_SLINK:
+ if ((ip->slink = strdup(val)) == NULL)
+ mtree_err("memory allocation error");
+ if (strunvis(ip->slink, val) == -1)
+ mtree_err("strunvis failed on `%s'", val);
+ break;
+ case F_TAGS:
+ len = strlen(val) + 3; /* "," + str + ",\0" */
+ if ((ip->tags = malloc(len)) == NULL)
+ mtree_err("memory allocation error");
+ snprintf(ip->tags, len, ",%s,", val);
+ break;
+ case F_TIME:
+ ip->st_mtimespec.tv_sec =
+ (time_t)strtoll(val, &ep, 10);
+ if (*ep != '.')
+ mtree_err("invalid time `%s'", val);
+ val = ep + 1;
+ ip->st_mtimespec.tv_nsec = strtol(val, &ep, 10);
+ if (*ep)
+ mtree_err("invalid time `%s'", val);
+ break;
+ case F_TYPE:
+ ip->type = parsetype(val);
+ break;
+ case F_UID:
+ ip->st_uid = (uid_t)strtoul(val, &ep, 10);
+ if (*ep)
+ mtree_err("invalid uid `%s'", val);
+ break;
+ case F_UNAME:
+ if (mtree_Wflag) /* don't parse if whacking */
+ break;
+ if (uid_from_user(val, &uid) == -1)
+ mtree_err("unknown user `%s'", val);
+ ip->st_uid = uid;
+ break;
+ case F_SHA256:
+ if (val[0]=='0' && val[1]=='x')
+ md=&val[2];
+ else
+ md=val;
+ if ((ip->sha256digest = strdup(md)) == NULL)
+ mtree_err("memory allocation error");
+ break;
+ case F_SHA384:
+ if (val[0]=='0' && val[1]=='x')
+ md=&val[2];
+ else
+ md=val;
+ if ((ip->sha384digest = strdup(md)) == NULL)
+ mtree_err("memory allocation error");
+ break;
+ case F_SHA512:
+ if (val[0]=='0' && val[1]=='x')
+ md=&val[2];
+ else
+ md=val;
+ if ((ip->sha512digest = strdup(md)) == NULL)
+ mtree_err("memory allocation error");
+ break;
+ default:
+ mtree_err(
+ "set(): unsupported key type 0x%x (INTERNAL ERROR)",
+ type);
+ /* NOTREACHED */
+ }
+ }
+}
+
+static void
+unset(char *t, NODE *ip)
+{
+ char *p;
+
+ while ((p = strsep(&t, " \t")) != NULL) {
+ if (*p == '\0')
+ continue;
+ ip->flags &= ~parsekey(p, NULL);
+ }
+}
+
+/*
+ * addchild --
+ * Add the centry node as a child of the pathparent node. If
+ * centry is a duplicate, call replacenode(). If centry is not
+ * a duplicate, insert it into the linked list referenced by
+ * pathparent->child. Keep the list sorted if Sflag is set.
+ */
+static void
+addchild(NODE *pathparent, NODE *centry)
+{
+ NODE *samename; /* node with the same name as centry */
+ NODE *replacepos; /* if non-NULL, centry should replace this node */
+ NODE *insertpos; /* if non-NULL, centry should be inserted
+ * after this node */
+ NODE *cur; /* for stepping through the list */
+ NODE *last; /* the last node in the list */
+ int cmp;
+
+ samename = NULL;
+ replacepos = NULL;
+ insertpos = NULL;
+ last = NULL;
+ cur = pathparent->child;
+ if (cur == NULL) {
+ /* centry is pathparent's first and only child node so far */
+ pathparent->child = centry;
+ return;
+ }
+
+ /*
+ * pathparent already has at least one other child, so add the
+ * centry node to the list.
+ *
+ * We first scan through the list looking for an existing node
+ * with the same name (setting samename), and also looking
+ * for the correct position to replace or insert the new node
+ * (setting replacepos and/or insertpos).
+ */
+ for (; cur != NULL; last = cur, cur = cur->next) {
+ if (strcmp(centry->name, cur->name) == 0) {
+ samename = cur;
+ }
+ if (mtree_Sflag) {
+ cmp = nodecmp(centry, cur);
+ if (cmp == 0) {
+ replacepos = cur;
+ } else if (cmp > 0) {
+ insertpos = cur;
+ }
+ }
+ }
+ if (! mtree_Sflag) {
+ if (samename != NULL) {
+ /* replace node with same name */
+ replacepos = samename;
+ } else {
+ /* add new node at end of list */
+ insertpos = last;
+ }
+ }
+
+ if (samename != NULL) {
+ /*
+ * We found a node with the same name above. Call
+ * replacenode(), which will either exit with an error,
+ * or replace the information in the samename node and
+ * free the information in the centry node.
+ */
+ replacenode(samename, centry);
+ if (samename == replacepos) {
+ /* The just-replaced node was in the correct position */
+ return;
+ }
+ if (samename == insertpos || samename->prev == insertpos) {
+ /*
+ * We thought the new node should be just before
+ * or just after the replaced node, but that would
+ * be equivalent to just retaining the replaced node.
+ */
+ return;
+ }
+
+ /*
+ * The just-replaced node is in the wrong position in
+ * the list. This can happen if sort order depends on
+ * criteria other than the node name.
+ *
+ * Make centry point to the just-replaced node. Unlink
+ * the just-replaced node from the list, and allow it to
+ * be insterted in the correct position later.
+ */
+ centry = samename;
+ if (centry->prev)
+ centry->prev->next = centry->next;
+ else {
+ /* centry->next is the new head of the list */
+ pathparent->child = centry->next;
+ assert(centry->next != NULL);
+ }
+ if (centry->next)
+ centry->next->prev = centry->prev;
+ centry->prev = NULL;
+ centry->next = NULL;
+ }
+
+ if (insertpos == NULL) {
+ /* insert centry at the beginning of the list */
+ pathparent->child->prev = centry;
+ centry->next = pathparent->child;
+ centry->prev = NULL;
+ pathparent->child = centry;
+ } else {
+ /* insert centry into the list just after insertpos */
+ centry->next = insertpos->next;
+ insertpos->next = centry;
+ centry->prev = insertpos;
+ if (centry->next)
+ centry->next->prev = centry;
+ }
+ return;
+}
+
+/*
+ * nodecmp --
+ * used as a comparison function by addchild() to control the order
+ * in which entries appear within a list of sibling nodes. We make
+ * directories sort after non-directories, but otherwise sort in
+ * strcmp() order.
+ *
+ * Keep this in sync with dcmp() in create.c.
+ */
+static int
+nodecmp(const NODE *a, const NODE *b)
+{
+
+ if ((a->type & F_DIR) != 0) {
+ if ((b->type & F_DIR) == 0)
+ return 1;
+ } else if ((b->type & F_DIR) != 0)
+ return -1;
+ return strcmp(a->name, b->name);
+}
--- /dev/null
+/* $NetBSD: verify.c,v 1.40 2012/03/25 16:07:04 christos Exp $ */
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+#if defined(__RCSID) && !defined(lint)
+#if 0
+static char sccsid[] = "@(#)verify.c 8.1 (Berkeley) 6/6/93";
+#else
+__RCSID("$NetBSD: verify.c,v 1.40 2012/03/25 16:07:04 christos Exp $");
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#if ! HAVE_NBTOOL_CONFIG_H
+#include <dirent.h>
+#endif
+
+#include <errno.h>
+#include <fnmatch.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static NODE *root;
+static char path[MAXPATHLEN];
+
+static void miss(NODE *, char *);
+static int vwalk(void);
+
+int
+verify(void)
+{
+ int rval;
+
+ root = spec(stdin);
+ rval = vwalk();
+ miss(root, path);
+ return (rval);
+}
+
+static int
+vwalk(void)
+{
+ FTS *t;
+ FTSENT *p;
+ NODE *ep, *level;
+ int specdepth, rval;
+ char *argv[2];
+ char dot[] = ".";
+ argv[0] = dot;
+ argv[1] = NULL;
+
+ if ((t = fts_open(argv, ftsoptions, NULL)) == NULL)
+ mtree_err("fts_open: %s", strerror(errno));
+ level = root;
+ specdepth = rval = 0;
+ while ((p = fts_read(t)) != NULL) {
+ if (check_excludes(p->fts_name, p->fts_path)) {
+ fts_set(t, p, FTS_SKIP);
+ continue;
+ }
+ switch(p->fts_info) {
+ case FTS_D:
+ case FTS_SL:
+ break;
+ case FTS_DP:
+ if (specdepth > p->fts_level) {
+ for (level = level->parent; level->prev;
+ level = level->prev)
+ continue;
+ --specdepth;
+ }
+ continue;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ warnx("%s: %s", RP(p), strerror(p->fts_errno));
+ continue;
+ default:
+ if (dflag)
+ continue;
+ }
+
+ if (specdepth != p->fts_level)
+ goto extra;
+ for (ep = level; ep; ep = ep->next)
+ if ((ep->flags & F_MAGIC &&
+ !fnmatch(ep->name, p->fts_name, FNM_PATHNAME)) ||
+ !strcmp(ep->name, p->fts_name)) {
+ ep->flags |= F_VISIT;
+ if (compare(ep, p))
+ rval = MISMATCHEXIT;
+ if (!(ep->flags & F_IGN) &&
+ ep->type == F_DIR &&
+ p->fts_info == FTS_D) {
+ if (ep->child) {
+ level = ep->child;
+ ++specdepth;
+ }
+ } else
+ fts_set(t, p, FTS_SKIP);
+ break;
+ }
+
+ if (ep)
+ continue;
+ extra:
+ if (!eflag && !(dflag && p->fts_info == FTS_SL)) {
+ printf("extra: %s", RP(p));
+ if (rflag) {
+ if ((S_ISDIR(p->fts_statp->st_mode)
+ ? rmdir : unlink)(p->fts_accpath)) {
+ printf(", not removed: %s",
+ strerror(errno));
+ } else
+ printf(", removed");
+ }
+ putchar('\n');
+ }
+ fts_set(t, p, FTS_SKIP);
+ }
+ fts_close(t);
+ if (sflag)
+ warnx("%s checksum: %u", fullpath, crc_total);
+ return (rval);
+}
+
+static void
+miss(NODE *p, char *tail)
+{
+ int create;
+ char *tp;
+ const char *type;
+ u_int32_t flags;
+
+ for (; p; p = p->next) {
+ if (p->flags & F_OPT && !(p->flags & F_VISIT))
+ continue;
+ if (p->type != F_DIR && (dflag || p->flags & F_VISIT))
+ continue;
+ strcpy(tail, p->name);
+ if (!(p->flags & F_VISIT))
+ printf("missing: %s", path);
+ switch (p->type) {
+ case F_BLOCK:
+ case F_CHAR:
+ type = "device";
+ break;
+ case F_DIR:
+ type = "directory";
+ break;
+ case F_LINK:
+ type = "symlink";
+ break;
+ default:
+ putchar('\n');
+ continue;
+ }
+
+ create = 0;
+ if (!(p->flags & F_VISIT) && uflag) {
+ if (mtree_Wflag || p->type == F_LINK)
+ goto createit;
+ if (!(p->flags & (F_UID | F_UNAME)))
+ printf(
+ " (%s not created: user not specified)", type);
+ else if (!(p->flags & (F_GID | F_GNAME)))
+ printf(
+ " (%s not created: group not specified)", type);
+ else if (!(p->flags & F_MODE))
+ printf(
+ " (%s not created: mode not specified)", type);
+ else
+ createit:
+ switch (p->type) {
+ case F_BLOCK:
+ case F_CHAR:
+ if (mtree_Wflag)
+ continue;
+ if (!(p->flags & F_DEV))
+ printf(
+ " (%s not created: device not specified)",
+ type);
+ else if (mknod(path,
+ p->st_mode | nodetoino(p->type),
+ p->st_rdev) == -1)
+ printf(" (%s not created: %s)\n",
+ type, strerror(errno));
+ else
+ create = 1;
+ break;
+ case F_LINK:
+ if (!(p->flags & F_SLINK))
+ printf(
+ " (%s not created: link not specified)\n",
+ type);
+ else if (symlink(p->slink, path))
+ printf(
+ " (%s not created: %s)\n",
+ type, strerror(errno));
+ else
+ create = 1;
+ break;
+ case F_DIR:
+ if (mkdir(path, S_IRWXU|S_IRWXG|S_IRWXO))
+ printf(" (not created: %s)",
+ strerror(errno));
+ else
+ create = 1;
+ break;
+ default:
+ mtree_err("can't create create %s",
+ nodetype(p->type));
+ }
+ }
+ if (create)
+ printf(" (created)");
+ if (p->type == F_DIR) {
+ if (!(p->flags & F_VISIT))
+ putchar('\n');
+ for (tp = tail; *tp; ++tp)
+ continue;
+ *tp = '/';
+ miss(p->child, tp + 1);
+ *tp = '\0';
+ } else
+ putchar('\n');
+
+ if (!create || mtree_Wflag)
+ continue;
+#ifndef __minix
+ if ((p->flags & (F_UID | F_UNAME)) &&
+ (p->flags & (F_GID | F_GNAME)) &&
+ (lchown(path, p->st_uid, p->st_gid))) {
+ printf("%s: user/group/mode not modified: %s\n",
+ path, strerror(errno));
+ printf("%s: warning: file mode %snot set\n", path,
+ (p->flags & F_FLAGS) ? "and file flags " : "");
+ continue;
+ }
+#else
+ if ((p->flags & (F_UID | F_UNAME)) &&
+ (p->flags & (F_GID | F_GNAME))) {
+ printf("Warning: unable to change user/group/mode due "
+ "to lack of lchown(3) support");
+ }
+#endif
+ if (p->flags & F_MODE) {
+#ifndef __minix
+ if (lchmod(path, p->st_mode))
+ printf("%s: permissions not set: %s\n",
+ path, strerror(errno));
+#else
+ printf("Warning: unable to set permissions due "
+ "to lack of lchmod(3) support");
+#endif
+ }
+#if HAVE_STRUCT_STAT_ST_FLAGS && !defined(__minix)
+ if ((p->flags & F_FLAGS) && p->st_flags) {
+ if (iflag)
+ flags = p->st_flags;
+ else
+ flags = p->st_flags & ~SP_FLGS;
+ if (lchflags(path, flags))
+ printf("%s: file flags not set: %s\n",
+ path, strerror(errno));
+ }
+#endif /* HAVE_STRUCT_STAT_ST_FLAGS */
+ }
+}