summaryrefslogtreecommitdiffstats
path: root/usr.sbin/mtree
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/mtree')
-rw-r--r--usr.sbin/mtree/Makefile8
-rw-r--r--usr.sbin/mtree/compare.c271
-rw-r--r--usr.sbin/mtree/create.c290
-rw-r--r--usr.sbin/mtree/extern.h44
-rw-r--r--usr.sbin/mtree/misc.c125
-rw-r--r--usr.sbin/mtree/mtree.8249
-rw-r--r--usr.sbin/mtree/mtree.c146
-rw-r--r--usr.sbin/mtree/mtree.h88
-rw-r--r--usr.sbin/mtree/spec.c280
-rw-r--r--usr.sbin/mtree/verify.c202
10 files changed, 1703 insertions, 0 deletions
diff --git a/usr.sbin/mtree/Makefile b/usr.sbin/mtree/Makefile
new file mode 100644
index 0000000..fb8b424
--- /dev/null
+++ b/usr.sbin/mtree/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= mtree
+#CFLAGS+=-DDEBUG
+SRCS= compare.c crc.c create.c misc.c mtree.c spec.c verify.c
+.PATH: ${.CURDIR}/../../usr.bin/cksum
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mtree/compare.c b/usr.sbin/mtree/compare.c
new file mode 100644
index 0000000..b258572
--- /dev/null
+++ b/usr.sbin/mtree/compare.c
@@ -0,0 +1,271 @@
+/*-
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)compare.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <errno.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include "mtree.h"
+#include "extern.h"
+
+extern int uflag;
+
+static char *ftype __P((u_int));
+
+#define INDENTNAMELEN 8
+#define LABEL \
+ if (!label++) { \
+ len = printf("%s: ", RP(p)); \
+ if (len > INDENTNAMELEN) { \
+ tab = "\t"; \
+ (void)printf("\n"); \
+ } else { \
+ tab = ""; \
+ (void)printf("%*s", INDENTNAMELEN - len, ""); \
+ } \
+ }
+
+int
+compare(name, s, p)
+ char *name;
+ register NODE *s;
+ register FTSENT *p;
+{
+ extern int uflag;
+ u_long len, val;
+ int fd, label;
+ char *cp, *tab;
+
+ 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;
+ case F_SOCK:
+ if (!S_ISSOCK(p->fts_statp->st_mode)) {
+typeerr: LABEL;
+ (void)printf("\ttype (%s, %s)\n",
+ ftype(s->type), inotype(p->fts_statp->st_mode));
+ }
+ break;
+ }
+ /* 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;
+ (void)printf("%suser (%u, %u",
+ tab, s->st_uid, p->fts_statp->st_uid);
+ if (uflag)
+ if (chown(p->fts_accpath, s->st_uid, -1))
+ (void)printf(", not modified: %s)\n",
+ strerror(errno));
+ else
+ (void)printf(", modified)\n");
+ else
+ (void)printf(")\n");
+ tab = "\t";
+ }
+ if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
+ LABEL;
+ (void)printf("%sgid (%u, %u",
+ tab, s->st_gid, p->fts_statp->st_gid);
+ if (uflag)
+ if (chown(p->fts_accpath, -1, s->st_gid))
+ (void)printf(", not modified: %s)\n",
+ strerror(errno));
+ else
+ (void)printf(", modified)\n");
+ else
+ (void)printf(")\n");
+ tab = "\t";
+ }
+ if (s->flags & F_MODE &&
+ s->st_mode != (p->fts_statp->st_mode & MBITS)) {
+ LABEL;
+ (void)printf("%spermissions (%#o, %#o",
+ tab, s->st_mode, p->fts_statp->st_mode & MBITS);
+ if (uflag)
+ if (chmod(p->fts_accpath, s->st_mode))
+ (void)printf(", not modified: %s)\n",
+ strerror(errno));
+ else
+ (void)printf(", modified)\n");
+ else
+ (void)printf(")\n");
+ tab = "\t";
+ }
+ if (s->flags & F_NLINK && s->type != F_DIR &&
+ s->st_nlink != p->fts_statp->st_nlink) {
+ LABEL;
+ (void)printf("%slink count (%u, %u)\n",
+ tab, s->st_nlink, p->fts_statp->st_nlink);
+ tab = "\t";
+ }
+ if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) {
+ LABEL;
+ (void)printf("%ssize (%qd, %qd)\n",
+ tab, s->st_size, p->fts_statp->st_size);
+ tab = "\t";
+ }
+ /*
+ * XXX
+ * Catches nano-second differences, but doesn't display them.
+ */
+ if (s->flags & F_TIME &&
+ s->st_mtimespec.ts_sec != p->fts_statp->st_mtimespec.ts_sec ||
+ s->st_mtimespec.ts_nsec != p->fts_statp->st_mtimespec.ts_nsec) {
+ LABEL;
+ (void)printf("%smodification time (%.24s, ",
+ tab, ctime(&s->st_mtimespec.ts_sec));
+ (void)printf("%.24s)\n",
+ ctime(&p->fts_statp->st_mtimespec.ts_sec));
+ tab = "\t";
+ }
+ if (s->flags & F_CKSUM)
+ if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
+ LABEL;
+ (void)printf("%scksum: %s: %s\n",
+ tab, p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else if (crc(fd, &val, &len)) {
+ (void)close(fd);
+ LABEL;
+ (void)printf("%scksum: %s: %s\n",
+ tab, p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else {
+ (void)close(fd);
+ if (s->cksum != val) {
+ LABEL;
+ (void)printf("%scksum (%lu, %lu)\n",
+ tab, s->cksum, val);
+ }
+ tab = "\t";
+ }
+ if (s->flags & F_SLINK && strcmp(cp = rlink(name), s->slink)) {
+ LABEL;
+ (void)printf("%slink ref (%s, %s)\n", tab, cp, s->slink);
+ }
+ return (label);
+}
+
+char *
+inotype(type)
+ 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");
+ case S_IFSOCK:
+ return ("socket");
+ default:
+ return ("unknown");
+ }
+ /* NOTREACHED */
+}
+
+static char *
+ftype(type)
+ u_int type;
+{
+ switch(type) {
+ case F_BLOCK:
+ return ("block");
+ case F_CHAR:
+ return ("char");
+ case F_DIR:
+ return ("dir");
+ case F_FIFO:
+ return ("fifo");
+ case F_FILE:
+ return ("file");
+ case F_LINK:
+ return ("link");
+ case F_SOCK:
+ return ("socket");
+ default:
+ return ("unknown");
+ }
+ /* NOTREACHED */
+}
+
+char *
+rlink(name)
+ char *name;
+{
+ static char lbuf[MAXPATHLEN];
+ register int len;
+
+ if ((len = readlink(name, lbuf, sizeof(lbuf))) == -1)
+ err("%s: %s", name, strerror(errno));
+ lbuf[len] = '\0';
+ return (lbuf);
+}
diff --git a/usr.sbin/mtree/create.c b/usr.sbin/mtree/create.c
new file mode 100644
index 0000000..9ce603e
--- /dev/null
+++ b/usr.sbin/mtree/create.c
@@ -0,0 +1,290 @@
+/*-
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)create.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <dirent.h>
+#include <grp.h>
+#include <pwd.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "mtree.h"
+#include "extern.h"
+
+#define INDENTNAMELEN 15
+#define MAXLINELEN 80
+
+extern int crc_total, ftsoptions;
+extern int dflag, sflag;
+extern u_short keys;
+extern char fullpath[MAXPATHLEN];
+
+static gid_t gid;
+static uid_t uid;
+static mode_t mode;
+
+static int dsort __P((const FTSENT **, const FTSENT **));
+static void output __P((int *, const char *, ...));
+static int statd __P((FTS *, FTSENT *, uid_t *, gid_t *, mode_t *));
+static void statf __P((FTSENT *));
+
+void
+cwalk()
+{
+ register FTS *t;
+ register FTSENT *p;
+ time_t clock;
+ char *argv[2], host[MAXHOSTNAMELEN];
+
+ (void)time(&clock);
+ (void)gethostname(host, sizeof(host));
+ (void)printf(
+ "#\t user: %s\n#\tmachine: %s\n#\t tree: %s\n#\t date: %s",
+ getlogin(), host, fullpath, ctime(&clock));
+
+ argv[0] = ".";
+ argv[1] = NULL;
+ if ((t = fts_open(argv, ftsoptions, dsort)) == NULL)
+ err("fts_open: %s", strerror(errno));
+ while (p = fts_read(t))
+ switch(p->fts_info) {
+ case FTS_D:
+ (void)printf("\n# %s\n", p->fts_path);
+ statd(t, p, &uid, &gid, &mode);
+ statf(p);
+ break;
+ case FTS_DP:
+ if (p->fts_level > 0)
+ (void)printf("# %s\n..\n\n", p->fts_path);
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ (void)fprintf(stderr,
+ "mtree: %s: %s\n", p->fts_path, strerror(errno));
+ break;
+ default:
+ if (!dflag)
+ statf(p);
+ break;
+
+ }
+ (void)fts_close(t);
+ if (sflag && keys & F_CKSUM)
+ (void)fprintf(stderr,
+ "mtree: %s checksum: %lu\n", fullpath, crc_total);
+}
+
+static void
+statf(p)
+ FTSENT *p;
+{
+ struct group *gr;
+ struct passwd *pw;
+ u_long len, val;
+ int fd, indent;
+
+ if (S_ISDIR(p->fts_statp->st_mode))
+ indent = printf("%s", p->fts_name);
+ else
+ indent = printf(" %s", 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 && (pw = getpwuid(p->fts_statp->st_uid)))
+ output(&indent, "uname=%s", pw->pw_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 && (gr = getgrgid(p->fts_statp->st_gid)))
+ output(&indent, "gname=%s", gr->gr_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_NLINK && p->fts_statp->st_nlink != 1)
+ output(&indent, "nlink=%u", p->fts_statp->st_nlink);
+ if (keys & F_SIZE)
+ output(&indent, "size=%qd", p->fts_statp->st_size);
+ if (keys & F_TIME)
+ output(&indent, "time=%ld.%ld",
+ p->fts_statp->st_mtimespec.ts_sec,
+ p->fts_statp->st_mtimespec.ts_nsec);
+ 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))
+ err("%s: %s", p->fts_accpath, strerror(errno));
+ (void)close(fd);
+ output(&indent, "cksum=%lu", val);
+ }
+ if (keys & F_SLINK &&
+ (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE))
+ output(&indent, "link=%s", rlink(p->fts_accpath));
+ (void)putchar('\n');
+}
+
+#define MAXGID 5000
+#define MAXUID 5000
+#define MAXMODE MBITS + 1
+
+static int
+statd(t, parent, puid, pgid, pmode)
+ FTS *t;
+ FTSENT *parent;
+ uid_t *puid;
+ gid_t *pgid;
+ mode_t *pmode;
+{
+ register FTSENT *p;
+ register gid_t sgid;
+ register uid_t suid;
+ register mode_t smode;
+ struct group *gr;
+ struct passwd *pw;
+ gid_t savegid;
+ uid_t saveuid;
+ mode_t savemode;
+ u_short maxgid, maxuid, maxmode, g[MAXGID], u[MAXUID], m[MAXMODE];
+
+ if ((p = fts_children(t, 0)) == NULL) {
+ if (errno)
+ err("%s: %s", RP(parent), strerror(errno));
+ return (1);
+ }
+
+ bzero(g, sizeof(g));
+ bzero(u, sizeof(u));
+ bzero(m, sizeof(m));
+
+ maxuid = maxgid = maxmode = 0;
+ for (; p; p = p->fts_link) {
+ smode = p->fts_statp->st_mode & MBITS;
+ if (smode < MAXMODE && ++m[smode] > maxmode) {
+ savemode = smode;
+ maxmode = m[smode];
+ }
+ sgid = p->fts_statp->st_gid;
+ if (sgid < MAXGID && ++g[sgid] > maxgid) {
+ savegid = sgid;
+ maxgid = g[sgid];
+ }
+ suid = p->fts_statp->st_uid;
+ if (suid < MAXUID && ++u[suid] > maxuid) {
+ saveuid = suid;
+ maxuid = u[suid];
+ }
+ }
+ (void)printf("/set type=file");
+ if (keys & F_GID)
+ (void)printf(" gid=%u", savegid);
+ if (keys & F_GNAME)
+ if ((gr = getgrgid(savegid)) != NULL)
+ (void)printf(" gname=%s", gr->gr_name);
+ else
+ (void)printf(" gid=%u", savegid);
+ if (keys & F_UNAME)
+ if ((pw = getpwuid(saveuid)) != NULL)
+ (void)printf(" uname=%s", pw->pw_name);
+ else
+ (void)printf(" uid=%u", saveuid);
+ if (keys & F_UID)
+ (void)printf(" uid=%u", saveuid);
+ if (keys & F_MODE)
+ (void)printf(" mode=%#o", savemode);
+ if (keys & F_NLINK)
+ (void)printf(" nlink=1");
+ (void)printf("\n");
+ *puid = saveuid;
+ *pgid = savegid;
+ *pmode = savemode;
+ return (0);
+}
+
+static int
+dsort(a, b)
+ const FTSENT **a, **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));
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+output(int *offset, const char *fmt, ...)
+#else
+output(offset, fmt, va_alist)
+ int *offset;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+ char buf[1024];
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (*offset + strlen(buf) > MAXLINELEN - 3) {
+ (void)printf(" \\\n%*s", INDENTNAMELEN, "");
+ *offset = INDENTNAMELEN;
+ }
+ *offset += printf(" %s", buf) + 1;
+}
diff --git a/usr.sbin/mtree/extern.h b/usr.sbin/mtree/extern.h
new file mode 100644
index 0000000..72c5dbc
--- /dev/null
+++ b/usr.sbin/mtree/extern.h
@@ -0,0 +1,44 @@
+/*-
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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
+ */
+
+int compare __P((char *, NODE *, FTSENT *));
+int crc __P((int, u_long *, u_long *));
+void cwalk __P((void));
+void err __P((const char *, ...));
+char *inotype __P((u_int));
+u_int parsekey __P((char *, int *));
+char *rlink __P((char *));
+NODE *spec __P((void));
+int verify __P((void));
diff --git a/usr.sbin/mtree/misc.c b/usr.sbin/mtree/misc.c
new file mode 100644
index 0000000..3169136
--- /dev/null
+++ b/usr.sbin/mtree/misc.c
@@ -0,0 +1,125 @@
+/*-
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fts.h>
+#include <stdio.h>
+#include "mtree.h"
+#include "extern.h"
+
+extern int lineno;
+
+typedef struct _key {
+ char *name; /* key name */
+ u_int val; /* value */
+
+#define NEEDVALUE 0x01
+ u_int flags;
+} KEY;
+
+/* NB: the following table must be sorted lexically. */
+static KEY keylist[] = {
+ "cksum", F_CKSUM, NEEDVALUE,
+ "gid", F_GID, NEEDVALUE,
+ "gname", F_GNAME, NEEDVALUE,
+ "ignore", F_IGN, 0,
+ "link", F_SLINK, NEEDVALUE,
+ "mode", F_MODE, NEEDVALUE,
+ "nlink", F_NLINK, NEEDVALUE,
+ "size", F_SIZE, NEEDVALUE,
+ "time", F_TIME, NEEDVALUE,
+ "type", F_TYPE, NEEDVALUE,
+ "uid", F_UID, NEEDVALUE,
+ "uname", F_UNAME, NEEDVALUE,
+};
+
+u_int
+parsekey(name, needvaluep)
+ char *name;
+ int *needvaluep;
+{
+ KEY *k, tmp;
+ int keycompare __P((const void *, const void *));
+
+ tmp.name = name;
+ k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY),
+ sizeof(KEY), keycompare);
+ if (k == NULL)
+ err("unknown keyword %s", name);
+
+ if (needvaluep)
+ *needvaluep = k->flags & NEEDVALUE ? 1 : 0;
+ return (k->val);
+}
+
+int
+keycompare(a, b)
+ const void *a, *b;
+{
+ return (strcmp(((KEY *)a)->name, ((KEY *)b)->name));
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+err(const char *fmt, ...)
+#else
+err(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)fprintf(stderr, "mtree: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ if (lineno)
+ (void)fprintf(stderr,
+ "mtree: failed at line %d of the specification\n", lineno);
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/mtree/mtree.8 b/usr.sbin/mtree/mtree.8
new file mode 100644
index 0000000..3c50f71
--- /dev/null
+++ b/usr.sbin/mtree/mtree.8
@@ -0,0 +1,249 @@
+.\" 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. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. 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.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt MTREE 8
+.Os
+.Sh NAME
+.Nm mtree
+.Nd map a directory hierarchy
+.Sh SYNOPSIS
+.Nm mtree
+.Op Fl cderux
+.Op Fl f Ar spec
+.Op Fl K Ar keywords
+.Op Fl k Ar keywords
+.Op Fl p Ar path
+.Op Fl s Ar seed
+.Sh DESCRIPTION
+The utility
+.Nm mtree
+compares 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 specifications, or which are
+missing from either the file hierarchy or the specification.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl c
+Print a specification for the file hierarchy to the standard output.
+.It Fl d
+Ignore everything except directory type files.
+.It Fl e
+Don't complain about files that are in the file hierarchy, but not in the
+specification.
+.It Fl f
+Read the specification from
+.Ar file ,
+instead of from the standard input.
+.It Fl K
+Add the specified (whitespace or comma separated) keywords to the current
+set of keywords.
+.It Fl k
+Use the ``type'' keyword plus the specified (whitespace or comma separated)
+keywords instead of the current set of keywords.
+.It Fl p
+Use the file hierarchy rooted in
+.Ar path ,
+instead of the current directory.
+.It Fl r
+Remove any files in the file hierarchy that are not described in the
+specification.
+.It Fl s
+Display a single checksum to the standard error output that represents all
+of the files for which the keyword
+.Cm cksum
+was specified.
+The checksum is seeded with the specified value.
+.It Fl u
+Modify the owner, group, and permissions of existing files to match
+the specification and create any missing directories.
+User, group, and permissions must all be specified for missing directories
+to be created.
+.It Fl x
+Don't descend below mount points in the file hierarchy.
+.El
+.Pp
+Specifications are mostly composed of ``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 Cm
+.It Cm cksum
+The checksum of the file using the default algorithm specified by
+the
+.Xr cksum 1
+utility.
+.It Cm ignore
+Ignore any file hierarchy below this file.
+.It Cm gid
+The file group as a numeric value.
+.It Cm gname
+The file group as a symbolic name.
+.It Cm mode
+The current file's permissions as a numeric (octal) or symbolic
+value.
+.It Cm nlink
+The number of hard links the file is expected to have.
+.It Cm uid
+The file owner as a numeric value.
+.It Cm uname
+The file group as a symbolic name.
+.It Cm size
+The size, in bytes, of the file.
+.It Cm link
+The file the symbolic link is expected to reference.
+.It Cm time
+The last modification time of the file.
+.It Cm type
+The type of the file; may be set to any one of the following:
+.sp
+.Bl -tag -width Cm -compact
+.It Cm block
+block special device
+.It Cm char
+character special device
+.It Cm dir
+directory
+.It Cm fifo
+fifo
+.It Cm file
+regular file
+.It Cm link
+symbolic link
+.It Cm socket
+socket
+.El
+.El
+.Pp
+The default set of keywords are
+.Cm gid ,
+.Cm mode ,
+.Cm nlink ,
+.Cm size ,
+.Cm slink ,
+.Cm time ,
+and
+.Cm uid .
+.Pp
+There are four types of lines in a specification.
+.Pp
+The first type of line sets a global value for a keyword, and consists of
+the string ``/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
+(``=''), followed by a value, without whitespace characters.
+Once a keyword has been set, its value remains unchanged until either
+reset or unset.
+.Pp
+The second type of line unsets keywords and consists of the string
+``/unset'', followed by whitespace, followed by one or more keywords,
+separated by whitespace.
+.Pp
+The third type of line is a file specification and consists of a file
+name, followed by whitespace, followed by zero or more whitespace
+separated keyword/value pairs.
+The file name may be preceded by whitespace characters.
+The file name may contain any of the standard file name matching
+characters (``['', ``]'', ``?'' or ``*''), in which case files
+in the hierarchy will be associated with the first pattern that
+they match.
+.Pp
+Each of the keyword/value pairs consist of a keyword, followed by an
+equals sign (``=''), followed by the keyword's value, without
+whitespace characters.
+These values override, without changing, the global value of the
+corresponding keyword.
+.Pp
+All paths are relative.
+Specifying a directory will cause subsequent files to be searched
+for in that directory hierarchy.
+Which brings us to the last type of line in a specification: a line
+containing only the string
+.Dq Nm \&..
+causes the current directory
+path to ascend one level.
+.Pp
+Empty lines and lines whose first non-whitespace character is a hash
+mark (``#'') are ignored.
+.Pp
+The
+.Nm mtree
+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 EXAMPLES
+To detect system binaries that have been ``trojan horsed'', it is recommended
+that
+.Nm mtree
+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 mtree
+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 FILES
+.Bl -tag -width /etc/mtree -compact
+.It Pa /etc/mtree
+system specification directory
+.El
+.Sh SEE ALSO
+.Xr chmod 1 ,
+.Xr chown 1 ,
+.Xr chgrp 1 ,
+.Xr cksum 1 ,
+.Xr stat 2 ,
+.Xr fts 3 ,
+.Sh HISTORY
+The
+.Nm mtree
+utility appeared in
+.Bx 4.3 Reno .
diff --git a/usr.sbin/mtree/mtree.c b/usr.sbin/mtree/mtree.c
new file mode 100644
index 0000000..17857c5
--- /dev/null
+++ b/usr.sbin/mtree/mtree.c
@@ -0,0 +1,146 @@
+/*-
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mtree.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fts.h>
+#include "mtree.h"
+#include "extern.h"
+
+extern int crc_total;
+
+int ftsoptions = FTS_PHYSICAL;
+int cflag, dflag, eflag, rflag, sflag, uflag;
+u_short keys;
+char fullpath[MAXPATHLEN];
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int optind;
+ extern char *optarg;
+ int ch;
+ char *dir, *p;
+
+ dir = NULL;
+ keys = KEYDEFAULT;
+ while ((ch = getopt(argc, argv, "cdef:K:k:p:rs:ux")) != EOF)
+ switch((char)ch) {
+ case 'c':
+ cflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case 'f':
+ if (!(freopen(optarg, "r", stdin)))
+ err("%s: %s", optarg, strerror(errno));
+ break;
+ case 'K':
+ while ((p = strsep(&optarg, " \t,")) != NULL)
+ if (*p != '\0')
+ keys |= parsekey(p, NULL);
+ break;
+ case 'k':
+ keys = F_TYPE;
+ while ((p = strsep(&optarg, " \t,")) != NULL)
+ if (*p != '\0')
+ keys |= parsekey(p, NULL);
+ break;
+ case 'p':
+ dir = optarg;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ crc_total = ~strtol(optarg, &p, 0);
+ if (*p)
+ err("illegal seed value -- %s", optarg);
+ case 'u':
+ uflag = 1;
+ break;
+ case 'x':
+ ftsoptions |= FTS_XDEV;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc)
+ usage();
+
+ if (dir && chdir(dir))
+ err("%s: %s", dir, strerror(errno));
+
+ if ((cflag || sflag) && !getwd(fullpath))
+ err("%s", fullpath);
+
+ if (cflag) {
+ cwalk();
+ exit(0);
+ }
+ exit(verify());
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: mtree [-cderux] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n");
+ exit(1);
+}
diff --git a/usr.sbin/mtree/mtree.h b/usr.sbin/mtree/mtree.h
new file mode 100644
index 0000000..5583127
--- /dev/null
+++ b/usr.sbin/mtree/mtree.h
@@ -0,0 +1,88 @@
+/*-
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. 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
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#define KEYDEFAULT \
+ (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | F_TIME | F_UID)
+
+#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 */
+ u_long cksum; /* check sum */
+ 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 */
+ nlink_t st_nlink; /* link count */
+
+#define F_CKSUM 0x0001 /* check sum */
+#define F_DONE 0x0002 /* directory done */
+#define F_GID 0x0004 /* gid */
+#define F_GNAME 0x0008 /* group name */
+#define F_IGN 0x0010 /* ignore */
+#define F_MAGIC 0x0020 /* name has magic chars */
+#define F_MODE 0x0040 /* mode */
+#define F_NLINK 0x0080 /* number of links */
+#define F_SIZE 0x0100 /* size */
+#define F_SLINK 0x0200 /* link count */
+#define F_TIME 0x0400 /* modification time */
+#define F_TYPE 0x0800 /* file type */
+#define F_UID 0x1000 /* uid */
+#define F_UNAME 0x2000 /* user name */
+#define F_VISIT 0x4000 /* file visited */
+ u_short 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 */
+ u_char type; /* file type */
+
+ char name[1]; /* file name (must be last) */
+} NODE;
+
+#define RP(p) \
+ ((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \
+ (p)->fts_path + 2 : (p)->fts_path)
diff --git a/usr.sbin/mtree/spec.c b/usr.sbin/mtree/spec.c
new file mode 100644
index 0000000..a50574f
--- /dev/null
+++ b/usr.sbin/mtree/spec.c
@@ -0,0 +1,280 @@
+/*-
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)spec.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fts.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "mtree.h"
+#include "extern.h"
+
+int lineno; /* Current spec line number. */
+
+static void set __P((char *, NODE *));
+static void unset __P((char *, NODE *));
+
+NODE *
+spec()
+{
+ register NODE *centry, *last;
+ register char *p;
+ NODE ginfo, *root;
+ int c_cur, c_next;
+ char buf[2048];
+
+ root = NULL;
+ bzero(&ginfo, sizeof(ginfo));
+ c_cur = c_next = 0;
+ for (lineno = 1; fgets(buf, sizeof(buf), stdin);
+ ++lineno, c_cur = c_next, c_next = 0) {
+ /* Skip empty lines. */
+ if (buf[0] == '\n')
+ continue;
+
+ /* Find end of line. */
+ if ((p = index(buf, '\n')) == NULL)
+ err("line %d too long", lineno);
+
+ /* See if next line is continuation line. */
+ if (p[-1] == '\\') {
+ --p;
+ c_next = 1;
+ }
+
+ /* Null-terminate the line. */
+ *p = '\0';
+
+ /* Skip leading whitespace. */
+ for (p = buf; *p && isspace(*p); ++p);
+
+ /* If nothing but whitespace or comment char, continue. */
+ if (!*p || *p == '#')
+ continue;
+
+#ifdef DEBUG
+ (void)fprintf(stderr, "line %d: {%s}\n", lineno, p);
+#endif
+ if (c_cur) {
+ set(p, centry);
+ continue;
+ }
+
+ /* Grab file name, "$", "set", or "unset". */
+ if ((p = strtok(p, "\n\t ")) == NULL)
+ err("missing field");
+
+ if (p[0] == '/')
+ switch(p[1]) {
+ case 's':
+ if (strcmp(p + 1, "set"))
+ break;
+ set(NULL, &ginfo);
+ continue;
+ case 'u':
+ if (strcmp(p + 1, "unset"))
+ break;
+ unset(NULL, &ginfo);
+ continue;
+ }
+
+ if (index(p, '/'))
+ err("slash character in file name");
+
+ if (!strcmp(p, "..")) {
+ /* Don't go up, if haven't gone down. */
+ if (!root)
+ 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: err("no parent node");
+ }
+
+ if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
+ err("%s", strerror(errno));
+ *centry = ginfo;
+ (void)strcpy(centry->name, p);
+#define MAGIC "?*["
+ if (strpbrk(p, MAGIC))
+ centry->flags |= F_MAGIC;
+ set(NULL, centry);
+
+ if (!root) {
+ last = root = centry;
+ root->parent = root;
+ } else if (last->type == F_DIR && !(last->flags & F_DONE)) {
+ centry->parent = last;
+ last = last->child = centry;
+ } else {
+ centry->parent = last->parent;
+ centry->prev = last;
+ last = last->next = centry;
+ }
+ }
+ return (root);
+}
+
+static void
+set(t, ip)
+ char *t;
+ register NODE *ip;
+{
+ register int type;
+ register char *kw, *val;
+ struct group *gr;
+ struct passwd *pw;
+ mode_t *m;
+ int value;
+ char *ep;
+
+ for (; kw = strtok(t, "= \t\n"); t = NULL) {
+ ip->flags |= type = parsekey(kw, &value);
+ if (value && (val = strtok(NULL, " \t\n")) == NULL)
+ err("missing value");
+ switch(type) {
+ case F_CKSUM:
+ ip->cksum = strtoul(val, &ep, 10);
+ if (*ep)
+ err("invalid checksum %s", val);
+ break;
+ case F_GID:
+ ip->st_gid = strtoul(val, &ep, 10);
+ if (*ep)
+ err("invalid gid %s", val);
+ break;
+ case F_GNAME:
+ if ((gr = getgrnam(val)) == NULL)
+ err("unknown group %s", val);
+ ip->st_gid = gr->gr_gid;
+ break;
+ case F_IGN:
+ /* just set flag bit */
+ break;
+ case F_MODE:
+ if ((m = setmode(val)) == NULL)
+ err("invalid file mode %s", val);
+ ip->st_mode = getmode(m, 0);
+ break;
+ case F_NLINK:
+ ip->st_nlink = strtoul(val, &ep, 10);
+ if (*ep)
+ err("invalid link count %s", val);
+ break;
+ case F_SIZE:
+ ip->st_size = strtoul(val, &ep, 10);
+ if (*ep)
+ err("invalid size %s", val);
+ break;
+ case F_SLINK:
+ if ((ip->slink = strdup(val)) == NULL)
+ err("%s", strerror(errno));
+ break;
+ case F_TIME:
+ ip->st_mtimespec.ts_sec = strtoul(val, &ep, 10);
+ if (*ep != '.')
+ err("invalid time %s", val);
+ val = ep + 1;
+ ip->st_mtimespec.ts_nsec = strtoul(val, &ep, 10);
+ if (*ep)
+ err("invalid time %s", val);
+ break;
+ case F_TYPE:
+ switch(*val) {
+ case 'b':
+ if (!strcmp(val, "block"))
+ ip->type = F_BLOCK;
+ break;
+ case 'c':
+ if (!strcmp(val, "char"))
+ ip->type = F_CHAR;
+ break;
+ case 'd':
+ if (!strcmp(val, "dir"))
+ ip->type = F_DIR;
+ break;
+ case 'f':
+ if (!strcmp(val, "file"))
+ ip->type = F_FILE;
+ if (!strcmp(val, "fifo"))
+ ip->type = F_FIFO;
+ break;
+ case 'l':
+ if (!strcmp(val, "link"))
+ ip->type = F_LINK;
+ break;
+ case 's':
+ if (!strcmp(val, "socket"))
+ ip->type = F_SOCK;
+ break;
+ default:
+ err("unknown file type %s", val);
+ }
+ break;
+ case F_UID:
+ ip->st_uid = strtoul(val, &ep, 10);
+ if (*ep)
+ err("invalid uid %s", val);
+ break;
+ case F_UNAME:
+ if ((pw = getpwnam(val)) == NULL)
+ err("unknown user %s", val);
+ ip->st_uid = pw->pw_uid;
+ break;
+ }
+ }
+}
+
+static void
+unset(t, ip)
+ char *t;
+ register NODE *ip;
+{
+ register char *p;
+
+ while (p = strtok(t, "\n\t "))
+ ip->flags &= ~parsekey(p, NULL);
+}
diff --git a/usr.sbin/mtree/verify.c b/usr.sbin/mtree/verify.c
new file mode 100644
index 0000000..27d9b90
--- /dev/null
+++ b/usr.sbin/mtree/verify.c
@@ -0,0 +1,202 @@
+/*-
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)verify.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fts.h>
+#include <fnmatch.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include "mtree.h"
+#include "extern.h"
+
+extern int crc_total, ftsoptions;
+extern int dflag, eflag, rflag, sflag, uflag;
+extern char fullpath[MAXPATHLEN];
+
+static NODE *root;
+static char path[MAXPATHLEN];
+
+static void miss __P((NODE *, char *));
+static int vwalk __P((void));
+
+int
+verify()
+{
+ int rval;
+
+ root = spec();
+ rval = vwalk();
+ miss(root, path);
+ return (rval);
+}
+
+static int
+vwalk()
+{
+ register FTS *t;
+ register FTSENT *p;
+ register NODE *ep, *level;
+ int ftsdepth, specdepth, rval;
+ char *argv[2];
+
+ argv[0] = ".";
+ argv[1] = NULL;
+ if ((t = fts_open(argv, ftsoptions, NULL)) == NULL)
+ err("fts_open: %s", strerror(errno));
+ level = root;
+ ftsdepth = specdepth = rval = 0;
+ while (p = fts_read(t)) {
+ switch(p->fts_info) {
+ case FTS_D:
+ ++ftsdepth;
+ break;
+ case FTS_DP:
+ --ftsdepth;
+ if (specdepth > ftsdepth) {
+ for (level = level->parent; level->prev;
+ level = level->prev);
+ --specdepth;
+ }
+ continue;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ (void)fprintf(stderr, "mtree: %s: %s\n",
+ RP(p), strerror(errno));
+ continue;
+ default:
+ if (dflag)
+ continue;
+ }
+
+ 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->name, ep, p))
+ rval = MISMATCHEXIT;
+ if (ep->flags & F_IGN)
+ (void)fts_set(t, p, FTS_SKIP);
+ else if (ep->child && ep->type == F_DIR &&
+ p->fts_info == FTS_D) {
+ level = ep->child;
+ ++specdepth;
+ }
+ break;
+ }
+
+ if (ep)
+ continue;
+ if (!eflag) {
+ (void)printf("extra: %s", RP(p));
+ if (rflag) {
+ if (unlink(p->fts_accpath)) {
+ (void)printf(", not removed: %s",
+ strerror(errno));
+ } else
+ (void)printf(", removed");
+ }
+ (void)putchar('\n');
+ }
+ (void)fts_set(t, p, FTS_SKIP);
+ }
+ (void)fts_close(t);
+ if (sflag)
+ (void)fprintf(stderr,
+ "mtree: %s checksum: %lu\n", fullpath, crc_total);
+ return (rval);
+}
+
+static void
+miss(p, tail)
+ register NODE *p;
+ register char *tail;
+{
+ register int create;
+ register char *tp;
+
+ for (; p; p = p->next) {
+ if (p->type != F_DIR && (dflag || p->flags & F_VISIT))
+ continue;
+ (void)strcpy(tail, p->name);
+ if (!(p->flags & F_VISIT))
+ (void)printf("missing: %s", path);
+ if (p->type != F_DIR) {
+ putchar('\n');
+ continue;
+ }
+
+ create = 0;
+ if (!(p->flags & F_VISIT) && uflag)
+ if (!(p->flags & (F_UID | F_UNAME)))
+ (void)printf(" (not created: user not specified)");
+ else if (!(p->flags & (F_GID | F_GNAME)))
+ (void)printf(" (not created: group not specified)");
+ else if (!(p->flags & F_MODE))
+ (void)printf(" (not created: mode not specified)");
+ else if (mkdir(path, S_IRWXU))
+ (void)printf(" (not created: %s)",
+ strerror(errno));
+ else {
+ create = 1;
+ (void)printf(" (created)");
+ }
+
+ if (!(p->flags & F_VISIT))
+ (void)putchar('\n');
+
+ for (tp = tail; *tp; ++tp);
+ *tp = '/';
+ miss(p->child, tp + 1);
+ *tp = '\0';
+
+ if (!create)
+ continue;
+ if (chown(path, p->st_uid, p->st_gid)) {
+ (void)printf("%s: user/group/mode not modified: %s\n",
+ path, strerror(errno));
+ continue;
+ }
+ if (chmod(path, p->st_mode))
+ (void)printf("%s: permissions not set: %s\n",
+ path, strerror(errno));
+ }
+}
OpenPOWER on IntegriCloud