]> Zhao Yanbai Git Server - minix.git/commitdiff
Import NetBSD mtree
authorThomas Veerman <thomas@minix3.org>
Wed, 6 Jun 2012 14:34:11 +0000 (14:34 +0000)
committerThomas Veerman <thomas@minix3.org>
Mon, 18 Jun 2012 10:54:49 +0000 (10:54 +0000)
mtree is only used for cross compilation at this point. Also, the
required patches to make it compile on Minix cripple it probably
enough to make it unusable anyway.

13 files changed:
usr.sbin/mtree/Makefile [new file with mode: 0644]
usr.sbin/mtree/compare.c [new file with mode: 0644]
usr.sbin/mtree/crc.c [new file with mode: 0644]
usr.sbin/mtree/create.c [new file with mode: 0644]
usr.sbin/mtree/excludes.c [new file with mode: 0644]
usr.sbin/mtree/extern.h [new file with mode: 0644]
usr.sbin/mtree/getid.c [new file with mode: 0644]
usr.sbin/mtree/misc.c [new file with mode: 0644]
usr.sbin/mtree/mtree.8 [new file with mode: 0644]
usr.sbin/mtree/mtree.c [new file with mode: 0644]
usr.sbin/mtree/mtree.h [new file with mode: 0644]
usr.sbin/mtree/spec.c [new file with mode: 0644]
usr.sbin/mtree/verify.c [new file with mode: 0644]

diff --git a/usr.sbin/mtree/Makefile b/usr.sbin/mtree/Makefile
new file mode 100644 (file)
index 0000000..41b7b66
--- /dev/null
@@ -0,0 +1,20 @@
+#      $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>
diff --git a/usr.sbin/mtree/compare.c b/usr.sbin/mtree/compare.c
new file mode 100644 (file)
index 0000000..8d96ed0
--- /dev/null
@@ -0,0 +1,534 @@
+/*     $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);
+}
diff --git a/usr.sbin/mtree/crc.c b/usr.sbin/mtree/crc.c
new file mode 100644 (file)
index 0000000..6e2dfd4
--- /dev/null
@@ -0,0 +1,164 @@
+/*     $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;
+}
diff --git a/usr.sbin/mtree/create.c b/usr.sbin/mtree/create.c
new file mode 100644 (file)
index 0000000..fdc8d69
--- /dev/null
@@ -0,0 +1,423 @@
+/*     $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;
+}
diff --git a/usr.sbin/mtree/excludes.c b/usr.sbin/mtree/excludes.c
new file mode 100644 (file)
index 0000000..4b46b89
--- /dev/null
@@ -0,0 +1,121 @@
+/*     $NetBSD: excludes.c,v 1.13 2004/06/20 22:20:18 jmc Exp $        */
+
+/*
+ * Copyright 2000 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.  M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if HAVE_NBTOOL_CONFIG_H
+#include "nbtool_config.h"
+#endif
+
+#include <sys/cdefs.h>
+
+#if defined(__RCSID) && !defined(lint)
+__RCSID("$NetBSD: excludes.c,v 1.13 2004/06/20 22:20:18 jmc Exp $");
+#endif
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <fnmatch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <util.h>
+
+#include "extern.h"
+
+
+/*
+ * We're assuming that there won't be a whole lot of excludes,
+ * so it's OK to use a stupid algorithm.
+ */
+struct exclude {
+       LIST_ENTRY(exclude) link;
+       const char *glob;
+       int pathname;
+};
+static LIST_HEAD(, exclude) excludes;
+
+
+void
+init_excludes(void)
+{
+
+       LIST_INIT(&excludes);
+}
+
+void
+read_excludes_file(const char *name)
+{
+       FILE *fp;
+       char *line;
+       struct exclude *e;
+
+       fp = fopen(name, "r");
+       if (fp == 0)
+               err(1, "%s", name);
+
+       while ((line = fparseln(fp, NULL, NULL, NULL,
+           FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT | FPARSELN_UNESCESC))
+           != NULL) {
+               if (line[0] == '\0')
+                       continue;
+
+               if ((e = malloc(sizeof *e)) == NULL)
+                       mtree_err("memory allocation error");
+
+               e->glob = line;
+               if (strchr(e->glob, '/') != NULL)
+                       e->pathname = 1;
+               else
+                       e->pathname = 0;
+               LIST_INSERT_HEAD(&excludes, e, link);
+       }
+       fclose(fp);
+}
+
+int
+check_excludes(const char *fname, const char *path)
+{
+       struct exclude *e;
+
+       /* fnmatch(3) has a funny return value convention... */
+#define MATCH(g, n) (fnmatch((g), (n), FNM_PATHNAME) == 0)
+
+       e = LIST_FIRST(&excludes);
+       while (e) {
+               if ((e->pathname && MATCH(e->glob, path))
+                   || MATCH(e->glob, fname)) {
+                       return (1);
+               }
+               e = LIST_NEXT(e, link);
+       }
+       return (0);
+}
diff --git a/usr.sbin/mtree/extern.h b/usr.sbin/mtree/extern.h
new file mode 100644 (file)
index 0000000..31d4792
--- /dev/null
@@ -0,0 +1,79 @@
+/*     $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;
+
diff --git a/usr.sbin/mtree/getid.c b/usr.sbin/mtree/getid.c
new file mode 100644 (file)
index 0000000..64eef27
--- /dev/null
@@ -0,0 +1,431 @@
+/*     $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;
+}
+
diff --git a/usr.sbin/mtree/misc.c b/usr.sbin/mtree/misc.c
new file mode 100644 (file)
index 0000000..ba6a5ee
--- /dev/null
@@ -0,0 +1,308 @@
+/*     $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 */
+}
diff --git a/usr.sbin/mtree/mtree.8 b/usr.sbin/mtree/mtree.8
new file mode 100644 (file)
index 0000000..8a150b6
--- /dev/null
@@ -0,0 +1,704 @@
+.\"    $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 .
diff --git a/usr.sbin/mtree/mtree.c b/usr.sbin/mtree/mtree.c
new file mode 100644 (file)
index 0000000..31219ab
--- /dev/null
@@ -0,0 +1,235 @@
+/*     $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);
+}
diff --git a/usr.sbin/mtree/mtree.h b/usr.sbin/mtree/mtree.h
new file mode 100644 (file)
index 0000000..81e5386
--- /dev/null
@@ -0,0 +1,139 @@
+/*     $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_ */
diff --git a/usr.sbin/mtree/spec.c b/usr.sbin/mtree/spec.c
new file mode 100644 (file)
index 0000000..1d52efd
--- /dev/null
@@ -0,0 +1,828 @@
+/*     $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);
+}
diff --git a/usr.sbin/mtree/verify.c b/usr.sbin/mtree/verify.c
new file mode 100644 (file)
index 0000000..9eb5451
--- /dev/null
@@ -0,0 +1,306 @@
+/*     $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 */
+       }
+}