diff options
Diffstat (limited to 'usr.sbin/fmtree')
-rw-r--r-- | usr.sbin/fmtree/Makefile | 21 | ||||
-rw-r--r-- | usr.sbin/fmtree/Makefile.depend | 19 | ||||
-rw-r--r-- | usr.sbin/fmtree/compare.c | 388 | ||||
-rw-r--r-- | usr.sbin/fmtree/create.c | 428 | ||||
-rw-r--r-- | usr.sbin/fmtree/excludes.c | 111 | ||||
-rw-r--r-- | usr.sbin/fmtree/extern.h | 59 | ||||
-rw-r--r-- | usr.sbin/fmtree/misc.c | 124 | ||||
-rw-r--r-- | usr.sbin/fmtree/mtree.8 | 401 | ||||
-rw-r--r-- | usr.sbin/fmtree/mtree.c | 191 | ||||
-rw-r--r-- | usr.sbin/fmtree/mtree.h | 98 | ||||
-rw-r--r-- | usr.sbin/fmtree/spec.c | 323 | ||||
-rw-r--r-- | usr.sbin/fmtree/specspec.c | 256 | ||||
-rw-r--r-- | usr.sbin/fmtree/test/test00.sh | 67 | ||||
-rw-r--r-- | usr.sbin/fmtree/test/test01.sh | 40 | ||||
-rw-r--r-- | usr.sbin/fmtree/test/test02.sh | 36 | ||||
-rw-r--r-- | usr.sbin/fmtree/test/test03.sh | 60 | ||||
-rw-r--r-- | usr.sbin/fmtree/test/test04.sh | 51 | ||||
-rw-r--r-- | usr.sbin/fmtree/test/test05.sh | 25 | ||||
-rw-r--r-- | usr.sbin/fmtree/verify.c | 259 |
19 files changed, 2957 insertions, 0 deletions
diff --git a/usr.sbin/fmtree/Makefile b/usr.sbin/fmtree/Makefile new file mode 100644 index 0000000..6d060e2 --- /dev/null +++ b/usr.sbin/fmtree/Makefile @@ -0,0 +1,21 @@ +# From: @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $FreeBSD$ + +.include <bsd.own.mk> + +.PATH: ${.CURDIR}/../../usr.bin/cksum + +PROG= fmtree +MAN= fmtree.8 +SRCS= compare.c crc.c create.c excludes.c misc.c mtree.c spec.c verify.c +SRCS+= specspec.c + +CFLAGS+= -DMD5 -DSHA1 -DRMD160 -DSHA256 +LIBADD= md + +CLEANFILES+= fmtree.8 + +fmtree.8: mtree.8 + ${CP} ${.ALLSRC} ${.TARGET} + +.include <bsd.prog.mk> diff --git a/usr.sbin/fmtree/Makefile.depend b/usr.sbin/fmtree/Makefile.depend new file mode 100644 index 0000000..064e492 --- /dev/null +++ b/usr.sbin/fmtree/Makefile.depend @@ -0,0 +1,19 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + gnu/lib/libgcc \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + lib/libmd \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/fmtree/compare.c b/usr.sbin/fmtree/compare.c new file mode 100644 index 0000000..fdd3767 --- /dev/null +++ b/usr.sbin/fmtree/compare.c @@ -0,0 +1,388 @@ +/*- + * 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 0 +#ifndef lint +static char sccsid[] = "@(#)compare.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/time.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#ifdef MD5 +#include <md5.h> +#endif +#ifdef RMD160 +#include <ripemd.h> +#endif +#ifdef SHA1 +#include <sha.h> +#endif +#ifdef SHA256 +#include <sha256.h> +#endif +#include <stdint.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> +#include <vis.h> + +#include "mtree.h" +#include "extern.h" + +#define INDENTNAMELEN 8 +#define LABEL \ + if (!label++) { \ + len = printf("%s changed\n", RP(p)); \ + tab = "\t"; \ + } + +int +compare(char *name __unused, NODE *s, FTSENT *p) +{ + struct timeval tv[2]; + uint32_t val; + int fd, label; + off_t len; + char *cp; + const char *tab = ""; + char *fflags; + + 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 expected %s found %s\n", + ftype(s->type), inotype(p->fts_statp->st_mode)); + return (label); + } + 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 expected %lu found %lu", + tab, (u_long)s->st_uid, (u_long)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 expected %lu found %lu", + tab, (u_long)s->st_gid, (u_long)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_ISLNK(p->fts_statp->st_mode) && + s->st_mode != (p->fts_statp->st_mode & MBITS)) { + LABEL; + (void)printf("%spermissions expected %#o found %#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 expected %u found %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 && + !S_ISDIR(p->fts_statp->st_mode)) { + LABEL; + (void)printf("%ssize expected %jd found %jd\n", tab, + (intmax_t)s->st_size, (intmax_t)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.tv_sec != p->fts_statp->st_mtim.tv_sec) || + (s->st_mtimespec.tv_nsec != p->fts_statp->st_mtim.tv_nsec))) { + LABEL; + (void)printf("%smodification time expected %.24s ", + tab, ctime(&s->st_mtimespec.tv_sec)); + (void)printf("found %.24s", + ctime(&p->fts_statp->st_mtim.tv_sec)); + if (uflag) { + tv[0].tv_sec = s->st_mtimespec.tv_sec; + tv[0].tv_usec = s->st_mtimespec.tv_nsec / 1000; + tv[1] = tv[0]; + if (utimes(p->fts_accpath, tv)) + (void)printf(" not modified: %s\n", + strerror(errno)); + else + (void)printf(" modified\n"); + } else + (void)printf("\n"); + 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 expected %lu found %lu\n", + tab, s->cksum, (unsigned long)val); + tab = "\t"; + } + } + } + if ((s->flags & F_FLAGS) && s->st_flags != p->fts_statp->st_flags) { + LABEL; + fflags = flags_to_string(s->st_flags); + (void)printf("%sflags expected \"%s\"", tab, fflags); + free(fflags); + + fflags = flags_to_string(p->fts_statp->st_flags); + (void)printf(" found \"%s\"", fflags); + free(fflags); + + if (uflag) + if (chflags(p->fts_accpath, s->st_flags)) + (void)printf(" not modified: %s\n", + strerror(errno)); + else + (void)printf(" modified\n"); + else + (void)printf("\n"); + tab = "\t"; + } +#ifdef MD5 + if (s->flags & F_MD5) { + char *new_digest, buf[33]; + + new_digest = MD5File(p->fts_accpath, buf); + if (!new_digest) { + LABEL; + printf("%sMD5: %s: %s\n", tab, p->fts_accpath, + strerror(errno)); + tab = "\t"; + } else if (strcmp(new_digest, s->md5digest)) { + LABEL; + printf("%sMD5 expected %s found %s\n", tab, s->md5digest, + new_digest); + tab = "\t"; + } + } +#endif /* MD5 */ +#ifdef SHA1 + if (s->flags & F_SHA1) { + char *new_digest, buf[41]; + + new_digest = SHA1_File(p->fts_accpath, buf); + if (!new_digest) { + LABEL; + printf("%sSHA-1: %s: %s\n", tab, p->fts_accpath, + strerror(errno)); + tab = "\t"; + } else if (strcmp(new_digest, s->sha1digest)) { + LABEL; + printf("%sSHA-1 expected %s found %s\n", + tab, s->sha1digest, new_digest); + tab = "\t"; + } + } +#endif /* SHA1 */ +#ifdef RMD160 + if (s->flags & F_RMD160) { + char *new_digest, buf[41]; + + new_digest = RIPEMD160_File(p->fts_accpath, buf); + if (!new_digest) { + LABEL; + printf("%sRIPEMD160: %s: %s\n", tab, + p->fts_accpath, strerror(errno)); + tab = "\t"; + } else if (strcmp(new_digest, s->rmd160digest)) { + LABEL; + printf("%sRIPEMD160 expected %s found %s\n", + tab, s->rmd160digest, new_digest); + tab = "\t"; + } + } +#endif /* RMD160 */ +#ifdef SHA256 + if (s->flags & F_SHA256) { + char *new_digest, buf[65]; + + new_digest = SHA256_File(p->fts_accpath, buf); + if (!new_digest) { + LABEL; + printf("%sSHA-256: %s: %s\n", tab, p->fts_accpath, + strerror(errno)); + tab = "\t"; + } else if (strcmp(new_digest, s->sha256digest)) { + LABEL; + printf("%sSHA-256 expected %s found %s\n", + tab, s->sha256digest, new_digest); + tab = "\t"; + } + } +#endif /* SHA256 */ + + if (s->flags & F_SLINK && + strcmp(cp = rlink(p->fts_accpath), s->slink)) { + LABEL; + (void)printf("%slink_ref expected %s found %s\n", + tab, s->slink, cp); + } + return (label); +} + +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"); + case S_IFSOCK: + return ("socket"); + default: + return ("unknown"); + } + /* NOTREACHED */ +} + +const char * +ftype(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(char *name) +{ + static char lbuf[MAXPATHLEN * 4]; + int len; + char tbuf[MAXPATHLEN]; + + if ((len = readlink(name, tbuf, sizeof(tbuf) - 1)) == -1) + err(1, "line %d: %s", lineno, name); + tbuf[len] = '\0'; + strvis(lbuf, tbuf, VIS_WHITE | VIS_OCTAL); + return (lbuf); +} diff --git a/usr.sbin/fmtree/create.c b/usr.sbin/fmtree/create.c new file mode 100644 index 0000000..8be9b02 --- /dev/null +++ b/usr.sbin/fmtree/create.c @@ -0,0 +1,428 @@ +/*- + * 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 0 +#ifndef lint +static char sccsid[] = "@(#)create.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/stat.h> +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <grp.h> +#ifdef MD5 +#include <md5.h> +#endif +#ifdef SHA1 +#include <sha.h> +#endif +#ifdef RMD160 +#include <ripemd.h> +#endif +#ifdef SHA256 +#include <sha256.h> +#endif +#include <pwd.h> +#include <stdint.h> +#include <stdio.h> +#include <time.h> +#include <unistd.h> +#include <vis.h> +#include "mtree.h" +#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 = 0xffffffff; + +static int dsort(const FTSENT * const *, const FTSENT * const *); +static void output(int, int *, const char *, ...) __printflike(3, 4); +static int statd(FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, u_long *); +static void statf(int, FTSENT *); + +void +cwalk(void) +{ + FTS *t; + FTSENT *p; + time_t cl; + char *argv[2], host[MAXHOSTNAMELEN]; + char dot[] = "."; + int indent = 0; + + if (!nflag) { + (void)time(&cl); + (void)gethostname(host, sizeof(host)); + (void)printf( + "#\t user: %s\n#\tmachine: %s\n", + getlogin(), host); + (void)printf( + "#\t tree: %s\n#\t date: %s", + fullpath, ctime(&cl)); + } + + argv[0] = dot; + argv[1] = NULL; + if ((t = fts_open(argv, ftsoptions, dsort)) == NULL) + err(1, "fts_open()"); + while ((p = fts_read(t))) { + if (iflag) + indent = p->fts_level * 4; + if (check_excludes(p->fts_name, p->fts_path)) { + fts_set(t, p, FTS_SKIP); + continue; + } + switch(p->fts_info) { + case FTS_D: + if (!dflag) + (void)printf("\n"); + if (!nflag) + (void)printf("# %s\n", p->fts_path); + statd(t, p, &uid, &gid, &mode, &flags); + statf(indent, p); + break; + case FTS_DP: + if (!nflag && (p->fts_level > 0)) + (void)printf("%*s# %s\n", indent, "", p->fts_path); + (void)printf("%*s..\n", indent, ""); + if (!dflag) + (void)printf("\n"); + break; + case FTS_DNR: + case FTS_ERR: + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + break; + default: + if (!dflag) + statf(indent, p); + break; + + } + } + (void)fts_close(t); + if (sflag && keys & F_CKSUM) + warnx("%s checksum: %lu", fullpath, (unsigned long)crc_total); +} + +static void +statf(int indent, FTSENT *p) +{ + struct group *gr; + struct passwd *pw; + uint32_t val; + off_t len; + int fd, offset; + char *fflags; + char *escaped_name; + + escaped_name = calloc(1, p->fts_namelen * 4 + 1); + if (escaped_name == NULL) + errx(1, "statf(): calloc() failed"); + strvis(escaped_name, p->fts_name, VIS_WHITE | VIS_OCTAL | VIS_GLOB); + + if (iflag || S_ISDIR(p->fts_statp->st_mode)) + offset = printf("%*s%s", indent, "", escaped_name); + else + offset = printf("%*s %s", indent, "", escaped_name); + + free(escaped_name); + + if (offset > (INDENTNAMELEN + indent)) + offset = MAXLINELEN; + else + offset += printf("%*s", (INDENTNAMELEN + indent) - offset, ""); + + if (!S_ISREG(p->fts_statp->st_mode) && !dflag) + output(indent, &offset, "type=%s", inotype(p->fts_statp->st_mode)); + if (p->fts_statp->st_uid != uid) { + if (keys & F_UNAME) { + pw = getpwuid(p->fts_statp->st_uid); + if (pw != NULL) + output(indent, &offset, "uname=%s", pw->pw_name); + else if (wflag) + warnx("Could not get uname for uid=%u", + p->fts_statp->st_uid); + else + errx(1, + "Could not get uname for uid=%u", + p->fts_statp->st_uid); + } + if (keys & F_UID) + output(indent, &offset, "uid=%u", p->fts_statp->st_uid); + } + if (p->fts_statp->st_gid != gid) { + if (keys & F_GNAME) { + gr = getgrgid(p->fts_statp->st_gid); + if (gr != NULL) + output(indent, &offset, "gname=%s", gr->gr_name); + else if (wflag) + warnx("Could not get gname for gid=%u", + p->fts_statp->st_gid); + else + errx(1, + "Could not get gname for gid=%u", + p->fts_statp->st_gid); + } + if (keys & F_GID) + output(indent, &offset, "gid=%u", p->fts_statp->st_gid); + } + if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode) + output(indent, &offset, "mode=%#o", p->fts_statp->st_mode & MBITS); + if (keys & F_NLINK && p->fts_statp->st_nlink != 1) + output(indent, &offset, "nlink=%u", p->fts_statp->st_nlink); + if (keys & F_SIZE && S_ISREG(p->fts_statp->st_mode)) + output(indent, &offset, "size=%jd", + (intmax_t)p->fts_statp->st_size); + if (keys & F_TIME) + output(indent, &offset, "time=%ld.%09ld", + (long)p->fts_statp->st_mtim.tv_sec, + p->fts_statp->st_mtim.tv_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(1, "%s", p->fts_accpath); + (void)close(fd); + output(indent, &offset, "cksum=%lu", (unsigned long)val); + } +#ifdef MD5 + if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) { + char *digest, buf[33]; + + digest = MD5File(p->fts_accpath, buf); + if (!digest) + err(1, "%s", p->fts_accpath); + output(indent, &offset, "md5digest=%s", digest); + } +#endif /* MD5 */ +#ifdef SHA1 + if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) { + char *digest, buf[41]; + + digest = SHA1_File(p->fts_accpath, buf); + if (!digest) + err(1, "%s", p->fts_accpath); + output(indent, &offset, "sha1digest=%s", digest); + } +#endif /* SHA1 */ +#ifdef RMD160 + if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) { + char *digest, buf[41]; + + digest = RIPEMD160_File(p->fts_accpath, buf); + if (!digest) + err(1, "%s", p->fts_accpath); + output(indent, &offset, "ripemd160digest=%s", digest); + } +#endif /* RMD160 */ +#ifdef SHA256 + if (keys & F_SHA256 && S_ISREG(p->fts_statp->st_mode)) { + char *digest, buf[65]; + + digest = SHA256_File(p->fts_accpath, buf); + if (!digest) + err(1, "%s", p->fts_accpath); + output(indent, &offset, "sha256digest=%s", digest); + } +#endif /* SHA256 */ + if (keys & F_SLINK && + (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) + output(indent, &offset, "link=%s", rlink(p->fts_accpath)); + if (keys & F_FLAGS && p->fts_statp->st_flags != flags) { + fflags = flags_to_string(p->fts_statp->st_flags); + output(indent, &offset, "flags=%s", fflags); + free(fflags); + } + (void)putchar('\n'); +} + +#define MAXGID 5000 +#define MAXUID 5000 +#define MAXMODE MBITS + 1 +#define MAXFLAGS 256 +#define 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; + struct group *gr; + struct passwd *pw; + gid_t savegid = *pgid; + uid_t saveuid = *puid; + mode_t savemode = *pmode; + u_long saveflags = *pflags; + u_short maxgid, maxuid, maxmode, maxflags; + u_short g[MAXGID], u[MAXUID], m[MAXMODE], f[MAXFLAGS]; + char *fflags; + static int first = 1; + + if ((p = fts_children(t, 0)) == NULL) { + if (errno) + err(1, "%s", RP(parent)); + return (1); + } + + bzero(g, sizeof(g)); + bzero(u, sizeof(u)); + bzero(m, sizeof(m)); + bzero(f, sizeof(f)); + + maxuid = maxgid = maxmode = maxflags = 0; + for (; p; p = p->fts_link) { + if (!dflag || (dflag && S_ISDIR(p->fts_statp->st_mode))) { + 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]; + } + + /* + * XXX + * note that the below will break when file flags + * are extended beyond the first 4 bytes of each + * half word of the flags + */ +#define FLAGS2IDX(f) ((f & 0xf) | ((f >> 12) & 0xf0)) + sflags = p->fts_statp->st_flags; + if (FLAGS2IDX(sflags) < MAXFLAGS && + ++f[FLAGS2IDX(sflags)] > maxflags) { + saveflags = sflags; + maxflags = f[FLAGS2IDX(sflags)]; + } + } + } + /* + * 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) | (keys & F_UID)) && (*puid != saveuid)) || + (((keys & F_GNAME) | (keys & F_GID)) && (*pgid != savegid)) || + ((keys & F_MODE) && (*pmode != savemode)) || + ((keys & F_FLAGS) && (*pflags != saveflags)) || + (first)) { + first = 0; + if (dflag) + (void)printf("/set type=dir"); + else + (void)printf("/set type=file"); + if (keys & F_UNAME) { + pw = getpwuid(saveuid); + if (pw != NULL) + (void)printf(" uname=%s", pw->pw_name); + else if (wflag) + warnx( "Could not get uname for uid=%u", saveuid); + else + errx(1, "Could not get uname for uid=%u", saveuid); + } + if (keys & F_UID) + (void)printf(" uid=%lu", (u_long)saveuid); + if (keys & F_GNAME) { + gr = getgrgid(savegid); + if (gr != NULL) + (void)printf(" gname=%s", gr->gr_name); + else if (wflag) + warnx("Could not get gname for gid=%u", savegid); + else + errx(1, "Could not get gname for gid=%u", savegid); + } + if (keys & F_GID) + (void)printf(" gid=%lu", (u_long)savegid); + if (keys & F_MODE) + (void)printf(" mode=%#o", savemode); + if (keys & F_NLINK) + (void)printf(" nlink=1"); + if (keys & F_FLAGS) { + fflags = flags_to_string(saveflags); + (void)printf(" flags=%s", fflags); + free(fflags); + } + (void)printf("\n"); + *puid = saveuid; + *pgid = savegid; + *pmode = savemode; + *pflags = saveflags; + } + return (0); +} + +static int +dsort(const FTSENT * const *a, const FTSENT * const *b) +{ + if (S_ISDIR((*a)->fts_statp->st_mode)) { + if (!S_ISDIR((*b)->fts_statp->st_mode)) + return (1); + } else if (S_ISDIR((*b)->fts_statp->st_mode)) + return (-1); + return (strcmp((*a)->fts_name, (*b)->fts_name)); +} + +#include <stdarg.h> + +void +output(int indent, int *offset, const char *fmt, ...) +{ + va_list ap; + char buf[1024]; + va_start(ap, fmt); + (void)vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (*offset + strlen(buf) > MAXLINELEN - 3) { + (void)printf(" \\\n%*s", INDENTNAMELEN + indent, ""); + *offset = INDENTNAMELEN + indent; + } + *offset += printf(" %s", buf) + 1; +} diff --git a/usr.sbin/fmtree/excludes.c b/usr.sbin/fmtree/excludes.c new file mode 100644 index 0000000..21a49b0 --- /dev/null +++ b/usr.sbin/fmtree/excludes.c @@ -0,0 +1,111 @@ +/* + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/time.h> /* XXX for mtree.h */ +#include <sys/queue.h> + +#include <err.h> +#include <fnmatch.h> +#include <fts.h> +#include <stdio.h> +#include <stdlib.h> + +#include "mtree.h" /* XXX for extern.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, *str; + struct exclude *e; + size_t len; + + fp = fopen(name, "r"); + if (fp == 0) + err(1, "%s", name); + + while ((line = fgetln(fp, &len)) != 0) { + if (line[len - 1] == '\n') + len--; + if (len == 0) + continue; + + str = malloc(len + 1); + e = malloc(sizeof *e); + if (str == 0 || e == 0) + errx(1, "memory allocation error"); + e->glob = str; + memcpy(str, line, len); + str[len] = '\0'; + if (strchr(str, '/')) + 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) + + LIST_FOREACH(e, &excludes, link) { + if ((e->pathname && MATCH(e->glob, path)) + || MATCH(e->glob, fname)) + return 1; + } + return 0; +} diff --git a/usr.sbin/fmtree/extern.h b/usr.sbin/fmtree/extern.h new file mode 100644 index 0000000..4b6fb3c --- /dev/null +++ b/usr.sbin/fmtree/extern.h @@ -0,0 +1,59 @@ +/*- + * 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 + * $FreeBSD$ + */ +extern uint32_t crc_total; + +#ifdef _FTS_H_ +int compare(char *, NODE *, FTSENT *); +#endif +int crc(int, uint32_t *, off_t *); +void cwalk(void); +char *flags_to_string(u_long); + +const char *inotype(u_int); +u_int parsekey(char *, int *); +char *rlink(char *); +NODE *mtree_readspec(FILE *fi); +int mtree_verifyspec(FILE *fi); +int mtree_specspec(FILE *fi, FILE *fj); + +int check_excludes(const char *, const char *); +void init_excludes(void); +void read_excludes_file(const char *); +const char * ftype(u_int type); + +extern int ftsoptions; +extern u_int keys; +extern int lineno; +extern int dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, wflag; +#ifdef MAXPATHLEN +extern char fullpath[MAXPATHLEN]; +#endif diff --git a/usr.sbin/fmtree/misc.c b/usr.sbin/fmtree/misc.c new file mode 100644 index 0000000..65667d6 --- /dev/null +++ b/usr.sbin/fmtree/misc.c @@ -0,0 +1,124 @@ +/*- + * 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. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93"; +#endif /*not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/stat.h> +#include <err.h> +#include <fts.h> +#include <stdio.h> +#include <unistd.h> +#include "mtree.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 table must be sorted lexically. */ +static KEY keylist[] = { + {"cksum", F_CKSUM, NEEDVALUE}, + {"flags", F_FLAGS, NEEDVALUE}, + {"gid", F_GID, NEEDVALUE}, + {"gname", F_GNAME, NEEDVALUE}, + {"ignore", F_IGN, 0}, + {"link", F_SLINK, NEEDVALUE}, +#ifdef MD5 + {"md5digest", F_MD5, NEEDVALUE}, +#endif + {"mode", F_MODE, NEEDVALUE}, + {"nlink", F_NLINK, NEEDVALUE}, + {"nochange", F_NOCHANGE, 0}, + {"optional", F_OPT, 0}, +#ifdef RMD160 + {"ripemd160digest", F_RMD160, NEEDVALUE}, +#endif +#ifdef SHA1 + {"sha1digest", F_SHA1, NEEDVALUE}, +#endif +#ifdef SHA256 + {"sha256digest", F_SHA256, NEEDVALUE}, +#endif + {"size", F_SIZE, NEEDVALUE}, + {"time", F_TIME, NEEDVALUE}, + {"type", F_TYPE, NEEDVALUE}, + {"uid", F_UID, NEEDVALUE}, + {"uname", F_UNAME, NEEDVALUE}, +}; + +int keycompare(const void *, const void *); + +u_int +parsekey(char *name, int *needvaluep) +{ + KEY *k, tmp; + + tmp.name = name; + k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY), + sizeof(KEY), keycompare); + if (k == NULL) + errx(1, "line %d: unknown keyword %s", lineno, name); + + if (needvaluep) + *needvaluep = k->flags & NEEDVALUE ? 1 : 0; + return (k->val); +} + +int +keycompare(const void *a, const void *b) +{ + return (strcmp(((const KEY *)a)->name, ((const KEY *)b)->name)); +} + +char * +flags_to_string(u_long fflags) +{ + char *string; + + string = fflagstostr(fflags); + if (string != NULL && *string == '\0') { + free(string); + string = strdup("none"); + } + if (string == NULL) + err(1, NULL); + + return string; +} diff --git a/usr.sbin/fmtree/mtree.8 b/usr.sbin/fmtree/mtree.8 new file mode 100644 index 0000000..fd073b9 --- /dev/null +++ b/usr.sbin/fmtree/mtree.8 @@ -0,0 +1,401 @@ +.\" 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. +.\" 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. +.\" +.\" From: @(#)mtree.8 8.2 (Berkeley) 12/11/93 +.\" $FreeBSD$ +.\" +.Dd June 16, 2007 +.Dt MTREE 8 +.Os +.Sh NAME +.Nm mtree +.Nd map a directory hierarchy +.Sh SYNOPSIS +.Nm +.Op Fl LPUcdeinqruxw +.Bk -words +.Op Fl f Ar spec +.Ek +.Bk -words +.Op Fl K Ar keywords +.Ek +.Bk -words +.Op Fl k Ar keywords +.Ek +.Bk -words +.Op Fl p Ar path +.Ek +.Bk -words +.Op Fl s Ar seed +.Ek +.Bk -words +.Op Fl X Ar exclude-list +.Ek +.Sh DESCRIPTION +The +.Nm +utility 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 L +Follow all symbolic links in the file hierarchy. +.It Fl P +Do not follow symbolic links in the file hierarchy, instead consider +the symbolic link itself in any comparisons. +This is the default. +.It Fl U +Modify the owner, group, permissions, and modification time of existing +files to match the specification and create any missing directories or +symbolic links. +User, group and permissions must all be specified for missing directories +to be created. +Corrected mismatches are not considered errors. +.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 +Do not complain about files that are in the file hierarchy, but not in the +specification. +.It Fl i +Indent the output 4 spaces each time a directory level is descended when +creating a specification with the +.Fl c +option. +This does not affect either the /set statements or the comment before each +directory. +It does however affect the comment before the close of each directory. +.It Fl n +Do not emit pathname comments when creating a specification. +Normally +a comment is emitted before each directory and before the close of that +directory when using the +.Fl c +option. +.It Fl q +Quiet mode. +Do not complain when a +.Dq missing +directory cannot be created because it already exists. +This occurs when the directory is a symbolic link. +.It Fl r +Remove any files in the file hierarchy that are not described in the +specification. +.It Fl u +Same as +.Fl U +except a status of 2 is returned if the file hierarchy did not match +the specification. +.It Fl w +Make some errors non-fatal warnings. +.It Fl x +Do not descend below mount points in the file hierarchy. +.It Fl f Ar file +Read the specification from +.Ar file , +instead of from the standard input. +.Pp +If this option is specified twice, the two specifications are compared +to each other rather than to the file hierarchy. +The specifications will be sorted like output generated using +.Fl c . +The output format in this case is somewhat remniscent of +.Xr comm 1 , +having "in first spec only", "in second spec only", and "different" +columns, prefixed by zero, one and two TAB characters respectively. +Each entry in the "different" column occupies two lines, one from each specification. +.It Fl K Ar keywords +Add the specified (whitespace or comma separated) +.Ar keywords +to the current set of keywords. +.It Fl k Ar keywords +Use the ``type'' keyword plus the specified (whitespace or comma separated) +.Ar keywords +instead of the current set of keywords. +.It Fl p Ar path +Use the file hierarchy rooted in +.Ar path , +instead of the current directory. +.It Fl s Ar seed +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 X Ar exclude-list +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. +No comments are allowed in +the +.Ar exclude-list +file. +.El +.Pp +Specifications are mostly composed of ``keywords'', i.e., strings +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 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 +.Dq none +may be used to override the current default. +.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 md5digest +The MD5 message digest of the file. +.It Cm sha1digest +The +.Tn FIPS +160-1 +.Pq Dq Tn SHA-1 +message digest of the file. +.It Cm sha256digest +The +.Tn FIPS +180-2 +.Pq Dq Tn SHA-256 +message digest of the file. +.It Cm ripemd160digest +The +.Tn RIPEMD160 +message digest of the file. +.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 nochange +Make sure this file or directory exists but otherwise ignore all attributes. +.It Cm optional +The file is optional; do not complain about the file if it is +not in the file hierarchy. +.It Cm uid +The file owner as a numeric value. +.It Cm uname +The file owner 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, in seconds and nanoseconds. +The value should include a period character and exactly nine digits +after the period. +.It Cm type +The type of the file; may be set to any one of the following: +.Pp +.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 flags , +.Cm gid , +.Cm link , +.Cm mode , +.Cm nlink , +.Cm size , +.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 Pa ..\& +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 +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. +A status of 2 is converted to a status of 0 if the +.Fl U +option is used. +.Sh FILES +.Bl -tag -width /etc/mtree -compact +.It Pa /etc/mtree +system specification directory +.El +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +To detect system binaries that have been ``trojan horsed'', it is recommended +that +.Nm +.Fl K +.Cm sha256digest +be run on the file systems, and a copy of the results stored on a different +machine, or, at least, in encrypted form. +The output file itself should be digested using the +.Xr sha256 1 +utility. +Then, periodically, +.Nm +and +.Xr sha256 1 +should be run against the on-line specifications. +While it is possible for the bad guys to change the on-line specifications +to conform to their modified binaries, it is believed to be +impractical for them to create a modified specification which has +the same SHA-256 digest as the original. +.Pp +The +.Fl d +and +.Fl u +options can be used in combination to create directory hierarchies +for distributions and other such things; the files in +.Pa /etc/mtree +were used to create almost all directories in this +.Fx +distribution. +.Pp +To create an +.Pa /etc/mtree +style BSD.*.dist file, use +.Nm +.Fl c +.Fl d +.Fl i +.Fl n +.Fl k +.Cm uname,gname,mode,nochange. +.Sh SEE ALSO +.Xr chflags 1 , +.Xr chgrp 1 , +.Xr chmod 1 , +.Xr cksum 1 , +.Xr md5 1 , +.Xr stat 2 , +.Xr fts 3 , +.Xr md5 3 , +.Xr chown 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.3 Reno . +The +.Tn MD5 +digest capability was added in +.Fx 2.1 , +in response to the widespread use of programs which can spoof +.Xr cksum 1 . +The +.Tn SHA-1 +and +.Tn RIPEMD160 +digests were added in +.Fx 4.0 , +as new attacks have demonstrated weaknesses in +.Tn MD5 . +The +.Tn SHA-256 +digest was added in +.Fx 6.0 . +Support for file flags was added in +.Fx 4.0 , +and mostly comes from +.Nx . diff --git a/usr.sbin/fmtree/mtree.c b/usr.sbin/fmtree/mtree.c new file mode 100644 index 0000000..e90a5bb --- /dev/null +++ b/usr.sbin/fmtree/mtree.c @@ -0,0 +1,191 @@ +/*- + * 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 0 +#ifndef lint +static const 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 */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/stat.h> +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <stdio.h> +#include <unistd.h> +#include "mtree.h" +#include "extern.h" + +int ftsoptions = FTS_PHYSICAL; +int dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, wflag; +static int cflag, Uflag; +u_int keys; +char fullpath[MAXPATHLEN]; + +static void usage(void); + +int +main(int argc, char *argv[]) +{ + int ch; + char *dir, *p; + int status; + FILE *spec1, *spec2; + + dir = NULL; + keys = KEYDEFAULT; + init_excludes(); + spec1 = stdin; + spec2 = NULL; + + while ((ch = getopt(argc, argv, "cdef:iK:k:LnPp:qrs:UuwxX:")) != -1) + switch((char)ch) { + case 'c': + cflag = 1; + break; + case 'd': + dflag = 1; + break; + case 'e': + eflag = 1; + break; + case 'f': + if (spec1 == stdin) { + spec1 = fopen(optarg, "r"); + if (spec1 == NULL) + err(1, "%s", optarg); + } else if (spec2 == NULL) { + spec2 = fopen(optarg, "r"); + if (spec2 == NULL) + err(1, "%s", optarg); + } else + usage(); + break; + case 'i': + iflag = 1; + 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 'L': + ftsoptions &= ~FTS_PHYSICAL; + ftsoptions |= FTS_LOGICAL; + break; + case 'n': + nflag = 1; + break; + case 'P': + ftsoptions &= ~FTS_LOGICAL; + ftsoptions |= FTS_PHYSICAL; + break; + case 'p': + dir = optarg; + break; + case 'q': + qflag = 1; + break; + case 'r': + rflag = 1; + break; + case 's': + sflag = 1; + crc_total = ~strtoul(optarg, &p, 0); + if (*p) + errx(1, "illegal seed value -- %s", optarg); + break; + case 'U': + Uflag = 1; + uflag = 1; + break; + case 'u': + uflag = 1; + break; + case 'w': + 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)) + err(1, "%s", dir); + + if ((cflag || sflag) && !getcwd(fullpath, sizeof(fullpath))) + errx(1, "%s", fullpath); + + if (cflag) { + cwalk(); + exit(0); + } + if (spec2 != NULL) + status = mtree_specspec(spec1, spec2); + else + status = mtree_verifyspec(spec1); + if (Uflag & (status == MISMATCHEXIT)) + status = 0; + exit(status); +} + +static void +usage(void) +{ + (void)fprintf(stderr, +"usage: mtree [-LPUcdeinqruxw] [-f spec] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n" +"\t[-X excludes]\n"); + exit(1); +} diff --git a/usr.sbin/fmtree/mtree.h b/usr.sbin/fmtree/mtree.h new file mode 100644 index 0000000..fb22f0d --- /dev/null +++ b/usr.sbin/fmtree/mtree.h @@ -0,0 +1,98 @@ +/*- + * 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 + * $FreeBSD$ + */ + +#include <string.h> +#include <stdlib.h> + +#define KEYDEFAULT \ + (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | F_TIME | 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 */ + u_long cksum; /* check sum */ + char *md5digest; /* MD5 digest */ + char *sha1digest; /* SHA-1 digest */ + char *sha256digest; /* SHA-256 digest */ + char *rmd160digest; /* RIPEMD160 digest */ + 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 */ + u_long st_flags; /* flags */ + 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 */ +#define F_MD5 0x8000 /* MD5 digest */ +#define F_NOCHANGE 0x10000 /* If owner/mode "wrong", do */ + /* not change */ +#define F_SHA1 0x20000 /* SHA-1 digest */ +#define F_RMD160 0x40000 /* RIPEMD160 digest */ +#define F_FLAGS 0x80000 /* file flags */ +#define F_SHA256 0x100000 /* SHA-256 digest */ +#define F_OPT 0x200000 /* existence optional */ + u_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 */ + 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/fmtree/spec.c b/usr.sbin/fmtree/spec.c new file mode 100644 index 0000000..417932d --- /dev/null +++ b/usr.sbin/fmtree/spec.c @@ -0,0 +1,323 @@ +/*- + * 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 0 +#ifndef lint +static char sccsid[] = "@(#)spec.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/stat.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <grp.h> +#include <pwd.h> +#include <stdio.h> +#include <unistd.h> +#include <vis.h> +#include "mtree.h" +#include "extern.h" + +int lineno; /* Current spec line number. */ + +static void set(char *, NODE *); +static void unset(char *, NODE *); + +NODE * +mtree_readspec(FILE *fi) +{ + NODE *centry, *last; + char *p; + NODE ginfo, *root; + int c_cur, c_next; + char buf[2048]; + + centry = last = root = NULL; + bzero(&ginfo, sizeof(ginfo)); + c_cur = c_next = 0; + for (lineno = 1; fgets(buf, sizeof(buf), fi); + ++lineno, c_cur = c_next, c_next = 0) { + /* Skip empty lines. */ + if (buf[0] == '\n') + continue; + + /* Find end of line. */ + if ((p = strchr(buf, '\n')) == NULL) + errx(1, "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) + errx(1, "line %d: missing field", lineno); + + 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 (strchr(p, '/')) + errx(1, "line %d: slash character in file name", + lineno); + + 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: errx(1, "line %d: no parent node", lineno); + } + + if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) + errx(1, "calloc"); + *centry = ginfo; +#define MAGIC "?*[" + if (strpbrk(p, MAGIC)) + centry->flags |= F_MAGIC; + if (strunvis(centry->name, p) == -1) + errx(1, "filename %s is ill-encoded", p); + 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(char *t, NODE *ip) +{ + int type; + char *kw, *val = NULL; + 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) + errx(1, "line %d: missing value", lineno); + switch(type) { + case F_CKSUM: + ip->cksum = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid checksum %s", + lineno, val); + break; + case F_MD5: + ip->md5digest = strdup(val); + if(!ip->md5digest) + errx(1, "strdup"); + break; + case F_SHA1: + ip->sha1digest = strdup(val); + if(!ip->sha1digest) + errx(1, "strdup"); + break; + case F_SHA256: + ip->sha256digest = strdup(val); + if(!ip->sha256digest) + errx(1, "strdup"); + break; + case F_RMD160: + ip->rmd160digest = strdup(val); + if(!ip->rmd160digest) + errx(1, "strdup"); + break; + case F_FLAGS: + if (strcmp("none", val) == 0) + ip->st_flags = 0; + else if (strtofflags(&val, &ip->st_flags, NULL) != 0) + errx(1, "line %d: invalid flag %s",lineno, val); + break; + case F_GID: + ip->st_gid = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid gid %s", lineno, val); + break; + case F_GNAME: + if ((gr = getgrnam(val)) == NULL) + errx(1, "line %d: unknown group %s", lineno, val); + ip->st_gid = gr->gr_gid; + break; + case F_IGN: + /* just set flag bit */ + break; + case F_MODE: + if ((m = setmode(val)) == NULL) + errx(1, "line %d: invalid file mode %s", + lineno, val); + ip->st_mode = getmode(m, 0); + free(m); + break; + case F_NLINK: + ip->st_nlink = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid link count %s", + lineno, val); + break; + case F_OPT: + /* just set flag bit */ + break; + case F_SIZE: + ip->st_size = strtoq(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid size %s", + lineno, val); + break; + case F_SLINK: + ip->slink = malloc(strlen(val) + 1); + if (ip->slink == NULL) + errx(1, "malloc"); + if (strunvis(ip->slink, val) == -1) + errx(1, "symlink %s is ill-encoded", val); + break; + case F_TIME: + ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10); + if (*ep == '.') { + /* Note: we require exactly nine + * digits after the decimal point. */ + val = ep + 1; + ip->st_mtimespec.tv_nsec + = strtoul(val, &ep, 10); + } else + ip->st_mtimespec.tv_nsec = 0; + if (*ep) + errx(1, "line %d: invalid time %s", + lineno, 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: + errx(1, "line %d: unknown file type %s", + lineno, val); + } + break; + case F_UID: + ip->st_uid = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid uid %s", lineno, val); + break; + case F_UNAME: + if ((pw = getpwnam(val)) == NULL) + errx(1, "line %d: unknown user %s", lineno, val); + ip->st_uid = pw->pw_uid; + break; + } + } +} + +static void +unset(char *t, NODE *ip) +{ + char *p; + + while ((p = strtok(t, "\n\t "))) + ip->flags &= ~parsekey(p, NULL); +} diff --git a/usr.sbin/fmtree/specspec.c b/usr.sbin/fmtree/specspec.c new file mode 100644 index 0000000..f85882e --- /dev/null +++ b/usr.sbin/fmtree/specspec.c @@ -0,0 +1,256 @@ +/*- + * Copyright (c) 2003 Poul-Henning Kamp + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <err.h> +#include <grp.h> +#include <pwd.h> +#include <stdio.h> +#include <stdint.h> +#include <unistd.h> +#include "mtree.h" +#include "extern.h" + +#define FF(a, b, c, d) \ + (((a)->flags & (c)) && ((b)->flags & (c)) && ((a)->d) != ((b)->d)) +#define FS(a, b, c, d) \ + (((a)->flags & (c)) && ((b)->flags & (c)) && strcmp((a)->d,(b)->d)) +#define FM(a, b, c, d) \ + (((a)->flags & (c)) && ((b)->flags & (c)) && memcmp(&(a)->d,&(b)->d, sizeof (a)->d)) + +static void +shownode(NODE *n, int f, char const *path) +{ + struct group *gr; + struct passwd *pw; + + printf("%s%s %s", path, n->name, ftype(n->type)); + if (f & F_CKSUM) + printf(" cksum=%lu", n->cksum); + if (f & F_GID) + printf(" gid=%d", n->st_gid); + if (f & F_GNAME) { + gr = getgrgid(n->st_gid); + if (gr == NULL) + printf(" gid=%d", n->st_gid); + else + printf(" gname=%s", gr->gr_name); + } + if (f & F_MODE) + printf(" mode=%o", n->st_mode); + if (f & F_NLINK) + printf(" nlink=%d", n->st_nlink); + if (f & F_SIZE) + printf(" size=%jd", (intmax_t)n->st_size); + if (f & F_UID) + printf(" uid=%d", n->st_uid); + if (f & F_UNAME) { + pw = getpwuid(n->st_uid); + if (pw == NULL) + printf(" uid=%d", n->st_uid); + else + printf(" uname=%s", pw->pw_name); + } + if (f & F_MD5) + printf(" md5digest=%s", n->md5digest); + if (f & F_SHA1) + printf(" sha1digest=%s", n->sha1digest); + if (f & F_RMD160) + printf(" rmd160digest=%s", n->rmd160digest); + if (f & F_SHA256) + printf(" sha256digest=%s", n->sha256digest); + if (f & F_FLAGS) + printf(" flags=%s", flags_to_string(n->st_flags)); + printf("\n"); +} + +static int +mismatch(NODE *n1, NODE *n2, int differ, char const *path) +{ + + if (n2 == NULL) { + shownode(n1, differ, path); + return (1); + } + if (n1 == NULL) { + printf("\t"); + shownode(n2, differ, path); + return (1); + } + if (!(differ & keys)) + return(0); + printf("\t\t"); + shownode(n1, differ, path); + printf("\t\t"); + shownode(n2, differ, path); + return (1); +} + +static int +compare_nodes(NODE *n1, NODE *n2, char const *path) +{ + int differs; + + if (n1 != NULL && n1->type == F_LINK) + n1->flags &= ~F_MODE; + if (n2 != NULL && n2->type == F_LINK) + n2->flags &= ~F_MODE; + differs = 0; + if (n1 == NULL && n2 != NULL) { + differs = n2->flags; + mismatch(n1, n2, differs, path); + return (1); + } + if (n1 != NULL && n2 == NULL) { + differs = n1->flags; + mismatch(n1, n2, differs, path); + return (1); + } + if (n1->type != n2->type) { + differs = 0; + mismatch(n1, n2, differs, path); + return (1); + } + if (FF(n1, n2, F_CKSUM, cksum)) + differs |= F_CKSUM; + if (FF(n1, n2, F_GID, st_gid)) + differs |= F_GID; + if (FF(n1, n2, F_GNAME, st_gid)) + differs |= F_GNAME; + if (FF(n1, n2, F_MODE, st_mode)) + differs |= F_MODE; + if (FF(n1, n2, F_NLINK, st_nlink)) + differs |= F_NLINK; + if (FF(n1, n2, F_SIZE, st_size)) + differs |= F_SIZE; + if (FS(n1, n2, F_SLINK, slink)) + differs |= F_SLINK; + if (FM(n1, n2, F_TIME, st_mtimespec)) + differs |= F_TIME; + if (FF(n1, n2, F_UID, st_uid)) + differs |= F_UID; + if (FF(n1, n2, F_UNAME, st_uid)) + differs |= F_UNAME; + if (FS(n1, n2, F_MD5, md5digest)) + differs |= F_MD5; + if (FS(n1, n2, F_SHA1, sha1digest)) + differs |= F_SHA1; + if (FS(n1, n2, F_RMD160, rmd160digest)) + differs |= F_RMD160; + if (FS(n1, n2, F_SHA256, sha256digest)) + differs |= F_SHA256; + if (FF(n1, n2, F_FLAGS, st_flags)) + differs |= F_FLAGS; + if (differs) { + mismatch(n1, n2, differs, path); + return (1); + } + return (0); +} +static int +walk_in_the_forest(NODE *t1, NODE *t2, char const *path) +{ + int r, i; + NODE *c1, *c2, *n1, *n2; + char *np; + + r = 0; + + if (t1 != NULL) + c1 = t1->child; + else + c1 = NULL; + if (t2 != NULL) + c2 = t2->child; + else + c2 = NULL; + while (c1 != NULL || c2 != NULL) { + n1 = n2 = NULL; + if (c1 != NULL) + n1 = c1->next; + if (c2 != NULL) + n2 = c2->next; + if (c1 != NULL && c2 != NULL) { + if (c1->type != F_DIR && c2->type == F_DIR) { + n2 = c2; + c2 = NULL; + } else if (c1->type == F_DIR && c2->type != F_DIR) { + n1 = c1; + c1 = NULL; + } else { + i = strcmp(c1->name, c2->name); + if (i > 0) { + n1 = c1; + c1 = NULL; + } else if (i < 0) { + n2 = c2; + c2 = NULL; + } + } + } + if (c1 == NULL && c2->type == F_DIR) { + asprintf(&np, "%s%s/", path, c2->name); + i = walk_in_the_forest(c1, c2, np); + free(np); + i += compare_nodes(c1, c2, path); + } else if (c2 == NULL && c1->type == F_DIR) { + asprintf(&np, "%s%s/", path, c1->name); + i = walk_in_the_forest(c1, c2, np); + free(np); + i += compare_nodes(c1, c2, path); + } else if (c1 == NULL || c2 == NULL) { + i = compare_nodes(c1, c2, path); + } else if (c1->type == F_DIR && c2->type == F_DIR) { + asprintf(&np, "%s%s/", path, c1->name); + i = walk_in_the_forest(c1, c2, np); + free(np); + i += compare_nodes(c1, c2, path); + } else { + i = compare_nodes(c1, c2, path); + } + r += i; + c1 = n1; + c2 = n2; + } + return (r); +} + +int +mtree_specspec(FILE *fi, FILE *fj) +{ + int rval; + NODE *root1, *root2; + + root1 = mtree_readspec(fi); + root2 = mtree_readspec(fj); + rval = walk_in_the_forest(root1, root2, ""); + rval += compare_nodes(root1, root2, ""); + if (rval > 0) + return (MISMATCHEXIT); + return (0); +} diff --git a/usr.sbin/fmtree/test/test00.sh b/usr.sbin/fmtree/test/test00.sh new file mode 100644 index 0000000..fce801b --- /dev/null +++ b/usr.sbin/fmtree/test/test00.sh @@ -0,0 +1,67 @@ +#!/bin/sh +# +# Copyright (c) 2003 Poul-Henning Kamp +# All rights reserved. +# +# Please see src/share/examples/etc/bsd-style-copyright. +# +# $FreeBSD$ +# + +set -e + +TMP=/tmp/mtree.$$ + +rm -rf ${TMP} +mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt + + +mkdir ${TMP}/mt/foo +mkdir ${TMP}/mr/\* +mtree -c -p ${TMP}/mr | mtree -U -r -p ${TMP}/mt > /dev/null 2>&1 +if [ -d ${TMP}/mt/foo ] ; then + echo "ERROR Mtree create fell for filename with '*' char" 1>&2 + rm -rf ${TMP} + exit 1 +fi +rmdir ${TMP}/mr/\* + +mkdir -p ${TMP}/mt/foo +mkdir ${TMP}/mr/\[f\]oo +mtree -c -p ${TMP}/mr | mtree -U -r -p ${TMP}/mt > /dev/null 2>&1 +if [ -d ${TMP}/mt/foo ] ; then + echo "ERROR Mtree create fell for filename with '[' char" 1>&2 + rm -rf ${TMP} + exit 1 +fi +rmdir ${TMP}/mr/\[f\]oo + +mkdir -p ${TMP}/mt/foo +mkdir ${TMP}/mr/\?oo +mtree -c -p ${TMP}/mr | mtree -U -r -p ${TMP}/mt > /dev/null 2>&1 +if [ -d ${TMP}/mt/foo ] ; then + echo "ERROR Mtree create fell for filename with '?' char" 1>&2 + rm -rf ${TMP} + exit 1 +fi +rmdir ${TMP}/mr/\?oo + +mkdir ${TMP}/mr/\# +mtree -c -p ${TMP}/mr > ${TMP}/_ +if mtree -U -r -p ${TMP}/mt < ${TMP}/_ > /dev/null 2>&1 ; then + true +else + echo "ERROR Mtree create fell for filename with '#' char" 1>&2 + rm -rf ${TMP} + exit 1 +fi + +if [ ! -d ${TMP}/mt/\# ] ; then + echo "ERROR Mtree update failed to create name with '#' char" 1>&2 + rm -rf ${TMP} + exit 1 +fi +rmdir ${TMP}/mr/\# + +rm -rf ${TMP} +exit 0 diff --git a/usr.sbin/fmtree/test/test01.sh b/usr.sbin/fmtree/test/test01.sh new file mode 100644 index 0000000..e4d3fc3 --- /dev/null +++ b/usr.sbin/fmtree/test/test01.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# +# Copyright (c) 2003 Poul-Henning Kamp +# All rights reserved. +# +# Please see src/share/examples/etc/bsd-style-copyright. +# +# $FreeBSD$ +# + +set -e + +TMP=/tmp/mtree.$$ + +rm -rf ${TMP} +mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt + + +ln -s "xx this=is=wrong" ${TMP}/mr/foo +mtree -c -p ${TMP}/mr > ${TMP}/_ + +if mtree -U -r -p ${TMP}/mt < ${TMP}/_ > /dev/null 2>&1 ; then + true +else + echo "ERROR Mtree failed on symlink with space char" 1>&2 + rm -rf ${TMP} + exit 1 +fi + +x=x`(cd ${TMP}/mr ; ls -l foo 2>&1) || true` +y=x`(cd ${TMP}/mt ; ls -l foo 2>&1) || true` + +if [ "$x" != "$y" ] ; then + echo "ERROR Recreation of spaced symlink failed" 1>&2 + rm -rf ${TMP} + exit 1 +fi + +rm -rf ${TMP} +exit 0 diff --git a/usr.sbin/fmtree/test/test02.sh b/usr.sbin/fmtree/test/test02.sh new file mode 100644 index 0000000..a7aa916 --- /dev/null +++ b/usr.sbin/fmtree/test/test02.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# +# Copyright (c) 2003 Dan Nelson +# All rights reserved. +# +# Please see src/share/examples/etc/bsd-style-copyright. +# +# $FreeBSD$ +# + +set -e + +TMP=/tmp/mtree.$$ + +rm -rf ${TMP} +mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt + +touch -t 199901020304 ${TMP}/mr/oldfile +touch ${TMP}/mt/oldfile + +mtree -c -p ${TMP}/mr > ${TMP}/_ + +mtree -U -r -p ${TMP}/mt < ${TMP}/_ > /dev/null + +x=x`(cd ${TMP}/mr ; ls -l 2>&1) || true` +y=x`(cd ${TMP}/mt ; ls -l 2>&1) || true` + +if [ "$x" != "$y" ] ; then + echo "ERROR Update of mtime failed" 1>&2 + rm -rf ${TMP} + exit 1 +fi + +rm -rf ${TMP} +exit 0 + diff --git a/usr.sbin/fmtree/test/test03.sh b/usr.sbin/fmtree/test/test03.sh new file mode 100644 index 0000000..bb3a5b5 --- /dev/null +++ b/usr.sbin/fmtree/test/test03.sh @@ -0,0 +1,60 @@ +#!/bin/sh +# +# Copyright (c) 2003 Poul-Henning Kamp +# All rights reserved. +# +# Please see src/share/examples/etc/bsd-style-copyright. +# +# $FreeBSD$ +# + +set -e + +TMP=/tmp/mtree.$$ + +rm -rf ${TMP} +mkdir -p ${TMP} + +K=uid,uname,gid,gname,flags,md5digest,size,ripemd160digest,sha1digest,sha256digest,cksum + +rm -rf _FOO +mkdir _FOO +touch _FOO/_uid +touch _FOO/_size +touch _FOO/zztype + +touch _FOO/_bar +mtree -c -K $K -p .. > ${TMP}/_r +mtree -c -K $K -p .. > ${TMP}/_r2 +rm -rf _FOO/_bar + +rm -rf _FOO/zztype +mkdir _FOO/zztype + +date > _FOO/_size + +chown nobody _FOO/_uid + +touch _FOO/_foo +mtree -c -K $K -p .. > ${TMP}/_t + +rm -fr _FOO + +if mtree -f ${TMP}/_r -f ${TMP}/_r2 ; then + true +else + echo "ERROR Compare identical failed" 1>&2 + exit 1 +fi + +if mtree -f ${TMP}/_r -f ${TMP}/_t > ${TMP}/_ ; then + echo "ERROR Compare different succeeded" 1>&2 + exit 1 +fi + +if [ `wc -l < ${TMP}/_` -ne 10 ] ; then + echo "ERROR wrong number of lines: `wc -l ${TMP}/_`" 1>&2 + exit 1 +fi + +exit 0 diff --git a/usr.sbin/fmtree/test/test04.sh b/usr.sbin/fmtree/test/test04.sh new file mode 100644 index 0000000..38acda2 --- /dev/null +++ b/usr.sbin/fmtree/test/test04.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# +# Copyright (c) 2003 Dan Nelson +# All rights reserved. +# +# Please see src/share/examples/etc/bsd-style-copyright. +# +# $FreeBSD$ +# + +set -e + +TMP=/tmp/mtree.$$ + +rm -rf ${TMP} +mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt + +mkdir ${TMP}/mr/a +mkdir ${TMP}/mr/b +mkdir ${TMP}/mt/a +mkdir ${TMP}/mt/b +touch ${TMP}/mt/z + +mtree -c -p ${TMP}/mr > ${TMP}/_r +mtree -c -p ${TMP}/mt > ${TMP}/_t + +if mtree -f ${TMP}/_r -f ${TMP}/_t > ${TMP}/_ ; then + echo "ERROR wrong exit on difference" 1>&2 + exit 1 +fi + +if [ `wc -l < ${TMP}/_` -ne 1 ] ; then + echo "ERROR spec/spec compare generated wrong output" 1>&2 + rm -rf ${TMP} + exit 1 +fi + +if mtree -f ${TMP}/_t -f ${TMP}/_r > ${TMP}/_ ; then + echo "ERROR wrong exit on difference" 1>&2 + exit 1 +fi + +if [ `wc -l < ${TMP}/_` -ne 1 ] ; then + echo "ERROR spec/spec compare generated wrong output" 1>&2 + rm -rf ${TMP} + exit 1 +fi + +rm -rf ${TMP} +exit 0 + diff --git a/usr.sbin/fmtree/test/test05.sh b/usr.sbin/fmtree/test/test05.sh new file mode 100644 index 0000000..eda544f --- /dev/null +++ b/usr.sbin/fmtree/test/test05.sh @@ -0,0 +1,25 @@ +#!/bin/sh +# +# $FreeBSD$ +# +# Test for 'optional' keyword. +# + +TMP=`mktemp -d /tmp/mtree.XXXXXX` +mkdir -p ${TMP}/mr ${TMP}/mr/optional-dir ${TMP}/mr/some-dir +touch ${TMP}/mr/optional-file ${TMP}/mr/some-file + +mtree -c -p ${TMP}/mr > ${TMP}/_ +rm -rf ${TMP}/mr/optional-file ${TMP}/mr/optional-dir +mtree -p ${TMP}/mr -K optional < ${TMP}/_ > /dev/null + +res=$? + +if [ $res -ne 0 ] ; then + echo "ERROR 'optional' keyword failed" 1>&2 + rm -rf ${TMP} + exit 1 +fi + +rm -rf ${TMP} +exit 0 diff --git a/usr.sbin/fmtree/verify.c b/usr.sbin/fmtree/verify.c new file mode 100644 index 0000000..c9291c0 --- /dev/null +++ b/usr.sbin/fmtree/verify.c @@ -0,0 +1,259 @@ +/*- + * 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 0 +#ifndef lint +static char sccsid[] = "@(#)verify.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/stat.h> +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <fnmatch.h> +#include <stdio.h> +#include <unistd.h> +#include "mtree.h" +#include "extern.h" + +static NODE *root; +static char path[MAXPATHLEN]; + +static void miss(NODE *, char *); +static int vwalk(void); + +int +mtree_verifyspec(FILE *fi) +{ + int rval; + + root = mtree_readspec(fi); + rval = vwalk(); + miss(root, path); + return (rval); +} + +static int +nsort(const FTSENT * const *a, const FTSENT * const *b) +{ + return (strcmp((*a)->fts_name, (*b)->fts_name)); +} + +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, nsort)) == NULL) + err(1, "line %d: fts_open", lineno); + level = root; + specdepth = rval = 0; + while ((p = fts_read(t))) { + 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); + --specdepth; + } + continue; + case FTS_DNR: + case FTS_ERR: + case FTS_NS: + warnx("%s: %s", RP(p), strerror(p->fts_errno)); + continue; + default: + if (dflag) + continue; + } + + if (specdepth != p->fts_level) + goto extra; + for (ep = level; ep; ep = ep->next) + if ((ep->flags & F_MAGIC && + !fnmatch(ep->name, p->fts_name, FNM_PATHNAME)) || + !strcmp(ep->name, p->fts_name)) { + ep->flags |= F_VISIT; + if ((ep->flags & F_NOCHANGE) == 0 && + compare(ep->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; +extra: + if (!eflag) { + (void)printf("%s extra", RP(p)); + if (rflag) { + if ((S_ISDIR(p->fts_statp->st_mode) + ? rmdir : 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) + warnx("%s checksum: %lu", fullpath, (unsigned long)crc_total); + return (rval); +} + +static void +miss(NODE *p, char *tail) +{ + int create; + char *tp; + const char *type, *what; + int serr; + + 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; + (void)strcpy(tail, p->name); + if (!(p->flags & F_VISIT)) { + /* Don't print missing message if file exists as a + symbolic link and the -q flag is set. */ + struct stat statbuf; + + if (qflag && stat(path, &statbuf) == 0) + p->flags |= F_VISIT; + else + (void)printf("%s missing", path); + } + if (p->type != F_DIR && p->type != F_LINK) { + putchar('\n'); + continue; + } + + create = 0; + if (p->type == F_LINK) + type = "symlink"; + else + type = "directory"; + if (!(p->flags & F_VISIT) && uflag) { + if (!(p->flags & (F_UID | F_UNAME))) + (void)printf(" (%s not created: user not specified)", type); + else if (!(p->flags & (F_GID | F_GNAME))) + (void)printf(" (%s not created: group not specified)", type); + else if (p->type == F_LINK) { + if (symlink(p->slink, path)) + (void)printf(" (symlink not created: %s)\n", + strerror(errno)); + else + (void)printf(" (created)\n"); + if (lchown(path, p->st_uid, p->st_gid) == -1) { + serr = errno; + if (p->st_uid == (uid_t)-1) + what = "group"; + else if (lchown(path, (uid_t)-1, + p->st_gid) == -1) + what = "user & group"; + else { + what = "user"; + errno = serr; + } + (void)printf("%s: %s not modified: %s" + "\n", path, what, strerror(errno)); + } + continue; + } else if (!(p->flags & F_MODE)) + (void)printf(" (directory not created: mode not specified)"); + else if (mkdir(path, S_IRWXU)) + (void)printf(" (directory 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 && !uflag) + continue; + if (chown(path, p->st_uid, p->st_gid) == -1) { + serr = errno; + if (p->st_uid == (uid_t)-1) + what = "group"; + else if (chown(path, (uid_t)-1, p->st_gid) == -1) + what = "user & group"; + else { + what = "user"; + errno = serr; + } + (void)printf("%s: %s not modified: %s\n", + path, what, strerror(errno)); + } + if (chmod(path, p->st_mode)) + (void)printf("%s: permissions not set: %s\n", + path, strerror(errno)); + if ((p->flags & F_FLAGS) && p->st_flags && + chflags(path, p->st_flags)) + (void)printf("%s: file flags not set: %s\n", + path, strerror(errno)); + } +} |