From 2f6d3139ecacca3533517516db002cf436f78918 Mon Sep 17 00:00:00 2001 From: Sascha Wildner Date: Sun, 22 Jul 2018 16:57:40 +0200 Subject: [PATCH] mtree(8): Replace our mtree(8) with NetBSD's version. Some of its files will be used by other stuff we want to upgrade or bring in, such as install(1) and makefs(8). Since mtree(8) is a bootstrap tool and I want this to be painless, I've imported some other source files that mtree(8) uses, such as from mknod(1), which we don't have, as part of mtree(8). --- usr.sbin/mtree/Makefile | 22 +- usr.sbin/mtree/compare.c | 592 +++++++++++++++++++---------- usr.sbin/mtree/crc.c | 155 ++++++++ usr.sbin/mtree/create.c | 882 ++++++++++++++++++++++++-------------------- usr.sbin/mtree/excludes.c | 60 +-- usr.sbin/mtree/extern.h | 72 +++- usr.sbin/mtree/getid.c | 443 ++++++++++++++++++++++ usr.sbin/mtree/misc.c | 250 +++++++++++-- usr.sbin/mtree/mtree.8 | 811 ++++++++++++++++++++++++++++++---------- usr.sbin/mtree/mtree.c | 231 ++++++++++-- usr.sbin/mtree/mtree.h | 128 +++++-- usr.sbin/mtree/only.c | 147 ++++++++ usr.sbin/mtree/pack_dev.c | 285 ++++++++++++++ usr.sbin/mtree/pack_dev.h | 47 +++ usr.sbin/mtree/spec.c | 878 ++++++++++++++++++++++++++++++++++--------- usr.sbin/mtree/specspec.c | 270 ++++++++++++++ usr.sbin/mtree/stat_flags.c | 179 +++++++++ usr.sbin/mtree/verify.c | 206 +++++++---- 18 files changed, 4493 insertions(+), 1165 deletions(-) create mode 100644 usr.sbin/mtree/crc.c rewrite usr.sbin/mtree/create.c (60%) create mode 100644 usr.sbin/mtree/getid.c create mode 100644 usr.sbin/mtree/only.c create mode 100644 usr.sbin/mtree/pack_dev.c create mode 100644 usr.sbin/mtree/pack_dev.h create mode 100644 usr.sbin/mtree/specspec.c create mode 100644 usr.sbin/mtree/stat_flags.c diff --git a/usr.sbin/mtree/Makefile b/usr.sbin/mtree/Makefile index 0c352936d8..5ce1dd96ef 100644 --- a/usr.sbin/mtree/Makefile +++ b/usr.sbin/mtree/Makefile @@ -1,20 +1,24 @@ -# From: @(#)Makefile 8.1 (Berkeley) 6/6/93 -# $FreeBSD: src/usr.sbin/mtree/Makefile,v 1.15.2.5 2001/09/24 15:44:22 ru Exp $ +# $NetBSD: Makefile,v 1.34 2013/02/03 19:15:16 christos Exp $ +# from: @(#)Makefile 8.2 (Berkeley) 4/27/95 -.PATH: ${.CURDIR}/../../usr.bin/cksum .PATH: ${.CURDIR}/../../sbin/md5 +.PATH: ${.CURDIR}/../../usr.bin/cksum PROG= mtree MAN= mtree.8 -SRCS= compare.c crc.c create.c excludes.c misc.c mtree.c spec.c verify.c +SRCS= compare.c crc.c create.c excludes.c misc.c mtree.c spec.c specspec.c \ + verify.c getid.c only.c -.if !defined(BOOTSTRAPPING) +SRCS+= pack_dev.c stat_flags.c SRCS+= sha1hl.c -CFLAGS+= -DUSE_MD5 -DUSE_SHA1 -DUSE_RMD160 -DPADD= ${LIBMD} ${LIBCRYPTO} -LDADD= -lmd -lprivate_crypto + +DPADD= ${LIBMD} ${LIBCRYPTO} ${LIBUTIL} +LDADD= -lmd -lprivate_crypto -lutil LDFLAGS+= ${PRIVATELIB_LDFLAGS} CFLAGS+= -I${.CURDIR}/../../crypto/libressl/include -.endif + +CFLAGS+=-I${_SHLIBDIRPREFIX}/usr/include/priv -I${.CURDIR}/../../sbin/md5 + +#CFLAGS+=-DDEBUG .include diff --git a/usr.sbin/mtree/compare.c b/usr.sbin/mtree/compare.c index 28ac909a47..ef97a36a0f 100644 --- a/usr.sbin/mtree/compare.c +++ b/usr.sbin/mtree/compare.c @@ -1,3 +1,6 @@ +/* @(#)compare.c 8.1 (Berkeley) 6/6/93 */ +/* $NetBSD: compare.c,v 1.58 2013/11/21 18:39:50 christos Exp $ */ + /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -25,51 +28,116 @@ * 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. - * - * @(#)compare.c 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.sbin/mtree/compare.c,v 1.15.2.4 2003/05/07 17:55:17 tobez Exp $ */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + #include #include -#include + #include #include -#include -#ifdef USE_MD5 +#include +#include +#include +#include +#include +#include + +#ifndef NO_MD5 #include #endif -#ifdef USE_SHA1 +#ifndef NO_RMD160 +#include +#include +#endif +#ifndef NO_SHA1 #include -#include "../../sbin/md5/sha1hl.h" +#include #endif -#ifdef USE_RMD160 -#include +#ifndef NO_SHA2 +#include +#include #endif -#include -#include -#include -#include "mtree.h" -#include "extern.h" -static const char *ftype(u_int); +#include "extern.h" #define INDENTNAMELEN 8 -#define LABEL \ - if (!label++) { \ - len = printf("%s changed\n", RP(p)); \ - tab = "\t"; \ +#define MARK \ +do { \ + if (flavor == F_FREEBSD9) { \ + len = printf("%s changed\n", RP(p)); \ + tab = "\t"; \ + } else { \ + len = printf("%s: ", RP(p)); \ + if (len > INDENTNAMELEN) { \ + tab = "\t"; \ + printf("\n"); \ + } else { \ + tab = ""; \ + printf("%*s", INDENTNAMELEN - (int)len, ""); \ + } \ + } \ +} while (0) +#define LABEL if (!label++) MARK + +#if HAVE_STRUCT_STAT_ST_FLAGS + + +#define CHANGEFLAGS \ + if (flags != p->fts_statp->st_flags) { \ + char *sf; \ + if (!label) { \ + MARK; \ + sf = flags_to_string(p->fts_statp->st_flags, "none"); \ + printf("%sflags (\"%s\"", tab, sf); \ + free(sf); \ + } \ + if (lchflags(p->fts_accpath, flags)) { \ + label++; \ + printf(", not modified: %s)\n", \ + strerror(errno)); \ + } else { \ + sf = flags_to_string(flags, "none"); \ + printf(", modified to \"%s\")\n", sf); \ + free(sf); \ + } \ } +/* SETFLAGS: + * given pflags, additionally set those flags specified in s->st_flags and + * selected by mask (the other flags are left unchanged). + */ +#define SETFLAGS(pflags, mask) \ +do { \ + flags = (s->st_flags & (mask)) | (pflags); \ + CHANGEFLAGS; \ +} while (0) + +/* CLEARFLAGS: + * given pflags, reset the flags specified in s->st_flags and selected by mask + * (the other flags are left unchanged). + */ +#define CLEARFLAGS(pflags, mask) \ +do { \ + flags = (~(s->st_flags & (mask)) & CH_MASK) & (pflags); \ + CHANGEFLAGS; \ +} while (0) +#endif /* HAVE_STRUCT_STAT_ST_FLAGS */ + int compare(NODE *s, FTSENT *p) { - uint32_t val; - off_t len; + u_int32_t len, val, flags; int fd, label; - char *cp, *fflags; - const char *tab = ""; + const char *cp, *tab; +#if !defined(NO_MD5) || !defined(NO_RMD160) || !defined(NO_SHA1) || !defined(NO_SHA2) + char *digestbuf; +#endif + tab = NULL; label = 0; switch(s->type) { case F_BLOCK: @@ -96,88 +164,237 @@ compare(NODE *s, FTSENT *p) if (!S_ISLNK(p->fts_statp->st_mode)) goto typeerr; break; +#ifdef S_ISSOCK case F_SOCK: - if (!S_ISSOCK(p->fts_statp->st_mode)) { -typeerr: LABEL; - printf("\ttype expected %s found %s\n", - ftype(s->type), inotype(p->fts_statp->st_mode)); - return (label); - } + if (!S_ISSOCK(p->fts_statp->st_mode)) + goto typeerr; break; +#endif +typeerr: LABEL; + printf(flavor == F_FREEBSD9 ? + "\ttype expected %s found %s\n" : "\ttype (%s, %s)\n", + nodetype(s->type), inotype(p->fts_statp->st_mode)); + return (label); + } + if (mtree_Wflag) + goto afterpermwhack; +#if HAVE_STRUCT_STAT_ST_FLAGS + if (iflag && !uflag) { + if (s->flags & F_FLAGS) + SETFLAGS(p->fts_statp->st_flags, SP_FLGS); + return (label); + } + if (mflag && !uflag) { + if (s->flags & F_FLAGS) + CLEARFLAGS(p->fts_statp->st_flags, SP_FLGS); + return (label); + } +#endif + if (s->flags & F_DEV && + (s->type == F_BLOCK || s->type == F_CHAR) && + s->st_rdev != p->fts_statp->st_rdev) { + LABEL; + printf(flavor == F_FREEBSD9 ? + "%sdevice expected %#jx found %#jx" : + "%sdevice (%#jx, %#jx", + tab, (uintmax_t)s->st_rdev, + (uintmax_t)p->fts_statp->st_rdev); + if (uflag) { + if ((unlink(p->fts_accpath) == -1) || + (mknod(p->fts_accpath, + s->st_mode | nodetoino(s->type), + s->st_rdev) == -1) || + (lchown(p->fts_accpath, p->fts_statp->st_uid, + p->fts_statp->st_gid) == -1) ) + printf(", not modified: %s%s\n", + strerror(errno), + flavor == F_FREEBSD9 ? "" : ")"); + else + printf(", modified%s\n", + flavor == F_FREEBSD9 ? "" : ")"); + } else + printf(")\n"); + tab = "\t"; } /* Set the uid/gid first, then set the mode. */ if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) { LABEL; - printf("%suser expected %lu found %lu", + printf(flavor == F_FREEBSD9 ? + "%suser expected %lu found %lu" : "%suser (%lu, %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)) - printf(" not modified: %s\n", - strerror(errno)); + if (uflag) { + if (lchown(p->fts_accpath, s->st_uid, -1)) + printf(", not modified: %s%s\n", + strerror(errno), + flavor == F_FREEBSD9 ? "" : ")"); else - printf(" modified\n"); - else - printf("\n"); + printf(", modified%s\n", + flavor == F_FREEBSD9 ? "" : ")"); + } else + printf(")\n"); tab = "\t"; } if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) { LABEL; - printf("%sgid expected %lu found %lu", + printf(flavor == F_FREEBSD9 ? + "%sgid expected %lu found %lu" : "%sgid (%lu, %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)) - printf(" not modified: %s\n", - strerror(errno)); + if (uflag) { + if (lchown(p->fts_accpath, -1, s->st_gid)) + printf(", not modified: %s%s\n", + strerror(errno), + flavor == F_FREEBSD9 ? "" : ")"); else - printf(" modified\n"); + printf(", modified%s\n", + flavor == F_FREEBSD9 ? "" : ")"); + } else - printf("\n"); + 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)) { + if (lflag) { + mode_t tmode, mode; + + tmode = s->st_mode; + mode = p->fts_statp->st_mode & MBITS; + /* + * if none of the suid/sgid/etc bits are set, + * then if the mode is a subset of the target, + * skip. + */ + if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) || + (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO)))) + if ((mode | tmode) == tmode) + goto skip; + } + LABEL; - printf("%spermissions expected %#o found %#o", - tab, s->st_mode, p->fts_statp->st_mode & MBITS); - if (uflag) - if (chmod(p->fts_accpath, s->st_mode)) - printf(" not modified: %s\n", - strerror(errno)); + printf(flavor == F_FREEBSD9 ? + "%spermissions expcted %#lo found %#lo" : + "%spermissions (%#lo, %#lo", + tab, (u_long)s->st_mode, + (u_long)p->fts_statp->st_mode & MBITS); + if (uflag) { + if (lchmod(p->fts_accpath, s->st_mode)) + printf(", not modified: %s%s\n", + strerror(errno), + flavor == F_FREEBSD9 ? "" : ")"); else - printf(" modified\n"); + printf(", modified%s\n", + flavor == F_FREEBSD9 ? "" : ")"); + } else - printf("\n"); + printf(")\n"); tab = "\t"; + skip: ; } if (s->flags & F_NLINK && s->type != F_DIR && s->st_nlink != p->fts_statp->st_nlink) { LABEL; - printf("%slink_count expected %u found %u\n", - tab, s->st_nlink, p->fts_statp->st_nlink); + printf(flavor == F_FREEBSD9 ? + "%slink count expected %lu found %lu\n" : + "%slink count (%lu, %lu)\n", + tab, (u_long)s->st_nlink, (u_long)p->fts_statp->st_nlink); tab = "\t"; } - if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size && - !S_ISDIR(p->fts_statp->st_mode)) { + if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) { LABEL; - printf("%ssize expected %jd found %jd\n", tab, - (intmax_t)s->st_size, (intmax_t)p->fts_statp->st_size); + printf(flavor == F_FREEBSD9 ? + "%ssize expected %ju found %ju\n" : "%ssize (%ju, %ju)\n", + tab, (uintmax_t)s->st_size, + (uintmax_t)p->fts_statp->st_size); tab = "\t"; } /* * XXX - * Catches nano-second differences, but doesn't display them. + * Since utimes(2) only takes a timeval, there's no point in + * comparing the low bits of the timespec nanosecond field. This + * will only result in mismatches that we can never fix. + * + * Doesn't display microsecond differences. */ - if ((s->flags & F_TIME) && - ((s->st_mtimespec.tv_sec != p->fts_statp->st_mtimespec.tv_sec) || - (s->st_mtimespec.tv_nsec != p->fts_statp->st_mtimespec.tv_nsec))) { - LABEL; - printf("%smodification time expected %.24s ", - tab, ctime(&s->st_mtimespec.tv_sec)); - printf("found %.24s\n", - ctime(&p->fts_statp->st_mtimespec.tv_sec)); + if (s->flags & F_TIME) { + struct timeval tv[2]; + struct stat *ps = p->fts_statp; + time_t smtime = s->st_mtimespec.tv_sec; + +#if defined(BSD4_4) && !defined(HAVE_NBTOOL_CONFIG_H) + time_t pmtime = ps->st_mtimespec.tv_sec; + + TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &ps->st_mtimespec); +#else + time_t pmtime = (time_t)ps->st_mtime; + + tv[0].tv_sec = smtime; + tv[0].tv_usec = 0; + tv[1].tv_sec = pmtime; + tv[1].tv_usec = 0; +#endif + + if (tv[0].tv_sec != tv[1].tv_sec || + tv[0].tv_usec != tv[1].tv_usec) { + LABEL; + printf(flavor == F_FREEBSD9 ? + "%smodification time expected %.24s found " : + "%smodification time (%.24s, ", + tab, ctime(&smtime)); + printf("%.24s", ctime(&pmtime)); + if (tflag) { + tv[1] = tv[0]; + if (utimes(p->fts_accpath, tv)) + printf(", not modified: %s%s\n", + strerror(errno), + flavor == F_FREEBSD9 ? "" : ")"); + else + printf(", modified%s\n", + flavor == F_FREEBSD9 ? "" : ")"); + } else + printf("%s\n", flavor == F_FREEBSD9 ? "" : ")"); + tab = "\t"; + } + } +#if HAVE_STRUCT_STAT_ST_FLAGS + /* + * XXX + * since lchflags(2) will reset file times, the utimes() above + * may have been useless! oh well, we'd rather have correct + * flags, rather than times? + */ + if ((s->flags & F_FLAGS) && ((s->st_flags != p->fts_statp->st_flags) + || mflag || iflag)) { + if (s->st_flags != p->fts_statp->st_flags) { + char *f_s; + LABEL; + f_s = flags_to_string(s->st_flags, "none"); + printf(flavor == F_FREEBSD9 ? + "%sflags expected \"%s\" found " : + "%sflags (\"%s\" is not ", tab, f_s); + free(f_s); + f_s = flags_to_string(p->fts_statp->st_flags, "none"); + printf("\"%s\"", f_s); + free(f_s); + } + if (uflag) { + if (iflag) + SETFLAGS(0, CH_MASK); + else if (mflag) + CLEARFLAGS(0, SP_FLGS); + else + SETFLAGS(0, (~SP_FLGS & CH_MASK)); + } else + printf("%s\n", flavor == F_FREEBSD9 ? "" : ")"); tab = "\t"; } +#endif /* HAVE_STRUCT_STAT_ST_FLAGS */ + + /* + * from this point, no more permission checking or whacking + * occurs, only checking of stuff like checksums and symlinks. + */ + afterpermwhack: if (s->flags & F_CKSUM) { if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) { LABEL; @@ -194,158 +411,163 @@ typeerr: LABEL; close(fd); if (s->cksum != val) { LABEL; - printf("%scksum expected %lu found %u\n", - tab, s->cksum, val); - tab = "\t"; + printf(flavor == F_FREEBSD9 ? + "%scksum expected %lu found %lu\n" : + "%scksum (%lu, %lu)\n", + tab, s->cksum, (unsigned long)val); } + tab = "\t"; } } - /* - * XXX - * since chflags(2) will reset file times, the utimes() above - * may have been useless! oh well, we'd rather have correct - * flags, rather than times? - */ - if ((s->flags & F_FLAGS) && s->st_flags != p->fts_statp->st_flags) { - LABEL; - fflags = flags_to_string(s->st_flags); - printf("%sflags expected \"%s\"", tab, fflags); - free(fflags); - - fflags = flags_to_string(p->fts_statp->st_flags); - printf(" found \"%s\"", fflags); - free(fflags); - - if (uflag) - if (chflags(p->fts_accpath, s->st_flags)) - printf(" not modified: %s\n", - strerror(errno)); - else - printf(" modified\n"); - else - printf("\n"); - tab = "\t"; - } -#ifdef USE_MD5 +#ifndef NO_MD5 if (s->flags & F_MD5) { - char *new_digest, buf[33]; - - new_digest = MD5File(p->fts_accpath, buf); - if (!new_digest) { + if ((digestbuf = MD5File(p->fts_accpath, NULL)) == NULL) { LABEL; - printf("%sMD5: %s: %s\n", tab, p->fts_accpath, - strerror(errno)); + printf("%s%s: %s: %s\n", + tab, MD5KEY, p->fts_accpath, strerror(errno)); + tab = "\t"; + } else { + if (strcmp(s->md5digest, digestbuf)) { + LABEL; + printf(flavor == F_FREEBSD9 ? + "%s%s expected %s found %s\n" : + "%s%s (0x%s, 0x%s)\n", + tab, MD5KEY, s->md5digest, digestbuf); + } tab = "\t"; - } else if (strcmp(new_digest, s->md5digest)) { + free(digestbuf); + } + } +#endif /* ! NO_MD5 */ +#ifndef NO_RMD160 + if (s->flags & F_RMD160) { + if ((digestbuf = RIPEMD160_File(p->fts_accpath, NULL)) == NULL) { LABEL; - printf("%sMD5 expected %s found %s\n", tab, s->md5digest, - new_digest); + printf("%s%s: %s: %s\n", + tab, RMD160KEY, p->fts_accpath, strerror(errno)); + tab = "\t"; + } else { + if (strcmp(s->rmd160digest, digestbuf)) { + LABEL; + printf(flavor == F_FREEBSD9 ? + "%s%s expected %s found %s\n" : + "%s%s (0x%s, 0x%s)\n", + tab, RMD160KEY, s->rmd160digest, digestbuf); + } tab = "\t"; + free(digestbuf); } } -#endif /* MD5 */ -#ifdef USE_SHA1 +#endif /* ! NO_RMD160 */ +#ifndef NO_SHA1 if (s->flags & F_SHA1) { - char *new_digest, buf[41]; - - new_digest = SHA1_File(p->fts_accpath, buf); - if (!new_digest) { + if ((digestbuf = SHA1_File(p->fts_accpath, NULL)) == NULL) { LABEL; - printf("%sSHA-1: %s: %s\n", tab, p->fts_accpath, - strerror(errno)); + printf("%s%s: %s: %s\n", + tab, SHA1KEY, p->fts_accpath, strerror(errno)); + tab = "\t"; + } else { + if (strcmp(s->sha1digest, digestbuf)) { + LABEL; + printf(flavor == F_FREEBSD9 ? + "%s%s expected %s found %s\n" : + "%s%s (0x%s, 0x%s)\n", + tab, SHA1KEY, s->sha1digest, digestbuf); + } tab = "\t"; - } else if (strcmp(new_digest, s->sha1digest)) { + free(digestbuf); + } + } +#endif /* ! NO_SHA1 */ +#ifndef NO_SHA2 + if (s->flags & F_SHA256) { + if ((digestbuf = SHA256_File(p->fts_accpath, NULL)) == NULL) { LABEL; - printf("%sSHA-1 expected %s found %s\n", - tab, s->sha1digest, new_digest); + printf("%s%s: %s: %s\n", + tab, SHA256KEY, p->fts_accpath, strerror(errno)); + tab = "\t"; + } else { + if (strcmp(s->sha256digest, digestbuf)) { + LABEL; + printf(flavor == F_FREEBSD9 ? + "%s%s expected %s found %s\n" : + "%s%s (0x%s, 0x%s)\n", + tab, SHA256KEY, s->sha256digest, digestbuf); + } tab = "\t"; + free(digestbuf); } } -#endif /* SHA1 */ -#ifdef USE_RMD160 - if (s->flags & F_RMD160) { - char *new_digest, buf[41]; - - new_digest = RIPEMD160_File(p->fts_accpath, buf); - if (!new_digest) { +#ifdef SHA384_BLOCK_LENGTH +#if !defined(__DragonFly__) + if (s->flags & F_SHA384) { + if ((digestbuf = SHA384_File(p->fts_accpath, NULL)) == NULL) { LABEL; - printf("%sRIPEMD160: %s: %s\n", tab, - p->fts_accpath, strerror(errno)); + printf("%s%s: %s: %s\n", + tab, SHA384KEY, p->fts_accpath, strerror(errno)); + tab = "\t"; + } else { + if (strcmp(s->sha384digest, digestbuf)) { + LABEL; + printf(flavor == F_FREEBSD9 ? + "%s%s expected %s found %s\n" : + "%s%s (0x%s, 0x%s)\n", + tab, SHA384KEY, s->sha384digest, digestbuf); + } tab = "\t"; - } else if (strcmp(new_digest, s->rmd160digest)) { + free(digestbuf); + } + } +#endif +#endif + if (s->flags & F_SHA512) { + if ((digestbuf = SHA512_File(p->fts_accpath, NULL)) == NULL) { LABEL; - printf("%sRIPEMD160 expected %s found %s\n", - tab, s->rmd160digest, new_digest); + printf("%s%s: %s: %s\n", + tab, SHA512KEY, p->fts_accpath, strerror(errno)); + tab = "\t"; + } else { + if (strcmp(s->sha512digest, digestbuf)) { + LABEL; + printf(flavor == F_FREEBSD9 ? + "%s%s expected %s found %s\n" : + "%s%s (0x%s, 0x%s)\n", + tab, SHA512KEY, s->sha512digest, digestbuf); + } tab = "\t"; + free(digestbuf); } } -#endif /* RMD160 */ - +#endif /* ! NO_SHA2 */ if (s->flags & F_SLINK && strcmp(cp = rlink(p->fts_accpath), s->slink)) { LABEL; - printf("%slink_ref expected %s found %s\n", - tab, s->slink, cp); + printf(flavor == F_FREEBSD9 ? + "%slink ref expected %s found %s" : + "%slink ref (%s, %s", tab, cp, s->slink); + if (uflag) { + if ((unlink(p->fts_accpath) == -1) || + (symlink(s->slink, p->fts_accpath) == -1) ) + printf(", not modified: %s%s\n", + strerror(errno), + flavor == F_FREEBSD9 ? "" : ")"); + else + printf(", modified%s\n", + flavor == F_FREEBSD9 ? "" : ")"); + } else + printf("%s\n", flavor == F_FREEBSD9 ? "" : ")"); } 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 */ -} - -static 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) +rlink(const char *name) { static char lbuf[MAXPATHLEN]; int len; if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1) - err(1, "line %d: %s", lineno, name); + mtree_err("%s: %s", name, strerror(errno)); lbuf[len] = '\0'; return (lbuf); } diff --git a/usr.sbin/mtree/crc.c b/usr.sbin/mtree/crc.c new file mode 100644 index 0000000000..f71bab48dc --- /dev/null +++ b/usr.sbin/mtree/crc.c @@ -0,0 +1,155 @@ +/* @(#)crc.c 8.1 (Berkeley) 6/17/93 */ +/* $NetBSD: crc.c,v 1.9 2012/10/05 00:40:51 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James W. Williams of NASA Goddard Space Flight Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include + +#include +#include +#include + +#include "extern.h" + +static const u_int32_t crctab[] = { + 0x0, + 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, + 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, + 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, + 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, + 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, + 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, + 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, + 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, + 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, + 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, + 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, + 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, + 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, + 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, + 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, + 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, + 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, + 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, + 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, + 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, + 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, + 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, + 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, + 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, + 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, + 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, + 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, + 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, + 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, + 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, + 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, + 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, + 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, + 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +/* + * Compute a POSIX 1003.2 checksum. This routine has been broken out so that + * other programs can use it. It takes a file descriptor to read from and + * locations to store the crc and the number of bytes read. It returns 0 on + * success and 1 on failure. Errno is set on failure. + */ +u_int32_t crc_total = ~0; /* The crc over a number of files. */ + +int +crc(int fd, u_int32_t *cval, u_int32_t *clen) +{ + u_char *p; + int nr; + u_int32_t thecrc, len; + u_int32_t crctot; + u_char buf[16 * 1024]; + +#define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)] + + thecrc = len = crctot = 0; + if (sflag) + crctot = ~crc_total; + while ((nr = read(fd, buf, sizeof(buf))) > 0) + if (sflag) { + for (len += nr, p = buf; nr--; ++p) { + COMPUTE(thecrc, *p); + COMPUTE(crctot, *p); + } + } else { + for (len += nr, p = buf; nr--; ++p) + COMPUTE(thecrc, *p); + } + if (nr < 0) + return 1; + + *clen = len; + + /* Include the length of the file. */ + if (sflag) { + for (; len != 0; len >>= 8) { + COMPUTE(thecrc, len & 0xff); + COMPUTE(crctot, len & 0xff); + } + } else { + for (; len != 0; len >>= 8) + COMPUTE(thecrc, len & 0xff); + } + + *cval = ~thecrc; + if (sflag) + crc_total = ~crctot; + return 0; +} diff --git a/usr.sbin/mtree/create.c b/usr.sbin/mtree/create.c dissimilarity index 60% index b6c0ab44bc..eb3d18b637 100644 --- a/usr.sbin/mtree/create.c +++ b/usr.sbin/mtree/create.c @@ -1,404 +1,478 @@ -/*- - * 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. - * - * @(#)create.c 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.sbin/mtree/create.c,v 1.18.2.3 2001/01/12 19:17:18 phk Exp $ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef MD5 -#include -#endif -#ifdef SHA1 -#include -#endif -#ifdef RMD160 -#include -#endif -#include -#include -#include -#include -#include -#include -#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 clk; - char *argv[2], host[MAXHOSTNAMELEN], dot[] = "."; - int indent = 0; - - time(&clk); - gethostname(host, sizeof(host)); - printf( - "#\t user: %s\n#\tmachine: %s\n#\t tree: %s\n#\t date: %s", - getlogin(), host, fullpath, ctime(&clk)); - - argv[0] = dot; - argv[1] = NULL; - if ((t = fts_open(argv, ftsoptions, dsort)) == NULL) - err(1, "line %d: fts_open", lineno); - 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) - printf("\n"); - if (!nflag) - 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)) - printf("%*s# %s\n", indent, "", p->fts_path); - printf("%*s..\n", indent, ""); - if (!dflag) - 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; - - } - } - fts_close(t); - if (sflag && keys & F_CKSUM) - warnx("%s checksum: %u", fullpath, 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); - - 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) { - if ((pw = getpwuid(p->fts_statp->st_uid)) != NULL) { - output(indent, &offset, "uname=%s", pw->pw_name); - } else { - errx(1, - "line %d: could not get uname for uid=%u", - lineno, 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) { - if ((gr = getgrgid(p->fts_statp->st_gid)) != NULL) { - output(indent, &offset, "gname=%s", gr->gr_name); - } else { - errx(1, - "line %d: could not get gname for gid=%u", - lineno, 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) - output(indent, &offset, "size=%jd", - (uintmax_t)p->fts_statp->st_size); - if (keys & F_TIME) - output(indent, &offset, "time=%ld.%ld", - p->fts_statp->st_mtimespec.tv_sec, - p->fts_statp->st_mtimespec.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, "line %d: %s", lineno, p->fts_accpath); - close(fd); - output(indent, &offset, "cksum=%u", 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, "line %d: %s", lineno, p->fts_accpath); - } else { - 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, "line %d: %s", lineno, p->fts_accpath); - } else { - 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, "line %d: %s", lineno, p->fts_accpath); - } else { - output(indent, &offset, "ripemd160digest=%s", digest); - } - } -#endif /* RMD160 */ - 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); - } - 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, "line %d: %s", lineno, 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) - printf("/set type=dir"); - else - printf("/set type=file"); - if (keys & F_UNAME) { - if ((pw = getpwuid(saveuid)) != NULL) - printf(" uname=%s", pw->pw_name); - else - errx(1, - "line %d: could not get uname for uid=%u", - lineno, saveuid); - } - if (keys & F_UID) - printf(" uid=%lu", (u_long)saveuid); - if (keys & F_GNAME) { - if ((gr = getgrgid(savegid)) != NULL) - printf(" gname=%s", gr->gr_name); - else - errx(1, - "line %d: could not get gname for gid=%u", - lineno, savegid); - } - if (keys & F_GID) - printf(" gid=%lu", (u_long)savegid); - if (keys & F_MODE) - printf(" mode=%#o", savemode); - if (keys & F_NLINK) - printf(" nlink=1"); - if (keys & F_FLAGS) { - fflags = flags_to_string(saveflags); - printf(" flags=%s", fflags); - free(fflags); - } - 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)); -} - -void -output(int indent, int *offset, const char *fmt, ...) -{ - va_list ap; - char buf[1024]; - - va_start(ap, fmt); - vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - if (*offset + strlen(buf) > MAXLINELEN - 3) { - printf(" \\\n%*s", INDENTNAMELEN + indent, ""); - *offset = INDENTNAMELEN + indent; - } - *offset += printf(" %s", buf) + 1; -} +/* @(#)create.c 8.1 (Berkeley) 6/6/93 */ +/* $NetBSD: create.c,v 1.75 2017/12/31 03:04:44 christos Exp $ */ + +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#include + +#if ! HAVE_NBTOOL_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef NO_MD5 +#include +#endif +#ifndef NO_RMD160 +#include +#include +#endif +#ifndef NO_SHA1 +#include +#include +#endif +#ifndef NO_SHA2 +#include +#include +#endif + +#include "extern.h" + +#define INDENTNAMELEN 15 +#define MAXLINELEN 80 + +static gid_t gid; +static uid_t uid; +static mode_t mode; +static u_long flags; + +#if defined(__FreeBSD__) || defined(__DragonFly__) +#define FTS_CONST const +#else +#define FTS_CONST +#endif + +static int dcmp(const FTSENT *FTS_CONST *, const FTSENT *FTS_CONST *); +static void output(FILE *, int, int *, const char *, ...) + __printflike(4, 5); +static int statd(FILE *, FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, + u_long *); +static void statf(FILE *, int, FTSENT *); + +void +cwalk(FILE *fp) +{ + FTS *t; + FTSENT *p; + time_t clocktime; + char host[MAXHOSTNAMELEN + 1]; + const char *user; + char *argv[2]; + char dot[] = "."; + int indent = 0; + + argv[0] = dot; + argv[1] = NULL; + + time(&clocktime); + gethostname(host, sizeof(host)); + host[sizeof(host) - 1] = '\0'; + if ((user = getlogin()) == NULL) { + struct passwd *pw; + user = (pw = getpwuid(getuid())) == NULL ? pw->pw_name : + ""; + } + + if (!nflag) + fprintf(fp, + "#\t user: %s\n#\tmachine: %s\n#\t tree: %s\n" + "#\t date: %s", + user, host, fullpath, ctime(&clocktime)); + + if ((t = fts_open(argv, ftsoptions, dcmp)) == NULL) + mtree_err("fts_open: %s", strerror(errno)); + while ((p = fts_read(t)) != NULL) { + if (jflag) + indent = p->fts_level * 4; + if (check_excludes(p->fts_name, p->fts_path)) { + fts_set(t, p, FTS_SKIP); + continue; + } + if (!find_only(p->fts_path)) { + fts_set(t, p, FTS_SKIP); + continue; + } + switch(p->fts_info) { + case FTS_D: + if (!bflag) + fprintf(fp, "\n"); + if (!nflag) + fprintf(fp, "# %s\n", p->fts_path); + statd(fp, t, p, &uid, &gid, &mode, &flags); + statf(fp, indent, p); + break; + case FTS_DP: + if (p->fts_level > 0) + if (!nflag) + fprintf(fp, "%*s# %s\n", indent, "", + p->fts_path); + if (p->fts_level > 0 || flavor == F_FREEBSD9) { + fprintf(fp, "%*s..\n", indent, ""); + if (!bflag) + fprintf(fp, "\n"); + } + break; + case FTS_DNR: + case FTS_ERR: + case FTS_NS: + mtree_err("%s: %s", + p->fts_path, strerror(p->fts_errno)); + break; + default: + if (!dflag) + statf(fp, indent, p); + break; + + } + } + fts_close(t); + if (sflag && keys & F_CKSUM) + mtree_err("%s checksum: %u", fullpath, crc_total); +} + +static void +dosum(FILE *fp, int indent, FTSENT *p, int *offset, int flag, + char * (*func)(const char *, char *), const char *key) +{ + char *digestbuf; + + if ((keys & flag) == 0) + return; + + digestbuf = (*func)(p->fts_accpath, NULL); + if (digestbuf != NULL) { + output(fp, indent, offset, "%s=%s", key, digestbuf); + free(digestbuf); + return; + } + + if (qflag) { + warn("%s: %s failed", p->fts_path, key); + return; + } + + mtree_err("%s: %s failed: %s", p->fts_path, key, strerror(errno)); +} + +static char * +crcFile(const char *fname, char *dummy __unused) +{ + char *ptr; + uint32_t val, len; + int fd, e; + + if ((fd = open(fname, O_RDONLY)) == -1) + goto out; + + e = crc(fd, &val, &len); + close(fd); + if (e) + goto out; + + if (asprintf(&ptr, "%u", val) < 0) + goto out; + + return ptr; +out: + mtree_err("%s: %s", fname, strerror(errno)); + return NULL; +} + +static void +statf(FILE *fp, int indent, FTSENT *p) +{ + int offset; + const char *name = NULL; + + offset = fprintf(fp, "%*s%s%s", indent, "", + S_ISDIR(p->fts_statp->st_mode) ? "" : " ", vispath(p->fts_name)); + + if (offset > (INDENTNAMELEN + indent)) + offset = MAXLINELEN; + else + offset += fprintf(fp, "%*s", + (INDENTNAMELEN + indent) - offset, ""); + + if (!S_ISREG(p->fts_statp->st_mode) && (flavor == F_NETBSD6 || !dflag)) + output(fp, indent, &offset, "type=%s", + inotype(p->fts_statp->st_mode)); + if (keys & (F_UID | F_UNAME) && p->fts_statp->st_uid != uid) { + if (keys & F_UNAME && + (name = user_from_uid(p->fts_statp->st_uid, 1)) != NULL) + output(fp, indent, &offset, "uname=%s", name); + if (keys & F_UID || (keys & F_UNAME && name == NULL)) + output(fp, indent, &offset, "uid=%u", + p->fts_statp->st_uid); + } + if (keys & (F_GID | F_GNAME) && p->fts_statp->st_gid != gid) { + if (keys & F_GNAME && + (name = group_from_gid(p->fts_statp->st_gid, 1)) != NULL) + output(fp, indent, &offset, "gname=%s", name); + if (keys & F_GID || (keys & F_GNAME && name == NULL)) + output(fp, indent, &offset, "gid=%u", + p->fts_statp->st_gid); + } + if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode) + output(fp, indent, &offset, "mode=%#o", + p->fts_statp->st_mode & MBITS); + if (keys & F_DEV && + (S_ISBLK(p->fts_statp->st_mode) || S_ISCHR(p->fts_statp->st_mode))) + output(fp, indent, &offset, "device=%#jx", + (uintmax_t)p->fts_statp->st_rdev); + if (keys & F_NLINK && p->fts_statp->st_nlink != 1) + output(fp, indent, &offset, "nlink=%ju", + (uintmax_t)p->fts_statp->st_nlink); + if (keys & F_SIZE && + (flavor == F_FREEBSD9 || S_ISREG(p->fts_statp->st_mode))) + output(fp, indent, &offset, "size=%ju", + (uintmax_t)p->fts_statp->st_size); + if (keys & F_TIME) +#if defined(BSD4_4) && !defined(HAVE_NBTOOL_CONFIG_H) + output(fp, indent, &offset, "time=%jd.%09ld", + (intmax_t)p->fts_statp->st_mtimespec.tv_sec, + p->fts_statp->st_mtimespec.tv_nsec); +#else + output(fp, indent, &offset, "time=%jd.%09ld", + (intmax_t)p->fts_statp->st_mtime, (long)0); +#endif + if (S_ISREG(p->fts_statp->st_mode)) { + dosum(fp, indent, p, &offset, F_CKSUM, crcFile, "cksum"); +#ifndef NO_MD5 + dosum(fp, indent, p, &offset, F_MD5, MD5File, MD5KEY); +#endif /* ! NO_MD5 */ +#ifndef NO_RMD160 + dosum(fp, indent, p, &offset, F_RMD160, RIPEMD160_File, RMD160KEY); +#endif /* ! NO_RMD160 */ +#ifndef NO_SHA1 + dosum(fp, indent, p, &offset, F_SHA1, SHA1_File, SHA1KEY); +#endif /* ! NO_SHA1 */ +#ifndef NO_SHA2 + dosum(fp, indent, p, &offset, F_SHA256, SHA256_File, SHA256KEY); +#ifdef SHA384_BLOCK_LENGTH +#if !defined(__DragonFly__) + dosum(fp, indent, p, &offset, F_SHA384, SHA384_File, SHA384KEY); +#endif +#endif + dosum(fp, indent, p, &offset, F_SHA512, SHA512_File, SHA512KEY); +#endif /* ! NO_SHA2 */ + } + if (keys & F_SLINK && + (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) + output(fp, indent, &offset, "link=%s", + vispath(rlink(p->fts_accpath))); +#if HAVE_STRUCT_STAT_ST_FLAGS + if (keys & F_FLAGS && p->fts_statp->st_flags != flags) { + char *str = flags_to_string(p->fts_statp->st_flags, "none"); + output(fp, indent, &offset, "flags=%s", str); + free(str); + } +#endif + putchar('\n'); +} + +/* XXX + * FLAGS2INDEX will fail once the user and system settable bits need more + * than one byte, respectively. + */ +#define FLAGS2INDEX(x) (((x >> 8) & 0x0000ff00) | (x & 0x000000ff)) + +#define MTREE_MAXGID 5000 +#define MTREE_MAXUID 5000 +#define MTREE_MAXMODE (MBITS + 1) +#if HAVE_STRUCT_STAT_ST_FLAGS +#define MTREE_MAXFLAGS (FLAGS2INDEX(CH_MASK) + 1) /* 1808 */ +#else +#define MTREE_MAXFLAGS 1 +#endif +#define MTREE_MAXS 16 + +static int +statd(FILE *fp, FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, + u_long *pflags) +{ + FTSENT *p; + gid_t sgid; + uid_t suid; + mode_t smode; + u_long sflags = 0; + const char *name = NULL; + gid_t savegid; + uid_t saveuid; + mode_t savemode; + u_long saveflags; + u_short maxgid, maxuid, maxmode, maxflags; + u_short g[MTREE_MAXGID], u[MTREE_MAXUID], + m[MTREE_MAXMODE], f[MTREE_MAXFLAGS]; + static int first = 1; + + savegid = *pgid; + saveuid = *puid; + savemode = *pmode; + saveflags = *pflags; + if ((p = fts_children(t, 0)) == NULL) { + if (errno) + mtree_err("%s: %s", RP(parent), strerror(errno)); + return (1); + } + + memset(g, 0, sizeof(g)); + memset(u, 0, sizeof(u)); + memset(m, 0, sizeof(m)); + memset(f, 0, sizeof(f)); + + maxuid = maxgid = maxmode = maxflags = 0; + for (; p; p = p->fts_link) { + if (flavor == F_NETBSD6 || !dflag || + (dflag && S_ISDIR(p->fts_statp->st_mode))) { + smode = p->fts_statp->st_mode & MBITS; + if (smode < MTREE_MAXMODE && ++m[smode] > maxmode) { + savemode = smode; + maxmode = m[smode]; + } + sgid = p->fts_statp->st_gid; + if (sgid < MTREE_MAXGID && ++g[sgid] > maxgid) { + savegid = sgid; + maxgid = g[sgid]; + } + suid = p->fts_statp->st_uid; + if (suid < MTREE_MAXUID && ++u[suid] > maxuid) { + saveuid = suid; + maxuid = u[suid]; + } + +#if HAVE_STRUCT_STAT_ST_FLAGS + sflags = FLAGS2INDEX(p->fts_statp->st_flags); + if (sflags < MTREE_MAXFLAGS && ++f[sflags] > maxflags) { + saveflags = p->fts_statp->st_flags; + maxflags = f[sflags]; + } +#endif + } + } + /* + * If the /set record is the same as the last one we do not need to + * output a new one. So first we check to see if anything changed. + * Note that we always output a /set record for the first directory. + */ + if (((keys & (F_UNAME | F_UID)) && (*puid != saveuid)) || + ((keys & (F_GNAME | F_GID)) && (*pgid != savegid)) || + ((keys & F_MODE) && (*pmode != savemode)) || + ((keys & F_FLAGS) && (*pflags != saveflags)) || + first) { + first = 0; + if (flavor != F_NETBSD6 && dflag) + fprintf(fp, "/set type=dir"); + else + fprintf(fp, "/set type=file"); + if (keys & (F_UID | F_UNAME)) { + if (keys & F_UNAME && + (name = user_from_uid(saveuid, 1)) != NULL) + fprintf(fp, " uname=%s", name); + if (keys & F_UID || (keys & F_UNAME && name == NULL)) + fprintf(fp, " uid=%lu", (u_long)saveuid); + } + if (keys & (F_GID | F_GNAME)) { + if (keys & F_GNAME && + (name = group_from_gid(savegid, 1)) != NULL) + fprintf(fp, " gname=%s", name); + if (keys & F_GID || (keys & F_GNAME && name == NULL)) + fprintf(fp, " gid=%lu", (u_long)savegid); + } + if (keys & F_MODE) + fprintf(fp, " mode=%#lo", (u_long)savemode); + if (keys & F_NLINK) + fprintf(fp, " nlink=1"); + if (keys & F_FLAGS) { + char *str = flags_to_string(saveflags, "none"); + fprintf(fp, " flags=%s", str); + free(str); + } + fprintf(fp, "\n"); + *puid = saveuid; + *pgid = savegid; + *pmode = savemode; + *pflags = saveflags; + } + return (0); +} + +/* + * dcmp -- + * used as a comparison function passed to fts_open() to control + * the order in which fts_read() returns results. We make + * directories sort after non-directories, but otherwise sort in + * strcmp() order. + * + * Keep this in sync with nodecmp() in spec.c. + */ +static int +dcmp(const FTSENT *FTS_CONST *a, const FTSENT *FTS_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)); +} + +void +output(FILE *fp, int indent, int *offset, const char *fmt, ...) +{ + va_list ap; + char buf[1024]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (*offset + strlen(buf) > MAXLINELEN - 3) { + fprintf(fp, " \\\n%*s", INDENTNAMELEN + indent, ""); + *offset = INDENTNAMELEN + indent; + } + *offset += fprintf(fp, " %s", buf) + 1; +} diff --git a/usr.sbin/mtree/excludes.c b/usr.sbin/mtree/excludes.c index ffcf3c32fa..9e889d494c 100644 --- a/usr.sbin/mtree/excludes.c +++ b/usr.sbin/mtree/excludes.c @@ -1,3 +1,5 @@ +/* $NetBSD: excludes.c,v 1.13 2004/06/20 22:20:18 jmc Exp $ */ + /* * Copyright 2000 Massachusetts Institute of Technology * @@ -12,7 +14,7 @@ * 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 @@ -25,25 +27,27 @@ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD: src/usr.sbin/mtree/excludes.c,v 1.1.2.4 2001/01/12 19:17:18 phk Exp $ */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + #include -#include /* XXX for mtree.h */ #include -#include #include -#include #include #include +#include +#include +#include -#include "mtree.h" /* XXX for extern.h */ #include "extern.h" + /* - * We're assuming that there won't be a whole lot of excludes, + * We're assuming that there won't be a whole lot of excludes, * so it's OK to use a stupid algorithm. */ struct exclude { @@ -53,6 +57,7 @@ struct exclude { }; static LIST_HEAD(, exclude) excludes; + void init_excludes(void) { @@ -64,28 +69,24 @@ void read_excludes_file(const char *name) { FILE *fp; - char *line, *str; + char *line; struct exclude *e; - size_t len; fp = fopen(name, "r"); - if (fp == NULL) + if (fp == 0) err(1, "%s", name); - while ((line = fgetln(fp, &len)) != NULL) { - if (line[len - 1] == '\n') - len--; - if (len == 0) + while ((line = fparseln(fp, NULL, NULL, NULL, + FPARSELN_UNESCCOMM | FPARSELN_UNESCCONT | FPARSELN_UNESCESC)) + != NULL) { + if (line[0] == '\0') continue; - str = malloc(len + 1); - e = malloc(sizeof *e); - if (str == NULL || e == NULL) - errx(1, "memory allocation error"); - e->glob = str; - memcpy(str, line, len); - str[len] = '\0'; - if (strchr(str, '/')) + if ((e = malloc(sizeof *e)) == NULL) + mtree_err("memory allocation error"); + + e->glob = line; + if (strchr(e->glob, '/') != NULL) e->pathname = 1; else e->pathname = 0; @@ -102,10 +103,13 @@ check_excludes(const char *fname, const char *path) /* 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; + e = LIST_FIRST(&excludes); + while (e) { + if ((e->pathname && MATCH(e->glob, path)) + || MATCH(e->glob, fname)) { + return (1); + } + e = LIST_NEXT(e, link); } - return 0; + return (0); } diff --git a/usr.sbin/mtree/extern.h b/usr.sbin/mtree/extern.h index 40b5fabcfb..78feacbafa 100644 --- a/usr.sbin/mtree/extern.h +++ b/usr.sbin/mtree/extern.h @@ -1,3 +1,5 @@ +/* $NetBSD: extern.h,v 1.39 2014/04/24 17:22:41 christos Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -27,29 +29,65 @@ * SUCH DAMAGE. * * @(#)extern.h 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.sbin/mtree/extern.h,v 1.3.2.2 2000/06/28 02:33:17 joe Exp $ */ -#ifdef MAXPATHLEN -extern char fullpath[MAXPATHLEN]; +#include "mtree.h" + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#define HAVE_STRUCT_STAT_ST_FLAGS 1 #endif -extern int dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag; -extern int ftsoptions; -extern int lineno; -extern uint32_t crc_total; -extern u_int keys; + +#include +#include +#include +#include -int compare(NODE *, FTSENT *); -int crc(int, uint32_t *, off_t *); -void cwalk(void); -char *flags_to_string(u_long); +#if HAVE_NETDB_H +/* For MAXHOSTNAMELEN on some platforms. */ +#include +#endif + +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 256 +#endif -const char *inotype(u_int); -u_int parsekey(char *, int *); -char *rlink(char *); -NODE *spec(void); -int verify(void); +enum flavor { + F_MTREE, + F_FREEBSD9, + F_NETBSD6 +}; +void addtag(slist_t *, char *); int check_excludes(const char *, const char *); +int compare(NODE *, FTSENT *); +int crc(int, u_int32_t *, u_int32_t *); +void cwalk(FILE *); +void dump_nodes(FILE *, const char *, NODE *, int); void init_excludes(void); +int matchtags(NODE *); +__dead2 __printflike(1,2) void mtree_err(const char *, ...); +const char *nodetype(u_int); +u_int parsekey(const char *, int *); +void parsetags(slist_t *, char *); +u_int parsetype(const char *); void read_excludes_file(const char *); +const char *rlink(const char *); +int verify(FILE *); +void load_only(const char *fname); +bool find_only(const char *path); + +char *flags_to_string(unsigned long, const char *); +int string_to_flags(char **, unsigned long *, unsigned long *); + +extern int bflag, dflag, eflag, iflag, jflag, lflag, mflag, + nflag, qflag, rflag, sflag, tflag, uflag; +extern int mtree_Mflag, mtree_Sflag, mtree_Wflag; +extern size_t mtree_lineno; +extern enum flavor flavor; +extern u_int32_t crc_total; +extern int ftsoptions, keys; +extern char fullpath[]; +extern slist_t includetags, excludetags; + diff --git a/usr.sbin/mtree/getid.c b/usr.sbin/mtree/getid.c new file mode 100644 index 0000000000..0f5322b53d --- /dev/null +++ b/usr.sbin/mtree/getid.c @@ -0,0 +1,443 @@ +/* $NetBSD: getid.c,v 1.10 2014/10/27 21:46:45 christos Exp $ */ +/* from: NetBSD: getpwent.c,v 1.48 2000/10/03 03:22:26 enami Exp */ +/* from: NetBSD: getgrent.c,v 1.41 2002/01/12 23:51:30 lukem Exp */ + +/* + * Copyright (c) 1987, 1988, 1989, 1993, 1994, 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn of Wasabi Systems. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +static struct group * gi_getgrnam(const char *); +static struct group * gi_getgrgid(gid_t); +static int gi_setgroupent(int); +static void gi_endgrent(void); +static int grstart(void); +static int grscan(int, gid_t, const char *); +static int grmatchline(int, gid_t, const char *); + +static struct passwd * gi_getpwnam(const char *); +static struct passwd * gi_getpwuid(uid_t); +static int gi_setpassent(int); +static void gi_endpwent(void); +static int pwstart(void); +static int pwscan(int, uid_t, const char *); +static int pwmatchline(int, uid_t, const char *); + +#define MAXGRP 200 +#define MAXLINELENGTH 1024 + +static FILE *_gr_fp; +static struct group _gr_group; +static int _gr_stayopen; +static int _gr_filesdone; +static FILE *_pw_fp; +static struct passwd _pw_passwd; /* password structure */ +static int _pw_stayopen; /* keep fd's open */ +static int _pw_filesdone; + +static char grfile[MAXPATHLEN]; +static char pwfile[MAXPATHLEN]; + +static char *members[MAXGRP]; +static char grline[MAXLINELENGTH]; +static char pwline[MAXLINELENGTH]; + +int +setup_getid(const char *dir) +{ + if (dir == NULL) + return (0); + + /* close existing databases */ + gi_endgrent(); + gi_endpwent(); + + /* build paths to new databases */ + snprintf(grfile, sizeof(grfile), "%s/group", dir); + snprintf(pwfile, sizeof(pwfile), "%s/master.passwd", dir); + + /* try to open new databases */ + if (!grstart() || !pwstart()) + return (0); + + /* switch pwcache(3) lookup functions */ + if (pwcache_groupdb(gi_setgroupent, gi_endgrent, + gi_getgrnam, gi_getgrgid) == -1 + || pwcache_userdb(gi_setpassent, gi_endpwent, + gi_getpwnam, gi_getpwuid) == -1) + return (0); + + return (1); +} + + +/* + * group lookup functions + */ + +static struct group * +gi_getgrnam(const char *name) +{ + int rval; + + if (!grstart()) + return NULL; + rval = grscan(1, 0, name); + if (!_gr_stayopen) + endgrent(); + return (rval) ? &_gr_group : NULL; +} + +static struct group * +gi_getgrgid(gid_t gid) +{ + int rval; + + if (!grstart()) + return NULL; + rval = grscan(1, gid, NULL); + if (!_gr_stayopen) + endgrent(); + return (rval) ? &_gr_group : NULL; +} + +static int +gi_setgroupent(int stayopen) +{ + + if (!grstart()) + return 0; + _gr_stayopen = stayopen; + return 1; +} + +static void +gi_endgrent(void) +{ + + _gr_filesdone = 0; + if (_gr_fp) { + (void)fclose(_gr_fp); + _gr_fp = NULL; + } +} + +static int +grstart(void) +{ + + _gr_filesdone = 0; + if (_gr_fp) { + rewind(_gr_fp); + return 1; + } + if (grfile[0] == '\0') /* sanity check */ + return 0; + + _gr_fp = fopen(grfile, "r"); + if (_gr_fp != NULL) + return 1; + warn("Can't open `%s'", grfile); + return 0; +} + + +static int +grscan(int search, gid_t gid, const char *name) +{ + + if (_gr_filesdone) + return 0; + for (;;) { + if (!fgets(grline, sizeof(grline), _gr_fp)) { + if (!search) + _gr_filesdone = 1; + return 0; + } + /* skip lines that are too big */ + if (!strchr(grline, '\n')) { + int ch; + + while ((ch = getc(_gr_fp)) != '\n' && ch != EOF) + ; + continue; + } + /* skip comments */ + if (grline[0] == '#') + continue; + if (grmatchline(search, gid, name)) + return 1; + } + /* NOTREACHED */ +} + +static int +grmatchline(int search, gid_t gid, const char *name) +{ + unsigned long id; + char **m; + char *cp, *bp, *ep; + + /* name may be NULL if search is nonzero */ + + bp = grline; + memset(&_gr_group, 0, sizeof(_gr_group)); + _gr_group.gr_name = strsep(&bp, ":\n"); + if (search && name && strcmp(_gr_group.gr_name, name)) + return 0; + _gr_group.gr_passwd = strsep(&bp, ":\n"); + if (!(cp = strsep(&bp, ":\n"))) + return 0; + id = strtoul(cp, &ep, 10); + if (id > GID_MAX || *ep != '\0') + return 0; + _gr_group.gr_gid = (gid_t)id; + if (search && name == NULL && _gr_group.gr_gid != gid) + return 0; + cp = NULL; + if (bp == NULL) + return 0; + for (_gr_group.gr_mem = m = members;; bp++) { + if (m == &members[MAXGRP - 1]) + break; + if (*bp == ',') { + if (cp) { + *bp = '\0'; + *m++ = cp; + cp = NULL; + } + } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') { + if (cp) { + *bp = '\0'; + *m++ = cp; + } + break; + } else if (cp == NULL) + cp = bp; + } + *m = NULL; + return 1; +} + + +/* + * user lookup functions + */ + +static struct passwd * +gi_getpwnam(const char *name) +{ + int rval; + + if (!pwstart()) + return NULL; + rval = pwscan(1, 0, name); + if (!_pw_stayopen) + endpwent(); + return (rval) ? &_pw_passwd : NULL; +} + +static struct passwd * +gi_getpwuid(uid_t uid) +{ + int rval; + + if (!pwstart()) + return NULL; + rval = pwscan(1, uid, NULL); + if (!_pw_stayopen) + endpwent(); + return (rval) ? &_pw_passwd : NULL; +} + +static int +gi_setpassent(int stayopen) +{ + + if (!pwstart()) + return 0; + _pw_stayopen = stayopen; + return 1; +} + +static void +gi_endpwent(void) +{ + + _pw_filesdone = 0; + if (_pw_fp) { + (void)fclose(_pw_fp); + _pw_fp = NULL; + } +} + +static int +pwstart(void) +{ + + _pw_filesdone = 0; + if (_pw_fp) { + rewind(_pw_fp); + return 1; + } + if (pwfile[0] == '\0') /* sanity check */ + return 0; + _pw_fp = fopen(pwfile, "r"); + if (_pw_fp != NULL) + return 1; + warn("Can't open `%s'", pwfile); + return 0; +} + + +static int +pwscan(int search, uid_t uid, const char *name) +{ + + if (_pw_filesdone) + return 0; + for (;;) { + if (!fgets(pwline, sizeof(pwline), _pw_fp)) { + if (!search) + _pw_filesdone = 1; + return 0; + } + /* skip lines that are too big */ + if (!strchr(pwline, '\n')) { + int ch; + + while ((ch = getc(_pw_fp)) != '\n' && ch != EOF) + ; + continue; + } + /* skip comments */ + if (pwline[0] == '#') + continue; + if (pwmatchline(search, uid, name)) + return 1; + } + /* NOTREACHED */ +} + +static int +pwmatchline(int search, uid_t uid, const char *name) +{ + unsigned long id; + char *cp, *bp, *ep; + + /* name may be NULL if search is nonzero */ + + bp = pwline; + memset(&_pw_passwd, 0, sizeof(_pw_passwd)); + _pw_passwd.pw_name = strsep(&bp, ":\n"); /* name */ + if (search && name && strcmp(_pw_passwd.pw_name, name)) + return 0; + + _pw_passwd.pw_passwd = strsep(&bp, ":\n"); /* passwd */ + + if (!(cp = strsep(&bp, ":\n"))) /* uid */ + return 0; + id = strtoul(cp, &ep, 10); + if (id > UID_MAX || *ep != '\0') + return 0; + _pw_passwd.pw_uid = (uid_t)id; + if (search && name == NULL && _pw_passwd.pw_uid != uid) + return 0; + + if (!(cp = strsep(&bp, ":\n"))) /* gid */ + return 0; + id = strtoul(cp, &ep, 10); + if (id > GID_MAX || *ep != '\0') + return 0; + _pw_passwd.pw_gid = (gid_t)id; + + if (!(ep = strsep(&bp, ":"))) /* class */ + return 0; + if (!(ep = strsep(&bp, ":"))) /* change */ + return 0; + if (!(ep = strsep(&bp, ":"))) /* expire */ + return 0; + + if (!(_pw_passwd.pw_gecos = strsep(&bp, ":\n"))) /* gecos */ + return 0; + if (!(_pw_passwd.pw_dir = strsep(&bp, ":\n"))) /* directory */ + return 0; + if (!(_pw_passwd.pw_shell = strsep(&bp, ":\n"))) /* shell */ + return 0; + + if (strchr(bp, ':') != NULL) + return 0; + + return 1; +} + diff --git a/usr.sbin/mtree/misc.c b/usr.sbin/mtree/misc.c index 7743f39633..4c450b7aa0 100644 --- a/usr.sbin/mtree/misc.c +++ b/usr.sbin/mtree/misc.c @@ -1,3 +1,5 @@ +/* $NetBSD: misc.c,v 1.34 2012/12/20 19:09:25 christos Exp $ */ + /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. @@ -26,70 +28,123 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * @(#)misc.c 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.sbin/mtree/misc.c,v 1.8.2.1 2000/06/28 02:33:17 joe Exp $ - * $DragonFly: src/usr.sbin/mtree/misc.c,v 1.5 2004/03/15 16:24:22 dillon Exp $ + * @(#)misc.c 8.1 (Berkeley) 6/6/93 */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + #include #include -#include -#include + +#include #include -#include -#include "mtree.h" +#include +#include + #include "extern.h" +enum flavor flavor = F_MTREE; + typedef struct _key { - const char *name; /* key name */ - u_int val; /* value */ + const char *name; /* key name */ + u_int val; /* value */ #define NEEDVALUE 0x01 - u_int flags; + u_int flags; } KEY; -/* NB: the following table must be sorted lexically. */ +/* NB: the following tables must be sorted lexically. */ static KEY keylist[] = { {"cksum", F_CKSUM, NEEDVALUE}, + {"device", F_DEV, NEEDVALUE}, {"flags", F_FLAGS, NEEDVALUE}, {"gid", F_GID, NEEDVALUE}, {"gname", F_GNAME, NEEDVALUE}, {"ignore", F_IGN, 0}, {"link", F_SLINK, NEEDVALUE}, -#ifdef MD5 + {"md5", F_MD5, NEEDVALUE}, {"md5digest", F_MD5, NEEDVALUE}, -#endif {"mode", F_MODE, NEEDVALUE}, {"nlink", F_NLINK, NEEDVALUE}, {"nochange", F_NOCHANGE, 0}, -#ifdef RMD160 + {"optional", F_OPT, 0}, {"ripemd160digest", F_RMD160, NEEDVALUE}, -#endif -#ifdef SHA1 + {"rmd160", F_RMD160, NEEDVALUE}, + {"rmd160digest",F_RMD160, NEEDVALUE}, + {"sha1", F_SHA1, NEEDVALUE}, {"sha1digest", F_SHA1, NEEDVALUE}, -#endif + {"sha256", F_SHA256, NEEDVALUE}, + {"sha256digest",F_SHA256, NEEDVALUE}, + {"sha384", F_SHA384, NEEDVALUE}, + {"sha384digest",F_SHA384, NEEDVALUE}, + {"sha512", F_SHA512, NEEDVALUE}, + {"sha512digest",F_SHA512, NEEDVALUE}, {"size", F_SIZE, NEEDVALUE}, + {"tags", F_TAGS, NEEDVALUE}, {"time", F_TIME, NEEDVALUE}, {"type", F_TYPE, NEEDVALUE}, {"uid", F_UID, NEEDVALUE}, - {"uname", F_UNAME, NEEDVALUE}, + {"uname", F_UNAME, NEEDVALUE} }; +static KEY typelist[] = { + {"block", F_BLOCK, 0}, + {"char", F_CHAR, 0}, + {"dir", F_DIR, 0}, +#ifdef S_IFDOOR + {"door", F_DOOR, 0}, +#endif + {"fifo", F_FIFO, 0}, + {"file", F_FILE, 0}, + {"link", F_LINK, 0}, + {"socket", F_SOCK, 0}, +}; + +slist_t excludetags, includetags; +int keys = KEYDEFAULT; + + int keycompare(const void *, const void *); u_int -parsekey(char *name, int *needvaluep) +parsekey(const char *name, int *needvaluep) { + static int allbits; KEY *k, tmp; + if (allbits == 0) { + size_t i; + + for (i = 0; i < sizeof(keylist) / sizeof(KEY); i++) + allbits |= keylist[i].val; + } tmp.name = name; + if (strcmp(name, "all") == 0) + return (allbits); k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY), sizeof(KEY), keycompare); if (k == NULL) - errx(1, "line %d: unknown keyword %s", lineno, name); + mtree_err("unknown keyword `%s'", name); if (needvaluep) *needvaluep = k->flags & NEEDVALUE ? 1 : 0; + + return (k->val); +} + +u_int +parsetype(const char *name) +{ + KEY *k, tmp; + + tmp.name = name; + k = (KEY *)bsearch(&tmp, typelist, sizeof(typelist) / sizeof(KEY), + sizeof(KEY), keycompare); + if (k == NULL) + mtree_err("unknown file type `%s'", name); + return (k->val); } @@ -100,18 +155,153 @@ keycompare(const void *a, const void *b) return (strcmp(((const KEY *)a)->name, ((const KEY *)b)->name)); } -char * -flags_to_string(u_long fflags) +void +mtree_err(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + if (mtree_lineno) + warnx("failed at line %lu of the specification", + (u_long) mtree_lineno); + exit(1); + /* NOTREACHED */ +} + +void +addtag(slist_t *list, char *elem) +{ + +#define TAG_CHUNK 20 + + if ((list->count % TAG_CHUNK) == 0) { + char **new; + + new = (char **)realloc(list->list, (list->count + TAG_CHUNK) + * sizeof(char *)); + if (new == NULL) + mtree_err("memory allocation error"); + list->list = new; + } + list->list[list->count] = elem; + list->count++; +} + +void +parsetags(slist_t *list, char *args) { - char *string; + char *p, *e; + int len; - string = fflagstostr(fflags); - if (string != NULL && *string == '\0') { - free(string); - string = strdup("none"); + if (args == NULL) { + addtag(list, NULL); + return; + } + while ((p = strsep(&args, ",")) != NULL) { + if (*p == '\0') + continue; + len = strlen(p) + 3; /* "," + p + ",\0" */ + if ((e = malloc(len)) == NULL) + mtree_err("memory allocation error"); + snprintf(e, len, ",%s,", p); + addtag(list, e); } - if (string == NULL) - err(1, NULL); +} - return string; +/* + * matchtags + * returns 0 if there's a match from the exclude list in the node's tags, + * or there's an include list and no match. + * return 1 otherwise. + */ +int +matchtags(NODE *node) +{ + int i; + + if (node->tags) { + for (i = 0; i < excludetags.count; i++) + if (strstr(node->tags, excludetags.list[i])) + break; + if (i < excludetags.count) + return (0); + + for (i = 0; i < includetags.count; i++) + if (strstr(node->tags, includetags.list[i])) + break; + if (i > 0 && i == includetags.count) + return (0); + } else if (includetags.count > 0) { + return (0); + } + return (1); +} + +u_int +nodetoino(u_int type) +{ + + switch (type) { + case F_BLOCK: + return S_IFBLK; + case F_CHAR: + return S_IFCHR; + case F_DIR: + return S_IFDIR; + case F_FIFO: + return S_IFIFO; + case F_FILE: + return S_IFREG; + case F_LINK: + return S_IFLNK; +#ifdef S_IFSOCK + case F_SOCK: + return S_IFSOCK; +#endif + default: + printf("unknown type %d", type); + abort(); + } + /* NOTREACHED */ +} + +const char * +nodetype(u_int type) +{ + + return (inotype(nodetoino(type))); +} + + +const char * +inotype(u_int type) +{ + + switch (type & S_IFMT) { + case S_IFBLK: + return ("block"); + case S_IFCHR: + return ("char"); + case S_IFDIR: + return ("dir"); + case S_IFIFO: + return ("fifo"); + case S_IFREG: + return ("file"); + case S_IFLNK: + return ("link"); +#ifdef S_IFSOCK + case S_IFSOCK: + return ("socket"); +#endif +#ifdef S_IFDOOR + case S_IFDOOR: + return ("door"); +#endif + default: + return ("unknown"); + } + /* NOTREACHED */ } diff --git a/usr.sbin/mtree/mtree.8 b/usr.sbin/mtree/mtree.8 index d18c06fb7a..c38aa44df8 100644 --- a/usr.sbin/mtree/mtree.8 +++ b/usr.sbin/mtree/mtree.8 @@ -1,3 +1,5 @@ +.\" $NetBSD: mtree.8,v 1.72 2017/02/22 14:15:15 abhinav Exp $ +.\" .\" Copyright (c) 1989, 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" @@ -25,10 +27,36 @@ .\" 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: src/usr.sbin/mtree/mtree.8,v 1.16.2.11 2003/03/11 22:31:29 trhodes Exp $ +.\" Copyright (c) 2001-2004 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Luke Mewburn of Wasabi Systems. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd February 26, 1999 +.\" @(#)mtree.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd July 22, 2018 .Dt MTREE 8 .Os .Sh NAME @@ -36,106 +64,305 @@ .Nd map a directory hierarchy .Sh SYNOPSIS .Nm -.Op Fl LPUcdeinqrux -.Bk -words +.Op Fl bCcDdejLlMnPqrStUuWx +.Op Fl i | Fl m +.Op Fl E Ar tags +.Op Fl F Ar flavor .Op Fl f Ar spec -.Ek -.Bk -words +.Op Fl I Ar tags .Op Fl K Ar keywords -.Ek -.Bk -words .Op Fl k Ar keywords -.Ek -.Bk -words +.Op Fl N Ar dbdir +.Op Fl O Ar onlyfile .Op Fl p Ar path -.Ek -.Bk -words +.Op Fl R Ar keywords .Op Fl s Ar seed -.Ek -.Bk -words -.Op Fl X Ar exclude-list -.Ek +.Op Fl X Ar exclude-file .Sh DESCRIPTION The .Nm -utility compares the file hierarchy rooted in the current directory against a -specification read from the standard input. +utility compares a file hierarchy against a specification, +creates a specification for a file hierarchy, or modifies +a specification. +.Pp +The default action, if not overridden by command line options, +is to compare the file hierarchy rooted in the current directory +against a specification read from the standard input. Messages are written to the standard output for any files whose -characteristics do not match the specifications, or which are +characteristics do not match the specification, or which are missing from either the file hierarchy or the specification. .Pp The options are as follows: -.Bl -tag -width flag -.It Fl L -Follow all symbolic links in the file hierarchy. -.It Fl P -Don't follow symbolic links in the file hierarchy, instead consider -the symbolic link itself in any comparisons. This is the default. -.It Fl U -Modify the owner, group and permissions 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. +.Bl -tag -width Xxxexcludexfilexx +.It Fl b +Suppress blank lines before entering and after exiting directories. +.It Fl C +Convert a specification into +a format that's easier to parse with various tools. +The input specification is read from standard input or +from the file given by +.Fl f Ar spec . +In the output, each file or directory is represented using a single line +(which might be very long). +The full path name +(beginning with +.Dq \&./ ) +is always printed as the first field; +.Fl K , +.Fl k , +and +.Fl R +can be used to control which other keywords are printed; +.Fl E +and +.Fl I +can be used to control which files are printed; +and the +.Fl S +option can be used to sort the output. .It Fl c -Print a specification for the file hierarchy to the standard output. +Print a specification for the file hierarchy originating at +the current working directory (or the directory provided by +.Fl p Ar path ) +to the standard output. +The output is in a style using relative path names. +.It Fl D +As per +.Fl C , +except that the path name is always printed as the last field instead of +the first. .It Fl d Ignore everything except directory type files. +.It Fl E Ar tags +Add the comma separated tags to the +.Dq exclusion +list. +Non-directories with tags which are in the exclusion list are not printed with +.Fl C +and +.Fl D . .It Fl e Don't complain about files that are in the file hierarchy, but not in the specification. +.It Fl F Ar flavor +Set the compatibility flavor of the +.Nm +utility. +The +.Ar flavor +can be one of +.Sy mtree , +.Sy freebsd9 , +or +.Sy netbsd6 . +The default is +.Sy mtree . +The +.Sy freebsd9 +and +.Sy netbsd6 +flavors attempt to preserve output compatibility and command line option +backward compatibility with +.Fx 9.0 +and +.Nx 6.0 +respectively. +.It Fl f Ar spec +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 reminiscent 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 I Ar tags +Add the comma separated tags to the +.Dq inclusion +list. +Non-directories with tags which are in the inclusion list are printed with +.Fl C +and +.Fl D . +If no inclusion list is provided, the default is to display all files. .It Fl i +If specified, set the schg and/or sappnd flags. +.It Fl j Indent the output 4 spaces each time a directory level is descended when -create a specification with the +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. +This is the equivalent of the +.Fl i +option in the +.Fx +version of +.Nm . +.It Fl K Ar keywords +Add the specified (whitespace or comma separated) keywords to the current +set of keywords. +If +.Ql all +is specified, add all of the other keywords. +.It Fl k Ar keywords +Use the +.Sy type +keyword plus the specified (whitespace or comma separated) +keywords instead of the current set of keywords. +If +.Ql all +is specified, use all of the other keywords. +If the +.Sy type +keyword is not desired, suppress it with +.Fl R Ar type . +.It Fl L +Follow all symbolic links in the file hierarchy. +.It Fl l +Do +.Dq loose +permissions checks, in which more stringent permissions +will match less stringent ones. +For example, a file marked mode 0444 +will pass a check for mode 0644. +.Dq Loose +checks apply only to read, write and execute permissions -- in +particular, if other bits like the sticky bit or suid/sgid bits are +set either in the specification or the file, exact checking will be +performed. +This option may not be set at the same time as the +.Fl U +or +.Fl u +option. +.It Fl M +Permit merging of specification entries with different types, +with the last entry taking precedence. +.It Fl m +If the schg and/or sappnd flags are specified, reset these flags. +Note that this is only possible with securelevel less than 1 (i.e., +in single user mode or while the system is running in insecure +mode). +See +.Xr init 8 +for information on security levels. .It Fl n -Do not emit pathname comments when creating a specification. Normally +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 N Ar dbdir +Use the user database text file +.Pa master.passwd +and group database text file +.Pa group +from +.Ar dbdir , +rather than using the results from the system's +.Xr getpwnam 3 +and +.Xr getgrnam 3 +(and related) library calls. +.It Fl O Ar onlypaths +Only include files included in this list of pathnames. +.It Fl P +Don't follow symbolic links in the file hierarchy, instead consider +the symbolic link itself in any comparisons. +This is the default. +.It Fl p Ar path +Use the file hierarchy rooted in +.Ar path , +instead of the current directory. .It Fl q -Quiet mode. Do not complain when a +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 Ar keywords +Remove the specified (whitespace or comma separated) keywords from the current +set of keywords. +If +.Ql all +is specified, remove all of the other keywords. .It Fl r Remove any files in the file hierarchy that are not described in the specification. -.It Fl u -Same as -.Fl U -except a status of 2 is returned if the file hierarchy did not match -the specification. -.It Fl x -Don't 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. -.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. +Repeating the flag more than once will attempt to reset all the +file flags via +.Xr lchflags 2 +before attempting to remove the file in case the file was immutable. +.It Fl S +When reading a specification into an internal data structure, +sort the entries. +Sorting will affect the order of the output produced by the +.Fl C +or +.Fl D +options, and will also affect the order in which +missing entries are created or reported when a directory tree is checked +against a specification. +.Pp +The sort order is the same as that used by the +.Fl c +option, which is that entries within the same directory are +sorted in the order used by +.Xr strcmp 3 , +except that entries for subdirectories sort after other entries. +By default, if the +.Fl S +option is not used, entries within the same directory are collected +together (separated from entries for other directories), but not sorted. .It Fl s Ar seed Display a single checksum to the standard error output that represents all of the files for which the keyword -.Cm cksum +.Sy cksum was specified. The checksum is seeded with the specified value. -.It Fl X Ar exclude-list +.It Fl t +Modify the modified time of existing files, the device type of devices, and +symbolic link targets, to match the specification. +.It Fl U +Same as +.Fl u +except that a mismatch is not considered to be an error if it was corrected. +.It Fl u +Modify the owner, group, permissions, and flags of existing files, +the device type of devices, and symbolic link targets, +to match the specification. +Create any missing directories, devices or symbolic links. +User, group, and permissions must all be specified for missing directories +to be created. +Note that unless the +.Fl i +option is given, the schg and sappnd flags will not be set, even if +specified. +If +.Fl m +is given, these flags will be reset. +Exit with a status of 0 on success, +2 if the file hierarchy did not match the specification, and +1 if any other error occurred. +.It Fl W +Don't attempt to set various file attributes such as the +ownership, mode, flags, or time +when creating new directories or changing existing entries. +This option will be most useful when used in conjunction with +.Fl U +or +.Fl u . +.It Fl X Ar exclude-file The specified file contains .Xr fnmatch 3 patterns matching files to be excluded from @@ -144,145 +371,311 @@ 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 +it will be matched against basenames only. +Comments are permitted in the .Ar exclude-list file. +.It Fl x +Don't descend below mount points in the file hierarchy. .El .Pp -Specifications are mostly composed of ``keywords'', i.e. strings -that specify values relating to files. +Specifications are mostly composed of +.Dq 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 +.Bl -tag -width sha384digestxx +.It Sy 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 +.It Sy device +The device number to use for +.Sy block +or +.Sy char +file types. +The argument must be one of the following forms: +.Bl -tag -width 4n +.It Ar format , Ns Ar major , Ns Ar minor +A device with +.Ar major +and +.Ar minor +fields, for an operating system specified with +.Ar format . +See below for valid formats. +.It Ar format , Ns Ar major , Ns Ar unit , Ns Ar subunit +A device with +.Ar major , +.Ar unit , +and +.Ar subunit +fields, for an operating system specified with +.Ar format . +(Currently this is only supported by the +.Sy bsdos +format.) +.It Ar number +Opaque number (as stored on the file system). +.El +.Pp +The following values for +.Ar format +are recognized: +.Sy native , +.Sy 386bsd , +.Sy 4bsd , +.Sy bsdos , +.Sy freebsd , +.Sy hpux , +.Sy isc , +.Sy linux , +.Sy netbsd , +.Sy osf1 , +.Sy sco , +.Sy solaris , +.Sy sunos , +.Sy svr3 , +.Sy svr4 , +and +.Sy ultrix . +.It Sy flags +The file flags as a symbolic name. +See .Xr chflags 1 -for information on these names. If no flags are to be set the string -.Dq none +for information on these names. +If no flags are to be set the string +.Ql none may be used to override the current default. -.It Cm ignore +Note that the schg and sappnd flags are treated specially (see the +.Fl i +and +.Fl m +options). +.It Sy ignore Ignore any file hierarchy below this file. -.It Cm gid +.It Sy gid The file group as a numeric value. -.It Cm gname +.It Sy 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 ripemd160digest +.It Sy link +The file the symbolic link is expected to reference. +.It Sy md5 The -.Tn RIPEMD160 -message digest of the file. -.It Cm mode +.Tn MD5 +cryptographic message digest of the file. +.It Sy md5digest +Synonym for +.Sy md5 . +.It Sy mode The current file's permissions as a numeric (octal) or symbolic value. -.It Cm nlink +.It Sy nlink The number of hard links the file is expected to have. -.It Cm nochange +.It Sy nochange Make sure this file or directory exists but otherwise ignore all attributes. -.It Cm uid -The file owner as a numeric value. -.It Cm uname -The file owner as a symbolic name. -.It Cm size +.It Sy optional +The file is optional; don't complain about the file if it's +not in the file hierarchy. +.It Sy ripemd160digest +Synonym for +.Sy rmd160 . +.It Sy rmd160 +The +.Tn RMD-160 +cryptographic message digest of the file. +.It Sy rmd160digest +Synonym for +.Sy rmd160 . +.It Sy sha1 +The +.Tn SHA-1 +cryptographic message digest of the file. +.It Sy sha1digest +Synonym for +.Sy sha1 . +.It Sy sha256 +The 256-bits +.Tn SHA-2 +cryptographic message digest of the file. +.It Sy sha256digest +Synonym for +.Sy sha256 . +.It Sy sha384 +The 384-bits +.Tn SHA-2 +cryptographic message digest of the file. +.It Sy sha384digest +Synonym for +.Sy sha384 . +.It Sy sha512 +The 512-bits +.Tn SHA-2 +cryptographic message digest of the file. +.It Sy sha512digest +Synonym for +.Sy sha512 . +.It Sy size The size, in bytes, of the file. -.It Cm link -The file the symbolic link is expected to reference. -.It Cm time -The last modification time of the file. -.It Cm type +.It Sy tags +Comma delimited tags to be matched with +.Fl E +and +.Fl I . +These may be specified without leading or trailing commas, but will be +stored internally with them. +.It Sy time +The last modification time of the file, +in second and nanoseconds. +The value should include a period character and exactly nine digits after +the period. +.It Sy type The type of the file; may be set to any one of the following: .Pp -.Bl -tag -width Cm -compact -.It Cm block +.Bl -tag -width Sy -compact +.It Sy block block special device -.It Cm char +.It Sy char character special device -.It Cm dir +.It Sy dir directory -.It Cm fifo +.It Sy fifo fifo -.It Cm file +.It Sy file regular file -.It Cm link +.It Sy link symbolic link -.It Cm socket +.It Sy socket socket .El +.It Sy uid +The file owner as a numeric value. +.It Sy uname +The file owner as a symbolic name. .El .Pp The default set of keywords are -.Cm flags , -.Cm gid , -.Cm mode , -.Cm nlink , -.Cm size , -.Cm link , -.Cm time , +.Sy flags , +.Sy gid , +.Sy link , +.Sy mode , +.Sy nlink , +.Sy size , +.Sy time , +.Sy type , and -.Cm uid . -.Pp -There are four types of lines in a specification. +.Sy uid . .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 +There are four types of lines in a specification: +.Bl -enum +.It +Set global values for a keyword. +This consists of the string +.Ql /set +followed by whitespace, followed by sets of keyword/value pairs, separated by whitespace. Keyword/value pairs consist of a keyword, followed by an equals sign -(``=''), followed by a value, without whitespace characters. +.Pq Ql = , +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, +.It +Unset global values for a keyword. +This consists of the string +.Ql /unset , +followed by whitespace, followed by one or more keywords, separated by whitespace. +If +.Ql all +is specified, unset all of the keywords. +.It +A file specification, consisting of a path name, followed by whitespace, +followed by zero or more whitespace separated keyword/value pairs. .Pp -The 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 +The path name may be preceded by whitespace characters. +The path name may contain any of the standard path name matching +characters +.Po +.Ql \&[ , +.Ql \&] , +.Ql \&? +or +.Ql * +.Pc , +in which case files in the hierarchy will be associated with the first pattern that they match. +.Nm +uses +.Xr strsvis 3 +(in VIS_CSTYLE format) to encode path names containing +non-printable characters. +Whitespace characters are encoded as +.Ql \es +(space), +.Ql \et +(tab), and +.Ql \en +(new line). +.Ql # +characters in path names are escaped by a preceding backslash +.Ql \e +to distinguish them from comments. .Pp Each of the keyword/value pairs consist of a keyword, followed by an -equals sign (``=''), followed by the keyword's value, without +equals sign +.Pq Ql = , +followed by the keyword's value, without whitespace characters. These values override, without changing, the global value of the corresponding keyword. .Pp -All paths are relative. +The first path name entry listed must be a directory named +.Ql \&. , +as this ensures that intermixing full and relative path names will +work consistently and correctly. +Multiple entries for a directory named +.Ql \&. +are permitted; the settings for the last such entry override those +of the existing entry. +.Pp +A path name that contains a slash +.Pq Ql / +that is not the first character will be treated as a full path +(relative to the root of the tree). +All parent directories referenced in the path name must exist. +The current directory path used by relative path names will be updated +appropriately. +Multiple entries for the same full path are permitted if the types +are the same (unless +.Fl M +is given, in which case the types may differ); +in this case the settings for the last entry take precedence. +.Pp +A path name that does not contain a slash will be treated as a relative path. Specifying a directory will cause subsequent files to be searched for in that directory hierarchy. -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. +.It +A line containing only the string +.Ql \&.. +which causes the current directory path (used by relative paths) +to ascend one level. +.El .Pp Empty lines and lines whose first non-whitespace character is a hash -mark (``#'') are ignored. +mark +.Pq Ql # +are ignored. .Pp The .Nm utility exits with a status of 0 on success, 1 if any error occurred, and 2 if the file hierarchy did not match the specification. -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 @@ -291,56 +684,71 @@ system specification directory .Sh EXIT STATUS .Ex -std .Sh EXAMPLES -To detect system binaries that have been ``trojan horsed'', it is recommended -that +To detect system binaries that have been +.Dq trojan horsed , +it is recommended that .Nm -.Fl K -.Cm sha1digest 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 md5 1 -utility. +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 -and -.Xr md5 1 -should be run against the on-line specifications. +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 is believed to be -impractical for them to create a modified specification which has -the same MD5 digest as the original. +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 +option can be used in combination with +.Fl U +or .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 -.Dx -distribution. +to create directory hierarchies for, for example, distributions. +.Sh COMPATIBILITY +The compatibility shims provided by the +.Fl F +option are incomplete by design. +Known limitations are described below. .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 . +The +.Sy freebsd9 +flavor retains the default handling of lookup failures for the +.Sy uname +and +.Sy group +keywords by replacing them with appropriate +.Sy uid +and +.Sy gid +keywords rather than failing and reporting an error. +The related +.Fl w +flag is a no-op rather than causing a warning to be printed and no +keyword to be emitted. +The latter behavior is not emulated as it is potentially dangerous in +the face of /set statements. +.Pp +The +.Sy netbsd6 +flavor does not replicate the historical bug that reported time as +seconds.nanoseconds without zero padding nanosecond values less than +100000000. .Sh SEE ALSO .Xr chflags 1 , .Xr chgrp 1 , .Xr chmod 1 , .Xr cksum 1 , -.Xr md5 1 , .Xr stat 2 , +.Xr fnmatch 3 , .Xr fts 3 , -.Xr md5 3 , +.Xr strsvis 3 , .Xr chown 8 .Sh HISTORY The @@ -348,20 +756,53 @@ The 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 . +.Sy optional +keyword appeared in +.Nx 1.2 . The -.Tn SHA-1 +.Fl U +option appeared in +.Nx 1.3 . +The +.Sy flags and -.Tn RIPEMD160 -digests were added in -.Fx 4.0 , -as new attacks have demonstrated weaknesses in -.Tn MD5 . -Support for file flags was added in -.Fx 4.0 , -and mostly comes from -.Nx . +.Sy md5 +keywords, and +.Fl i +and +.Fl m +options +appeared in +.Nx 1.4 . +The +.Sy device , +.Sy rmd160 , +.Sy sha1 , +.Sy tags , +and +.Sy all +keywords, +.Fl D , +.Fl E , +.Fl I , +.Fl L , +.Fl l , +.Fl N , +.Fl P , +.Fl R , +.Fl W , +and +.Fl X +options, and support for full paths appeared in +.Nx 1.6 . +The +.Sy sha256 , +.Sy sha384 , +and +.Sy sha512 +keywords appeared in +.Nx 3.0 . +The +.Fl S +option appeared in +.Nx 6.0 . diff --git a/usr.sbin/mtree/mtree.c b/usr.sbin/mtree/mtree.c index 74c6908dbc..ced0a57492 100644 --- a/usr.sbin/mtree/mtree.c +++ b/usr.sbin/mtree/mtree.c @@ -1,3 +1,6 @@ +/* @(#)mtree.c 8.1 (Berkeley) 6/6/93 */ +/* $NetBSD: mtree.c,v 1.50 2015/01/23 02:27:01 christos Exp $ */ + /*- * Copyright (c) 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. @@ -25,102 +28,191 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * @(#) Copyright (c) 1989, 1990, 1993 The Regents of the University of California. All rights reserved. - * @(#)mtree.c 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.sbin/mtree/mtree.c,v 1.8.2.3 2003/05/07 17:55:17 tobez Exp $ */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + #include #include -#include + #include -#include #include +#include +#include #include -#include "mtree.h" + #include "extern.h" -int ftsoptions = FTS_PHYSICAL; -int cflag, dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, Uflag; -u_int keys; -char fullpath[MAXPATHLEN]; +int ftsoptions = FTS_PHYSICAL; +int bflag, dflag, eflag, iflag, jflag, lflag, mflag, nflag, qflag, rflag, + sflag, tflag, uflag; +char fullpath[MAXPATHLEN]; + +static struct { + enum flavor flavor; + const char name[9]; +} flavors[] = { + {F_MTREE, "mtree"}, + {F_FREEBSD9, "freebsd9"}, + {F_NETBSD6, "netbsd6"}, +}; -static void usage(void); +__dead2 static void usage(void); int -main(int argc, char *argv[]) +main(int argc, char **argv) { - int ch; - char *dir, *p; - int status; + int ch, status; + unsigned int i; + int cflag, Cflag, Dflag, Uflag, wflag; + char *dir, *p; + FILE *spec1, *spec2; + setprogname(argv[0]); + + cflag = Cflag = Dflag = Uflag = wflag = 0; dir = NULL; - keys = KEYDEFAULT; init_excludes(); + spec1 = stdin; + spec2 = NULL; - while ((ch = getopt(argc, argv, "cdef:iK:k:LnPp:qrs:UuxX:")) != -1) + while ((ch = getopt(argc, argv, + "bcCdDeE:f:F:I:ijk:K:lLmMnN:O:p:PqrR:s:StuUwWxX:")) + != -1) { switch((char)ch) { + case 'b': + bflag = 1; + break; case 'c': cflag = 1; break; + case 'C': + Cflag = 1; + break; case 'd': dflag = 1; break; + case 'D': + Dflag = 1; + break; + case 'E': + parsetags(&excludetags, optarg); + break; case 'e': eflag = 1; break; case 'f': - if (!(freopen(optarg, "r", stdin))) - err(1, "%s", optarg); + if (spec1 == stdin) { + spec1 = fopen(optarg, "r"); + if (spec1 == NULL) + mtree_err("%s: %s", optarg, + strerror(errno)); + } else if (spec2 == NULL) { + spec2 = fopen(optarg, "r"); + if (spec2 == NULL) + mtree_err("%s: %s", optarg, + strerror(errno)); + } else + usage(); + break; + case 'F': + for (i = 0; i < NELEM(flavors); i++) + if (strcmp(optarg, flavors[i].name) == 0) { + flavor = flavors[i].flavor; + break; + } + if (i == NELEM(flavors)) + usage(); break; case 'i': iflag = 1; break; - case 'K': + case 'I': + parsetags(&includetags, optarg); + break; + case 'j': + jflag = 1; + break; + case 'k': + keys = F_TYPE; while ((p = strsep(&optarg, " \t,")) != NULL) if (*p != '\0') keys |= parsekey(p, NULL); break; - case 'k': - keys = F_TYPE; + case 'K': while ((p = strsep(&optarg, " \t,")) != NULL) if (*p != '\0') keys |= parsekey(p, NULL); break; + case 'l': + lflag = 1; + break; case 'L': ftsoptions &= ~FTS_PHYSICAL; ftsoptions |= FTS_LOGICAL; break; + case 'm': + mflag = 1; + break; + case 'M': + mtree_Mflag = 1; + break; case 'n': nflag = 1; break; - case 'P': - ftsoptions &= ~FTS_LOGICAL; - ftsoptions |= FTS_PHYSICAL; + case 'N': + if (! setup_getid(optarg)) + mtree_err( + "Unable to use user and group databases in `%s'", + optarg); + break; + case 'O': + load_only(optarg); break; case 'p': dir = optarg; break; + case 'P': + ftsoptions &= ~FTS_LOGICAL; + ftsoptions |= FTS_PHYSICAL; + break; case 'q': qflag = 1; break; case 'r': - rflag = 1; + rflag++; + break; + case 'R': + while ((p = strsep(&optarg, " \t,")) != NULL) + if (*p != '\0') + keys &= ~parsekey(p, NULL); break; case 's': sflag = 1; crc_total = ~strtol(optarg, &p, 0); if (*p) - errx(1, "illegal seed value -- %s", optarg); + mtree_err("illegal seed value -- %s", optarg); break; - case 'U': - Uflag = 1; - uflag = 1; + case 'S': + mtree_Sflag = 1; + break; + case 't': + tflag = 1; break; case 'u': uflag = 1; break; + case 'U': + Uflag = uflag = 1; + break; + case 'w': + wflag = 1; + break; + case 'W': + mtree_Wflag = 1; + break; case 'x': ftsoptions |= FTS_XDEV; break; @@ -131,24 +223,78 @@ main(int argc, char *argv[]) default: usage(); } + } argc -= optind; argv += optind; if (argc) usage(); + switch (flavor) { + case F_FREEBSD9: + if (cflag && iflag) { + warnx("-c and -i passed, replacing -i with -j for " + "FreeBSD compatibility"); + iflag = 0; + jflag = 1; + } + if (dflag && !bflag) { + warnx("Adding -b to -d for FreeBSD compatibility"); + bflag = 1; + } + if (uflag && !iflag) { + warnx("Adding -i to -%c for FreeBSD compatibility", + Uflag ? 'U' : 'u'); + iflag = 1; + } + if (uflag && !tflag) { + warnx("Adding -t to -%c for FreeBSD compatibility", + Uflag ? 'U' : 'u'); + tflag = 1; + } + if (wflag) + warnx("The -w flag is a no-op"); + break; + default: + if (wflag) + usage(); + } + + if (spec2 && (cflag || Cflag || Dflag)) + mtree_err("Double -f, -c, -C and -D flags are mutually " + "exclusive"); + + if (dir && spec2) + mtree_err("Double -f and -p flags are mutually exclusive"); + if (dir && chdir(dir)) - err(1, "%s", dir); + mtree_err("%s: %s", dir, strerror(errno)); if ((cflag || sflag) && !getcwd(fullpath, sizeof(fullpath))) - errx(1, "%s", fullpath); + mtree_err("%s", strerror(errno)); + + if ((cflag && Cflag) || (cflag && Dflag) || (Cflag && Dflag)) + mtree_err("-c, -C and -D flags are mutually exclusive"); + + if (iflag && mflag) + mtree_err("-i and -m flags are mutually exclusive"); + + if (lflag && uflag) + mtree_err("-l and -u flags are mutually exclusive"); if (cflag) { - cwalk(); + cwalk(stdout); + exit(0); + } + if (Cflag || Dflag) { + dump_nodes(stdout, "", spec(spec1), Dflag); exit(0); } - status = verify(); - if (Uflag & (status == MISMATCHEXIT)) + if (spec2 != NULL) + status = mtree_specspec(spec1, spec2); + else + status = verify(spec1); + if (Uflag && (status == MISMATCHEXIT)) status = 0; exit(status); } @@ -156,9 +302,18 @@ main(int argc, char *argv[]) static void usage(void) { + unsigned int i; fprintf(stderr, -"usage: mtree [-LPUcdeinqrux] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n" -"\t[-X excludes]\n"); + "usage: %s [-bCcDdejLlMnPqrStUuWx] [-i|-m] [-E tags]\n" + "\t\t[-f spec] [-f spec]\n" + "\t\t[-I tags] [-K keywords] [-k keywords] [-N dbdir] [-p path]\n" + "\t\t[-R keywords] [-s seed] [-X exclude-file]\n" + "\t\t[-F flavor]\n", + getprogname()); + fprintf(stderr, "\nflavors:"); + for (i = 0; i < NELEM(flavors); i++) + fprintf(stderr, " %s", flavors[i].name); + fprintf(stderr, "\n"); exit(1); } diff --git a/usr.sbin/mtree/mtree.h b/usr.sbin/mtree/mtree.h index be38ef9584..93d6cdfb0e 100644 --- a/usr.sbin/mtree/mtree.h +++ b/usr.sbin/mtree/mtree.h @@ -1,3 +1,5 @@ +/* $NetBSD: mtree.h,v 1.31 2012/10/05 09:17:29 wiz Exp $ */ + /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. @@ -27,15 +29,13 @@ * SUCH DAMAGE. * * @(#)mtree.h 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.sbin/mtree/mtree.h,v 1.5 1999/12/09 20:38:35 joe Exp $ - * $DragonFly: src/usr.sbin/mtree/mtree.h,v 1.2 2003/06/17 04:29:57 dillon Exp $ */ -#include -#include +#ifndef _MTREE_H_ +#define _MTREE_H_ -#define KEYDEFAULT \ - (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | F_TIME | F_UID | F_FLAGS) +#define KEYDEFAULT (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | \ + F_TIME | F_TYPE | F_UID | F_FLAGS) #define MISMATCHEXIT 2 @@ -44,40 +44,55 @@ typedef struct _node { 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 *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 */ + dev_t st_rdev; /* device type */ u_long st_flags; /* flags */ nlink_t st_nlink; /* link count */ + u_long cksum; /* check sum */ + char *md5digest; /* MD5 digest */ + char *rmd160digest; /* RMD-160 digest */ + char *sha1digest; /* SHA1 digest */ + char *sha256digest; /* SHA256 digest */ + char *sha384digest; /* SHA384 digest */ + char *sha512digest; /* SHA512 digest */ + char *tags; /* tags, comma delimited, + * also with leading and + * trailing commas */ + size_t lineno; /* line # entry came from */ + +#define F_CKSUM 0x00000001 /* cksum(1) check sum */ +#define F_DEV 0x00000002 /* device type */ +#define F_DONE 0x00000004 /* directory done */ +#define F_FLAGS 0x00000008 /* file flags */ +#define F_GID 0x00000010 /* gid */ +#define F_GNAME 0x00000020 /* group name */ +#define F_IGN 0x00000040 /* ignore */ +#define F_MAGIC 0x00000080 /* name has magic chars */ +#define F_MD5 0x00000100 /* MD5 digest */ +#define F_MODE 0x00000200 /* mode */ +#define F_NLINK 0x00000400 /* number of links */ +#define F_OPT 0x00000800 /* existence optional */ +#define F_RMD160 0x00001000 /* RMD-160 digest */ +#define F_SHA1 0x00002000 /* SHA1 digest */ +#define F_SIZE 0x00004000 /* size */ +#define F_SLINK 0x00008000 /* symbolic link */ +#define F_TAGS 0x00010000 /* tags */ +#define F_TIME 0x00020000 /* modification time */ +#define F_TYPE 0x00040000 /* file type */ +#define F_UID 0x00080000 /* uid */ +#define F_UNAME 0x00100000 /* user name */ +#define F_VISIT 0x00200000 /* file visited */ +#define F_NOCHANGE 0x00400000 /* check existence, but not */ + /* other properties */ +#define F_SHA256 0x00800000 /* SHA256 digest */ +#define F_SHA384 0x01000000 /* SHA384 digest */ +#define F_SHA512 0x02000000 /* SHA512 digest */ -#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 */ - u_int flags; /* items set */ + int flags; /* items set */ #define F_BLOCK 0x001 /* block special */ #define F_CHAR 0x002 /* char special */ @@ -86,11 +101,58 @@ typedef struct _node { #define F_FILE 0x010 /* regular file */ #define F_LINK 0x020 /* symbolic link */ #define F_SOCK 0x040 /* socket */ - u_char type; /* file type */ +#define F_DOOR 0x080 /* door */ + int type; /* file type */ char name[1]; /* file name (must be last) */ } NODE; + +typedef struct { + char **list; + int count; +} slist_t; + + +/* + * prototypes for functions published to other programs which want to use + * the specfile parser but don't want to pull in all of "extern.h" + */ +const char *inotype(u_int); +u_int nodetoino(u_int); +int setup_getid(const char *); +NODE *spec(FILE *); +int mtree_specspec(FILE *, FILE *); +void free_nodes(NODE *); +char *vispath(const char *); + +#ifdef __FreeBSD__ +#define KEY_DIGEST "digest" +#else +#define KEY_DIGEST +#endif + +#define MD5KEY "md5" KEY_DIGEST +#ifdef __FreeBSD__ +#define RMD160KEY "ripemd160" KEY_DIGEST +#else +#define RMD160KEY "rmd160" KEY_DIGEST +#endif +#define SHA1KEY "sha1" KEY_DIGEST +#define SHA256KEY "sha256" KEY_DIGEST +#define SHA384KEY "sha384" +#define SHA512KEY "sha512" + #define RP(p) \ ((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \ (p)->fts_path + 2 : (p)->fts_path) + +#define UF_MASK ((UF_NODUMP | UF_IMMUTABLE | \ + UF_APPEND | UF_OPAQUE) \ + & UF_SETTABLE) /* user settable flags */ +#define SF_MASK ((SF_ARCHIVED | SF_IMMUTABLE | \ + SF_APPEND) & SF_SETTABLE) /* root settable flags */ +#define CH_MASK (UF_MASK | SF_MASK) /* all settable flags */ +#define SP_FLGS (SF_IMMUTABLE | SF_APPEND) /* special flags */ + +#endif /* _MTREE_H_ */ diff --git a/usr.sbin/mtree/only.c b/usr.sbin/mtree/only.c new file mode 100644 index 0000000000..edc5ae0c4c --- /dev/null +++ b/usr.sbin/mtree/only.c @@ -0,0 +1,147 @@ +/* $NetBSD: only.c,v 1.3 2017/09/07 04:04:13 nakayama Exp $ */ + +/*- + * Copyright (c) 2013 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * 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 NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +struct hentry { + char *str; + uint32_t hash; + struct hentry *next; +}; + +static struct hentry *table[1024]; +static bool loaded; + +static uint32_t +hash_str(const char *str) +{ + const uint8_t *s = (const uint8_t *)str; + uint8_t c; + uint32_t hash = 0; + while ((c = *s++) != '\0') + hash = hash * 33 + c; /* "perl": k=33, r=r+r/32 */ + return hash + (hash >> 5); +} + +static bool +hash_find(const char *str, uint32_t *h) +{ + struct hentry *e; + *h = hash_str(str) % NELEM(table); + + for (e = table[*h]; e; e = e->next) + if (e->hash == *h && strcmp(e->str, str) == 0) + return true; + return false; +} + +static void +hash_insert(char *str, uint32_t h) +{ + struct hentry *e; + char *x; + + if ((e = malloc(sizeof(*e))) == NULL) + mtree_err("memory allocation error"); + if ((x = strdup(str)) == NULL) + mtree_err("memory allocation error"); + + e->str = x; + e->hash = h; + e->next = table[h]; + table[h] = e; +} + +static void +fill(char *str) +{ + uint32_t h; + char *ptr = strrchr(str, '/'); + + if (ptr == NULL) + return; + + *ptr = '\0'; + if (!hash_find(str, &h)) { + hash_insert(str, h); + fill(str); + } + *ptr = '/'; +} + +void +load_only(const char *fname) +{ + FILE *fp; + char *line; + size_t len, lineno; + + if ((fp = fopen(fname, "r")) == NULL) + err(1, "Cannot open `%s'", fname); + + while ((line = fparseln(fp, &len, &lineno, NULL, FPARSELN_UNESCALL))) { + uint32_t h; + if (hash_find(line, &h)) + err(1, "Duplicate entry %s", line); + hash_insert(line, h); + fill(line); + free(line); + } + + fclose(fp); + loaded = true; +} + +bool +find_only(const char *path) +{ + uint32_t h; + + if (!loaded) + return true; + return hash_find(path, &h); +} diff --git a/usr.sbin/mtree/pack_dev.c b/usr.sbin/mtree/pack_dev.c new file mode 100644 index 0000000000..23275d3132 --- /dev/null +++ b/usr.sbin/mtree/pack_dev.c @@ -0,0 +1,285 @@ +/* $NetBSD: pack_dev.c,v 1.12 2013/06/14 16:28:20 tsutsui Exp $ */ + +/*- + * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#include + +#include +#include +#include +#include +#include + +#include "pack_dev.h" + +static pack_t pack_netbsd; +static pack_t pack_freebsd; +static pack_t pack_8_8; +static pack_t pack_12_20; +static pack_t pack_14_18; +static pack_t pack_8_24; +static pack_t pack_bsdos; +static int compare_format(const void *, const void *); + +static const char iMajorError[] = "invalid major number"; +static const char iMinorError[] = "invalid minor number"; +static const char tooManyFields[] = "too many fields for format"; + + /* exported */ +dev_t +pack_native(int n, u_long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = makedev(numbers[0], numbers[1]); + if ((u_long)major(dev) != numbers[0]) + *error = iMajorError; + else if ((u_long)minor(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +static dev_t +pack_netbsd(int n, u_long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = makedev_netbsd(numbers[0], numbers[1]); + if ((u_long)major_netbsd(dev) != numbers[0]) + *error = iMajorError; + else if ((u_long)minor_netbsd(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_freebsd(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) +#define minor_freebsd(x) ((int32_t)(((x) & 0xffff00ff) >> 0)) +#define makedev_freebsd(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \ + (((y) << 0) & 0xffff00ff))) + +static dev_t +pack_freebsd(int n, u_long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = makedev_freebsd(numbers[0], numbers[1]); + if ((u_long)major_freebsd(dev) != numbers[0]) + *error = iMajorError; + if ((u_long)minor_freebsd(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_8_8(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) +#define minor_8_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) +#define makedev_8_8(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \ + (((y) << 0) & 0x000000ff))) + +static dev_t +pack_8_8(int n, u_long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = makedev_8_8(numbers[0], numbers[1]); + if ((u_long)major_8_8(dev) != numbers[0]) + *error = iMajorError; + if ((u_long)minor_8_8(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_12_20(x) ((int32_t)(((x) & 0xfff00000) >> 20)) +#define minor_12_20(x) ((int32_t)(((x) & 0x000fffff) >> 0)) +#define makedev_12_20(x,y) ((dev_t)((((x) << 20) & 0xfff00000) | \ + (((y) << 0) & 0x000fffff))) + +static dev_t +pack_12_20(int n, u_long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = makedev_12_20(numbers[0], numbers[1]); + if ((u_long)major_12_20(dev) != numbers[0]) + *error = iMajorError; + if ((u_long)minor_12_20(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_14_18(x) ((int32_t)(((x) & 0xfffc0000) >> 18)) +#define minor_14_18(x) ((int32_t)(((x) & 0x0003ffff) >> 0)) +#define makedev_14_18(x,y) ((dev_t)((((x) << 18) & 0xfffc0000) | \ + (((y) << 0) & 0x0003ffff))) + +static dev_t +pack_14_18(int n, u_long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = makedev_14_18(numbers[0], numbers[1]); + if ((u_long)major_14_18(dev) != numbers[0]) + *error = iMajorError; + if ((u_long)minor_14_18(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_8_24(x) ((int32_t)(((x) & 0xff000000) >> 24)) +#define minor_8_24(x) ((int32_t)(((x) & 0x00ffffff) >> 0)) +#define makedev_8_24(x,y) ((dev_t)((((x) << 24) & 0xff000000) | \ + (((y) << 0) & 0x00ffffff))) + +static dev_t +pack_8_24(int n, u_long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = makedev_8_24(numbers[0], numbers[1]); + if ((u_long)major_8_24(dev) != numbers[0]) + *error = iMajorError; + if ((u_long)minor_8_24(dev) != numbers[1]) + *error = iMinorError; + } else + *error = tooManyFields; + return (dev); +} + + +#define major_12_12_8(x) ((int32_t)(((x) & 0xfff00000) >> 20)) +#define unit_12_12_8(x) ((int32_t)(((x) & 0x000fff00) >> 8)) +#define subunit_12_12_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) +#define makedev_12_12_8(x,y,z) ((dev_t)((((x) << 20) & 0xfff00000) | \ + (((y) << 8) & 0x000fff00) | \ + (((z) << 0) & 0x000000ff))) + +static dev_t +pack_bsdos(int n, u_long numbers[], const char **error) +{ + dev_t dev = 0; + + if (n == 2) { + dev = makedev_12_20(numbers[0], numbers[1]); + if ((u_long)major_12_20(dev) != numbers[0]) + *error = iMajorError; + if ((u_long)minor_12_20(dev) != numbers[1]) + *error = iMinorError; + } else if (n == 3) { + dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]); + if ((u_long)major_12_12_8(dev) != numbers[0]) + *error = iMajorError; + if ((u_long)unit_12_12_8(dev) != numbers[1]) + *error = "invalid unit number"; + if ((u_long)subunit_12_12_8(dev) != numbers[2]) + *error = "invalid subunit number"; + } else + *error = tooManyFields; + return (dev); +} + + + /* list of formats and pack functions */ + /* this list must be sorted lexically */ +static struct format { + const char *name; + pack_t *pack; +} formats[] = { + {"386bsd", pack_8_8}, + {"4bsd", pack_8_8}, + {"bsdos", pack_bsdos}, + {"freebsd", pack_freebsd}, + {"hpux", pack_8_24}, + {"isc", pack_8_8}, + {"linux", pack_8_8}, + {"native", pack_native}, + {"netbsd", pack_netbsd}, + {"osf1", pack_12_20}, + {"sco", pack_8_8}, + {"solaris", pack_14_18}, + {"sunos", pack_8_8}, + {"svr3", pack_8_8}, + {"svr4", pack_14_18}, + {"ultrix", pack_8_8}, +}; + +static int +compare_format(const void *key, const void *element) +{ + const char *name; + const struct format *format; + + name = key; + format = element; + + return (strcmp(name, format->name)); +} + + +pack_t * +pack_find(const char *name) +{ + struct format *format; + + format = bsearch(name, formats, + sizeof(formats)/sizeof(formats[0]), + sizeof(formats[0]), compare_format); + if (format == 0) + return (NULL); + return (format->pack); +} diff --git a/usr.sbin/mtree/pack_dev.h b/usr.sbin/mtree/pack_dev.h new file mode 100644 index 0000000000..d0cb75e6dc --- /dev/null +++ b/usr.sbin/mtree/pack_dev.h @@ -0,0 +1,47 @@ +/* $NetBSD: pack_dev.h,v 1.8 2013/06/14 16:28:20 tsutsui Exp $ */ + +/*- + * Copyright (c) 1998, 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _PACK_DEV_H +#define _PACK_DEV_H + +typedef dev_t pack_t(int, u_long [], const char **); + +pack_t *pack_find(const char *); +pack_t pack_native; + +#define major_netbsd(x) ((int32_t)((((x) & 0x000fff00) >> 8))) +#define minor_netbsd(x) ((int32_t)((((x) & 0xfff00000) >> 12) | \ + (((x) & 0x000000ff) >> 0))) +#define makedev_netbsd(x,y) ((dev_t)((((x) << 8) & 0x000fff00) | \ + (((y) << 12) & 0xfff00000) | \ + (((y) << 0) & 0x000000ff))) + +#endif /* _PACK_DEV_H */ diff --git a/usr.sbin/mtree/spec.c b/usr.sbin/mtree/spec.c index 40634c27f8..53f627a8ab 100644 --- a/usr.sbin/mtree/spec.c +++ b/usr.sbin/mtree/spec.c @@ -1,3 +1,6 @@ +/* @(#)spec.c 8.2 (Berkeley) 4/28/95 */ +/* $NetBSD: spec.c,v 1.90 2017/12/14 18:34:41 christos Exp $ */ + /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. @@ -25,102 +28,126 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. + */ + +/*- + * Copyright (c) 2001-2004 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Luke Mewburn of Wasabi Systems. * - * @(#)spec.c 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.sbin/mtree/spec.c,v 1.13.2.1 2000/06/28 02:33:17 joe Exp $ - * $DragonFly: src/usr.sbin/mtree/spec.c,v 1.6 2004/08/30 19:27:22 eirikn Exp $ + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ -#include +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include #include + +#include #include -#include #include -#include #include #include +#include #include +#include +#include +#include #include #include -#include "mtree.h" +#include + #include "extern.h" +#include "pack_dev.h" + +size_t mtree_lineno; /* Current spec line number */ +int mtree_Mflag; /* Merge duplicate entries */ +int mtree_Wflag; /* Don't "whack" permissions */ +int mtree_Sflag; /* Sort entries */ -int lineno; /* Current spec line number. */ +static dev_t parsedev(char *); +static void replacenode(NODE *, NODE *); +static void set(char *, NODE *); +static void unset(char *, NODE *); +static void addchild(NODE *, NODE *); +static int nodecmp(const NODE *, const NODE *); +static int appendfield(FILE *, int, const char *, ...) __printflike(3, 4); -static void set(char *, NODE *); -static void unset(char *, NODE *); +#define REPLACEPTR(x,v) do { if ((x)) free((x)); (x) = (v); } while (0) NODE * -spec(void) +spec(FILE *fp) { - NODE *centry, *last; - char *p; + NODE *centry, *last, *pathparent, *cur; + char *p, *e, *next; 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), stdin); - ++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'; + char *buf, *tname, *ntname; + size_t tnamelen, plen; + root = NULL; + centry = last = NULL; + tname = NULL; + tnamelen = 0; + memset(&ginfo, 0, sizeof(ginfo)); + for (mtree_lineno = 0; + (buf = fparseln(fp, NULL, &mtree_lineno, NULL, + FPARSELN_UNESCCOMM)); + free(buf)) { /* Skip leading whitespace. */ - for (p = buf; *p && isspace(*p); ++p); + for (p = buf; *p && isspace((unsigned char)*p); ++p) + continue; - /* If nothing but whitespace or comment char, continue. */ - if (!*p || *p == '#') + /* If nothing but whitespace, continue. */ + if (!*p) continue; #ifdef DEBUG - fprintf(stderr, "line %d: {%s}\n", lineno, p); + fprintf(stderr, "line %lu: {%s}\n", + (u_long)mtree_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; - } + next = buf; + while ((p = strsep(&next, " \t")) != NULL && *p == '\0') + continue; + if (p == NULL) + mtree_err("missing field"); - if (strchr(p, '/')) - errx(1, "line %d: slash character in file name", - lineno); + if (p[0] == '/') { + if (strcmp(p + 1, "set") == 0) + set(next, &ginfo); + else if (strcmp(p + 1, "unset") == 0) + unset(next, &ginfo); + else + mtree_err("invalid specification `%s'", p); + continue; + } - if (!strcmp(p, "..")) { + if (strcmp(p, "..") == 0) { /* Don't go up, if haven't gone down. */ - if (!root) + if (root == NULL) goto noparent; if (last->type != F_DIR || last->flags & F_DONE) { if (last == root) @@ -130,173 +157,534 @@ spec(void) last->flags |= F_DONE; continue; -noparent: errx(1, "line %d: no parent node", lineno); +noparent: mtree_err("no parent node"); + } + + plen = strlen(p) + 1; + if (plen > tnamelen) { + if ((ntname = realloc(tname, plen)) == NULL) + mtree_err("realloc: %s", strerror(errno)); + tname = ntname; + tnamelen = plen; + } + if (strunvis(tname, p) == -1) + mtree_err("strunvis failed on `%s'", p); + p = tname; + + pathparent = NULL; + if (strchr(p, '/') != NULL) { + cur = root; + for (; (e = strchr(p, '/')) != NULL; p = e+1) { + if (p == e) + continue; /* handle // */ + *e = '\0'; + if (strcmp(p, ".") != 0) { + while (cur && + strcmp(cur->name, p) != 0) { + cur = cur->next; + } + } + if (cur == NULL || cur->type != F_DIR) { + mtree_err("%s: %s", tname, + "missing directory in specification"); + } + *e = '/'; + pathparent = cur; + cur = cur->child; + } + if (*p == '\0') + mtree_err("%s: empty leaf element", tname); } if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) - errx(1, "calloc"); + mtree_err("%s", strerror(errno)); *centry = ginfo; + centry->lineno = mtree_lineno; + strcpy(centry->name, p); #define MAGIC "?*[" if (strpbrk(p, MAGIC)) centry->flags |= F_MAGIC; - if (strunvis(centry->name, p) == -1) { - warnx("filename %s is ill-encoded and literally used", - p); - strcpy(centry->name, p); - } - set(NULL, centry); + set(next, centry); - if (!root) { + if (root == NULL) { + /* + * empty tree + */ + /* + * Allow a bare "." root node by forcing it to + * type=dir for compatibility with FreeBSD. + */ + if (strcmp(centry->name, ".") == 0 && centry->type == 0) + centry->type = F_DIR; + if (strcmp(centry->name, ".") != 0 || + centry->type != F_DIR) + mtree_err( + "root node must be the directory `.'"); last = root = centry; root->parent = root; + } else if (pathparent != NULL) { + /* + * full path entry; add or replace + */ + centry->parent = pathparent; + addchild(pathparent, centry); + last = centry; + } else if (strcmp(centry->name, ".") == 0) { + /* + * duplicate "." entry; always replace + */ + replacenode(root, centry); } else if (last->type == F_DIR && !(last->flags & F_DONE)) { + /* + * new relative child in current dir; + * add or replace + */ centry->parent = last; - last = last->child = centry; + addchild(last, centry); + last = centry; } else { + /* + * new relative child in parent dir + * (after encountering ".." entry); + * add or replace + */ centry->parent = last->parent; - centry->prev = last; - last = last->next = centry; + addchild(last->parent, centry); + last = centry; } } return (root); } +void +free_nodes(NODE *root) +{ + NODE *cur, *next; + + if (root == NULL) + return; + + next = NULL; + for (cur = root; cur != NULL; cur = next) { + next = cur->next; + free_nodes(cur->child); + REPLACEPTR(cur->slink, NULL); + REPLACEPTR(cur->md5digest, NULL); + REPLACEPTR(cur->rmd160digest, NULL); + REPLACEPTR(cur->sha1digest, NULL); + REPLACEPTR(cur->sha256digest, NULL); + REPLACEPTR(cur->sha384digest, NULL); + REPLACEPTR(cur->sha512digest, NULL); + REPLACEPTR(cur->tags, NULL); + REPLACEPTR(cur, NULL); + } +} + +/* + * appendfield -- + * Like fprintf(), but output a space either before or after + * the regular output, according to the pathlast flag. + */ +static int +appendfield(FILE *fp, int pathlast, const char *fmt, ...) +{ + va_list ap; + int result; + + va_start(ap, fmt); + if (!pathlast) + fprintf(fp, " "); + result = vprintf(fmt, ap); + if (pathlast) + fprintf(fp, " "); + va_end(ap); + return result; +} + +/* + * dump_nodes -- + * dump the NODEs from `cur', based in the directory `dir'. + * if pathlast is none zero, print the path last, otherwise print + * it first. + */ +void +dump_nodes(FILE *fp, const char *dir, NODE *root, int pathlast) +{ + NODE *cur; + char path[MAXPATHLEN]; + const char *name; + char *str; + char *p, *q; + + for (cur = root; cur != NULL; cur = cur->next) { + if (cur->type != F_DIR && !matchtags(cur)) + continue; + + if (snprintf(path, sizeof(path), "%s%s%s", + dir, *dir ? "/" : "", cur->name) + >= (int)sizeof(path)) + mtree_err("Pathname too long."); + + if (!pathlast) + fprintf(fp, "%s", vispath(path)); + +#define MATCHFLAG(f) ((keys & (f)) && (cur->flags & (f))) + if (MATCHFLAG(F_TYPE)) + appendfield(fp, pathlast, "type=%s", + nodetype(cur->type)); + if (MATCHFLAG(F_UID | F_UNAME)) { + if (keys & F_UNAME && + (name = user_from_uid(cur->st_uid, 1)) != NULL) + appendfield(fp, pathlast, "uname=%s", name); + else + appendfield(fp, pathlast, "uid=%u", + cur->st_uid); + } + if (MATCHFLAG(F_GID | F_GNAME)) { + if (keys & F_GNAME && + (name = group_from_gid(cur->st_gid, 1)) != NULL) + appendfield(fp, pathlast, "gname=%s", name); + else + appendfield(fp, pathlast, "gid=%u", + cur->st_gid); + } + if (MATCHFLAG(F_MODE)) + appendfield(fp, pathlast, "mode=%#o", cur->st_mode); + if (MATCHFLAG(F_DEV) && + (cur->type == F_BLOCK || cur->type == F_CHAR)) + appendfield(fp, pathlast, "device=%#jx", + (uintmax_t)cur->st_rdev); + if (MATCHFLAG(F_NLINK)) + appendfield(fp, pathlast, "nlink=%ju", + (uintmax_t)cur->st_nlink); + if (MATCHFLAG(F_SLINK)) + appendfield(fp, pathlast, "link=%s", + vispath(cur->slink)); + if (MATCHFLAG(F_SIZE)) + appendfield(fp, pathlast, "size=%ju", + (uintmax_t)cur->st_size); + if (MATCHFLAG(F_TIME)) + appendfield(fp, pathlast, "time=%jd.%09ld", + (intmax_t)cur->st_mtimespec.tv_sec, + cur->st_mtimespec.tv_nsec); + if (MATCHFLAG(F_CKSUM)) + appendfield(fp, pathlast, "cksum=%lu", cur->cksum); + if (MATCHFLAG(F_MD5)) + appendfield(fp, pathlast, "%s=%s", MD5KEY, + cur->md5digest); + if (MATCHFLAG(F_RMD160)) + appendfield(fp, pathlast, "%s=%s", RMD160KEY, + cur->rmd160digest); + if (MATCHFLAG(F_SHA1)) + appendfield(fp, pathlast, "%s=%s", SHA1KEY, + cur->sha1digest); + if (MATCHFLAG(F_SHA256)) + appendfield(fp, pathlast, "%s=%s", SHA256KEY, + cur->sha256digest); + if (MATCHFLAG(F_SHA384)) + appendfield(fp, pathlast, "%s=%s", SHA384KEY, + cur->sha384digest); + if (MATCHFLAG(F_SHA512)) + appendfield(fp, pathlast, "%s=%s", SHA512KEY, + cur->sha512digest); + if (MATCHFLAG(F_FLAGS)) { + str = flags_to_string(cur->st_flags, "none"); + appendfield(fp, pathlast, "flags=%s", str); + free(str); + } + if (MATCHFLAG(F_IGN)) + appendfield(fp, pathlast, "ignore"); + if (MATCHFLAG(F_OPT)) + appendfield(fp, pathlast, "optional"); + if (MATCHFLAG(F_TAGS)) { + /* don't output leading or trailing commas */ + p = cur->tags; + while (*p == ',') + p++; + q = p + strlen(p); + while(q > p && q[-1] == ',') + q--; + appendfield(fp, pathlast, "tags=%.*s", (int)(q - p), p); + } + puts(pathlast ? vispath(path) : ""); + + if (cur->child) + dump_nodes(fp, path, cur->child, pathlast); + } +} + +/* + * vispath -- + * strsvis(3) encodes path, which must not be longer than MAXPATHLEN + * characters long, and returns a pointer to a static buffer containing + * the result. + */ +char * +vispath(const char *path) +{ + static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' }; + static const char extra_glob[] = { ' ', '\t', '\n', '\\', '#', '*', + '?', '[', '\0' }; + static char pathbuf[4*MAXPATHLEN + 1]; + + if (flavor == F_NETBSD6) + strsvis(pathbuf, path, VIS_CSTYLE, extra); + else + strsvis(pathbuf, path, VIS_OCTAL, extra_glob); + return pathbuf; +} + + +static dev_t +parsedev(char *arg) +{ +#define MAX_PACK_ARGS 3 + u_long numbers[MAX_PACK_ARGS]; + char *p, *ep, *dev; + int argc; + pack_t *pack; + dev_t result; + const char *error = NULL; + + if ((dev = strchr(arg, ',')) != NULL) { + *dev++='\0'; + if ((pack = pack_find(arg)) == NULL) + mtree_err("unknown format `%s'", arg); + argc = 0; + while ((p = strsep(&dev, ",")) != NULL) { + if (*p == '\0') + mtree_err("missing number"); + numbers[argc++] = strtoul(p, &ep, 0); + if (*ep != '\0') + mtree_err("invalid number `%s'", + p); + if (argc > MAX_PACK_ARGS) + mtree_err("too many arguments"); + } + if (argc < 2) + mtree_err("not enough arguments"); + result = (*pack)(argc, numbers, &error); + if (error != NULL) + mtree_err("%s", error); + } else { + result = (dev_t)strtoul(arg, &ep, 0); + if (*ep != '\0') + mtree_err("invalid device `%s'", arg); + } + return (result); +} + +static void +replacenode(NODE *cur, NODE *new) +{ + +#define REPLACE(x) cur->x = new->x +#define REPLACESTR(x) REPLACEPTR(cur->x,new->x) + + if (cur->type != new->type) { + if (mtree_Mflag) { + /* + * merge entries with different types; we + * don't want children retained in this case. + */ + REPLACE(type); + free_nodes(cur->child); + cur->child = NULL; + } else { + mtree_err( + "existing entry for `%s', type `%s'" + " does not match type `%s'", + cur->name, nodetype(cur->type), + nodetype(new->type)); + } + } + + REPLACE(st_size); + REPLACE(st_mtimespec); + REPLACESTR(slink); + if (cur->slink != NULL) { + if ((cur->slink = strdup(new->slink)) == NULL) + mtree_err("memory allocation error"); + if (strunvis(cur->slink, new->slink) == -1) + mtree_err("strunvis failed on `%s'", new->slink); + free(new->slink); + } + REPLACE(st_uid); + REPLACE(st_gid); + REPLACE(st_mode); + REPLACE(st_rdev); + REPLACE(st_flags); + REPLACE(st_nlink); + REPLACE(cksum); + REPLACESTR(md5digest); + REPLACESTR(rmd160digest); + REPLACESTR(sha1digest); + REPLACESTR(sha256digest); + REPLACESTR(sha384digest); + REPLACESTR(sha512digest); + REPLACESTR(tags); + REPLACE(lineno); + REPLACE(flags); + free(new); +} + static void set(char *t, NODE *ip) { - int type; - 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) { + int type, value, len; + gid_t gid; + uid_t uid; + char *kw, *val, *md, *ep; + void *m; + + while ((kw = strsep(&t, "= \t")) != NULL) { + if (*kw == '\0') + continue; + if (strcmp(kw, "all") == 0) + mtree_err("invalid keyword `all'"); ip->flags |= type = parsekey(kw, &value); - if (value && (val = strtok(NULL, " \t\n")) == NULL) - errx(1, "line %d: missing value", lineno); - switch(type) { + if (!value) + /* Just set flag bit (F_IGN and F_OPT) */ + continue; + while ((val = strsep(&t, " \t")) != NULL && *val == '\0') + continue; + if (val == NULL) + mtree_err("missing value"); + switch (type) { case F_CKSUM: ip->cksum = strtoul(val, &ep, 10); if (*ep) - errx(1, "line %d: invalid checksum %s", - lineno, val); - break; - case F_MD5: - ip->md5digest = strdup(val); - if(!ip->md5digest) { - errx(1, "strdup"); - } + mtree_err("invalid checksum `%s'", val); break; - case F_SHA1: - ip->sha1digest = strdup(val); - if(!ip->sha1digest) { - errx(1, "strdup"); - } - break; - case F_RMD160: - ip->rmd160digest = strdup(val); - if(!ip->rmd160digest) { - errx(1, "strdup"); - } + case F_DEV: + ip->st_rdev = parsedev(val); 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; + else if (string_to_flags(&val, &ip->st_flags, NULL) + != 0) + mtree_err("invalid flag `%s'", val); + break; case F_GID: - ip->st_gid = strtoul(val, &ep, 10); + ip->st_gid = (gid_t)strtoul(val, &ep, 10); if (*ep) - errx(1, "line %d: invalid gid %s", lineno, val); + mtree_err("invalid gid `%s'", 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; + if (mtree_Wflag) /* don't parse if whacking */ + break; + if (gid_from_group(val, &gid) == -1) + mtree_err("unknown group `%s'", val); + ip->st_gid = gid; break; - case F_IGN: - /* just set flag bit */ + case F_MD5: + if (val[0]=='0' && val[1]=='x') + md=&val[2]; + else + md=val; + if ((ip->md5digest = strdup(md)) == NULL) + mtree_err("memory allocation error"); break; case F_MODE: if ((m = setmode(val)) == NULL) - errx(1, "line %d: invalid file mode %s", - lineno, val); + mtree_err("cannot set file mode `%s' (%s)", + val, strerror(errno)); ip->st_mode = getmode(m, 0); free(m); break; case F_NLINK: - ip->st_nlink = strtoul(val, &ep, 10); + ip->st_nlink = (nlink_t)strtoul(val, &ep, 10); if (*ep) - errx(1, "line %d: invalid link count %s", - lineno, val); + mtree_err("invalid link count `%s'", val); + break; + case F_RMD160: + if (val[0]=='0' && val[1]=='x') + md=&val[2]; + else + md=val; + if ((ip->rmd160digest = strdup(md)) == NULL) + mtree_err("memory allocation error"); + break; + case F_SHA1: + if (val[0]=='0' && val[1]=='x') + md=&val[2]; + else + md=val; + if ((ip->sha1digest = strdup(md)) == NULL) + mtree_err("memory allocation error"); break; case F_SIZE: - ip->st_size = strtoq(val, &ep, 10); + ip->st_size = (off_t)strtoll(val, &ep, 10); if (*ep) - errx(1, "line %d: invalid size %s", - lineno, val); + mtree_err("invalid size `%s'", val); break; case F_SLINK: if ((ip->slink = strdup(val)) == NULL) - errx(1, "strdup"); + mtree_err("memory allocation error"); + if (strunvis(ip->slink, val) == -1) + mtree_err("strunvis failed on `%s'", val); + break; + case F_TAGS: + len = strlen(val) + 3; /* "," + str + ",\0" */ + if ((ip->tags = malloc(len)) == NULL) + mtree_err("memory allocation error"); + snprintf(ip->tags, len, ",%s,", val); break; case F_TIME: - ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10); + ip->st_mtimespec.tv_sec = + (time_t)strtoll(val, &ep, 10); if (*ep != '.') - errx(1, "line %d: invalid time %s", - lineno, val); + mtree_err("invalid time `%s'", val); val = ep + 1; - ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10); + ip->st_mtimespec.tv_nsec = strtol(val, &ep, 10); if (*ep) - errx(1, "line %d: invalid time %s", - lineno, val); + mtree_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: - errx(1, "line %d: unknown file type %s", - lineno, val); - } + ip->type = parsetype(val); break; case F_UID: - ip->st_uid = strtoul(val, &ep, 10); + ip->st_uid = (uid_t)strtoul(val, &ep, 10); if (*ep) - errx(1, "line %d: invalid uid %s", lineno, val); + mtree_err("invalid uid `%s'", 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; + if (mtree_Wflag) /* don't parse if whacking */ + break; + if (uid_from_user(val, &uid) == -1) + mtree_err("unknown user `%s'", val); + ip->st_uid = uid; + break; + case F_SHA256: + if (val[0]=='0' && val[1]=='x') + md=&val[2]; + else + md=val; + if ((ip->sha256digest = strdup(md)) == NULL) + mtree_err("memory allocation error"); + break; + case F_SHA384: + if (val[0]=='0' && val[1]=='x') + md=&val[2]; + else + md=val; + if ((ip->sha384digest = strdup(md)) == NULL) + mtree_err("memory allocation error"); break; + case F_SHA512: + if (val[0]=='0' && val[1]=='x') + md=&val[2]; + else + md=val; + if ((ip->sha512digest = strdup(md)) == NULL) + mtree_err("memory allocation error"); + break; + default: + mtree_err( + "set(): unsupported key type 0x%x (INTERNAL ERROR)", + type); + /* NOTREACHED */ } } } @@ -306,6 +694,152 @@ unset(char *t, NODE *ip) { char *p; - while ((p = strtok(t, "\n\t "))) + while ((p = strsep(&t, " \t")) != NULL) { + if (*p == '\0') + continue; ip->flags &= ~parsekey(p, NULL); + } +} + +/* + * addchild -- + * Add the centry node as a child of the pathparent node. If + * centry is a duplicate, call replacenode(). If centry is not + * a duplicate, insert it into the linked list referenced by + * pathparent->child. Keep the list sorted if Sflag is set. + */ +static void +addchild(NODE *pathparent, NODE *centry) +{ + NODE *samename; /* node with the same name as centry */ + NODE *replacepos; /* if non-NULL, centry should replace this node */ + NODE *insertpos; /* if non-NULL, centry should be inserted + * after this node */ + NODE *cur; /* for stepping through the list */ + NODE *last; /* the last node in the list */ + int cmp; + + samename = NULL; + replacepos = NULL; + insertpos = NULL; + last = NULL; + cur = pathparent->child; + if (cur == NULL) { + /* centry is pathparent's first and only child node so far */ + pathparent->child = centry; + return; + } + + /* + * pathparent already has at least one other child, so add the + * centry node to the list. + * + * We first scan through the list looking for an existing node + * with the same name (setting samename), and also looking + * for the correct position to replace or insert the new node + * (setting replacepos and/or insertpos). + */ + for (; cur != NULL; last = cur, cur = cur->next) { + if (strcmp(centry->name, cur->name) == 0) { + samename = cur; + } + if (mtree_Sflag) { + cmp = nodecmp(centry, cur); + if (cmp == 0) { + replacepos = cur; + } else if (cmp > 0) { + insertpos = cur; + } + } + } + if (! mtree_Sflag) { + if (samename != NULL) { + /* replace node with same name */ + replacepos = samename; + } else { + /* add new node at end of list */ + insertpos = last; + } + } + + if (samename != NULL) { + /* + * We found a node with the same name above. Call + * replacenode(), which will either exit with an error, + * or replace the information in the samename node and + * free the information in the centry node. + */ + replacenode(samename, centry); + if (samename == replacepos) { + /* The just-replaced node was in the correct position */ + return; + } + if (samename == insertpos || samename->prev == insertpos) { + /* + * We thought the new node should be just before + * or just after the replaced node, but that would + * be equivalent to just retaining the replaced node. + */ + return; + } + + /* + * The just-replaced node is in the wrong position in + * the list. This can happen if sort order depends on + * criteria other than the node name. + * + * Make centry point to the just-replaced node. Unlink + * the just-replaced node from the list, and allow it to + * be insterted in the correct position later. + */ + centry = samename; + if (centry->prev) + centry->prev->next = centry->next; + else { + /* centry->next is the new head of the list */ + pathparent->child = centry->next; + assert(centry->next != NULL); + } + if (centry->next) + centry->next->prev = centry->prev; + centry->prev = NULL; + centry->next = NULL; + } + + if (insertpos == NULL) { + /* insert centry at the beginning of the list */ + pathparent->child->prev = centry; + centry->next = pathparent->child; + centry->prev = NULL; + pathparent->child = centry; + } else { + /* insert centry into the list just after insertpos */ + centry->next = insertpos->next; + insertpos->next = centry; + centry->prev = insertpos; + if (centry->next) + centry->next->prev = centry; + } + return; +} + +/* + * nodecmp -- + * used as a comparison function by addchild() to control the order + * in which entries appear within a list of sibling nodes. We make + * directories sort after non-directories, but otherwise sort in + * strcmp() order. + * + * Keep this in sync with dcmp() in create.c. + */ +static int +nodecmp(const NODE *a, const NODE *b) +{ + + if ((a->type & F_DIR) != 0) { + if ((b->type & F_DIR) == 0) + return 1; + } else if ((b->type & F_DIR) != 0) + return -1; + return strcmp(a->name, b->name); } diff --git a/usr.sbin/mtree/specspec.c b/usr.sbin/mtree/specspec.c new file mode 100644 index 0000000000..b1276c3acc --- /dev/null +++ b/usr.sbin/mtree/specspec.c @@ -0,0 +1,270 @@ +/* $NetBSD: specspec.c,v 1.4 2017/12/14 18:34:41 christos Exp $ */ + +/*- + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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, inotype(nodetoino(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=%ju", (uintmax_t)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(" %s=%s", MD5KEY, n->md5digest); + if (f & F_SHA1) + printf(" %s=%s", SHA1KEY, n->sha1digest); + if (f & F_RMD160) + printf(" %s=%s", RMD160KEY, n->rmd160digest); + if (f & F_SHA256) + printf(" %s=%s", SHA256KEY, n->sha256digest); + if (f & F_SHA384) + printf(" %s=%s", SHA384KEY, n->sha384digest); + if (f & F_SHA512) + printf(" %s=%s", SHA512KEY, n->sha512digest); + if (f & F_FLAGS) + printf(" flags=%s", flags_to_string(n->st_flags, "none")); + 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 (FS(n1, n2, F_SHA384, sha384digest)) + differs |= F_SHA384; + if (FS(n1, n2, F_SHA512, sha512digest)) + differs |= F_SHA512; + 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 = spec(fi); + root2 = spec(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/mtree/stat_flags.c b/usr.sbin/mtree/stat_flags.c new file mode 100644 index 0000000000..35b8f1e58d --- /dev/null +++ b/usr.sbin/mtree/stat_flags.c @@ -0,0 +1,179 @@ +/* @(#)stat_flags.c 8.2 (Berkeley) 7/28/94 */ +/* $NetBSD: stat_flags.c,v 1.2 2007/01/16 17:34:02 cbiere Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#define HAVE_STRUCT_STAT_ST_FLAGS 1 +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +#define SAPPEND(s) do { \ + if (prefix != NULL) \ + (void)strlcat(string, prefix, sizeof(string)); \ + (void)strlcat(string, s, sizeof(string)); \ + prefix = ","; \ +} while (/* CONSTCOND */ 0) + +/* + * flags_to_string -- + * Convert stat flags to a comma-separated string. If no flags + * are set, return the default string. + */ +char * +flags_to_string(u_long flags, const char *def) +{ + char string[128]; + const char *prefix; + + string[0] = '\0'; + prefix = NULL; +#if HAVE_STRUCT_STAT_ST_FLAGS + if (flags & UF_APPEND) + SAPPEND("uappnd"); + if (flags & UF_IMMUTABLE) + SAPPEND("uchg"); + if (flags & UF_NODUMP) + SAPPEND("nodump"); + if (flags & UF_OPAQUE) + SAPPEND("opaque"); + if (flags & SF_APPEND) + SAPPEND("sappnd"); + if (flags & SF_ARCHIVED) + SAPPEND("arch"); + if (flags & SF_IMMUTABLE) + SAPPEND("schg"); +#ifdef SF_SNAPSHOT + if (flags & SF_SNAPSHOT) + SAPPEND("snap"); +#endif +#endif + if (prefix != NULL) + return strdup(string); + return strdup(def); +} + +#define TEST(a, b, f) { \ + if (!strcmp(a, b)) { \ + if (clear) { \ + if (clrp) \ + *clrp |= (f); \ + if (setp) \ + *setp &= ~(f); \ + } else { \ + if (setp) \ + *setp |= (f); \ + if (clrp) \ + *clrp &= ~(f); \ + } \ + break; \ + } \ +} + +/* + * string_to_flags -- + * Take string of arguments and return stat flags. Return 0 on + * success, 1 on failure. On failure, stringp is set to point + * to the offending token. + */ +int +string_to_flags(char **stringp, u_long *setp, u_long *clrp) +{ + int clear; + char *string, *p; + + if (setp) + *setp = 0; + if (clrp) + *clrp = 0; + +#if HAVE_STRUCT_STAT_ST_FLAGS + string = *stringp; + while ((p = strsep(&string, "\t ,")) != NULL) { + clear = 0; + *stringp = p; + if (*p == '\0') + continue; + if (p[0] == 'n' && p[1] == 'o') { + clear = 1; + p += 2; + } + switch (p[0]) { + case 'a': + TEST(p, "arch", SF_ARCHIVED); + TEST(p, "archived", SF_ARCHIVED); + return (1); + case 'd': + clear = !clear; + TEST(p, "dump", UF_NODUMP); + return (1); + case 'n': + /* + * Support `nonodump'. Note that + * the state of clear is not changed. + */ + TEST(p, "nodump", UF_NODUMP); + return (1); + case 'o': + TEST(p, "opaque", UF_OPAQUE); + return (1); + case 's': + TEST(p, "sappnd", SF_APPEND); + TEST(p, "sappend", SF_APPEND); + TEST(p, "schg", SF_IMMUTABLE); + TEST(p, "schange", SF_IMMUTABLE); + TEST(p, "simmutable", SF_IMMUTABLE); + return (1); + case 'u': + TEST(p, "uappnd", UF_APPEND); + TEST(p, "uappend", UF_APPEND); + TEST(p, "uchg", UF_IMMUTABLE); + TEST(p, "uchange", UF_IMMUTABLE); + TEST(p, "uimmutable", UF_IMMUTABLE); + return (1); + default: + return (1); + } + } +#endif + + return (0); +} diff --git a/usr.sbin/mtree/verify.c b/usr.sbin/mtree/verify.c index 2e6ca80a4c..0f02030750 100644 --- a/usr.sbin/mtree/verify.c +++ b/usr.sbin/mtree/verify.c @@ -1,3 +1,6 @@ +/* @(#)verify.c 8.1 (Berkeley) 6/6/93 */ +/* $NetBSD: verify.c,v 1.46 2015/01/23 20:28:24 christos Exp $ */ + /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. @@ -25,21 +28,25 @@ * 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. - * - * @(#)verify.c 8.1 (Berkeley) 6/6/93 - * $FreeBSD: src/usr.sbin/mtree/verify.c,v 1.10.2.2 2001/01/12 19:17:18 phk Exp $ */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + #include #include + +#if ! HAVE_NBTOOL_CONFIG_H #include -#include +#endif + #include -#include #include #include +#include #include -#include "mtree.h" + #include "extern.h" static NODE *root; @@ -49,11 +56,11 @@ static void miss(NODE *, char *); static int vwalk(void); int -verify(void) +verify(FILE *fi) { int rval; - root = spec(); + root = spec(fi); rval = vwalk(); miss(root, path); return (rval); @@ -66,19 +73,24 @@ vwalk(void) FTSENT *p; NODE *ep, *level; int specdepth, rval; - char *argv[2], dot[] = "."; - + char *argv[2]; + char dot[] = "."; argv[0] = dot; argv[1] = NULL; + if ((t = fts_open(argv, ftsoptions, NULL)) == NULL) - err(1, "line %d: fts_open", lineno); + mtree_err("fts_open: %s", strerror(errno)); level = root; specdepth = rval = 0; - while ((p = fts_read(t))) { + while ((p = fts_read(t)) != NULL) { if (check_excludes(p->fts_name, p->fts_path)) { fts_set(t, p, FTS_SKIP); continue; } + if (!find_only(p->fts_path)) { + fts_set(t, p, FTS_SKIP); + continue; + } switch(p->fts_info) { case FTS_D: case FTS_SL: @@ -86,7 +98,8 @@ vwalk(void) case FTS_DP: if (specdepth > p->fts_level) { for (level = level->parent; level->prev; - level = level->prev); + level = level->prev) + continue; --specdepth; } continue; @@ -110,22 +123,30 @@ vwalk(void) if ((ep->flags & F_NOCHANGE) == 0 && compare(ep, p)) rval = MISMATCHEXIT; - if (ep->flags & F_IGN) - fts_set(t, p, FTS_SKIP); - else if (ep->child && ep->type == F_DIR && + if (!(ep->flags & F_IGN) && + ep->type == F_DIR && p->fts_info == FTS_D) { - level = ep->child; - ++specdepth; - } + if (ep->child) { + level = ep->child; + ++specdepth; + } + } else + fts_set(t, p, FTS_SKIP); break; } if (ep) continue; -extra: - if (!eflag) { - printf("%s extra", RP(p)); + extra: + if (!eflag && !(dflag && p->fts_info == FTS_SL)) { + printf("extra: %s", RP(p)); if (rflag) { +#if HAVE_STRUCT_STAT_ST_FLAGS + if (rflag > 1 && + lchflags(p->fts_accpath, 0) == -1) + printf(" (chflags %s)", + strerror(errno)); +#endif if ((S_ISDIR(p->fts_statp->st_mode) ? rmdir : unlink)(p->fts_accpath)) { printf(", not removed: %s", @@ -149,79 +170,136 @@ miss(NODE *p, char *tail) int create; char *tp; const char *type; + u_int32_t flags; for (; p; p = p->next) { + if (p->flags & F_OPT && !(p->flags & F_VISIT)) + continue; if (p->type != F_DIR && (dflag || p->flags & F_VISIT)) continue; strcpy(tail, p->name); if (!(p->flags & F_VISIT)) { - /* Don't print missing message if file exists as a + /* 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) + + if (qflag && stat(path, &statbuf) == 0 && + S_ISDIR(statbuf.st_mode)) p->flags |= F_VISIT; else - printf("%s missing", path); + (void)printf("%s missing", path); } - if (p->type != F_DIR && p->type != F_LINK) { + switch (p->type) { + case F_BLOCK: + case F_CHAR: + type = "device"; + break; + case F_DIR: + type = "directory"; + break; + case F_LINK: + type = "symlink"; + break; + default: putchar('\n'); continue; } create = 0; - if (p->type == F_LINK) - type = "symlink"; - else - type = "directory"; if (!(p->flags & F_VISIT) && uflag) { + if (mtree_Wflag || p->type == F_LINK) + goto createit; if (!(p->flags & (F_UID | F_UNAME))) - printf(" (%s not created: user not specified)", type); + printf( + " (%s not created: user not specified)", type); else if (!(p->flags & (F_GID | F_GNAME))) - printf(" (%s not created: group not specified)", type); - else if (p->type == F_LINK) { - if (symlink(p->slink, path)) - printf(" (symlink not created: %s)\n", + printf( + " (%s not created: group not specified)", type); + else if (!(p->flags & F_MODE)) + printf( + " (%s not created: mode not specified)", type); + else + createit: + switch (p->type) { + case F_BLOCK: + case F_CHAR: + if (mtree_Wflag) + continue; + if (!(p->flags & F_DEV)) + printf( + " (%s not created: device not specified)", + type); + else if (mknod(path, + p->st_mode | nodetoino(p->type), + p->st_rdev) == -1) + printf(" (%s not created: %s)\n", + type, strerror(errno)); + else + create = 1; + break; + case F_LINK: + if (!(p->flags & F_SLINK)) + printf( + " (%s not created: link not specified)\n", + type); + else if (symlink(p->slink, path)) + printf( + " (%s not created: %s)\n", + type, strerror(errno)); + else + create = 1; + break; + case F_DIR: + if (mkdir(path, S_IRWXU|S_IRWXG|S_IRWXO)) + printf(" (not created: %s)", strerror(errno)); else - printf(" (created)\n"); - if (lchown(path, p->st_uid, p->st_gid)) - printf("%s: user/group not modified: %s\n", - path, strerror(errno)); - continue; - } else if (!(p->flags & F_MODE)) - printf(" (directory not created: mode not specified)"); - else if (mkdir(path, S_IRWXU)) - printf(" (directory not created: %s)", - strerror(errno)); - else { - create = 1; - printf(" (created)"); + create = 1; + break; + default: + mtree_err("can't create create %s", + nodetype(p->type)); } } - if (!(p->flags & F_VISIT)) + if (create) + printf(" (created)"); + if (p->type == F_DIR) { + if (!(p->flags & F_VISIT)) + putchar('\n'); + for (tp = tail; *tp; ++tp) + continue; + *tp = '/'; + miss(p->child, tp + 1); + *tp = '\0'; + } else putchar('\n'); - for (tp = tail; *tp; ++tp); - *tp = '/'; - miss(p->child, tp + 1); - *tp = '\0'; - - if (!create) + if (!create || mtree_Wflag) continue; - if (chown(path, p->st_uid, p->st_gid)) { + if ((p->flags & (F_UID | F_UNAME)) && + (p->flags & (F_GID | F_GNAME)) && + (lchown(path, p->st_uid, p->st_gid))) { printf("%s: user/group/mode not modified: %s\n", path, strerror(errno)); printf("%s: warning: file mode %snot set\n", path, (p->flags & F_FLAGS) ? "and file flags " : ""); continue; } - if (chmod(path, p->st_mode)) - printf("%s: permissions not set: %s\n", - path, strerror(errno)); - if ((p->flags & F_FLAGS) && p->st_flags && - chflags(path, p->st_flags)) - printf("%s: file flags not set: %s\n", - path, strerror(errno)); + if (p->flags & F_MODE) { + if (lchmod(path, p->st_mode)) + printf("%s: permissions not set: %s\n", + path, strerror(errno)); + } +#if HAVE_STRUCT_STAT_ST_FLAGS + if ((p->flags & F_FLAGS) && p->st_flags) { + if (iflag) + flags = p->st_flags; + else + flags = p->st_flags & ~SP_FLGS; + if (lchflags(path, flags)) + printf("%s: file flags not set: %s\n", + path, strerror(errno)); + } +#endif /* HAVE_STRUCT_STAT_ST_FLAGS */ } } -- 2.11.4.GIT