diff --git a/bin/chflags/chflags.c b/bin/chflags/chflags.c index c1d9c18b9b6b..55dedfce53f1 100644 --- a/bin/chflags/chflags.c +++ b/bin/chflags/chflags.c @@ -1,224 +1,224 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1992, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif #ifndef lint static char sccsid[] = "@(#)chflags.c 8.5 (Berkeley) 4/1/94"; #endif #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include static volatile sig_atomic_t siginfo; static void usage(void); static void siginfo_handler(int sig __unused) { siginfo = 1; } int main(int argc, char *argv[]) { FTS *ftsp; FTSENT *p; u_long clear, newflags, set; long val; int Hflag, Lflag, Rflag, fflag, hflag, vflag, xflag; int ch, fts_options, oct, rval; char *flags, *ep; Hflag = Lflag = Rflag = fflag = hflag = vflag = xflag = 0; while ((ch = getopt(argc, argv, "HLPRfhvx")) != -1) switch (ch) { case 'H': Hflag = 1; Lflag = 0; break; case 'L': Lflag = 1; Hflag = 0; break; case 'P': Hflag = Lflag = 0; break; case 'R': Rflag = 1; break; case 'f': fflag = 1; break; case 'h': hflag = 1; break; case 'v': vflag++; break; case 'x': xflag = 1; break; case '?': default: usage(); } argv += optind; argc -= optind; if (argc < 2) usage(); (void)signal(SIGINFO, siginfo_handler); if (Rflag) { if (hflag) errx(1, "the -R and -h options may not be " "specified together."); if (Lflag) { fts_options = FTS_LOGICAL; } else { fts_options = FTS_PHYSICAL; if (Hflag) { fts_options |= FTS_COMFOLLOW; } } } else if (hflag) { fts_options = FTS_PHYSICAL; } else { fts_options = FTS_LOGICAL; } if (xflag) fts_options |= FTS_XDEV; flags = *argv; if (*flags >= '0' && *flags <= '7') { errno = 0; val = strtol(flags, &ep, 8); if (val < 0) errno = ERANGE; if (errno) err(1, "invalid flags: %s", flags); if (*ep) errx(1, "invalid flags: %s", flags); set = val; oct = 1; } else { if (strtofflags(&flags, &set, &clear)) errx(1, "invalid flag: %s", flags); clear = ~clear; oct = 0; } if ((ftsp = fts_open(++argv, fts_options , 0)) == NULL) err(1, NULL); - for (rval = 0; (p = fts_read(ftsp)) != NULL;) { + for (rval = 0; errno = 0, (p = fts_read(ftsp)) != NULL;) { int atflag; if ((fts_options & FTS_LOGICAL) || ((fts_options & FTS_COMFOLLOW) && p->fts_level == FTS_ROOTLEVEL)) atflag = 0; else atflag = AT_SYMLINK_NOFOLLOW; switch (p->fts_info) { case FTS_D: /* Change it at FTS_DP if we're recursive. */ if (!Rflag) fts_set(ftsp, p, FTS_SKIP); continue; case FTS_DNR: /* Warn, chflags. */ warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; break; case FTS_ERR: /* Warn, continue. */ case FTS_NS: warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; continue; default: break; } if (oct) newflags = set; else newflags = (p->fts_statp->st_flags | set) & clear; if (newflags == p->fts_statp->st_flags) continue; if (chflagsat(AT_FDCWD, p->fts_accpath, newflags, atflag) == -1 && !fflag) { warn("%s", p->fts_path); rval = 1; } else if (vflag || siginfo) { (void)printf("%s", p->fts_path); if (vflag > 1 || siginfo) (void)printf(": 0%lo -> 0%lo", (u_long)p->fts_statp->st_flags, newflags); (void)printf("\n"); siginfo = 0; } } if (errno) err(1, "fts_read"); exit(rval); } static void usage(void) { (void)fprintf(stderr, "usage: chflags [-fhvx] [-R [-H | -L | -P]] flags file ...\n"); exit(1); } diff --git a/bin/chmod/chmod.c b/bin/chmod/chmod.c index 07b7c37312d5..e8657af0d97b 100644 --- a/bin/chmod/chmod.c +++ b/bin/chmod/chmod.c @@ -1,262 +1,262 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static char const copyright[] = "@(#) Copyright (c) 1989, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)chmod.c 8.8 (Berkeley) 4/1/94"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include static volatile sig_atomic_t siginfo; static void usage(void); static int may_have_nfs4acl(const FTSENT *ent, int hflag); static void siginfo_handler(int sig __unused) { siginfo = 1; } int main(int argc, char *argv[]) { FTS *ftsp; FTSENT *p; mode_t *set; int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval; int vflag; char *mode; mode_t newmode; set = NULL; Hflag = Lflag = Rflag = fflag = hflag = vflag = 0; while ((ch = getopt(argc, argv, "HLPRXfghorstuvwx")) != -1) switch (ch) { case 'H': Hflag = 1; Lflag = 0; break; case 'L': Lflag = 1; Hflag = 0; break; case 'P': Hflag = Lflag = 0; break; case 'R': Rflag = 1; break; case 'f': fflag = 1; break; case 'h': /* * In System V the -h option causes chmod to change * the mode of the symbolic link. 4.4BSD's symbolic * links didn't have modes, so it was an undocumented * noop. In FreeBSD 3.0, lchmod(2) is introduced and * this option does real work. */ hflag = 1; break; /* * XXX * "-[rwx]" are valid mode commands. If they are the entire * argument, getopt has moved past them, so decrement optind. * Regardless, we're done argument processing. */ case 'g': case 'o': case 'r': case 's': case 't': case 'u': case 'w': case 'X': case 'x': if (argv[optind - 1][0] == '-' && argv[optind - 1][1] == ch && argv[optind - 1][2] == '\0') --optind; goto done; case 'v': vflag++; break; case '?': default: usage(); } done: argv += optind; argc -= optind; if (argc < 2) usage(); (void)signal(SIGINFO, siginfo_handler); if (Rflag) { if (hflag) errx(1, "the -R and -h options may not be " "specified together."); if (Lflag) { fts_options = FTS_LOGICAL; } else { fts_options = FTS_PHYSICAL; if (Hflag) { fts_options |= FTS_COMFOLLOW; } } } else if (hflag) { fts_options = FTS_PHYSICAL; } else { fts_options = FTS_LOGICAL; } mode = *argv; if ((set = setmode(mode)) == NULL) errx(1, "invalid file mode: %s", mode); if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) err(1, "fts_open"); - for (rval = 0; (p = fts_read(ftsp)) != NULL;) { + for (rval = 0; errno = 0, (p = fts_read(ftsp)) != NULL;) { int atflag; if ((fts_options & FTS_LOGICAL) || ((fts_options & FTS_COMFOLLOW) && p->fts_level == FTS_ROOTLEVEL)) atflag = 0; else atflag = AT_SYMLINK_NOFOLLOW; switch (p->fts_info) { case FTS_D: if (!Rflag) fts_set(ftsp, p, FTS_SKIP); break; case FTS_DNR: /* Warn, chmod. */ warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; break; case FTS_DP: /* Already changed at FTS_D. */ continue; case FTS_ERR: /* Warn, continue. */ case FTS_NS: warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; continue; default: break; } newmode = getmode(set, p->fts_statp->st_mode); /* * With NFSv4 ACLs, it is possible that applying a mode * identical to the one computed from an ACL will change * that ACL. */ if (may_have_nfs4acl(p, hflag) == 0 && (newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS)) continue; if (fchmodat(AT_FDCWD, p->fts_accpath, newmode, atflag) == -1 && !fflag) { warn("%s", p->fts_path); rval = 1; } else if (vflag || siginfo) { (void)printf("%s", p->fts_path); if (vflag > 1 || siginfo) { char m1[12], m2[12]; strmode(p->fts_statp->st_mode, m1); strmode((p->fts_statp->st_mode & S_IFMT) | newmode, m2); (void)printf(": 0%o [%s] -> 0%o [%s]", p->fts_statp->st_mode, m1, (p->fts_statp->st_mode & S_IFMT) | newmode, m2); } (void)printf("\n"); siginfo = 0; } } if (errno) err(1, "fts_read"); exit(rval); } static void usage(void) { (void)fprintf(stderr, "usage: chmod [-fhv] [-R [-H | -L | -P]] mode file ...\n"); exit(1); } static int may_have_nfs4acl(const FTSENT *ent, int hflag) { int ret; static dev_t previous_dev = NODEV; static int supports_acls = -1; if (previous_dev != ent->fts_statp->st_dev) { previous_dev = ent->fts_statp->st_dev; supports_acls = 0; if (hflag) ret = lpathconf(ent->fts_accpath, _PC_ACL_NFS4); else ret = pathconf(ent->fts_accpath, _PC_ACL_NFS4); if (ret > 0) supports_acls = 1; else if (ret < 0 && errno != EINVAL) warn("%s", ent->fts_path); } return (supports_acls); } diff --git a/bin/cp/cp.c b/bin/cp/cp.c index 6c9a57d30d78..3a23394df35d 100644 --- a/bin/cp/cp.c +++ b/bin/cp/cp.c @@ -1,503 +1,504 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * David Hitz of Auspex Systems Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static char const copyright[] = "@(#) Copyright (c) 1988, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)cp.c 8.2 (Berkeley) 4/1/94"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); /* * Cp copies source files to target files. * * The global PATH_T structure "to" always contains the path to the * current target file. Since fts(3) does not change directories, * this path can be either absolute or dot-relative. * * The basic algorithm is to initialize "to" and use fts(3) to traverse * the file hierarchy rooted in the argument list. A trivial case is the * case of 'cp file1 file2'. The more interesting case is the case of * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the * path (relative to the root of the traversal) is appended to dir (stored * in "to") to form the final target path. */ #include #include #include #include #include #include #include #include #include #include #include #include "extern.h" #define STRIP_TRAILING_SLASH(p) { \ while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \ *--(p).p_end = 0; \ } static char emptystring[] = ""; PATH_T to = { to.p_path, emptystring, "" }; int fflag, iflag, lflag, nflag, pflag, sflag, vflag; static int Rflag, rflag; volatile sig_atomic_t info; enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; static int copy(char *[], enum op, int); static void siginfo(int __unused); int main(int argc, char *argv[]) { struct stat to_stat, tmp_stat; enum op type; int Hflag, Lflag, ch, fts_options, r, have_trailing_slash; char *target; fts_options = FTS_NOCHDIR | FTS_PHYSICAL; Hflag = Lflag = 0; while ((ch = getopt(argc, argv, "HLPRafilnprsvx")) != -1) switch (ch) { case 'H': Hflag = 1; Lflag = 0; break; case 'L': Lflag = 1; Hflag = 0; break; case 'P': Hflag = Lflag = 0; break; case 'R': Rflag = 1; break; case 'a': pflag = 1; Rflag = 1; Hflag = Lflag = 0; break; case 'f': fflag = 1; iflag = nflag = 0; break; case 'i': iflag = 1; fflag = nflag = 0; break; case 'l': lflag = 1; break; case 'n': nflag = 1; fflag = iflag = 0; break; case 'p': pflag = 1; break; case 'r': rflag = Lflag = 1; Hflag = 0; break; case 's': sflag = 1; break; case 'v': vflag = 1; break; case 'x': fts_options |= FTS_XDEV; break; default: usage(); break; } argc -= optind; argv += optind; if (argc < 2) usage(); if (Rflag && rflag) errx(1, "the -R and -r options may not be specified together"); if (lflag && sflag) errx(1, "the -l and -s options may not be specified together"); if (rflag) Rflag = 1; if (Rflag) { if (Hflag) fts_options |= FTS_COMFOLLOW; if (Lflag) { fts_options &= ~FTS_PHYSICAL; fts_options |= FTS_LOGICAL; } } else { fts_options &= ~FTS_PHYSICAL; fts_options |= FTS_LOGICAL | FTS_COMFOLLOW; } (void)signal(SIGINFO, siginfo); /* Save the target base in "to". */ target = argv[--argc]; if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path)) errx(1, "%s: name too long", target); to.p_end = to.p_path + strlen(to.p_path); if (to.p_path == to.p_end) { *to.p_end++ = '.'; *to.p_end = 0; } have_trailing_slash = (to.p_end[-1] == '/'); if (have_trailing_slash) STRIP_TRAILING_SLASH(to); to.target_end = to.p_end; /* Set end of argument list for fts(3). */ argv[argc] = NULL; /* * Cp has two distinct cases: * * cp [-R] source target * cp [-R] source1 ... sourceN directory * * In both cases, source can be either a file or a directory. * * In (1), the target becomes a copy of the source. That is, if the * source is a file, the target will be a file, and likewise for * directories. * * In (2), the real target is not directory, but "directory/source". */ r = stat(to.p_path, &to_stat); if (r == -1 && errno != ENOENT) err(1, "%s", to.p_path); if (r == -1 || !S_ISDIR(to_stat.st_mode)) { /* * Case (1). Target is not a directory. */ if (argc > 1) errx(1, "%s is not a directory", to.p_path); /* * Need to detect the case: * cp -R dir foo * Where dir is a directory and foo does not exist, where * we want pathname concatenations turned on but not for * the initial mkdir(). */ if (r == -1) { if (Rflag && (Lflag || Hflag)) stat(*argv, &tmp_stat); else lstat(*argv, &tmp_stat); if (S_ISDIR(tmp_stat.st_mode) && Rflag) type = DIR_TO_DNE; else type = FILE_TO_FILE; } else type = FILE_TO_FILE; if (have_trailing_slash && type == FILE_TO_FILE) { if (r == -1) { errx(1, "directory %s does not exist", to.p_path); } else errx(1, "%s is not a directory", to.p_path); } } else /* * Case (2). Target is a directory. */ type = FILE_TO_DIR; exit (copy(argv, type, fts_options)); } static int copy(char *argv[], enum op type, int fts_options) { struct stat to_stat; FTS *ftsp; FTSENT *curr; int base = 0, dne, badcp, rval; size_t nlen; char *p, *target_mid; mode_t mask, mode; /* * Keep an inverted copy of the umask, for use in correcting * permissions on created directories when not using -p. */ mask = ~umask(0777); umask(~mask); if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL) err(1, "fts_open"); - for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) { + for (badcp = rval = 0; errno = 0, (curr = fts_read(ftsp)) != NULL; + badcp = 0) { switch (curr->fts_info) { case FTS_NS: case FTS_DNR: case FTS_ERR: warnx("%s: %s", curr->fts_path, strerror(curr->fts_errno)); badcp = rval = 1; continue; case FTS_DC: /* Warn, continue. */ warnx("%s: directory causes a cycle", curr->fts_path); badcp = rval = 1; continue; default: ; } /* * If we are in case (2) or (3) above, we need to append the * source name to the target name. */ if (type != FILE_TO_FILE) { /* * Need to remember the roots of traversals to create * correct pathnames. If there's a directory being * copied to a non-existent directory, e.g. * cp -R a/dir noexist * the resulting path name should be noexist/foo, not * noexist/dir/foo (where foo is a file in dir), which * is the case where the target exists. * * Also, check for "..". This is for correct path * concatenation for paths ending in "..", e.g. * cp -R .. /tmp * Paths ending in ".." are changed to ".". This is * tricky, but seems the easiest way to fix the problem. * * XXX * Since the first level MUST be FTS_ROOTLEVEL, base * is always initialized. */ if (curr->fts_level == FTS_ROOTLEVEL) { if (type != DIR_TO_DNE) { p = strrchr(curr->fts_path, '/'); base = (p == NULL) ? 0 : (int)(p - curr->fts_path + 1); if (!strcmp(&curr->fts_path[base], "..")) base += 1; } else base = curr->fts_pathlen; } p = &curr->fts_path[base]; nlen = curr->fts_pathlen - base; target_mid = to.target_end; if (*p != '/' && target_mid[-1] != '/') *target_mid++ = '/'; *target_mid = 0; if (target_mid - to.p_path + nlen >= PATH_MAX) { warnx("%s%s: name too long (not copied)", to.p_path, p); badcp = rval = 1; continue; } (void)strncat(target_mid, p, nlen); to.p_end = target_mid + nlen; *to.p_end = 0; STRIP_TRAILING_SLASH(to); } if (curr->fts_info == FTS_DP) { /* * We are nearly finished with this directory. If we * didn't actually copy it, or otherwise don't need to * change its attributes, then we are done. */ if (!curr->fts_number) continue; /* * If -p is in effect, set all the attributes. * Otherwise, set the correct permissions, limited * by the umask. Optimise by avoiding a chmod() * if possible (which is usually the case if we * made the directory). Note that mkdir() does not * honour setuid, setgid and sticky bits, but we * normally want to preserve them on directories. */ if (pflag) { if (setfile(curr->fts_statp, -1)) rval = 1; if (preserve_dir_acls(curr->fts_statp, curr->fts_accpath, to.p_path) != 0) rval = 1; } else { mode = curr->fts_statp->st_mode; if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) || ((mode | S_IRWXU) & mask) != (mode & mask)) if (chmod(to.p_path, mode & mask) != 0) { warn("chmod: %s", to.p_path); rval = 1; } } continue; } /* Not an error but need to remember it happened. */ if (stat(to.p_path, &to_stat) == -1) dne = 1; else { if (to_stat.st_dev == curr->fts_statp->st_dev && to_stat.st_ino == curr->fts_statp->st_ino) { warnx("%s and %s are identical (not copied).", to.p_path, curr->fts_path); badcp = rval = 1; if (S_ISDIR(curr->fts_statp->st_mode)) (void)fts_set(ftsp, curr, FTS_SKIP); continue; } if (!S_ISDIR(curr->fts_statp->st_mode) && S_ISDIR(to_stat.st_mode)) { warnx("cannot overwrite directory %s with " "non-directory %s", to.p_path, curr->fts_path); badcp = rval = 1; continue; } dne = 0; } switch (curr->fts_statp->st_mode & S_IFMT) { case S_IFLNK: /* Catch special case of a non-dangling symlink. */ if ((fts_options & FTS_LOGICAL) || ((fts_options & FTS_COMFOLLOW) && curr->fts_level == 0)) { if (copy_file(curr, dne)) badcp = rval = 1; } else { if (copy_link(curr, !dne)) badcp = rval = 1; } break; case S_IFDIR: if (!Rflag) { warnx("%s is a directory (not copied).", curr->fts_path); (void)fts_set(ftsp, curr, FTS_SKIP); badcp = rval = 1; break; } /* * If the directory doesn't exist, create the new * one with the from file mode plus owner RWX bits, * modified by the umask. Trade-off between being * able to write the directory (if from directory is * 555) and not causing a permissions race. If the * umask blocks owner writes, we fail. */ if (dne) { if (mkdir(to.p_path, curr->fts_statp->st_mode | S_IRWXU) < 0) err(1, "%s", to.p_path); } else if (!S_ISDIR(to_stat.st_mode)) { errno = ENOTDIR; err(1, "%s", to.p_path); } /* * Arrange to correct directory attributes later * (in the post-order phase) if this is a new * directory, or if the -p flag is in effect. */ curr->fts_number = pflag || dne; break; case S_IFBLK: case S_IFCHR: if (Rflag && !sflag) { if (copy_special(curr->fts_statp, !dne)) badcp = rval = 1; } else { if (copy_file(curr, dne)) badcp = rval = 1; } break; case S_IFSOCK: warnx("%s is a socket (not copied).", curr->fts_path); break; case S_IFIFO: if (Rflag && !sflag) { if (copy_fifo(curr->fts_statp, !dne)) badcp = rval = 1; } else { if (copy_file(curr, dne)) badcp = rval = 1; } break; default: if (copy_file(curr, dne)) badcp = rval = 1; break; } if (vflag && !badcp) (void)printf("%s -> %s\n", curr->fts_path, to.p_path); } if (errno) err(1, "fts_read"); fts_close(ftsp); return (rval); } static void siginfo(int sig __unused) { info = 1; } diff --git a/bin/ls/ls.c b/bin/ls/ls.c index 7378268867ef..b0fe8d79e3ea 100644 --- a/bin/ls/ls.c +++ b/bin/ls/ls.c @@ -1,1045 +1,1045 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Michael Fischbein. * * 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #if 0 #ifndef lint static char sccsid[] = "@(#)ls.c 8.5 (Berkeley) 4/2/94"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef COLORLS #include #include #endif #include "ls.h" #include "extern.h" /* * Upward approximation of the maximum number of characters needed to * represent a value of integral type t as a string, excluding the * NUL terminator, with provision for a sign. */ #define STRBUF_SIZEOF(t) (1 + CHAR_BIT * sizeof(t) / 3 + 1) /* * MAKENINES(n) turns n into (10**n)-1. This is useful for converting a width * into a number that wide in decimal. * XXX: Overflows are not considered. */ #define MAKENINES(n) \ do { \ intmax_t i; \ \ /* Use a loop as all values of n are small. */ \ for (i = 1; n > 0; i *= 10) \ n--; \ n = i - 1; \ } while(0) static void display(const FTSENT *, FTSENT *, int); static int mastercmp(const FTSENT * const *, const FTSENT * const *); static void traverse(int, char **, int); #define COLOR_OPT (CHAR_MAX + 1) static const struct option long_opts[] = { #ifdef COLORLS {"color", optional_argument, NULL, COLOR_OPT}, #endif {NULL, no_argument, NULL, 0} }; static void (*printfcn)(const DISPLAY *); static int (*sortfcn)(const FTSENT *, const FTSENT *); long blocksize; /* block size units */ int termwidth = 80; /* default terminal width */ /* flags */ int f_accesstime; /* use time of last access */ int f_birthtime; /* use time of birth */ int f_flags; /* show flags associated with a file */ int f_humanval; /* show human-readable file sizes */ int f_inode; /* print inode */ static int f_kblocks; /* print size in kilobytes */ int f_label; /* show MAC label */ static int f_listdir; /* list actual directory, not contents */ static int f_listdot; /* list files beginning with . */ int f_longform; /* long listing format */ static int f_noautodot; /* do not automatically enable -A for root */ static int f_nofollow; /* don't follow symbolic link arguments */ int f_nonprint; /* show unprintables as ? */ static int f_nosort; /* don't sort output */ int f_notabs; /* don't use tab-separated multi-col output */ static int f_numericonly; /* don't convert uid/gid to name */ int f_octal; /* show unprintables as \xxx */ int f_octal_escape; /* like f_octal but use C escapes if possible */ static int f_recursive; /* ls subdirectories also */ static int f_reversesort; /* reverse whatever sort is used */ int f_samesort; /* sort time and name in same direction */ int f_sectime; /* print full time information */ static int f_singlecol; /* use single column output */ int f_size; /* list size in short listing */ static int f_sizesort; int f_slash; /* similar to f_type, but only for dirs */ int f_sortacross; /* sort across rows, not down columns */ int f_statustime; /* use time of last mode change */ static int f_stream; /* stream the output, separate with commas */ int f_thousands; /* show file sizes with thousands separators */ char *f_timeformat; /* user-specified time format */ static int f_timesort; /* sort by time vice name */ int f_type; /* add type character for non-regular files */ static int f_whiteout; /* show whiteout entries */ #ifdef COLORLS int colorflag = COLORFLAG_NEVER; /* passed in colorflag */ int f_color; /* add type in color for non-regular files */ bool explicitansi; /* Explicit ANSI sequences, no termcap(5) */ char *ansi_bgcol; /* ANSI sequence to set background colour */ char *ansi_fgcol; /* ANSI sequence to set foreground colour */ char *ansi_coloff; /* ANSI sequence to reset colours */ char *attrs_off; /* ANSI sequence to turn off attributes */ char *enter_bold; /* ANSI sequence to set color to bold mode */ #endif static int rval; static bool do_color_from_env(void) { const char *p; bool doit; doit = false; p = getenv("CLICOLOR"); if (p == NULL) { /* * COLORTERM is the more standard name for this variable. We'll * honor it as long as it's both set and not empty. */ p = getenv("COLORTERM"); if (p != NULL && *p != '\0') doit = true; } else doit = true; return (doit && (isatty(STDOUT_FILENO) || getenv("CLICOLOR_FORCE"))); } static bool do_color(void) { #ifdef COLORLS if (colorflag == COLORFLAG_NEVER) return (false); else if (colorflag == COLORFLAG_ALWAYS) return (true); #endif return (do_color_from_env()); } #ifdef COLORLS static bool do_color_always(const char *term) { return (strcmp(term, "always") == 0 || strcmp(term, "yes") == 0 || strcmp(term, "force") == 0); } static bool do_color_never(const char *term) { return (strcmp(term, "never") == 0 || strcmp(term, "no") == 0 || strcmp(term, "none") == 0); } static bool do_color_auto(const char *term) { return (strcmp(term, "auto") == 0 || strcmp(term, "tty") == 0 || strcmp(term, "if-tty") == 0); } #endif /* COLORLS */ int main(int argc, char *argv[]) { static char dot[] = ".", *dotav[] = {dot, NULL}; struct winsize win; int ch, fts_options, notused; char *p; const char *errstr = NULL; #ifdef COLORLS char termcapbuf[1024]; /* termcap definition buffer */ char tcapbuf[512]; /* capability buffer */ char *bp = tcapbuf, *term; #endif (void)setlocale(LC_ALL, ""); /* Terminal defaults to -Cq, non-terminal defaults to -1. */ if (isatty(STDOUT_FILENO)) { termwidth = 80; if ((p = getenv("COLUMNS")) != NULL && *p != '\0') termwidth = strtonum(p, 0, INT_MAX, &errstr); else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) != -1 && win.ws_col > 0) termwidth = win.ws_col; f_nonprint = 1; } else { f_singlecol = 1; /* retrieve environment variable, in case of explicit -C */ p = getenv("COLUMNS"); if (p) termwidth = strtonum(p, 0, INT_MAX, &errstr); } if (errstr) termwidth = 80; fts_options = FTS_PHYSICAL; if (getenv("LS_SAMESORT")) f_samesort = 1; /* * For historical compatibility, we'll use our autodetection if CLICOLOR * is set. */ #ifdef COLORLS if (getenv("CLICOLOR")) colorflag = COLORFLAG_AUTO; #endif while ((ch = getopt_long(argc, argv, "+1ABCD:FGHILPRSTUWXZabcdfghiklmnopqrstuwxy,", long_opts, NULL)) != -1) { switch (ch) { /* * The -1, -C, -x and -l options all override each other so * shell aliasing works right. */ case '1': f_singlecol = 1; f_longform = 0; f_stream = 0; break; case 'C': f_sortacross = f_longform = f_singlecol = 0; break; case 'l': f_longform = 1; f_singlecol = 0; f_stream = 0; break; case 'x': f_sortacross = 1; f_longform = 0; f_singlecol = 0; break; /* The -c, -u, and -U options override each other. */ case 'c': f_statustime = 1; f_accesstime = 0; f_birthtime = 0; break; case 'u': f_accesstime = 1; f_statustime = 0; f_birthtime = 0; break; case 'U': f_birthtime = 1; f_accesstime = 0; f_statustime = 0; break; case 'f': f_nosort = 1; /* FALLTHROUGH */ case 'a': fts_options |= FTS_SEEDOT; /* FALLTHROUGH */ case 'A': f_listdot = 1; break; /* The -t and -S options override each other. */ case 'S': f_sizesort = 1; f_timesort = 0; break; case 't': f_timesort = 1; f_sizesort = 0; break; /* Other flags. Please keep alphabetic. */ case ',': f_thousands = 1; break; case 'B': f_nonprint = 0; f_octal = 1; f_octal_escape = 0; break; case 'D': f_timeformat = optarg; break; case 'F': f_type = 1; f_slash = 0; break; case 'G': /* * We both set CLICOLOR here and set colorflag to * COLORFLAG_AUTO, because -G should not force color if * stdout isn't a tty. */ setenv("CLICOLOR", "", 1); #ifdef COLORLS colorflag = COLORFLAG_AUTO; #endif break; case 'H': fts_options |= FTS_COMFOLLOW; f_nofollow = 0; break; case 'I': f_noautodot = 1; break; case 'L': fts_options &= ~FTS_PHYSICAL; fts_options |= FTS_LOGICAL; f_nofollow = 0; break; case 'P': fts_options &= ~FTS_COMFOLLOW; fts_options &= ~FTS_LOGICAL; fts_options |= FTS_PHYSICAL; f_nofollow = 1; break; case 'R': f_recursive = 1; break; case 'T': f_sectime = 1; break; case 'W': f_whiteout = 1; break; case 'Z': f_label = 1; break; case 'b': f_nonprint = 0; f_octal = 0; f_octal_escape = 1; break; /* The -d option turns off the -R option. */ case 'd': f_listdir = 1; f_recursive = 0; break; case 'g': /* Compatibility with 4.3BSD. */ break; case 'h': f_humanval = 1; break; case 'i': f_inode = 1; break; case 'k': f_humanval = 0; f_kblocks = 1; break; case 'm': f_stream = 1; f_singlecol = 0; f_longform = 0; break; case 'n': f_numericonly = 1; break; case 'o': f_flags = 1; break; case 'p': f_slash = 1; f_type = 1; break; case 'q': f_nonprint = 1; f_octal = 0; f_octal_escape = 0; break; case 'r': f_reversesort = 1; break; case 's': f_size = 1; break; case 'w': f_nonprint = 0; f_octal = 0; f_octal_escape = 0; break; case 'y': f_samesort = 1; break; #ifdef COLORLS case COLOR_OPT: if (optarg == NULL || do_color_always(optarg)) colorflag = COLORFLAG_ALWAYS; else if (do_color_auto(optarg)) colorflag = COLORFLAG_AUTO; else if (do_color_never(optarg)) colorflag = COLORFLAG_NEVER; else errx(2, "unsupported --color value '%s' (must be always, auto, or never)", optarg); break; #endif default: case '?': usage(); } } argc -= optind; argv += optind; /* Root is -A automatically unless -I. */ if (!f_listdot && getuid() == (uid_t)0 && !f_noautodot) f_listdot = 1; /* * Enabling of colours is conditional on the environment in conjunction * with the --color and -G arguments, if supplied. */ if (do_color()) { #ifdef COLORLS if ((term = getenv("TERM")) != NULL && tgetent(termcapbuf, term) == 1) { ansi_fgcol = tgetstr("AF", &bp); ansi_bgcol = tgetstr("AB", &bp); attrs_off = tgetstr("me", &bp); enter_bold = tgetstr("md", &bp); /* To switch colours off use 'op' if * available, otherwise use 'oc', or * don't do colours at all. */ ansi_coloff = tgetstr("op", &bp); if (!ansi_coloff) ansi_coloff = tgetstr("oc", &bp); if (ansi_fgcol && ansi_bgcol && ansi_coloff) f_color = 1; } else if (colorflag == COLORFLAG_ALWAYS) { /* * If we're *always* doing color but we don't have * a functional TERM supplied, we'll fallback to * outputting raw ANSI sequences. */ f_color = 1; explicitansi = true; } #else warnx("color support not compiled in"); #endif /*COLORLS*/ } #ifdef COLORLS if (f_color) { /* * We can't put tabs and color sequences together: * column number will be incremented incorrectly * for "stty oxtabs" mode. */ f_notabs = 1; (void)signal(SIGINT, colorquit); (void)signal(SIGQUIT, colorquit); parsecolors(getenv("LSCOLORS")); } #endif /* * If not -F, -i, -l, -s, -S or -t options, don't require stat * information, unless in color mode in which case we do * need this to determine which colors to display. */ if (!f_inode && !f_longform && !f_size && !f_timesort && !f_sizesort && !f_type #ifdef COLORLS && !f_color #endif ) fts_options |= FTS_NOSTAT; /* * If not -F, -P, -d or -l options, follow any symbolic links listed on * the command line, unless in color mode in which case we need to * distinguish file type for a symbolic link itself and its target. */ if (!f_nofollow && !f_longform && !f_listdir && (!f_type || f_slash) #ifdef COLORLS && !f_color #endif ) fts_options |= FTS_COMFOLLOW; /* * If -W, show whiteout entries */ #ifdef FTS_WHITEOUT if (f_whiteout) fts_options |= FTS_WHITEOUT; #endif /* If -i, -l or -s, figure out block size. */ if (f_inode || f_longform || f_size) { if (f_kblocks) blocksize = 2; else { (void)getbsize(¬used, &blocksize); blocksize /= 512; } } /* Select a sort function. */ if (f_reversesort) { if (!f_timesort && !f_sizesort) sortfcn = revnamecmp; else if (f_sizesort) sortfcn = revsizecmp; else if (f_accesstime) sortfcn = revacccmp; else if (f_birthtime) sortfcn = revbirthcmp; else if (f_statustime) sortfcn = revstatcmp; else /* Use modification time. */ sortfcn = revmodcmp; } else { if (!f_timesort && !f_sizesort) sortfcn = namecmp; else if (f_sizesort) sortfcn = sizecmp; else if (f_accesstime) sortfcn = acccmp; else if (f_birthtime) sortfcn = birthcmp; else if (f_statustime) sortfcn = statcmp; else /* Use modification time. */ sortfcn = modcmp; } /* Select a print function. */ if (f_singlecol) printfcn = printscol; else if (f_longform) printfcn = printlong; else if (f_stream) printfcn = printstream; else printfcn = printcol; if (argc) traverse(argc, argv, fts_options); else traverse(1, dotav, fts_options); exit(rval); } static int output; /* If anything output. */ /* * Traverse() walks the logical directory structure specified by the argv list * in the order specified by the mastercmp() comparison function. During the * traversal it passes linked lists of structures to display() which represent * a superset (may be exact set) of the files to be displayed. */ static void traverse(int argc, char *argv[], int options) { FTS *ftsp; FTSENT *p, *chp; int ch_options; if ((ftsp = fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) err(1, "fts_open"); /* * We ignore errors from fts_children here since they will be * replicated and signalled on the next call to fts_read() below. */ chp = fts_children(ftsp, 0); if (chp != NULL) display(NULL, chp, options); if (f_listdir) return; /* * If not recursing down this tree and don't need stat info, just get * the names. */ ch_options = !f_recursive && !f_label && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; - while ((p = fts_read(ftsp)) != NULL) + while (errno = 0, (p = fts_read(ftsp)) != NULL) switch (p->fts_info) { case FTS_DC: warnx("%s: directory causes a cycle", p->fts_name); break; case FTS_DNR: case FTS_ERR: warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; break; case FTS_D: if (p->fts_level != FTS_ROOTLEVEL && p->fts_name[0] == '.' && !f_listdot) break; /* * If already output something, put out a newline as * a separator. If multiple arguments, precede each * directory with its name. */ if (output) { putchar('\n'); (void)printname(p->fts_path); puts(":"); } else if (argc > 1) { (void)printname(p->fts_path); puts(":"); output = 1; } chp = fts_children(ftsp, ch_options); display(p, chp, options); if (!f_recursive && chp != NULL) (void)fts_set(ftsp, p, FTS_SKIP); break; default: break; } if (errno) err(1, "fts_read"); } /* * Display() takes a linked list of FTSENT structures and passes the list * along with any other necessary information to the print function. P * points to the parent directory of the display list. */ static void display(const FTSENT *p, FTSENT *list, int options) { struct stat *sp; DISPLAY d; FTSENT *cur; NAMES *np; off_t maxsize; long maxblock; uintmax_t maxinode; u_long btotal, labelstrlen, maxlen, maxnlink; u_long maxlabelstr; u_int sizelen; int maxflags; gid_t maxgroup; uid_t maxuser; size_t flen, ulen, glen; char *initmax; int entries, needstats; const char *user, *group; char *flags, *labelstr = NULL; char ngroup[STRBUF_SIZEOF(uid_t) + 1]; char nuser[STRBUF_SIZEOF(gid_t) + 1]; needstats = f_inode || f_longform || f_size; flen = 0; btotal = 0; initmax = getenv("LS_COLWIDTHS"); /* Fields match -lios order. New ones should be added at the end. */ maxlabelstr = maxblock = maxlen = maxnlink = 0; maxuser = maxgroup = maxflags = maxsize = 0; maxinode = 0; if (initmax != NULL && *initmax != '\0') { char *initmax2, *jinitmax; int ninitmax; /* Fill-in "::" as "0:0:0" for the sake of scanf. */ jinitmax = malloc(strlen(initmax) * 2 + 2); if (jinitmax == NULL) err(1, "malloc"); initmax2 = jinitmax; if (*initmax == ':') strcpy(initmax2, "0:"), initmax2 += 2; else *initmax2++ = *initmax, *initmax2 = '\0'; for (initmax++; *initmax != '\0'; initmax++) { if (initmax[-1] == ':' && initmax[0] == ':') { *initmax2++ = '0'; *initmax2++ = initmax[0]; initmax2[1] = '\0'; } else { *initmax2++ = initmax[0]; initmax2[1] = '\0'; } } if (initmax2[-1] == ':') strcpy(initmax2, "0"); ninitmax = sscanf(jinitmax, " %ju : %ld : %lu : %u : %u : %i : %jd : %lu : %lu ", &maxinode, &maxblock, &maxnlink, &maxuser, &maxgroup, &maxflags, &maxsize, &maxlen, &maxlabelstr); f_notabs = 1; switch (ninitmax) { case 0: maxinode = 0; /* FALLTHROUGH */ case 1: maxblock = 0; /* FALLTHROUGH */ case 2: maxnlink = 0; /* FALLTHROUGH */ case 3: maxuser = 0; /* FALLTHROUGH */ case 4: maxgroup = 0; /* FALLTHROUGH */ case 5: maxflags = 0; /* FALLTHROUGH */ case 6: maxsize = 0; /* FALLTHROUGH */ case 7: maxlen = 0; /* FALLTHROUGH */ case 8: maxlabelstr = 0; /* FALLTHROUGH */ #ifdef COLORLS if (!f_color) #endif f_notabs = 0; /* FALLTHROUGH */ default: break; } MAKENINES(maxinode); MAKENINES(maxblock); MAKENINES(maxnlink); MAKENINES(maxsize); free(jinitmax); } d.s_size = 0; sizelen = 0; flags = NULL; for (cur = list, entries = 0; cur; cur = cur->fts_link) { if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { warnx("%s: %s", cur->fts_name, strerror(cur->fts_errno)); cur->fts_number = NO_PRINT; rval = 1; continue; } /* * P is NULL if list is the argv list, to which different rules * apply. */ if (p == NULL) { /* Directories will be displayed later. */ if (cur->fts_info == FTS_D && !f_listdir) { cur->fts_number = NO_PRINT; continue; } } else { /* Only display dot file if -a/-A set. */ if (cur->fts_name[0] == '.' && !f_listdot) { cur->fts_number = NO_PRINT; continue; } } if (cur->fts_namelen > maxlen) maxlen = cur->fts_namelen; if (f_octal || f_octal_escape) { u_long t = len_octal(cur->fts_name, cur->fts_namelen); if (t > maxlen) maxlen = t; } if (needstats) { sp = cur->fts_statp; if (sp->st_blocks > maxblock) maxblock = sp->st_blocks; if (sp->st_ino > maxinode) maxinode = sp->st_ino; if (sp->st_nlink > maxnlink) maxnlink = sp->st_nlink; if (sp->st_size > maxsize) maxsize = sp->st_size; btotal += sp->st_blocks; if (f_longform) { if (f_numericonly) { (void)snprintf(nuser, sizeof(nuser), "%u", sp->st_uid); (void)snprintf(ngroup, sizeof(ngroup), "%u", sp->st_gid); user = nuser; group = ngroup; } else { user = user_from_uid(sp->st_uid, 0); /* * user_from_uid(..., 0) only returns * NULL in OOM conditions. We could * format the uid here, but (1) in * general ls(1) exits on OOM, and (2) * there is another allocation/exit * path directly below, which will * likely exit anyway. */ if (user == NULL) err(1, "user_from_uid"); group = group_from_gid(sp->st_gid, 0); /* Ditto. */ if (group == NULL) err(1, "group_from_gid"); } if ((ulen = strlen(user)) > maxuser) maxuser = ulen; if ((glen = strlen(group)) > maxgroup) maxgroup = glen; if (f_flags) { flags = fflagstostr(sp->st_flags); if (flags != NULL && *flags == '\0') { free(flags); flags = strdup("-"); } if (flags == NULL) err(1, "fflagstostr"); flen = strlen(flags); if (flen > (size_t)maxflags) maxflags = flen; } else flen = 0; labelstr = NULL; if (f_label) { char name[PATH_MAX + 1]; mac_t label; int error; error = mac_prepare_file_label(&label); if (error == -1) { warn("MAC label for %s/%s", cur->fts_parent->fts_path, cur->fts_name); goto label_out; } if (cur->fts_level == FTS_ROOTLEVEL) snprintf(name, sizeof(name), "%s", cur->fts_name); else snprintf(name, sizeof(name), "%s/%s", cur->fts_parent-> fts_accpath, cur->fts_name); if (options & FTS_LOGICAL) error = mac_get_file(name, label); else error = mac_get_link(name, label); if (error == -1) { warn("MAC label for %s/%s", cur->fts_parent->fts_path, cur->fts_name); mac_free(label); goto label_out; } error = mac_to_text(label, &labelstr); if (error == -1) { warn("MAC label for %s/%s", cur->fts_parent->fts_path, cur->fts_name); mac_free(label); goto label_out; } mac_free(label); label_out: if (labelstr == NULL) labelstr = strdup("-"); labelstrlen = strlen(labelstr); if (labelstrlen > maxlabelstr) maxlabelstr = labelstrlen; } else labelstrlen = 0; if ((np = malloc(sizeof(NAMES) + labelstrlen + ulen + glen + flen + 4)) == NULL) err(1, "malloc"); np->user = &np->data[0]; (void)strcpy(np->user, user); np->group = &np->data[ulen + 1]; (void)strcpy(np->group, group); if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) { sizelen = snprintf(NULL, 0, "%#jx", (uintmax_t)sp->st_rdev); if (d.s_size < sizelen) d.s_size = sizelen; } if (f_flags) { np->flags = &np->data[ulen + glen + 2]; (void)strcpy(np->flags, flags); free(flags); } if (f_label) { np->label = &np->data[ulen + glen + 2 + (f_flags ? flen + 1 : 0)]; (void)strcpy(np->label, labelstr); free(labelstr); } cur->fts_pointer = np; } } ++entries; } /* * If there are no entries to display, we normally stop right * here. However, we must continue if we have to display the * total block count. In this case, we display the total only * on the second (p != NULL) pass. */ if (!entries && (!(f_longform || f_size) || p == NULL)) return; d.list = list; d.entries = entries; d.maxlen = maxlen; if (needstats) { d.btotal = btotal; d.s_block = snprintf(NULL, 0, "%lu", howmany(maxblock, blocksize)); d.s_flags = maxflags; d.s_label = maxlabelstr; d.s_group = maxgroup; d.s_inode = snprintf(NULL, 0, "%ju", maxinode); d.s_nlink = snprintf(NULL, 0, "%lu", maxnlink); sizelen = f_humanval ? HUMANVALSTR_LEN : snprintf(NULL, 0, "%ju", maxsize); if (d.s_size < sizelen) d.s_size = sizelen; d.s_user = maxuser; } if (f_thousands) /* make space for commas */ d.s_size += (d.s_size - 1) / 3; printfcn(&d); output = 1; if (f_longform) for (cur = list; cur; cur = cur->fts_link) free(cur->fts_pointer); } /* * Ordering for mastercmp: * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories * as larger than directories. Within either group, use the sort function. * All other levels use the sort function. Error entries remain unsorted. */ static int mastercmp(const FTSENT * const *a, const FTSENT * const *b) { int a_info, b_info; a_info = (*a)->fts_info; if (a_info == FTS_ERR) return (0); b_info = (*b)->fts_info; if (b_info == FTS_ERR) return (0); if (a_info == FTS_NS || b_info == FTS_NS) return (namecmp(*a, *b)); if (a_info != b_info && (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) { if (a_info == FTS_D) return (1); if (b_info == FTS_D) return (-1); } return (sortfcn(*a, *b)); } diff --git a/bin/rm/rm.c b/bin/rm/rm.c index 30eb3501eb2c..111f4a1326c4 100644 --- a/bin/rm/rm.c +++ b/bin/rm/rm.c @@ -1,551 +1,551 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1990, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int dflag, eval, fflag, iflag, vflag, Wflag, stdin_ok; static int rflag, Iflag, xflag; static uid_t uid; static volatile sig_atomic_t info; static int check(const char *, const char *, struct stat *); static int check2(char **); static void checkdot(char **); static void checkslash(char **); static void rm_file(char **); static void rm_tree(char **); static void siginfo(int __unused); static void usage(void); /* * rm -- * This rm is different from historic rm's, but is expected to match * POSIX 1003.2 behavior. The most visible difference is that -f * has two specific effects now, ignore non-existent files and force * file removal. */ int main(int argc, char *argv[]) { int ch; char *p; (void)setlocale(LC_ALL, ""); /* * Test for the special case where the utility is called as * "unlink", for which the functionality provided is greatly * simplified. */ if ((p = strrchr(argv[0], '/')) == NULL) p = argv[0]; else ++p; if (strcmp(p, "unlink") == 0) { if (argc == 2) rm_file(&argv[1]); else if (argc == 3 && strcmp(argv[1], "--") == 0) rm_file(&argv[2]); else usage(); exit(eval); } rflag = xflag = 0; while ((ch = getopt(argc, argv, "dfiIPRrvWx")) != -1) switch(ch) { case 'd': dflag = 1; break; case 'f': fflag = 1; iflag = 0; break; case 'i': fflag = 0; iflag = 1; break; case 'I': Iflag = 1; break; case 'P': /* Compatibility no-op. */ break; case 'R': case 'r': /* Compatibility. */ rflag = 1; break; case 'v': vflag = 1; break; case 'W': Wflag = 1; break; case 'x': xflag = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc < 1) { if (fflag) return (0); usage(); } checkdot(argv); checkslash(argv); uid = geteuid(); (void)signal(SIGINFO, siginfo); if (*argv) { stdin_ok = isatty(STDIN_FILENO); if (Iflag) { if (check2(argv) == 0) exit (1); } if (rflag) rm_tree(argv); else rm_file(argv); } exit (eval); } static void rm_tree(char **argv) { FTS *fts; FTSENT *p; int needstat; int flags; int rval; /* * Remove a file hierarchy. If forcing removal (-f), or interactive * (-i) or can't ask anyway (stdin_ok), don't stat the file. */ needstat = !uid || (!fflag && !iflag && stdin_ok); /* * If the -i option is specified, the user can skip on the pre-order * visit. The fts_number field flags skipped directories. */ #define SKIPPED 1 flags = FTS_PHYSICAL; if (!needstat) flags |= FTS_NOSTAT; if (Wflag) flags |= FTS_WHITEOUT; if (xflag) flags |= FTS_XDEV; if (!(fts = fts_open(argv, flags, NULL))) { if (fflag && errno == ENOENT) return; err(1, "fts_open"); } - while ((p = fts_read(fts)) != NULL) { + while (errno = 0, (p = fts_read(fts)) != NULL) { switch (p->fts_info) { case FTS_DNR: if (!fflag || p->fts_errno != ENOENT) { warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); eval = 1; } continue; case FTS_ERR: errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); case FTS_NS: /* * Assume that since fts_read() couldn't stat the * file, it can't be unlinked. */ if (!needstat) break; if (!fflag || p->fts_errno != ENOENT) { warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); eval = 1; } continue; case FTS_D: /* Pre-order: give user chance to skip. */ if (!fflag && !check(p->fts_path, p->fts_accpath, p->fts_statp)) { (void)fts_set(fts, p, FTS_SKIP); p->fts_number = SKIPPED; } else if (!uid && (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && lchflags(p->fts_accpath, p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) goto err; continue; case FTS_DP: /* Post-order: see if user skipped. */ if (p->fts_number == SKIPPED) continue; break; default: if (!fflag && !check(p->fts_path, p->fts_accpath, p->fts_statp)) continue; } rval = 0; if (!uid && (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))) rval = lchflags(p->fts_accpath, p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); if (rval == 0) { /* * If we can't read or search the directory, may still be * able to remove it. Don't print out the un{read,search}able * message unless the remove fails. */ switch (p->fts_info) { case FTS_DP: case FTS_DNR: rval = rmdir(p->fts_accpath); if (rval == 0 || (fflag && errno == ENOENT)) { if (rval == 0 && vflag) (void)printf("%s\n", p->fts_path); if (rval == 0 && info) { info = 0; (void)printf("%s\n", p->fts_path); } continue; } break; case FTS_W: rval = undelete(p->fts_accpath); if (rval == 0 && (fflag && errno == ENOENT)) { if (vflag) (void)printf("%s\n", p->fts_path); if (info) { info = 0; (void)printf("%s\n", p->fts_path); } continue; } break; case FTS_NS: /* * Assume that since fts_read() couldn't stat * the file, it can't be unlinked. */ if (fflag) continue; /* FALLTHROUGH */ case FTS_F: case FTS_NSOK: default: rval = unlink(p->fts_accpath); if (rval == 0 || (fflag && errno == ENOENT)) { if (rval == 0 && vflag) (void)printf("%s\n", p->fts_path); if (rval == 0 && info) { info = 0; (void)printf("%s\n", p->fts_path); } continue; } } } err: warn("%s", p->fts_path); eval = 1; } if (!fflag && errno) err(1, "fts_read"); fts_close(fts); } static void rm_file(char **argv) { struct stat sb; int rval; char *f; /* * Remove a file. POSIX 1003.2 states that, by default, attempting * to remove a directory is an error, so must always stat the file. */ while ((f = *argv++) != NULL) { /* Assume if can't stat the file, can't unlink it. */ if (lstat(f, &sb)) { if (Wflag) { sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR; } else { if (!fflag || errno != ENOENT) { warn("%s", f); eval = 1; } continue; } } else if (Wflag) { warnx("%s: %s", f, strerror(EEXIST)); eval = 1; continue; } if (S_ISDIR(sb.st_mode) && !dflag) { warnx("%s: is a directory", f); eval = 1; continue; } if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb)) continue; rval = 0; if (!uid && !S_ISWHT(sb.st_mode) && (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) && !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE))) rval = lchflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE)); if (rval == 0) { if (S_ISWHT(sb.st_mode)) rval = undelete(f); else if (S_ISDIR(sb.st_mode)) rval = rmdir(f); else rval = unlink(f); } if (rval && (!fflag || errno != ENOENT)) { warn("%s", f); eval = 1; } if (vflag && rval == 0) (void)printf("%s\n", f); if (info && rval == 0) { info = 0; (void)printf("%s\n", f); } } } static int check(const char *path, const char *name, struct stat *sp) { int ch, first; char modep[15], *flagsp; /* Check -i first. */ if (iflag) (void)fprintf(stderr, "remove %s? ", path); else { /* * If it's not a symbolic link and it's unwritable and we're * talking to a terminal, ask. Symbolic links are excluded * because their permissions are meaningless. Check stdin_ok * first because we may not have stat'ed the file. */ if (!stdin_ok || S_ISLNK(sp->st_mode) || (!access(name, W_OK) && !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))) return (1); strmode(sp->st_mode, modep); if ((flagsp = fflagstostr(sp->st_flags)) == NULL) err(1, "fflagstostr"); (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ", modep + 1, modep[10] == ' ' ? "" : " ", user_from_uid(sp->st_uid, 0), group_from_gid(sp->st_gid, 0), *flagsp ? flagsp : "", *flagsp ? " " : "", path); free(flagsp); } (void)fflush(stderr); first = ch = getchar(); while (ch != '\n' && ch != EOF) ch = getchar(); return (first == 'y' || first == 'Y'); } #define ISSLASH(a) ((a)[0] == '/' && (a)[1] == '\0') static void checkslash(char **argv) { char **t, **u; int complained; complained = 0; for (t = argv; *t;) { if (ISSLASH(*t)) { if (!complained++) warnx("\"/\" may not be removed"); eval = 1; for (u = t; u[0] != NULL; ++u) u[0] = u[1]; } else { ++t; } } } static int check2(char **argv) { struct stat st; int first; int ch; int fcount = 0; int dcount = 0; int i; const char *dname = NULL; for (i = 0; argv[i]; ++i) { if (lstat(argv[i], &st) == 0) { if (S_ISDIR(st.st_mode)) { ++dcount; dname = argv[i]; /* only used if 1 dir */ } else { ++fcount; } } } first = 0; while (first != 'n' && first != 'N' && first != 'y' && first != 'Y') { if (dcount && rflag) { fprintf(stderr, "recursively remove"); if (dcount == 1) fprintf(stderr, " %s", dname); else fprintf(stderr, " %d dirs", dcount); if (fcount == 1) fprintf(stderr, " and 1 file"); else if (fcount > 1) fprintf(stderr, " and %d files", fcount); } else if (dcount + fcount > 3) { fprintf(stderr, "remove %d files", dcount + fcount); } else { return(1); } fprintf(stderr, "? "); fflush(stderr); first = ch = getchar(); while (ch != '\n' && ch != EOF) ch = getchar(); if (ch == EOF) break; } return (first == 'y' || first == 'Y'); } #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) static void checkdot(char **argv) { char *p, **save, **t; int complained; complained = 0; for (t = argv; *t;) { if ((p = strrchr(*t, '/')) != NULL) ++p; else p = *t; if (ISDOT(p)) { if (!complained++) warnx("\".\" and \"..\" may not be removed"); eval = 1; for (save = t; (t[0] = t[1]) != NULL; ++t) continue; t = save; } else ++t; } } static void usage(void) { (void)fprintf(stderr, "%s\n%s\n", "usage: rm [-f | -i] [-dIPRrvWx] file ...", " unlink [--] file"); exit(EX_USAGE); } static void siginfo(int sig __unused) { info = 1; } diff --git a/bin/setfacl/setfacl.c b/bin/setfacl/setfacl.c index 7b0d617812dc..033f9d2572ca 100644 --- a/bin/setfacl/setfacl.c +++ b/bin/setfacl/setfacl.c @@ -1,505 +1,507 @@ /*- * Copyright (c) 2001 Chris D. Faulhaber * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "setfacl.h" /* file operations */ #define OP_MERGE_ACL 0x00 /* merge acl's (-mM) */ #define OP_REMOVE_DEF 0x01 /* remove default acl's (-k) */ #define OP_REMOVE_EXT 0x02 /* remove extended acl's (-b) */ #define OP_REMOVE_ACL 0x03 /* remove acl's (-xX) */ #define OP_REMOVE_BY_NUMBER 0x04 /* remove acl's (-xX) by acl entry number */ #define OP_ADD_ACL 0x05 /* add acls entries at a given position */ /* TAILQ entry for acl operations */ struct sf_entry { uint op; acl_t acl; uint entry_number; TAILQ_ENTRY(sf_entry) next; }; static TAILQ_HEAD(, sf_entry) entrylist; bool have_mask; bool have_stdin; bool n_flag; static bool h_flag; static bool H_flag; static bool L_flag; static bool R_flag; static bool need_mask; static acl_type_t acl_type = ACL_TYPE_ACCESS; static int handle_file(FTS *ftsp, FTSENT *file); static acl_t clear_inheritance_flags(acl_t acl); static char **stdin_files(void); static void usage(void); static void usage(void) { fprintf(stderr, "usage: setfacl [-R [-H | -L | -P]] [-bdhkn] " "[-a position entries] [-m entries] [-M file] " "[-x entries] [-X file] [file ...]\n"); exit(1); } static char ** stdin_files(void) { char **files_list; char filename[PATH_MAX]; size_t fl_count, i; if (have_stdin) err(1, "cannot have more than one stdin"); i = 0; have_stdin = true; bzero(&filename, sizeof(filename)); /* Start with an array size sufficient for basic cases. */ fl_count = 1024; files_list = zmalloc(fl_count * sizeof(char *)); while (fgets(filename, (int)sizeof(filename), stdin)) { /* remove the \n */ filename[strlen(filename) - 1] = '\0'; files_list[i] = strdup(filename); if (files_list[i] == NULL) err(1, "strdup() failed"); /* Grow array if necessary. */ if (++i == fl_count) { fl_count <<= 1; if (fl_count > SIZE_MAX / sizeof(char *)) errx(1, "Too many input files"); files_list = zrealloc(files_list, fl_count * sizeof(char *)); } } /* fts_open() requires the last array element to be NULL. */ files_list[i] = NULL; return (files_list); } /* * Remove any inheritance flags from NFSv4 ACLs when running in recursive * mode. This is to avoid files being assigned identical ACLs to their * parent directory while also being set to inherit them. * * The acl argument is assumed to be valid. */ static acl_t clear_inheritance_flags(acl_t acl) { acl_t nacl; acl_entry_t acl_entry; acl_flagset_t acl_flagset; int acl_brand, entry_id; (void)acl_get_brand_np(acl, &acl_brand); if (acl_brand != ACL_BRAND_NFS4) return (acl); nacl = acl_dup(acl); if (nacl == NULL) { warn("acl_dup() failed"); return (acl); } entry_id = ACL_FIRST_ENTRY; while (acl_get_entry(nacl, entry_id, &acl_entry) == 1) { entry_id = ACL_NEXT_ENTRY; if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) { warn("acl_get_flagset_np() failed"); continue; } if (acl_get_flag_np(acl_flagset, ACL_ENTRY_INHERIT_ONLY) == 1) { if (acl_delete_entry(nacl, acl_entry) != 0) warn("acl_delete_entry() failed"); continue; } if (acl_delete_flag_np(acl_flagset, ACL_ENTRY_FILE_INHERIT | ACL_ENTRY_DIRECTORY_INHERIT | ACL_ENTRY_NO_PROPAGATE_INHERIT) != 0) warn("acl_delete_flag_np() failed"); } return (nacl); } static int handle_file(FTS *ftsp, FTSENT *file) { acl_t acl, nacl; acl_entry_t unused_entry; int local_error, ret; struct sf_entry *entry; bool follow_symlink; local_error = 0; switch (file->fts_info) { case FTS_D: /* Do not recurse if -R not specified. */ if (!R_flag) fts_set(ftsp, file, FTS_SKIP); break; case FTS_DP: /* Skip the second visit to a directory. */ return (0); case FTS_DNR: case FTS_ERR: warnx("%s: %s", file->fts_path, strerror(file->fts_errno)); return (0); default: break; } if (acl_type == ACL_TYPE_DEFAULT && file->fts_info != FTS_D) { warnx("%s: default ACL may only be set on a directory", file->fts_path); return (1); } follow_symlink = (!R_flag && !h_flag) || (R_flag && L_flag) || (R_flag && H_flag && file->fts_level == FTS_ROOTLEVEL); if (follow_symlink) ret = pathconf(file->fts_accpath, _PC_ACL_NFS4); else ret = lpathconf(file->fts_accpath, _PC_ACL_NFS4); if (ret > 0) { if (acl_type == ACL_TYPE_DEFAULT) { warnx("%s: there are no default entries in NFSv4 ACLs", file->fts_path); return (1); } acl_type = ACL_TYPE_NFS4; } else if (ret == 0) { if (acl_type == ACL_TYPE_NFS4) acl_type = ACL_TYPE_ACCESS; } else if (ret < 0 && errno != EINVAL && errno != ENOENT) { warn("%s: pathconf(_PC_ACL_NFS4) failed", file->fts_path); } if (follow_symlink) acl = acl_get_file(file->fts_accpath, acl_type); else acl = acl_get_link_np(file->fts_accpath, acl_type); if (acl == NULL) { if (follow_symlink) warn("%s: acl_get_file() failed", file->fts_path); else warn("%s: acl_get_link_np() failed", file->fts_path); return (1); } /* Cycle through each option. */ TAILQ_FOREACH(entry, &entrylist, next) { nacl = entry->acl; switch (entry->op) { case OP_ADD_ACL: if (R_flag && file->fts_info != FTS_D && acl_type == ACL_TYPE_NFS4) nacl = clear_inheritance_flags(nacl); local_error += add_acl(nacl, entry->entry_number, &acl, file->fts_path); break; case OP_MERGE_ACL: if (R_flag && file->fts_info != FTS_D && acl_type == ACL_TYPE_NFS4) nacl = clear_inheritance_flags(nacl); local_error += merge_acl(nacl, &acl, file->fts_path); need_mask = true; break; case OP_REMOVE_EXT: /* * Don't try to call remove_ext() for empty * default ACL. */ if (acl_type == ACL_TYPE_DEFAULT && acl_get_entry(acl, ACL_FIRST_ENTRY, &unused_entry) == 0) { local_error += remove_default(&acl, file->fts_path); break; } remove_ext(&acl, file->fts_path); need_mask = false; break; case OP_REMOVE_DEF: if (acl_type == ACL_TYPE_NFS4) { warnx("%s: there are no default entries in " "NFSv4 ACLs; cannot remove", file->fts_path); local_error++; break; } if (acl_delete_def_file(file->fts_accpath) == -1) { warn("%s: acl_delete_def_file() failed", file->fts_path); local_error++; } if (acl_type == ACL_TYPE_DEFAULT) local_error += remove_default(&acl, file->fts_path); need_mask = false; break; case OP_REMOVE_ACL: local_error += remove_acl(nacl, &acl, file->fts_path); need_mask = true; break; case OP_REMOVE_BY_NUMBER: local_error += remove_by_number(entry->entry_number, &acl, file->fts_path); need_mask = true; break; } if (nacl != entry->acl) { acl_free(nacl); nacl = NULL; } if (local_error) break; } ret = 0; /* * Don't try to set an empty default ACL; it will always fail. * Use acl_delete_def_file(3) instead. */ if (acl_type == ACL_TYPE_DEFAULT && acl_get_entry(acl, ACL_FIRST_ENTRY, &unused_entry) == 0) { if (acl_delete_def_file(file->fts_accpath) == -1) { warn("%s: acl_delete_def_file() failed", file->fts_path); ret = 1; } goto out; } /* Don't bother setting the ACL if something is broken. */ if (local_error) { ret = 1; } else if (acl_type != ACL_TYPE_NFS4 && need_mask && set_acl_mask(&acl, file->fts_path) == -1) { warnx("%s: failed to set ACL mask", file->fts_path); ret = 1; } else if (follow_symlink) { if (acl_set_file(file->fts_accpath, acl_type, acl) == -1) { warn("%s: acl_set_file() failed", file->fts_path); ret = 1; } } else { if (acl_set_link_np(file->fts_accpath, acl_type, acl) == -1) { warn("%s: acl_set_link_np() failed", file->fts_path); ret = 1; } } out: acl_free(acl); return (ret); } int main(int argc, char *argv[]) { int carried_error, ch, entry_number, fts_options; FTS *ftsp; FTSENT *file; char **files_list; struct sf_entry *entry; char *end; acl_type = ACL_TYPE_ACCESS; carried_error = fts_options = 0; have_mask = have_stdin = n_flag = false; TAILQ_INIT(&entrylist); while ((ch = getopt(argc, argv, "HLM:PRX:a:bdhkm:nx:")) != -1) switch(ch) { case 'H': H_flag = true; L_flag = false; break; case 'L': L_flag = true; H_flag = false; break; case 'M': entry = zmalloc(sizeof(struct sf_entry)); entry->acl = get_acl_from_file(optarg); if (entry->acl == NULL) err(1, "%s: get_acl_from_file() failed", optarg); entry->op = OP_MERGE_ACL; TAILQ_INSERT_TAIL(&entrylist, entry, next); break; case 'P': H_flag = L_flag = false; break; case 'R': R_flag = true; break; case 'X': entry = zmalloc(sizeof(struct sf_entry)); entry->acl = get_acl_from_file(optarg); entry->op = OP_REMOVE_ACL; TAILQ_INSERT_TAIL(&entrylist, entry, next); break; case 'a': entry = zmalloc(sizeof(struct sf_entry)); entry_number = strtol(optarg, &end, 10); if (end - optarg != (int)strlen(optarg)) errx(1, "%s: invalid entry number", optarg); if (entry_number < 0) errx(1, "%s: entry number cannot be less than zero", optarg); entry->entry_number = entry_number; if (argv[optind] == NULL) errx(1, "missing ACL"); entry->acl = acl_from_text(argv[optind]); if (entry->acl == NULL) err(1, "%s", argv[optind]); optind++; entry->op = OP_ADD_ACL; TAILQ_INSERT_TAIL(&entrylist, entry, next); break; case 'b': entry = zmalloc(sizeof(struct sf_entry)); entry->op = OP_REMOVE_EXT; TAILQ_INSERT_TAIL(&entrylist, entry, next); break; case 'd': acl_type = ACL_TYPE_DEFAULT; break; case 'h': h_flag = 1; break; case 'k': entry = zmalloc(sizeof(struct sf_entry)); entry->op = OP_REMOVE_DEF; TAILQ_INSERT_TAIL(&entrylist, entry, next); break; case 'm': entry = zmalloc(sizeof(struct sf_entry)); entry->acl = acl_from_text(optarg); if (entry->acl == NULL) err(1, "%s", optarg); entry->op = OP_MERGE_ACL; TAILQ_INSERT_TAIL(&entrylist, entry, next); break; case 'n': n_flag = true; break; case 'x': entry = zmalloc(sizeof(struct sf_entry)); entry_number = strtol(optarg, &end, 10); if (end - optarg == (int)strlen(optarg)) { if (entry_number < 0) errx(1, "%s: entry number cannot be less than zero", optarg); entry->entry_number = entry_number; entry->op = OP_REMOVE_BY_NUMBER; } else { entry->acl = acl_from_text(optarg); if (entry->acl == NULL) err(1, "%s", optarg); entry->op = OP_REMOVE_ACL; } TAILQ_INSERT_TAIL(&entrylist, entry, next); break; default: usage(); break; } argc -= optind; argv += optind; if (!n_flag && TAILQ_EMPTY(&entrylist)) usage(); /* Take list of files from stdin. */ if (argc == 0 || strcmp(argv[0], "-") == 0) { files_list = stdin_files(); } else files_list = argv; if (R_flag) { if (h_flag) errx(1, "the -R and -h options may not be " "specified together."); if (L_flag) { fts_options = FTS_LOGICAL; } else { fts_options = FTS_PHYSICAL; if (H_flag) { fts_options |= FTS_COMFOLLOW; } } } else if (h_flag) { fts_options = FTS_PHYSICAL; } else { fts_options = FTS_LOGICAL; } /* Open all files. */ if ((ftsp = fts_open(files_list, fts_options | FTS_NOSTAT, 0)) == NULL) err(1, "fts_open"); - while ((file = fts_read(ftsp)) != NULL) + while (errno = 0, (file = fts_read(ftsp)) != NULL) carried_error += handle_file(ftsp, file); + if (errno != 0) + err(1, "fts_read"); return (carried_error); } diff --git a/contrib/mtree/create.c b/contrib/mtree/create.c index dc3af7447a39..7a09a1cc3951 100644 --- a/contrib/mtree/create.c +++ b/contrib/mtree/create.c @@ -1,477 +1,479 @@ /* $NetBSD: create.c,v 1.73 2014/04/24 17:22:41 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 #if defined(__RCSID) && !defined(lint) #if 0 static char sccsid[] = "@(#)create.c 8.1 (Berkeley) 6/6/93"; #else __RCSID("$NetBSD: create.c,v 1.73 2014/04/24 17:22:41 christos Exp $"); #endif #endif /* not lint */ #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 #endif #ifndef NO_SHA1 #include #endif #ifndef NO_SHA2 #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; #ifdef __FreeBSD__ #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) { + while (errno = 0, (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; } } + if (errno != 0) + mtree_err("fts_read: %s", strerror(errno)); fts_close(t); if (sflag && keys & F_CKSUM) mtree_err("%s checksum: %u", fullpath, crc_total); } static void statf(FILE *fp, int indent, FTSENT *p) { u_int32_t len, val; int fd, offset; const char *name = NULL; #if !defined(NO_MD5) || !defined(NO_RMD160) || !defined(NO_SHA1) || !defined(NO_SHA2) char *digestbuf; #endif 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 (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) { if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 || crc(fd, &val, &len)) mtree_err("%s: %s", p->fts_accpath, strerror(errno)); close(fd); output(fp, indent, &offset, "cksum=%lu", (long)val); } #ifndef NO_MD5 if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) { if ((digestbuf = MD5File(p->fts_accpath, NULL)) == NULL) mtree_err("%s: MD5File failed: %s", p->fts_accpath, strerror(errno)); output(fp, indent, &offset, "%s=%s", MD5KEY, digestbuf); free(digestbuf); } #endif /* ! NO_MD5 */ #ifndef NO_RMD160 if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) { if ((digestbuf = RMD160File(p->fts_accpath, NULL)) == NULL) mtree_err("%s: RMD160File failed: %s", p->fts_accpath, strerror(errno)); output(fp, indent, &offset, "%s=%s", RMD160KEY, digestbuf); free(digestbuf); } #endif /* ! NO_RMD160 */ #ifndef NO_SHA1 if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) { if ((digestbuf = SHA1File(p->fts_accpath, NULL)) == NULL) mtree_err("%s: SHA1File failed: %s", p->fts_accpath, strerror(errno)); output(fp, indent, &offset, "%s=%s", SHA1KEY, digestbuf); free(digestbuf); } #endif /* ! NO_SHA1 */ #ifndef NO_SHA2 if (keys & F_SHA256 && S_ISREG(p->fts_statp->st_mode)) { if ((digestbuf = SHA256_File(p->fts_accpath, NULL)) == NULL) mtree_err("%s: SHA256_File failed: %s", p->fts_accpath, strerror(errno)); output(fp, indent, &offset, "%s=%s", SHA256KEY, digestbuf); free(digestbuf); } #ifdef SHA384_BLOCK_LENGTH if (keys & F_SHA384 && S_ISREG(p->fts_statp->st_mode)) { if ((digestbuf = SHA384_File(p->fts_accpath, NULL)) == NULL) mtree_err("%s: SHA384_File failed: %s", p->fts_accpath, strerror(errno)); output(fp, indent, &offset, "%s=%s", SHA384KEY, digestbuf); free(digestbuf); } #endif if (keys & F_SHA512 && S_ISREG(p->fts_statp->st_mode)) { if ((digestbuf = SHA512_File(p->fts_accpath, NULL)) == NULL) mtree_err("%s: SHA512_File failed: %s", p->fts_accpath, strerror(errno)); output(fp, indent, &offset, "%s=%s", SHA512KEY, digestbuf); free(digestbuf); } #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/contrib/mtree/verify.c b/contrib/mtree/verify.c index 66b020ac8b9f..4aca42c52aea 100644 --- a/contrib/mtree/verify.c +++ b/contrib/mtree/verify.c @@ -1,307 +1,309 @@ /* $NetBSD: verify.c,v 1.44 2013/02/03 19:15:17 christos Exp $ */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #if defined(__RCSID) && !defined(lint) #if 0 static char sccsid[] = "@(#)verify.c 8.1 (Berkeley) 6/6/93"; #else __RCSID("$NetBSD: verify.c,v 1.44 2013/02/03 19:15:17 christos Exp $"); #endif #endif /* not lint */ #include #include #if ! HAVE_NBTOOL_CONFIG_H #include #endif #include #include #include #include #include #include "extern.h" static NODE *root; static char path[MAXPATHLEN]; static void miss(NODE *, char *); static int vwalk(void); int verify(FILE *fi) { int rval; root = spec(fi); rval = vwalk(); miss(root, path); return (rval); } static int vwalk(void) { FTS *t; FTSENT *p; NODE *ep, *level; int specdepth, rval; char *argv[2]; char dot[] = "."; argv[0] = dot; argv[1] = NULL; if ((t = fts_open(argv, ftsoptions, NULL)) == NULL) mtree_err("fts_open: %s", strerror(errno)); level = root; specdepth = rval = 0; - while ((p = fts_read(t)) != NULL) { + while (errno = 0, (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: break; case FTS_DP: if (specdepth > p->fts_level) { for (level = level->parent; level->prev; level = level->prev) continue; --specdepth; } continue; case FTS_DNR: case FTS_ERR: case FTS_NS: warnx("%s: %s", RP(p), strerror(p->fts_errno)); continue; default: if (dflag) continue; } if (specdepth != p->fts_level) goto extra; for (ep = level; ep; ep = ep->next) if ((ep->flags & F_MAGIC && !fnmatch(ep->name, p->fts_name, FNM_PATHNAME)) || !strcmp(ep->name, p->fts_name)) { ep->flags |= F_VISIT; if ((ep->flags & F_NOCHANGE) == 0 && compare(ep, p)) rval = MISMATCHEXIT; if (!(ep->flags & F_IGN) && ep->type == F_DIR && p->fts_info == FTS_D) { if (ep->child) { level = ep->child; ++specdepth; } } else fts_set(t, p, FTS_SKIP); break; } if (ep) continue; extra: if (!eflag && !(dflag && p->fts_info == FTS_SL)) { printf("extra: %s", RP(p)); if (rflag) { if ((S_ISDIR(p->fts_statp->st_mode) ? rmdir : unlink)(p->fts_accpath)) { printf(", not removed: %s", strerror(errno)); } else printf(", removed"); } putchar('\n'); } fts_set(t, p, FTS_SKIP); } + if (errno != 0) + mtree_err("fts_read: %s", strerror(errno)); fts_close(t); if (sflag) warnx("%s checksum: %u", fullpath, crc_total); return (rval); } static void miss(NODE *p, char *tail) { int create; char *tp; const char *type; u_int32_t flags; for (; p; p = p->next) { if (p->flags & F_OPT && !(p->flags & F_VISIT)) continue; if (p->type != F_DIR && (dflag || p->flags & F_VISIT)) continue; strcpy(tail, p->name); if (!(p->flags & F_VISIT)) { /* 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 && S_ISDIR(statbuf.st_mode)) p->flags |= F_VISIT; else (void)printf("%s missing", path); } switch (p->type) { case F_BLOCK: case F_CHAR: type = "device"; break; case F_DIR: type = "directory"; break; case F_LINK: type = "symlink"; break; default: putchar('\n'); continue; } create = 0; if (!(p->flags & F_VISIT) && uflag) { if (mtree_Wflag || p->type == F_LINK) goto createit; if (!(p->flags & (F_UID | F_UNAME))) printf( " (%s not created: user not specified)", type); else if (!(p->flags & (F_GID | F_GNAME))) printf( " (%s not created: group not specified)", type); else if (!(p->flags & F_MODE)) printf( " (%s not created: mode not specified)", type); else createit: switch (p->type) { case F_BLOCK: case F_CHAR: if (mtree_Wflag) continue; if (!(p->flags & F_DEV)) printf( " (%s not created: device not specified)", type); else if (mknod(path, p->st_mode | nodetoino(p->type), p->st_rdev) == -1) printf(" (%s not created: %s)\n", type, strerror(errno)); else create = 1; break; case F_LINK: if (!(p->flags & F_SLINK)) printf( " (%s not created: link not specified)\n", type); else if (symlink(p->slink, path)) printf( " (%s not created: %s)\n", type, strerror(errno)); else create = 1; break; case F_DIR: if (mkdir(path, S_IRWXU|S_IRWXG|S_IRWXO)) printf(" (not created: %s)", strerror(errno)); else create = 1; break; default: mtree_err("can't create create %s", nodetype(p->type)); } } if (create) printf(" (created)"); if (p->type == F_DIR) { if (!(p->flags & F_VISIT)) putchar('\n'); for (tp = tail; *tp; ++tp) continue; *tp = '/'; miss(p->child, tp + 1); *tp = '\0'; } else putchar('\n'); if (!create || mtree_Wflag) continue; 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 (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 */ } } diff --git a/usr.bin/du/du.c b/usr.bin/du/du.c index 2365e19a67e9..012e439bba34 100644 --- a/usr.bin/du/du.c +++ b/usr.bin/du/du.c @@ -1,563 +1,563 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1989, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Newcomb. * * 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. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1989, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static const char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SI_OPT (CHAR_MAX + 1) #define UNITS_2 1 #define UNITS_SI 2 static SLIST_HEAD(ignhead, ignentry) ignores; struct ignentry { char *mask; SLIST_ENTRY(ignentry) next; }; static int linkchk(FTSENT *); static void usage(void); static void prthumanval(int64_t); static void ignoreadd(const char *); static void ignoreclean(void); static int ignorep(FTSENT *); static void siginfo(int __unused); static int nodumpflag = 0; static int Aflag, hflag; static long blocksize, cblocksize; static volatile sig_atomic_t info; static const struct option long_options[] = { { "si", no_argument, NULL, SI_OPT }, { NULL, no_argument, NULL, 0 }, }; int main(int argc, char *argv[]) { FTS *fts; FTSENT *p; off_t savednumber, curblocks; off_t threshold, threshold_sign; int ftsoptions; int depth; int Hflag, Lflag, aflag, sflag, dflag, cflag; int lflag, ch, notused, rval; char **save; static char dot[] = "."; setlocale(LC_ALL, ""); Hflag = Lflag = aflag = sflag = dflag = cflag = lflag = Aflag = 0; save = argv; ftsoptions = FTS_PHYSICAL; savednumber = 0; threshold = 0; threshold_sign = 1; cblocksize = DEV_BSIZE; blocksize = 0; depth = INT_MAX; SLIST_INIT(&ignores); while ((ch = getopt_long(argc, argv, "+AB:HI:LPasd:cghklmnrt:x", long_options, NULL)) != -1) switch (ch) { case 'A': Aflag = 1; break; case 'B': errno = 0; cblocksize = atoi(optarg); if (errno == ERANGE || cblocksize <= 0) { warnx("invalid argument to option B: %s", optarg); usage(); } break; case 'H': Hflag = 1; Lflag = 0; break; case 'I': ignoreadd(optarg); break; case 'L': Lflag = 1; Hflag = 0; break; case 'P': Hflag = Lflag = 0; break; case 'a': aflag = 1; break; case 's': sflag = 1; break; case 'd': dflag = 1; errno = 0; depth = atoi(optarg); if (errno == ERANGE || depth < 0) { warnx("invalid argument to option d: %s", optarg); usage(); } break; case 'c': cflag = 1; break; case 'g': hflag = 0; blocksize = 1073741824; break; case 'h': hflag = UNITS_2; break; case 'k': hflag = 0; blocksize = 1024; break; case 'l': lflag = 1; break; case 'm': hflag = 0; blocksize = 1048576; break; case 'n': nodumpflag = 1; break; case 'r': /* Compatibility. */ break; case 't' : if (expand_number(optarg, &threshold) != 0 || threshold == 0) { warnx("invalid threshold: %s", optarg); usage(); } else if (threshold < 0) threshold_sign = -1; break; case 'x': ftsoptions |= FTS_XDEV; break; case SI_OPT: hflag = UNITS_SI; break; case '?': default: usage(); /* NOTREACHED */ } argc -= optind; argv += optind; /* * XXX * Because of the way that fts(3) works, logical walks will not count * the blocks actually used by symbolic links. We rationalize this by * noting that users computing logical sizes are likely to do logical * copies, so not counting the links is correct. The real reason is * that we'd have to re-implement the kernel's symbolic link traversing * algorithm to get this right. If, for example, you have relative * symbolic links referencing other relative symbolic links, it gets * very nasty, very fast. The bottom line is that it's documented in * the man page, so it's a feature. */ if (Hflag) ftsoptions |= FTS_COMFOLLOW; if (Lflag) { ftsoptions &= ~FTS_PHYSICAL; ftsoptions |= FTS_LOGICAL; } if (!Aflag && (cblocksize % DEV_BSIZE) != 0) cblocksize = howmany(cblocksize, DEV_BSIZE) * DEV_BSIZE; if (aflag + dflag + sflag > 1) usage(); if (sflag) depth = 0; if (!*argv) { argv = save; argv[0] = dot; argv[1] = NULL; } if (blocksize == 0) (void)getbsize(¬used, &blocksize); if (!Aflag) { cblocksize /= DEV_BSIZE; blocksize /= DEV_BSIZE; } if (threshold != 0) threshold = howmany(threshold / DEV_BSIZE * cblocksize, blocksize); rval = 0; (void)signal(SIGINFO, siginfo); if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) err(1, "fts_open"); - while ((p = fts_read(fts)) != NULL) { + while (errno = 0, (p = fts_read(fts)) != NULL) { switch (p->fts_info) { case FTS_D: /* Ignore. */ if (ignorep(p)) fts_set(fts, p, FTS_SKIP); break; case FTS_DP: if (ignorep(p)) break; curblocks = Aflag ? howmany(p->fts_statp->st_size, cblocksize) : howmany(p->fts_statp->st_blocks, cblocksize); p->fts_parent->fts_bignum += p->fts_bignum += curblocks; if (p->fts_level <= depth && threshold <= threshold_sign * howmany(p->fts_bignum * cblocksize, blocksize)) { if (hflag > 0) { prthumanval(p->fts_bignum); (void)printf("\t%s\n", p->fts_path); } else { (void)printf("%jd\t%s\n", (intmax_t)howmany(p->fts_bignum * cblocksize, blocksize), p->fts_path); } } if (info) { info = 0; (void)printf("\t%s\n", p->fts_path); } break; case FTS_DC: /* Ignore. */ break; case FTS_DNR: /* Warn, continue. */ case FTS_ERR: case FTS_NS: warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; break; default: if (ignorep(p)) break; if (lflag == 0 && p->fts_statp->st_nlink > 1 && linkchk(p)) break; curblocks = Aflag ? howmany(p->fts_statp->st_size, cblocksize) : howmany(p->fts_statp->st_blocks, cblocksize); if (aflag || p->fts_level == 0) { if (hflag > 0) { prthumanval(curblocks); (void)printf("\t%s\n", p->fts_path); } else { (void)printf("%jd\t%s\n", (intmax_t)howmany(curblocks * cblocksize, blocksize), p->fts_path); } } p->fts_parent->fts_bignum += curblocks; } savednumber = p->fts_parent->fts_bignum; } if (errno) err(1, "fts_read"); if (cflag) { if (hflag > 0) { prthumanval(savednumber); (void)printf("\ttotal\n"); } else { (void)printf("%jd\ttotal\n", (intmax_t)howmany( savednumber * cblocksize, blocksize)); } } ignoreclean(); exit(rval); } static int linkchk(FTSENT *p) { struct links_entry { struct links_entry *next; struct links_entry *previous; int links; dev_t dev; ino_t ino; }; static const size_t links_hash_initial_size = 8192; static struct links_entry **buckets; static struct links_entry *free_list; static size_t number_buckets; static unsigned long number_entries; static char stop_allocating; struct links_entry *le, **new_buckets; struct stat *st; size_t i, new_size; int hash; st = p->fts_statp; /* If necessary, initialize the hash table. */ if (buckets == NULL) { number_buckets = links_hash_initial_size; buckets = malloc(number_buckets * sizeof(buckets[0])); if (buckets == NULL) errx(1, "No memory for hardlink detection"); for (i = 0; i < number_buckets; i++) buckets[i] = NULL; } /* If the hash table is getting too full, enlarge it. */ if (number_entries > number_buckets * 10 && !stop_allocating) { new_size = number_buckets * 2; new_buckets = calloc(new_size, sizeof(struct links_entry *)); /* Try releasing the free list to see if that helps. */ if (new_buckets == NULL && free_list != NULL) { while (free_list != NULL) { le = free_list; free_list = le->next; free(le); } new_buckets = calloc(new_size, sizeof(new_buckets[0])); } if (new_buckets == NULL) { stop_allocating = 1; warnx("No more memory for tracking hard links"); } else { for (i = 0; i < number_buckets; i++) { while (buckets[i] != NULL) { /* Remove entry from old bucket. */ le = buckets[i]; buckets[i] = le->next; /* Add entry to new bucket. */ hash = (le->dev ^ le->ino) % new_size; if (new_buckets[hash] != NULL) new_buckets[hash]->previous = le; le->next = new_buckets[hash]; le->previous = NULL; new_buckets[hash] = le; } } free(buckets); buckets = new_buckets; number_buckets = new_size; } } /* Try to locate this entry in the hash table. */ hash = ( st->st_dev ^ st->st_ino ) % number_buckets; for (le = buckets[hash]; le != NULL; le = le->next) { if (le->dev == st->st_dev && le->ino == st->st_ino) { /* * Save memory by releasing an entry when we've seen * all of its links. */ if (--le->links <= 0) { if (le->previous != NULL) le->previous->next = le->next; if (le->next != NULL) le->next->previous = le->previous; if (buckets[hash] == le) buckets[hash] = le->next; number_entries--; /* Recycle this node through the free list */ if (stop_allocating) { free(le); } else { le->next = free_list; free_list = le; } } return (1); } } if (stop_allocating) return (0); /* Add this entry to the links cache. */ if (free_list != NULL) { /* Pull a node from the free list if we can. */ le = free_list; free_list = le->next; } else /* Malloc one if we have to. */ le = malloc(sizeof(struct links_entry)); if (le == NULL) { stop_allocating = 1; warnx("No more memory for tracking hard links"); return (0); } le->dev = st->st_dev; le->ino = st->st_ino; le->links = st->st_nlink - 1; number_entries++; le->next = buckets[hash]; le->previous = NULL; if (buckets[hash] != NULL) buckets[hash]->previous = le; buckets[hash] = le; return (0); } static void prthumanval(int64_t bytes) { char buf[5]; int flags; bytes *= cblocksize; flags = HN_B | HN_NOSPACE | HN_DECIMAL; if (!Aflag) bytes *= DEV_BSIZE; if (hflag == UNITS_SI) flags |= HN_DIVISOR_1000; humanize_number(buf, sizeof(buf), bytes, "", HN_AUTOSCALE, flags); (void)printf("%4s", buf); } static void usage(void) { (void)fprintf(stderr, "usage: du [-Aclnx] [-H | -L | -P] [-g | -h | -k | -m] " "[-a | -s | -d depth] [-B blocksize] [-I mask] " "[-t threshold] [file ...]\n"); exit(EX_USAGE); } static void ignoreadd(const char *mask) { struct ignentry *ign; ign = calloc(1, sizeof(*ign)); if (ign == NULL) errx(1, "cannot allocate memory"); ign->mask = strdup(mask); if (ign->mask == NULL) errx(1, "cannot allocate memory"); SLIST_INSERT_HEAD(&ignores, ign, next); } static void ignoreclean(void) { struct ignentry *ign; while (!SLIST_EMPTY(&ignores)) { ign = SLIST_FIRST(&ignores); SLIST_REMOVE_HEAD(&ignores, next); free(ign->mask); free(ign); } } static int ignorep(FTSENT *ent) { struct ignentry *ign; if (nodumpflag && (ent->fts_statp->st_flags & UF_NODUMP)) return 1; SLIST_FOREACH(ign, &ignores, next) if (fnmatch(ign->mask, ent->fts_name, 0) != FNM_NOMATCH) return 1; return 0; } static void siginfo(int sig __unused) { info = 1; } diff --git a/usr.bin/grep/util.c b/usr.bin/grep/util.c index 33afe4d6b030..e517e4eaee6d 100644 --- a/usr.bin/grep/util.c +++ b/usr.bin/grep/util.c @@ -1,777 +1,779 @@ /* $NetBSD: util.c,v 1.9 2011/02/27 17:33:37 joerg Exp $ */ /* $FreeBSD$ */ /* $OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav * Copyright (C) 2008-2010 Gabor Kovesdan * Copyright (C) 2017 Kyle Evans * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "grep.h" static bool first_match = true; /* * Match printing context */ struct mprintc { long long tail; /* Number of trailing lines to record */ int last_outed; /* Number of lines since last output */ bool doctx; /* Printing context? */ bool printmatch; /* Printing matches? */ bool same_file; /* Same file as previously printed? */ }; static void procmatch_match(struct mprintc *mc, struct parsec *pc); static void procmatch_nomatch(struct mprintc *mc, struct parsec *pc); static bool procmatches(struct mprintc *mc, struct parsec *pc, bool matched); #ifdef WITH_INTERNAL_NOSPEC static int litexec(const struct pat *pat, const char *string, size_t nmatch, regmatch_t pmatch[]); #endif static bool procline(struct parsec *pc); static void printline(struct parsec *pc, int sep); static void printline_metadata(struct str *line, int sep); bool file_matching(const char *fname) { char *fname_base, *fname_buf; bool ret; ret = finclude ? false : true; fname_buf = strdup(fname); if (fname_buf == NULL) err(2, "strdup"); fname_base = basename(fname_buf); for (unsigned int i = 0; i < fpatterns; ++i) { if (fnmatch(fpattern[i].pat, fname, 0) == 0 || fnmatch(fpattern[i].pat, fname_base, 0) == 0) /* * The last pattern matched wins exclusion/inclusion * rights, so we can't reasonably bail out early here. */ ret = (fpattern[i].mode != EXCL_PAT); } free(fname_buf); return (ret); } static inline bool dir_matching(const char *dname) { bool ret; ret = dinclude ? false : true; for (unsigned int i = 0; i < dpatterns; ++i) { if (dname != NULL && fnmatch(dpattern[i].pat, dname, 0) == 0) /* * The last pattern matched wins exclusion/inclusion * rights, so we can't reasonably bail out early here. */ ret = (dpattern[i].mode != EXCL_PAT); } return (ret); } /* * Processes a directory when a recursive search is performed with * the -R option. Each appropriate file is passed to procfile(). */ bool grep_tree(char **argv) { FTS *fts; FTSENT *p; int fts_flags; bool matched, ok; const char *wd[] = { ".", NULL }; matched = false; /* This switch effectively initializes 'fts_flags' */ switch(linkbehave) { case LINK_EXPLICIT: fts_flags = FTS_COMFOLLOW; break; case LINK_SKIP: fts_flags = FTS_PHYSICAL; break; default: fts_flags = FTS_LOGICAL; } fts_flags |= FTS_NOSTAT | FTS_NOCHDIR; fts = fts_open((argv[0] == NULL) ? __DECONST(char * const *, wd) : argv, fts_flags, NULL); if (fts == NULL) err(2, "fts_open"); - while ((p = fts_read(fts)) != NULL) { + while (errno = 0, (p = fts_read(fts)) != NULL) { switch (p->fts_info) { case FTS_DNR: /* FALLTHROUGH */ case FTS_ERR: file_err = true; if(!sflag) warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); break; case FTS_D: /* FALLTHROUGH */ case FTS_DP: if (dexclude || dinclude) if (!dir_matching(p->fts_name) || !dir_matching(p->fts_path)) fts_set(fts, p, FTS_SKIP); break; case FTS_DC: /* Print a warning for recursive directory loop */ warnx("warning: %s: recursive directory loop", p->fts_path); break; default: /* Check for file exclusion/inclusion */ ok = true; if (fexclude || finclude) ok &= file_matching(p->fts_path); if (ok && procfile(p->fts_path)) matched = true; break; } } + if (errno != 0) + err(2, "fts_read"); fts_close(fts); return (matched); } static void procmatch_match(struct mprintc *mc, struct parsec *pc) { if (mc->doctx) { if (!first_match && (!mc->same_file || mc->last_outed > 0)) printf("--\n"); if (Bflag > 0) printqueue(); mc->tail = Aflag; } /* Print the matching line, but only if not quiet/binary */ if (mc->printmatch) { printline(pc, ':'); while (pc->matchidx >= MAX_MATCHES) { /* Reset matchidx and try again */ pc->matchidx = 0; if (procline(pc) == !vflag) printline(pc, ':'); else break; } first_match = false; mc->same_file = true; mc->last_outed = 0; } } static void procmatch_nomatch(struct mprintc *mc, struct parsec *pc) { /* Deal with any -A context as needed */ if (mc->tail > 0) { grep_printline(&pc->ln, '-'); mc->tail--; if (Bflag > 0) clearqueue(); } else if (Bflag == 0 || (Bflag > 0 && enqueue(&pc->ln))) /* * Enqueue non-matching lines for -B context. If we're not * actually doing -B context or if the enqueue resulted in a * line being rotated out, then go ahead and increment * last_outed to signify a gap between context/match. */ ++mc->last_outed; } /* * Process any matches in the current parsing context, return a boolean * indicating whether we should halt any further processing or not. 'true' to * continue processing, 'false' to halt. */ static bool procmatches(struct mprintc *mc, struct parsec *pc, bool matched) { /* * XXX TODO: This should loop over pc->matches and handle things on a * line-by-line basis, setting up a `struct str` as needed. */ /* Deal with any -B context or context separators */ if (matched) { procmatch_match(mc, pc); /* Count the matches if we have a match limit */ if (mflag) { /* XXX TODO: Decrement by number of matched lines */ mcount -= 1; if (mcount <= 0) return (false); } } else if (mc->doctx) procmatch_nomatch(mc, pc); return (true); } /* * Opens a file and processes it. Each file is processed line-by-line * passing the lines to procline(). */ bool procfile(const char *fn) { struct parsec pc; struct mprintc mc; struct file *f; struct stat sb; mode_t s; int lines; bool line_matched; if (strcmp(fn, "-") == 0) { fn = label != NULL ? label : errstr[1]; f = grep_open(NULL); } else { if (stat(fn, &sb) == 0) { /* Check if we need to process the file */ s = sb.st_mode & S_IFMT; if (dirbehave == DIR_SKIP && s == S_IFDIR) return (false); if (devbehave == DEV_SKIP && (s == S_IFIFO || s == S_IFCHR || s == S_IFBLK || s == S_IFSOCK)) return (false); } f = grep_open(fn); } if (f == NULL) { file_err = true; if (!sflag) warn("%s", fn); return (false); } pc.ln.file = grep_strdup(fn); pc.ln.line_no = 0; pc.ln.len = 0; pc.ln.boff = 0; pc.ln.off = -1; pc.binary = f->binary; pc.cntlines = false; memset(&mc, 0, sizeof(mc)); mc.printmatch = true; if ((pc.binary && binbehave == BINFILE_BIN) || cflag || qflag || lflag || Lflag) mc.printmatch = false; if (mc.printmatch && (Aflag != 0 || Bflag != 0)) mc.doctx = true; if (mc.printmatch && (Aflag != 0 || Bflag != 0 || mflag || nflag)) pc.cntlines = true; mcount = mlimit; for (lines = 0; lines == 0 || !(lflag || qflag); ) { /* * XXX TODO: We need to revisit this in a chunking world. We're * not going to be doing per-line statistics because of the * overhead involved. procmatches can figure that stuff out as * needed. */ /* Reset per-line statistics */ pc.printed = 0; pc.matchidx = 0; pc.lnstart = 0; pc.ln.boff = 0; pc.ln.off += pc.ln.len + 1; /* XXX TODO: Grab a chunk */ if ((pc.ln.dat = grep_fgetln(f, &pc)) == NULL || pc.ln.len == 0) break; if (pc.ln.len > 0 && pc.ln.dat[pc.ln.len - 1] == fileeol) --pc.ln.len; pc.ln.line_no++; /* Return if we need to skip a binary file */ if (pc.binary && binbehave == BINFILE_SKIP) { grep_close(f); free(pc.ln.file); free(f); return (0); } line_matched = procline(&pc) == !vflag; if (line_matched) ++lines; /* Halt processing if we hit our match limit */ if (!procmatches(&mc, &pc, line_matched)) break; } if (Bflag > 0) clearqueue(); grep_close(f); if (cflag) { if (!hflag) printf("%s:", pc.ln.file); printf("%u\n", lines); } if (lflag && !qflag && lines != 0) printf("%s%c", fn, nullflag ? 0 : '\n'); if (Lflag && !qflag && lines == 0) printf("%s%c", fn, nullflag ? 0 : '\n'); if (lines != 0 && !cflag && !lflag && !Lflag && binbehave == BINFILE_BIN && f->binary && !qflag) printf(errstr[7], fn); free(pc.ln.file); free(f); return (lines != 0); } #ifdef WITH_INTERNAL_NOSPEC /* * Internal implementation of literal string search within a string, modeled * after regexec(3), for use when the regex(3) implementation doesn't offer * either REG_NOSPEC or REG_LITERAL. This does not apply in the default FreeBSD * config, but in other scenarios such as building against libgnuregex or on * some non-FreeBSD OSes. */ static int litexec(const struct pat *pat, const char *string, size_t nmatch, regmatch_t pmatch[]) { char *(*strstr_fn)(const char *, const char *); char *sub, *subject; const char *search; size_t idx, n, ofs, stringlen; if (cflags & REG_ICASE) strstr_fn = strcasestr; else strstr_fn = strstr; idx = 0; ofs = pmatch[0].rm_so; stringlen = pmatch[0].rm_eo; if (ofs >= stringlen) return (REG_NOMATCH); subject = strndup(string, stringlen); if (subject == NULL) return (REG_ESPACE); for (n = 0; ofs < stringlen;) { search = (subject + ofs); if ((unsigned long)pat->len > strlen(search)) break; sub = strstr_fn(search, pat->pat); /* * Ignoring the empty string possibility due to context: grep optimizes * for empty patterns and will never reach this point. */ if (sub == NULL) break; ++n; /* Fill in pmatch if necessary */ if (nmatch > 0) { pmatch[idx].rm_so = ofs + (sub - search); pmatch[idx].rm_eo = pmatch[idx].rm_so + pat->len; if (++idx == nmatch) break; ofs = pmatch[idx].rm_so + 1; } else /* We only needed to know if we match or not */ break; } free(subject); if (n > 0 && nmatch > 0) for (n = idx; n < nmatch; ++n) pmatch[n].rm_so = pmatch[n].rm_eo = -1; return (n > 0 ? 0 : REG_NOMATCH); } #endif /* WITH_INTERNAL_NOSPEC */ #define iswword(x) (iswalnum((x)) || (x) == L'_') /* * Processes a line comparing it with the specified patterns. Each pattern * is looped to be compared along with the full string, saving each and every * match, which is necessary to colorize the output and to count the * matches. The matching lines are passed to printline() to display the * appropriate output. */ static bool procline(struct parsec *pc) { regmatch_t pmatch, lastmatch, chkmatch; wchar_t wbegin, wend; size_t st, nst; unsigned int i; int r = 0, leflags = eflags; size_t startm = 0, matchidx; unsigned int retry; bool lastmatched, matched; matchidx = pc->matchidx; /* * With matchall (empty pattern), we can try to take some shortcuts. * Emtpy patterns trivially match every line except in the -w and -x * cases. For -w (whole-word) cases, we only match if the first * character isn't a word-character. For -x (whole-line) cases, we only * match if the line is empty. */ if (matchall) { if (pc->ln.len == 0) return (true); if (wflag) { wend = L' '; if (sscanf(&pc->ln.dat[0], "%lc", &wend) == 1 && !iswword(wend)) return (true); } else if (!xflag) return (true); /* * If we don't have any other patterns, we really don't match. * If we do have other patterns, we must fall through and check * them. */ if (patterns == 0) return (false); } matched = false; st = pc->lnstart; nst = 0; /* Initialize to avoid a false positive warning from GCC. */ lastmatch.rm_so = lastmatch.rm_eo = 0; /* Loop to process the whole line */ while (st <= pc->ln.len) { lastmatched = false; startm = matchidx; retry = 0; if (st > 0 && pc->ln.dat[st - 1] != fileeol) leflags |= REG_NOTBOL; /* Loop to compare with all the patterns */ for (i = 0; i < patterns; i++) { pmatch.rm_so = st; pmatch.rm_eo = pc->ln.len; #ifdef WITH_INTERNAL_NOSPEC if (grepbehave == GREP_FIXED) r = litexec(&pattern[i], pc->ln.dat, 1, &pmatch); else #endif r = regexec(&r_pattern[i], pc->ln.dat, 1, &pmatch, leflags); if (r != 0) continue; /* Check for full match */ if (xflag && (pmatch.rm_so != 0 || (size_t)pmatch.rm_eo != pc->ln.len)) continue; /* Check for whole word match */ if (wflag) { wbegin = wend = L' '; if (pmatch.rm_so != 0 && sscanf(&pc->ln.dat[pmatch.rm_so - 1], "%lc", &wbegin) != 1) r = REG_NOMATCH; else if ((size_t)pmatch.rm_eo != pc->ln.len && sscanf(&pc->ln.dat[pmatch.rm_eo], "%lc", &wend) != 1) r = REG_NOMATCH; else if (iswword(wbegin) || iswword(wend)) r = REG_NOMATCH; /* * If we're doing whole word matching and we * matched once, then we should try the pattern * again after advancing just past the start of * the earliest match. This allows the pattern * to match later on in the line and possibly * still match a whole word. */ if (r == REG_NOMATCH && (retry == pc->lnstart || (unsigned int)pmatch.rm_so + 1 < retry)) retry = pmatch.rm_so + 1; if (r == REG_NOMATCH) continue; } lastmatched = true; lastmatch = pmatch; if (matchidx == 0) matched = true; /* * Replace previous match if the new one is earlier * and/or longer. This will lead to some amount of * extra work if -o/--color are specified, but it's * worth it from a correctness point of view. */ if (matchidx > startm) { chkmatch = pc->matches[matchidx - 1]; if (pmatch.rm_so < chkmatch.rm_so || (pmatch.rm_so == chkmatch.rm_so && (pmatch.rm_eo - pmatch.rm_so) > (chkmatch.rm_eo - chkmatch.rm_so))) { pc->matches[matchidx - 1] = pmatch; nst = pmatch.rm_eo; } } else { /* Advance as normal if not */ pc->matches[matchidx++] = pmatch; nst = pmatch.rm_eo; } /* avoid excessive matching - skip further patterns */ if ((color == NULL && !oflag) || qflag || lflag || matchidx >= MAX_MATCHES) { pc->lnstart = nst; lastmatched = false; break; } } /* * Advance to just past the start of the earliest match, try * again just in case we still have a chance to match later in * the string. */ if (!lastmatched && retry > pc->lnstart) { st = retry; continue; } /* XXX TODO: We will need to keep going, since we're chunky */ /* One pass if we are not recording matches */ if (!wflag && ((color == NULL && !oflag) || qflag || lflag || Lflag)) break; /* If we didn't have any matches or REG_NOSUB set */ if (!lastmatched || (cflags & REG_NOSUB)) nst = pc->ln.len; if (!lastmatched) /* No matches */ break; else if (st == nst && lastmatch.rm_so == lastmatch.rm_eo) /* Zero-length match -- advance one more so we don't get stuck */ nst++; /* Advance st based on previous matches */ st = nst; pc->lnstart = st; } /* Reflect the new matchidx in the context */ pc->matchidx = matchidx; return matched; } /* * Safe malloc() for internal use. */ void * grep_malloc(size_t size) { void *ptr; if ((ptr = malloc(size)) == NULL) err(2, "malloc"); return (ptr); } /* * Safe calloc() for internal use. */ void * grep_calloc(size_t nmemb, size_t size) { void *ptr; if ((ptr = calloc(nmemb, size)) == NULL) err(2, "calloc"); return (ptr); } /* * Safe realloc() for internal use. */ void * grep_realloc(void *ptr, size_t size) { if ((ptr = realloc(ptr, size)) == NULL) err(2, "realloc"); return (ptr); } /* * Safe strdup() for internal use. */ char * grep_strdup(const char *str) { char *ret; if ((ret = strdup(str)) == NULL) err(2, "strdup"); return (ret); } /* * Print an entire line as-is, there are no inline matches to consider. This is * used for printing context. */ void grep_printline(struct str *line, int sep) { printline_metadata(line, sep); fwrite(line->dat, line->len, 1, stdout); putchar(fileeol); } static void printline_metadata(struct str *line, int sep) { bool printsep; printsep = false; if (!hflag) { if (!nullflag) { fputs(line->file, stdout); printsep = true; } else { printf("%s", line->file); putchar(0); } } if (nflag) { if (printsep) putchar(sep); printf("%d", line->line_no); printsep = true; } if (bflag) { if (printsep) putchar(sep); printf("%lld", (long long)(line->off + line->boff)); printsep = true; } if (printsep) putchar(sep); } /* * Prints a matching line according to the command line options. */ static void printline(struct parsec *pc, int sep) { size_t a = 0; size_t i, matchidx; regmatch_t match; /* If matchall, everything matches but don't actually print for -o */ if (oflag && matchall) return; matchidx = pc->matchidx; /* --color and -o */ if ((oflag || color) && matchidx > 0) { /* Only print metadata once per line if --color */ if (!oflag && pc->printed == 0) printline_metadata(&pc->ln, sep); for (i = 0; i < matchidx; i++) { match = pc->matches[i]; /* Don't output zero length matches */ if (match.rm_so == match.rm_eo) continue; /* * Metadata is printed on a per-line basis, so every * match gets file metadata with the -o flag. */ if (oflag) { pc->ln.boff = match.rm_so; printline_metadata(&pc->ln, sep); } else fwrite(pc->ln.dat + a, match.rm_so - a, 1, stdout); if (color) fprintf(stdout, "\33[%sm\33[K", color); fwrite(pc->ln.dat + match.rm_so, match.rm_eo - match.rm_so, 1, stdout); if (color) fprintf(stdout, "\33[m\33[K"); a = match.rm_eo; if (oflag) putchar('\n'); } if (!oflag) { if (pc->ln.len - a > 0) fwrite(pc->ln.dat + a, pc->ln.len - a, 1, stdout); putchar('\n'); } } else grep_printline(&pc->ln, sep); pc->printed++; } diff --git a/usr.bin/gzip/gzip.c b/usr.bin/gzip/gzip.c index 47cfd225b844..5128e7ed43e0 100644 --- a/usr.bin/gzip/gzip.c +++ b/usr.bin/gzip/gzip.c @@ -1,2363 +1,2365 @@ /* $NetBSD: gzip.c,v 1.116 2018/10/27 11:39:12 skrll Exp $ */ /*- * SPDX-License-Identifier: BSD-2-Clause-NetBSD * * Copyright (c) 1997, 1998, 2003, 2004, 2006, 2008, 2009, 2010, 2011, 2015, 2017 * Matthew R. Green * 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 ``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 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 #ifndef lint __COPYRIGHT("@(#) Copyright (c) 1997, 1998, 2003, 2004, 2006, 2008,\ 2009, 2010, 2011, 2015, 2017 Matthew R. Green. All rights reserved."); __FBSDID("$FreeBSD$"); #endif /* not lint */ /* * gzip.c -- GPL free gzip using zlib. * * RFC 1950 covers the zlib format * RFC 1951 covers the deflate format * RFC 1952 covers the gzip format * * TODO: * - use mmap where possible * - make bzip2/compress -v/-t/-l support work as well as possible */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* what type of file are we dealing with */ enum filetype { FT_GZIP, #ifndef NO_BZIP2_SUPPORT FT_BZIP2, #endif #ifndef NO_COMPRESS_SUPPORT FT_Z, #endif #ifndef NO_PACK_SUPPORT FT_PACK, #endif #ifndef NO_XZ_SUPPORT FT_XZ, #endif #ifndef NO_LZ_SUPPORT FT_LZ, #endif FT_LAST, FT_UNKNOWN }; #ifndef NO_BZIP2_SUPPORT #include #define BZ2_SUFFIX ".bz2" #define BZIP2_MAGIC "BZh" #endif #ifndef NO_COMPRESS_SUPPORT #define Z_SUFFIX ".Z" #define Z_MAGIC "\037\235" #endif #ifndef NO_PACK_SUPPORT #define PACK_MAGIC "\037\036" #endif #ifndef NO_XZ_SUPPORT #include #define XZ_SUFFIX ".xz" #define XZ_MAGIC "\3757zXZ" #endif #ifndef NO_LZ_SUPPORT #define LZ_SUFFIX ".lz" #define LZ_MAGIC "LZIP" #endif #define GZ_SUFFIX ".gz" #define BUFLEN (64 * 1024) #define GZIP_MAGIC0 0x1F #define GZIP_MAGIC1 0x8B #define GZIP_OMAGIC1 0x9E #define GZIP_TIMESTAMP (off_t)4 #define GZIP_ORIGNAME (off_t)10 #define HEAD_CRC 0x02 #define EXTRA_FIELD 0x04 #define ORIG_NAME 0x08 #define COMMENT 0x10 #define OS_CODE 3 /* Unix */ typedef struct { const char *zipped; int ziplen; const char *normal; /* for unzip - must not be longer than zipped */ } suffixes_t; static suffixes_t suffixes[] = { #define SUFFIX(Z, N) {Z, sizeof Z - 1, N} SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S .xxx */ #ifndef SMALL SUFFIX(GZ_SUFFIX, ""), SUFFIX(".z", ""), SUFFIX("-gz", ""), SUFFIX("-z", ""), SUFFIX("_z", ""), SUFFIX(".taz", ".tar"), SUFFIX(".tgz", ".tar"), #ifndef NO_BZIP2_SUPPORT SUFFIX(BZ2_SUFFIX, ""), SUFFIX(".tbz", ".tar"), SUFFIX(".tbz2", ".tar"), #endif #ifndef NO_COMPRESS_SUPPORT SUFFIX(Z_SUFFIX, ""), #endif #ifndef NO_XZ_SUPPORT SUFFIX(XZ_SUFFIX, ""), #endif #ifndef NO_LZ_SUPPORT SUFFIX(LZ_SUFFIX, ""), #endif SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S "" */ #endif /* SMALL */ #undef SUFFIX }; #define NUM_SUFFIXES (nitems(suffixes)) #define SUFFIX_MAXLEN 30 static const char gzip_version[] = "FreeBSD gzip 20190107"; #ifndef SMALL static const char gzip_copyright[] = \ " Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n" " All rights reserved.\n" "\n" " Redistribution and use in source and binary forms, with or without\n" " modification, are permitted provided that the following conditions\n" " are met:\n" " 1. Redistributions of source code must retain the above copyright\n" " notice, this list of conditions and the following disclaimer.\n" " 2. Redistributions in binary form must reproduce the above copyright\n" " notice, this list of conditions and the following disclaimer in the\n" " documentation and/or other materials provided with the distribution.\n" "\n" " THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n" " IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n" " OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n" " IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n" " INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n" " BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n" " LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n" " AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n" " OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n" " OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n" " SUCH DAMAGE."; #endif static int cflag; /* stdout mode */ static int dflag; /* decompress mode */ static int lflag; /* list mode */ static int numflag = 6; /* gzip -1..-9 value */ static const char *remove_file = NULL; /* file to be removed upon SIGINT */ static int fflag; /* force mode */ #ifndef SMALL static int kflag; /* don't delete input files */ static int nflag; /* don't save name/timestamp */ static int Nflag; /* don't restore name/timestamp */ static int qflag; /* quiet mode */ static int rflag; /* recursive mode */ static int tflag; /* test */ static int vflag; /* verbose mode */ static sig_atomic_t print_info = 0; #else #define qflag 0 #define tflag 0 #endif static int exit_value = 0; /* exit value */ static const char *infile; /* name of file coming in */ static void maybe_err(const char *fmt, ...) __printflike(1, 2) __dead2; #if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \ !defined(NO_XZ_SUPPORT) static void maybe_errx(const char *fmt, ...) __printflike(1, 2) __dead2; #endif static void maybe_warn(const char *fmt, ...) __printflike(1, 2); static void maybe_warnx(const char *fmt, ...) __printflike(1, 2); static enum filetype file_gettype(u_char *); #ifdef SMALL #define gz_compress(if, of, sz, fn, tm) gz_compress(if, of, sz) #endif static off_t gz_compress(int, int, off_t *, const char *, uint32_t); static off_t gz_uncompress(int, int, char *, size_t, off_t *, const char *); static off_t file_compress(char *, char *, size_t); static off_t file_uncompress(char *, char *, size_t); static void handle_pathname(char *); static void handle_file(char *, struct stat *); static void handle_stdin(void); static void handle_stdout(void); static void print_ratio(off_t, off_t, FILE *); static void print_list(int fd, off_t, const char *, time_t); static void usage(void) __dead2; static void display_version(void) __dead2; #ifndef SMALL static void display_license(void); #endif static const suffixes_t *check_suffix(char *, int); static ssize_t read_retry(int, void *, size_t); static ssize_t write_retry(int, const void *, size_t); static void print_list_out(off_t, off_t, const char*); #ifdef SMALL #define infile_set(f,t) infile_set(f) #endif static void infile_set(const char *newinfile, off_t total); #ifdef SMALL #define unlink_input(f, sb) unlink(f) #define check_siginfo() /* nothing */ #define setup_signals() /* nothing */ #define infile_newdata(t) /* nothing */ #else static off_t infile_total; /* total expected to read/write */ static off_t infile_current; /* current read/write */ static void check_siginfo(void); static off_t cat_fd(unsigned char *, size_t, off_t *, int fd); static void prepend_gzip(char *, int *, char ***); static void handle_dir(char *); static void print_verbage(const char *, const char *, off_t, off_t); static void print_test(const char *, int); static void copymodes(int fd, const struct stat *, const char *file); static int check_outfile(const char *outfile); static void setup_signals(void); static void infile_newdata(size_t newdata); static void infile_clear(void); #endif #ifndef NO_BZIP2_SUPPORT static off_t unbzip2(int, int, char *, size_t, off_t *); #endif #ifndef NO_COMPRESS_SUPPORT static FILE *zdopen(int); static off_t zuncompress(FILE *, FILE *, char *, size_t, off_t *); #endif #ifndef NO_PACK_SUPPORT static off_t unpack(int, int, char *, size_t, off_t *); #endif #ifndef NO_XZ_SUPPORT static off_t unxz(int, int, char *, size_t, off_t *); static off_t unxz_len(int); #endif #ifndef NO_LZ_SUPPORT static off_t unlz(int, int, char *, size_t, off_t *); #endif #ifdef SMALL #define getopt_long(a,b,c,d,e) getopt(a,b,c) #else static const struct option longopts[] = { { "stdout", no_argument, 0, 'c' }, { "to-stdout", no_argument, 0, 'c' }, { "decompress", no_argument, 0, 'd' }, { "uncompress", no_argument, 0, 'd' }, { "force", no_argument, 0, 'f' }, { "help", no_argument, 0, 'h' }, { "keep", no_argument, 0, 'k' }, { "list", no_argument, 0, 'l' }, { "no-name", no_argument, 0, 'n' }, { "name", no_argument, 0, 'N' }, { "quiet", no_argument, 0, 'q' }, { "recursive", no_argument, 0, 'r' }, { "suffix", required_argument, 0, 'S' }, { "test", no_argument, 0, 't' }, { "verbose", no_argument, 0, 'v' }, { "version", no_argument, 0, 'V' }, { "fast", no_argument, 0, '1' }, { "best", no_argument, 0, '9' }, { "ascii", no_argument, 0, 'a' }, { "license", no_argument, 0, 'L' }, { NULL, no_argument, 0, 0 }, }; #endif int main(int argc, char **argv) { const char *progname = getprogname(); #ifndef SMALL char *gzip; int len; #endif int ch; setup_signals(); #ifndef SMALL if ((gzip = getenv("GZIP")) != NULL) prepend_gzip(gzip, &argc, &argv); #endif /* * XXX * handle being called `gunzip', `zcat' and `gzcat' */ if (strcmp(progname, "gunzip") == 0) dflag = 1; else if (strcmp(progname, "zcat") == 0 || strcmp(progname, "gzcat") == 0) dflag = cflag = 1; #ifdef SMALL #define OPT_LIST "123456789cdhlV" #else #define OPT_LIST "123456789acdfhklLNnqrS:tVv" #endif while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) { switch (ch) { case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': numflag = ch - '0'; break; case 'c': cflag = 1; break; case 'd': dflag = 1; break; case 'l': lflag = 1; dflag = 1; break; case 'V': display_version(); /* NOTREACHED */ #ifndef SMALL case 'a': fprintf(stderr, "%s: option --ascii ignored on this system\n", progname); break; case 'f': fflag = 1; break; case 'k': kflag = 1; break; case 'L': display_license(); /* NOT REACHED */ case 'N': nflag = 0; Nflag = 1; break; case 'n': nflag = 1; Nflag = 0; break; case 'q': qflag = 1; break; case 'r': rflag = 1; break; case 'S': len = strlen(optarg); if (len != 0) { if (len > SUFFIX_MAXLEN) errx(1, "incorrect suffix: '%s': too long", optarg); suffixes[0].zipped = optarg; suffixes[0].ziplen = len; } else { suffixes[NUM_SUFFIXES - 1].zipped = ""; suffixes[NUM_SUFFIXES - 1].ziplen = 0; } break; case 't': cflag = 1; tflag = 1; dflag = 1; break; case 'v': vflag = 1; break; #endif default: usage(); /* NOTREACHED */ } } argv += optind; argc -= optind; if (argc == 0) { if (dflag) /* stdin mode */ handle_stdin(); else /* stdout mode */ handle_stdout(); } else { do { handle_pathname(argv[0]); } while (*++argv); } #ifndef SMALL if (qflag == 0 && lflag && argc > 1) print_list(-1, 0, "(totals)", 0); #endif exit(exit_value); } /* maybe print a warning */ void maybe_warn(const char *fmt, ...) { va_list ap; if (qflag == 0) { va_start(ap, fmt); vwarn(fmt, ap); va_end(ap); } if (exit_value == 0) exit_value = 1; } /* ... without an errno. */ void maybe_warnx(const char *fmt, ...) { va_list ap; if (qflag == 0) { va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } if (exit_value == 0) exit_value = 1; } /* maybe print an error */ void maybe_err(const char *fmt, ...) { va_list ap; if (qflag == 0) { va_start(ap, fmt); vwarn(fmt, ap); va_end(ap); } exit(2); } #if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \ !defined(NO_XZ_SUPPORT) /* ... without an errno. */ void maybe_errx(const char *fmt, ...) { va_list ap; if (qflag == 0) { va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } exit(2); } #endif #ifndef SMALL /* split up $GZIP and prepend it to the argument list */ static void prepend_gzip(char *gzip, int *argc, char ***argv) { char *s, **nargv, **ac; int nenvarg = 0, i; /* scan how many arguments there are */ for (s = gzip;;) { while (*s == ' ' || *s == '\t') s++; if (*s == 0) goto count_done; nenvarg++; while (*s != ' ' && *s != '\t') if (*s++ == 0) goto count_done; } count_done: /* punt early */ if (nenvarg == 0) return; *argc += nenvarg; ac = *argv; nargv = (char **)malloc((*argc + 1) * sizeof(char *)); if (nargv == NULL) maybe_err("malloc"); /* stash this away */ *argv = nargv; /* copy the program name first */ i = 0; nargv[i++] = *(ac++); /* take a copy of $GZIP and add it to the array */ s = strdup(gzip); if (s == NULL) maybe_err("strdup"); for (;;) { /* Skip whitespaces. */ while (*s == ' ' || *s == '\t') s++; if (*s == 0) goto copy_done; nargv[i++] = s; /* Find the end of this argument. */ while (*s != ' ' && *s != '\t') if (*s++ == 0) /* Argument followed by NUL. */ goto copy_done; /* Terminate by overwriting ' ' or '\t' with NUL. */ *s++ = 0; } copy_done: /* copy the original arguments and a NULL */ while (*ac) nargv[i++] = *(ac++); nargv[i] = NULL; } #endif /* compress input to output. Return bytes read, -1 on error */ static off_t gz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime) { z_stream z; char *outbufp, *inbufp; off_t in_tot = 0, out_tot = 0; ssize_t in_size; int i, error; uLong crc; #ifdef SMALL static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0, 0, 0, 0, 0, 0, OS_CODE }; #endif outbufp = malloc(BUFLEN); inbufp = malloc(BUFLEN); if (outbufp == NULL || inbufp == NULL) { maybe_err("malloc failed"); goto out; } memset(&z, 0, sizeof z); z.zalloc = Z_NULL; z.zfree = Z_NULL; z.opaque = 0; #ifdef SMALL memcpy(outbufp, header, sizeof header); i = sizeof header; #else if (nflag != 0) { mtime = 0; origname = ""; } i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s", GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, *origname ? ORIG_NAME : 0, mtime & 0xff, (mtime >> 8) & 0xff, (mtime >> 16) & 0xff, (mtime >> 24) & 0xff, numflag == 1 ? 4 : numflag == 9 ? 2 : 0, OS_CODE, origname); if (i >= BUFLEN) /* this need PATH_MAX > BUFLEN ... */ maybe_err("snprintf"); if (*origname) i++; #endif z.next_out = (unsigned char *)outbufp + i; z.avail_out = BUFLEN - i; error = deflateInit2(&z, numflag, Z_DEFLATED, (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY); if (error != Z_OK) { maybe_warnx("deflateInit2 failed"); in_tot = -1; goto out; } crc = crc32(0L, Z_NULL, 0); for (;;) { if (z.avail_out == 0) { if (write_retry(out, outbufp, BUFLEN) != BUFLEN) { maybe_warn("write"); out_tot = -1; goto out; } out_tot += BUFLEN; z.next_out = (unsigned char *)outbufp; z.avail_out = BUFLEN; } if (z.avail_in == 0) { in_size = read(in, inbufp, BUFLEN); if (in_size < 0) { maybe_warn("read"); in_tot = -1; goto out; } if (in_size == 0) break; infile_newdata(in_size); crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size); in_tot += in_size; z.next_in = (unsigned char *)inbufp; z.avail_in = in_size; } error = deflate(&z, Z_NO_FLUSH); if (error != Z_OK && error != Z_STREAM_END) { maybe_warnx("deflate failed"); in_tot = -1; goto out; } } /* clean up */ for (;;) { size_t len; ssize_t w; error = deflate(&z, Z_FINISH); if (error != Z_OK && error != Z_STREAM_END) { maybe_warnx("deflate failed"); in_tot = -1; goto out; } len = (char *)z.next_out - outbufp; w = write_retry(out, outbufp, len); if (w == -1 || (size_t)w != len) { maybe_warn("write"); out_tot = -1; goto out; } out_tot += len; z.next_out = (unsigned char *)outbufp; z.avail_out = BUFLEN; if (error == Z_STREAM_END) break; } if (deflateEnd(&z) != Z_OK) { maybe_warnx("deflateEnd failed"); in_tot = -1; goto out; } i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c", (int)crc & 0xff, (int)(crc >> 8) & 0xff, (int)(crc >> 16) & 0xff, (int)(crc >> 24) & 0xff, (int)in_tot & 0xff, (int)(in_tot >> 8) & 0xff, (int)(in_tot >> 16) & 0xff, (int)(in_tot >> 24) & 0xff); if (i != 8) maybe_err("snprintf"); if (write_retry(out, outbufp, i) != i) { maybe_warn("write"); in_tot = -1; } else out_tot += i; out: if (inbufp != NULL) free(inbufp); if (outbufp != NULL) free(outbufp); if (gsizep) *gsizep = out_tot; return in_tot; } /* * uncompress input to output then close the input. return the * uncompressed size written, and put the compressed sized read * into `*gsizep'. */ static off_t gz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep, const char *filename) { z_stream z; char *outbufp, *inbufp; off_t out_tot = -1, in_tot = 0; uint32_t out_sub_tot = 0; enum { GZSTATE_MAGIC0, GZSTATE_MAGIC1, GZSTATE_METHOD, GZSTATE_FLAGS, GZSTATE_SKIPPING, GZSTATE_EXTRA, GZSTATE_EXTRA2, GZSTATE_EXTRA3, GZSTATE_ORIGNAME, GZSTATE_COMMENT, GZSTATE_HEAD_CRC1, GZSTATE_HEAD_CRC2, GZSTATE_INIT, GZSTATE_READ, GZSTATE_CRC, GZSTATE_LEN, } state = GZSTATE_MAGIC0; int flags = 0, skip_count = 0; int error = Z_STREAM_ERROR, done_reading = 0; uLong crc = 0; ssize_t wr; int needmore = 0; #define ADVANCE() { z.next_in++; z.avail_in--; } if ((outbufp = malloc(BUFLEN)) == NULL) { maybe_err("malloc failed"); goto out2; } if ((inbufp = malloc(BUFLEN)) == NULL) { maybe_err("malloc failed"); goto out1; } memset(&z, 0, sizeof z); z.avail_in = prelen; z.next_in = (unsigned char *)pre; z.avail_out = BUFLEN; z.next_out = (unsigned char *)outbufp; z.zalloc = NULL; z.zfree = NULL; z.opaque = 0; in_tot = prelen; out_tot = 0; for (;;) { check_siginfo(); if ((z.avail_in == 0 || needmore) && done_reading == 0) { ssize_t in_size; if (z.avail_in > 0) { memmove(inbufp, z.next_in, z.avail_in); } z.next_in = (unsigned char *)inbufp; in_size = read(in, z.next_in + z.avail_in, BUFLEN - z.avail_in); if (in_size == -1) { maybe_warn("failed to read stdin"); goto stop_and_fail; } else if (in_size == 0) { done_reading = 1; } infile_newdata(in_size); z.avail_in += in_size; needmore = 0; in_tot += in_size; } if (z.avail_in == 0) { if (done_reading && state != GZSTATE_MAGIC0) { maybe_warnx("%s: unexpected end of file", filename); goto stop_and_fail; } goto stop; } switch (state) { case GZSTATE_MAGIC0: if (*z.next_in != GZIP_MAGIC0) { if (in_tot > 0) { maybe_warnx("%s: trailing garbage " "ignored", filename); exit_value = 2; goto stop; } maybe_warnx("input not gziped (MAGIC0)"); goto stop_and_fail; } ADVANCE(); state++; out_sub_tot = 0; crc = crc32(0L, Z_NULL, 0); break; case GZSTATE_MAGIC1: if (*z.next_in != GZIP_MAGIC1 && *z.next_in != GZIP_OMAGIC1) { maybe_warnx("input not gziped (MAGIC1)"); goto stop_and_fail; } ADVANCE(); state++; break; case GZSTATE_METHOD: if (*z.next_in != Z_DEFLATED) { maybe_warnx("unknown compression method"); goto stop_and_fail; } ADVANCE(); state++; break; case GZSTATE_FLAGS: flags = *z.next_in; ADVANCE(); skip_count = 6; state++; break; case GZSTATE_SKIPPING: if (skip_count > 0) { skip_count--; ADVANCE(); } else state++; break; case GZSTATE_EXTRA: if ((flags & EXTRA_FIELD) == 0) { state = GZSTATE_ORIGNAME; break; } skip_count = *z.next_in; ADVANCE(); state++; break; case GZSTATE_EXTRA2: skip_count |= ((*z.next_in) << 8); ADVANCE(); state++; break; case GZSTATE_EXTRA3: if (skip_count > 0) { skip_count--; ADVANCE(); } else state++; break; case GZSTATE_ORIGNAME: if ((flags & ORIG_NAME) == 0) { state++; break; } if (*z.next_in == 0) state++; ADVANCE(); break; case GZSTATE_COMMENT: if ((flags & COMMENT) == 0) { state++; break; } if (*z.next_in == 0) state++; ADVANCE(); break; case GZSTATE_HEAD_CRC1: if (flags & HEAD_CRC) skip_count = 2; else skip_count = 0; state++; break; case GZSTATE_HEAD_CRC2: if (skip_count > 0) { skip_count--; ADVANCE(); } else state++; break; case GZSTATE_INIT: if (inflateInit2(&z, -MAX_WBITS) != Z_OK) { maybe_warnx("failed to inflateInit"); goto stop_and_fail; } state++; break; case GZSTATE_READ: error = inflate(&z, Z_FINISH); switch (error) { /* Z_BUF_ERROR goes with Z_FINISH... */ case Z_BUF_ERROR: if (z.avail_out > 0 && !done_reading) continue; case Z_STREAM_END: case Z_OK: break; case Z_NEED_DICT: maybe_warnx("Z_NEED_DICT error"); goto stop_and_fail; case Z_DATA_ERROR: maybe_warnx("data stream error"); goto stop_and_fail; case Z_STREAM_ERROR: maybe_warnx("internal stream error"); goto stop_and_fail; case Z_MEM_ERROR: maybe_warnx("memory allocation error"); goto stop_and_fail; default: maybe_warn("unknown error from inflate(): %d", error); } wr = BUFLEN - z.avail_out; if (wr != 0) { crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr); if ( #ifndef SMALL /* don't write anything with -t */ tflag == 0 && #endif write_retry(out, outbufp, wr) != wr) { maybe_warn("error writing to output"); goto stop_and_fail; } out_tot += wr; out_sub_tot += wr; } if (error == Z_STREAM_END) { inflateEnd(&z); state++; } z.next_out = (unsigned char *)outbufp; z.avail_out = BUFLEN; break; case GZSTATE_CRC: { uLong origcrc; if (z.avail_in < 4) { if (!done_reading) { needmore = 1; continue; } maybe_warnx("truncated input"); goto stop_and_fail; } origcrc = le32dec(&z.next_in[0]); if (origcrc != crc) { maybe_warnx("invalid compressed" " data--crc error"); goto stop_and_fail; } } z.avail_in -= 4; z.next_in += 4; if (!z.avail_in && done_reading) { goto stop; } state++; break; case GZSTATE_LEN: { uLong origlen; if (z.avail_in < 4) { if (!done_reading) { needmore = 1; continue; } maybe_warnx("truncated input"); goto stop_and_fail; } origlen = le32dec(&z.next_in[0]); if (origlen != out_sub_tot) { maybe_warnx("invalid compressed" " data--length error"); goto stop_and_fail; } } z.avail_in -= 4; z.next_in += 4; if (error < 0) { maybe_warnx("decompression error"); goto stop_and_fail; } state = GZSTATE_MAGIC0; break; } continue; stop_and_fail: out_tot = -1; stop: break; } if (state > GZSTATE_INIT) inflateEnd(&z); free(inbufp); out1: free(outbufp); out2: if (gsizep) *gsizep = in_tot; return (out_tot); } #ifndef SMALL /* * set the owner, mode, flags & utimes using the given file descriptor. * file is only used in possible warning messages. */ static void copymodes(int fd, const struct stat *sbp, const char *file) { struct timespec times[2]; struct stat sb; /* * If we have no info on the input, give this file some * default values and return.. */ if (sbp == NULL) { mode_t mask = umask(022); (void)fchmod(fd, DEFFILEMODE & ~mask); (void)umask(mask); return; } sb = *sbp; /* if the chown fails, remove set-id bits as-per compress(1) */ if (fchown(fd, sb.st_uid, sb.st_gid) < 0) { if (errno != EPERM) maybe_warn("couldn't fchown: %s", file); sb.st_mode &= ~(S_ISUID|S_ISGID); } /* we only allow set-id and the 9 normal permission bits */ sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; if (fchmod(fd, sb.st_mode) < 0) maybe_warn("couldn't fchmod: %s", file); times[0] = sb.st_atim; times[1] = sb.st_mtim; if (futimens(fd, times) < 0) maybe_warn("couldn't futimens: %s", file); /* only try flags if they exist already */ if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0) maybe_warn("couldn't fchflags: %s", file); } #endif /* what sort of file is this? */ static enum filetype file_gettype(u_char *buf) { if (buf[0] == GZIP_MAGIC0 && (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1)) return FT_GZIP; else #ifndef NO_BZIP2_SUPPORT if (memcmp(buf, BZIP2_MAGIC, 3) == 0 && buf[3] >= '0' && buf[3] <= '9') return FT_BZIP2; else #endif #ifndef NO_COMPRESS_SUPPORT if (memcmp(buf, Z_MAGIC, 2) == 0) return FT_Z; else #endif #ifndef NO_PACK_SUPPORT if (memcmp(buf, PACK_MAGIC, 2) == 0) return FT_PACK; else #endif #ifndef NO_XZ_SUPPORT if (memcmp(buf, XZ_MAGIC, 4) == 0) /* XXX: We only have 4 bytes */ return FT_XZ; else #endif #ifndef NO_LZ_SUPPORT if (memcmp(buf, LZ_MAGIC, 4) == 0) return FT_LZ; else #endif return FT_UNKNOWN; } #ifndef SMALL /* check the outfile is OK. */ static int check_outfile(const char *outfile) { struct stat sb; int ok = 1; if (lflag == 0 && stat(outfile, &sb) == 0) { if (fflag) unlink(outfile); else if (isatty(STDIN_FILENO)) { char ans[10] = { 'n', '\0' }; /* default */ fprintf(stderr, "%s already exists -- do you wish to " "overwrite (y or n)? " , outfile); (void)fgets(ans, sizeof(ans) - 1, stdin); if (ans[0] != 'y' && ans[0] != 'Y') { fprintf(stderr, "\tnot overwriting\n"); ok = 0; } else unlink(outfile); } else { maybe_warnx("%s already exists -- skipping", outfile); ok = 0; } } return ok; } static void unlink_input(const char *file, const struct stat *sb) { struct stat nsb; if (kflag) return; if (stat(file, &nsb) != 0) /* Must be gone already */ return; if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino) /* Definitely a different file */ return; unlink(file); } static void got_sigint(int signo __unused) { if (remove_file != NULL) unlink(remove_file); _exit(2); } static void got_siginfo(int signo __unused) { print_info = 1; } static void setup_signals(void) { signal(SIGINFO, got_siginfo); signal(SIGINT, got_sigint); } static void infile_newdata(size_t newdata) { infile_current += newdata; } #endif static void infile_set(const char *newinfile, off_t total) { if (newinfile) infile = newinfile; #ifndef SMALL infile_total = total; #endif } static void infile_clear(void) { infile = NULL; #ifndef SMALL infile_total = infile_current = 0; #endif } static const suffixes_t * check_suffix(char *file, int xlate) { const suffixes_t *s; int len = strlen(file); char *sp; for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) { /* if it doesn't fit in "a.suf", don't bother */ if (s->ziplen >= len) continue; sp = file + len - s->ziplen; if (strcmp(s->zipped, sp) != 0) continue; if (xlate) strcpy(sp, s->normal); return s; } return NULL; } /* * compress the given file: create a corresponding .gz file and remove the * original. */ static off_t file_compress(char *file, char *outfile, size_t outsize) { int in; int out; off_t size, in_size; #ifndef SMALL struct stat isb, osb; const suffixes_t *suff; #endif in = open(file, O_RDONLY); if (in == -1) { maybe_warn("can't open %s", file); return (-1); } #ifndef SMALL if (fstat(in, &isb) != 0) { maybe_warn("couldn't stat: %s", file); close(in); return (-1); } #endif #ifndef SMALL if (fstat(in, &isb) != 0) { close(in); maybe_warn("can't stat %s", file); return -1; } infile_set(file, isb.st_size); #endif if (cflag == 0) { #ifndef SMALL if (isb.st_nlink > 1 && fflag == 0) { maybe_warnx("%s has %ju other link%s -- " "skipping", file, (uintmax_t)isb.st_nlink - 1, isb.st_nlink == 1 ? "" : "s"); close(in); return -1; } if (fflag == 0 && (suff = check_suffix(file, 0)) && suff->zipped[0] != 0) { maybe_warnx("%s already has %s suffix -- unchanged", file, suff->zipped); close(in); return (-1); } #endif /* Add (usually) .gz to filename */ if ((size_t)snprintf(outfile, outsize, "%s%s", file, suffixes[0].zipped) >= outsize) memcpy(outfile + outsize - suffixes[0].ziplen - 1, suffixes[0].zipped, suffixes[0].ziplen + 1); #ifndef SMALL if (check_outfile(outfile) == 0) { close(in); return (-1); } #endif } if (cflag == 0) { out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600); if (out == -1) { maybe_warn("could not create output: %s", outfile); fclose(stdin); return (-1); } #ifndef SMALL remove_file = outfile; #endif } else out = STDOUT_FILENO; in_size = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime); (void)close(in); /* * If there was an error, in_size will be -1. * If we compressed to stdout, just return the size. * Otherwise stat the file and check it is the correct size. * We only blow away the file if we can stat the output and it * has the expected size. */ if (cflag != 0) return in_size == -1 ? -1 : size; #ifndef SMALL if (fstat(out, &osb) != 0) { maybe_warn("couldn't stat: %s", outfile); goto bad_outfile; } if (osb.st_size != size) { maybe_warnx("output file: %s wrong size (%ju != %ju), deleting", outfile, (uintmax_t)osb.st_size, (uintmax_t)size); goto bad_outfile; } copymodes(out, &isb, outfile); remove_file = NULL; #endif if (close(out) == -1) maybe_warn("couldn't close output"); /* output is good, ok to delete input */ unlink_input(file, &isb); return (size); #ifndef SMALL bad_outfile: if (close(out) == -1) maybe_warn("couldn't close output"); maybe_warnx("leaving original %s", file); unlink(outfile); return (size); #endif } /* uncompress the given file and remove the original */ static off_t file_uncompress(char *file, char *outfile, size_t outsize) { struct stat isb, osb; off_t size; ssize_t rbytes; unsigned char fourbytes[4]; enum filetype method; int fd, ofd, zfd = -1; int error; size_t in_size; #ifndef SMALL ssize_t rv; time_t timestamp = 0; char name[PATH_MAX + 1]; #endif /* gather the old name info */ fd = open(file, O_RDONLY); if (fd < 0) { maybe_warn("can't open %s", file); goto lose; } if (fstat(fd, &isb) != 0) { maybe_warn("can't stat %s", file); goto lose; } if (S_ISREG(isb.st_mode)) in_size = isb.st_size; else in_size = 0; infile_set(file, in_size); strlcpy(outfile, file, outsize); if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) { maybe_warnx("%s: unknown suffix -- ignored", file); goto lose; } rbytes = read(fd, fourbytes, sizeof fourbytes); if (rbytes != sizeof fourbytes) { /* we don't want to fail here. */ #ifndef SMALL if (fflag) goto lose; #endif if (rbytes == -1) maybe_warn("can't read %s", file); else goto unexpected_EOF; goto lose; } infile_newdata(rbytes); method = file_gettype(fourbytes); #ifndef SMALL if (fflag == 0 && method == FT_UNKNOWN) { maybe_warnx("%s: not in gzip format", file); goto lose; } #endif #ifndef SMALL if (method == FT_GZIP && Nflag) { unsigned char ts[4]; /* timestamp */ rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP); if (rv >= 0 && rv < (ssize_t)(sizeof ts)) goto unexpected_EOF; if (rv == -1) { if (!fflag) maybe_warn("can't read %s", file); goto lose; } infile_newdata(rv); timestamp = le32dec(&ts[0]); if (fourbytes[3] & ORIG_NAME) { rbytes = pread(fd, name, sizeof(name) - 1, GZIP_ORIGNAME); if (rbytes < 0) { maybe_warn("can't read %s", file); goto lose; } if (name[0] != '\0') { char *dp, *nf; /* Make sure that name is NUL-terminated */ name[rbytes] = '\0'; /* strip saved directory name */ nf = strrchr(name, '/'); if (nf == NULL) nf = name; else nf++; /* preserve original directory name */ dp = strrchr(file, '/'); if (dp == NULL) dp = file; else dp++; snprintf(outfile, outsize, "%.*s%.*s", (int) (dp - file), file, (int) rbytes, nf); } } } #endif lseek(fd, 0, SEEK_SET); if (cflag == 0 || lflag) { #ifndef SMALL if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) { maybe_warnx("%s has %ju other links -- skipping", file, (uintmax_t)isb.st_nlink - 1); goto lose; } if (nflag == 0 && timestamp) isb.st_mtime = timestamp; if (check_outfile(outfile) == 0) goto lose; #endif } if (cflag) zfd = STDOUT_FILENO; else if (lflag) zfd = -1; else { zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600); if (zfd == STDOUT_FILENO) { /* We won't close STDOUT_FILENO later... */ zfd = dup(zfd); close(STDOUT_FILENO); } if (zfd == -1) { maybe_warn("can't open %s", outfile); goto lose; } remove_file = outfile; } switch (method) { #ifndef NO_BZIP2_SUPPORT case FT_BZIP2: /* XXX */ if (lflag) { maybe_warnx("no -l with bzip2 files"); goto lose; } size = unbzip2(fd, zfd, NULL, 0, NULL); break; #endif #ifndef NO_COMPRESS_SUPPORT case FT_Z: { FILE *in, *out; /* XXX */ if (lflag) { maybe_warnx("no -l with Lempel-Ziv files"); goto lose; } if ((in = zdopen(fd)) == NULL) { maybe_warn("zdopen for read: %s", file); goto lose; } out = fdopen(dup(zfd), "w"); if (out == NULL) { maybe_warn("fdopen for write: %s", outfile); fclose(in); goto lose; } size = zuncompress(in, out, NULL, 0, NULL); /* need to fclose() if ferror() is true... */ error = ferror(in); if (error | fclose(in)) { if (error) maybe_warn("failed infile"); else maybe_warn("failed infile fclose"); if (cflag == 0) unlink(outfile); (void)fclose(out); goto lose; } if (fclose(out) != 0) { maybe_warn("failed outfile fclose"); if (cflag == 0) unlink(outfile); goto lose; } break; } #endif #ifndef NO_PACK_SUPPORT case FT_PACK: if (lflag) { maybe_warnx("no -l with packed files"); goto lose; } size = unpack(fd, zfd, NULL, 0, NULL); break; #endif #ifndef NO_XZ_SUPPORT case FT_XZ: if (lflag) { size = unxz_len(fd); print_list_out(in_size, size, file); return -1; } size = unxz(fd, zfd, NULL, 0, NULL); break; #endif #ifndef NO_LZ_SUPPORT case FT_LZ: if (lflag) { maybe_warnx("no -l with lzip files"); goto lose; } size = unlz(fd, zfd, NULL, 0, NULL); break; #endif #ifndef SMALL case FT_UNKNOWN: if (lflag) { maybe_warnx("no -l for unknown filetypes"); goto lose; } size = cat_fd(NULL, 0, NULL, fd); break; #endif default: if (lflag) { print_list(fd, in_size, outfile, isb.st_mtime); close(fd); return -1; /* XXX */ } size = gz_uncompress(fd, zfd, NULL, 0, NULL, file); break; } if (close(fd) != 0) maybe_warn("couldn't close input"); if (zfd != STDOUT_FILENO && close(zfd) != 0) maybe_warn("couldn't close output"); if (size == -1) { if (cflag == 0) unlink(outfile); maybe_warnx("%s: uncompress failed", file); return -1; } /* if testing, or we uncompressed to stdout, this is all we need */ #ifndef SMALL if (tflag) return size; #endif /* if we are uncompressing to stdin, don't remove the file. */ if (cflag) return size; /* * if we create a file... */ /* * if we can't stat the file don't remove the file. */ ofd = open(outfile, O_RDWR, 0); if (ofd == -1) { maybe_warn("couldn't open (leaving original): %s", outfile); return -1; } if (fstat(ofd, &osb) != 0) { maybe_warn("couldn't stat (leaving original): %s", outfile); close(ofd); return -1; } if (osb.st_size != size) { maybe_warnx("stat gave different size: %ju != %ju (leaving original)", (uintmax_t)size, (uintmax_t)osb.st_size); close(ofd); unlink(outfile); return -1; } #ifndef SMALL copymodes(ofd, &isb, outfile); remove_file = NULL; #endif close(ofd); unlink_input(file, &isb); return size; unexpected_EOF: maybe_warnx("%s: unexpected end of file", file); lose: if (fd != -1) close(fd); if (zfd != -1 && zfd != STDOUT_FILENO) close(zfd); return -1; } #ifndef SMALL static void check_siginfo(void) { if (print_info == 0) return; if (infile) { if (infile_total) { int pcent = (int)((100.0 * infile_current) / infile_total); fprintf(stderr, "%s: done %llu/%llu bytes %d%%\n", infile, (unsigned long long)infile_current, (unsigned long long)infile_total, pcent); } else fprintf(stderr, "%s: done %llu bytes\n", infile, (unsigned long long)infile_current); } print_info = 0; } static off_t cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd) { char buf[BUFLEN]; off_t in_tot; ssize_t w; in_tot = count; w = write_retry(STDOUT_FILENO, prepend, count); if (w == -1 || (size_t)w != count) { maybe_warn("write to stdout"); return -1; } for (;;) { ssize_t rv; rv = read(fd, buf, sizeof buf); if (rv == 0) break; if (rv < 0) { maybe_warn("read from fd %d", fd); break; } infile_newdata(rv); if (write_retry(STDOUT_FILENO, buf, rv) != rv) { maybe_warn("write to stdout"); break; } in_tot += rv; } if (gsizep) *gsizep = in_tot; return (in_tot); } #endif static void handle_stdin(void) { struct stat isb; unsigned char fourbytes[4]; size_t in_size; off_t usize, gsize; enum filetype method; ssize_t bytes_read; #ifndef NO_COMPRESS_SUPPORT FILE *in; #endif #ifndef SMALL if (fflag == 0 && lflag == 0 && isatty(STDIN_FILENO)) { maybe_warnx("standard input is a terminal -- ignoring"); goto out; } #endif if (fstat(STDIN_FILENO, &isb) < 0) { maybe_warn("fstat"); goto out; } if (S_ISREG(isb.st_mode)) in_size = isb.st_size; else in_size = 0; infile_set("(stdin)", in_size); if (lflag) { print_list(STDIN_FILENO, in_size, infile, isb.st_mtime); goto out; } bytes_read = read_retry(STDIN_FILENO, fourbytes, sizeof fourbytes); if (bytes_read == -1) { maybe_warn("can't read stdin"); goto out; } else if (bytes_read != sizeof(fourbytes)) { maybe_warnx("(stdin): unexpected end of file"); goto out; } method = file_gettype(fourbytes); switch (method) { default: #ifndef SMALL if (fflag == 0) { maybe_warnx("unknown compression format"); goto out; } usize = cat_fd(fourbytes, sizeof fourbytes, &gsize, STDIN_FILENO); break; #endif case FT_GZIP: usize = gz_uncompress(STDIN_FILENO, STDOUT_FILENO, (char *)fourbytes, sizeof fourbytes, &gsize, "(stdin)"); break; #ifndef NO_BZIP2_SUPPORT case FT_BZIP2: usize = unbzip2(STDIN_FILENO, STDOUT_FILENO, (char *)fourbytes, sizeof fourbytes, &gsize); break; #endif #ifndef NO_COMPRESS_SUPPORT case FT_Z: if ((in = zdopen(STDIN_FILENO)) == NULL) { maybe_warnx("zopen of stdin"); goto out; } usize = zuncompress(in, stdout, (char *)fourbytes, sizeof fourbytes, &gsize); fclose(in); break; #endif #ifndef NO_PACK_SUPPORT case FT_PACK: usize = unpack(STDIN_FILENO, STDOUT_FILENO, (char *)fourbytes, sizeof fourbytes, &gsize); break; #endif #ifndef NO_XZ_SUPPORT case FT_XZ: usize = unxz(STDIN_FILENO, STDOUT_FILENO, (char *)fourbytes, sizeof fourbytes, &gsize); break; #endif #ifndef NO_LZ_SUPPORT case FT_LZ: usize = unlz(STDIN_FILENO, STDOUT_FILENO, (char *)fourbytes, sizeof fourbytes, &gsize); break; #endif } #ifndef SMALL if (vflag && !tflag && usize != -1 && gsize != -1) print_verbage(NULL, NULL, usize, gsize); if (vflag && tflag) print_test("(stdin)", usize != -1); #else (void)&usize; #endif out: infile_clear(); } static void handle_stdout(void) { off_t gsize; #ifndef SMALL off_t usize; struct stat sb; time_t systime; uint32_t mtime; int ret; infile_set("(stdout)", 0); if (fflag == 0 && isatty(STDOUT_FILENO)) { maybe_warnx("standard output is a terminal -- ignoring"); return; } /* If stdin is a file use its mtime, otherwise use current time */ ret = fstat(STDIN_FILENO, &sb); if (ret < 0) { maybe_warn("Can't stat stdin"); return; } if (S_ISREG(sb.st_mode)) { infile_set("(stdout)", sb.st_size); mtime = (uint32_t)sb.st_mtime; } else { systime = time(NULL); if (systime == -1) { maybe_warn("time"); return; } mtime = (uint32_t)systime; } usize = #endif gz_compress(STDIN_FILENO, STDOUT_FILENO, &gsize, "", mtime); #ifndef SMALL if (vflag && !tflag && usize != -1 && gsize != -1) print_verbage(NULL, NULL, usize, gsize); #endif } /* do what is asked for, for the path name */ static void handle_pathname(char *path) { char *opath = path, *s = NULL; ssize_t len; int slen; struct stat sb; /* check for stdout/stdin */ if (path[0] == '-' && path[1] == '\0') { if (dflag) handle_stdin(); else handle_stdout(); return; } retry: if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 && lstat(path, &sb) != 0)) { /* lets try .gz if we're decompressing */ if (dflag && s == NULL && errno == ENOENT) { len = strlen(path); slen = suffixes[0].ziplen; s = malloc(len + slen + 1); if (s == NULL) maybe_err("malloc"); memcpy(s, path, len); memcpy(s + len, suffixes[0].zipped, slen + 1); path = s; goto retry; } maybe_warn("can't stat: %s", opath); goto out; } if (S_ISDIR(sb.st_mode)) { #ifndef SMALL if (rflag) handle_dir(path); else #endif maybe_warnx("%s is a directory", path); goto out; } if (S_ISREG(sb.st_mode)) handle_file(path, &sb); else maybe_warnx("%s is not a regular file", path); out: if (s) free(s); } /* compress/decompress a file */ static void handle_file(char *file, struct stat *sbp) { off_t usize, gsize; char outfile[PATH_MAX]; infile_set(file, sbp->st_size); if (dflag) { usize = file_uncompress(file, outfile, sizeof(outfile)); #ifndef SMALL if (vflag && tflag) print_test(file, usize != -1); #endif if (usize == -1) return; gsize = sbp->st_size; } else { gsize = file_compress(file, outfile, sizeof(outfile)); if (gsize == -1) return; usize = sbp->st_size; } infile_clear(); #ifndef SMALL if (vflag && !tflag) print_verbage(file, (cflag) ? NULL : outfile, usize, gsize); #endif } #ifndef SMALL /* this is used with -r to recursively descend directories */ static void handle_dir(char *dir) { char *path_argv[2]; FTS *fts; FTSENT *entry; path_argv[0] = dir; path_argv[1] = 0; fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); if (fts == NULL) { warn("couldn't fts_open %s", dir); return; } - while ((entry = fts_read(fts))) { + while (errno = 0, (entry = fts_read(fts))) { switch(entry->fts_info) { case FTS_D: case FTS_DP: continue; case FTS_DNR: case FTS_ERR: case FTS_NS: maybe_warn("%s", entry->fts_path); continue; case FTS_F: handle_file(entry->fts_path, entry->fts_statp); } } + if (errno != 0) + warn("error with fts_read %s", dir); (void)fts_close(fts); } #endif /* print a ratio - size reduction as a fraction of uncompressed size */ static void print_ratio(off_t in, off_t out, FILE *where) { int percent10; /* 10 * percent */ off_t diff; char buff[8]; int len; diff = in - out/2; if (in == 0 && out == 0) percent10 = 0; else if (diff < 0) /* * Output is more than double size of input! print -99.9% * Quite possibly we've failed to get the original size. */ percent10 = -999; else { /* * We only need 12 bits of result from the final division, * so reduce the values until a 32bit division will suffice. */ while (in > 0x100000) { diff >>= 1; in >>= 1; } if (in != 0) percent10 = ((u_int)diff * 2000) / (u_int)in - 1000; else percent10 = 0; } len = snprintf(buff, sizeof buff, "%2.2d.", percent10); /* Move the '.' to before the last digit */ buff[len - 1] = buff[len - 2]; buff[len - 2] = '.'; fprintf(where, "%5s%%", buff); } #ifndef SMALL /* print compression statistics, and the new name (if there is one!) */ static void print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize) { if (file) fprintf(stderr, "%s:%s ", file, strlen(file) < 7 ? "\t\t" : "\t"); print_ratio(usize, gsize, stderr); if (nfile) fprintf(stderr, " -- replaced with %s", nfile); fprintf(stderr, "\n"); fflush(stderr); } /* print test results */ static void print_test(const char *file, int ok) { if (exit_value == 0 && ok == 0) exit_value = 1; fprintf(stderr, "%s:%s %s\n", file, strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK"); fflush(stderr); } #endif /* print a file's info ala --list */ /* eg: compressed uncompressed ratio uncompressed_name 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar */ static void print_list(int fd, off_t out, const char *outfile, time_t ts) { static int first = 1; #ifndef SMALL static off_t in_tot, out_tot; uint32_t crc = 0; #endif off_t in = 0, rv; if (first) { #ifndef SMALL if (vflag) printf("method crc date time "); #endif if (qflag == 0) printf(" compressed uncompressed " "ratio uncompressed_name\n"); } first = 0; /* print totals? */ #ifndef SMALL if (fd == -1) { in = in_tot; out = out_tot; } else #endif { /* read the last 4 bytes - this is the uncompressed size */ rv = lseek(fd, (off_t)(-8), SEEK_END); if (rv != -1) { unsigned char buf[8]; uint32_t usize; rv = read(fd, (char *)buf, sizeof(buf)); if (rv == -1) maybe_warn("read of uncompressed size"); else if (rv != sizeof(buf)) maybe_warnx("read of uncompressed size"); else { usize = le32dec(&buf[4]); in = (off_t)usize; #ifndef SMALL crc = le32dec(&buf[0]); #endif } } } #ifndef SMALL if (vflag && fd == -1) printf(" "); else if (vflag) { char *date = ctime(&ts); /* skip the day, 1/100th second, and year */ date += 4; date[12] = 0; printf("%5s %08x %11s ", "defla"/*XXX*/, crc, date); } in_tot += in; out_tot += out; #else (void)&ts; /* XXX */ #endif print_list_out(out, in, outfile); } static void print_list_out(off_t out, off_t in, const char *outfile) { printf("%12llu %12llu ", (unsigned long long)out, (unsigned long long)in); print_ratio(in, out, stdout); printf(" %s\n", outfile); } /* display the usage of NetBSD gzip */ static void usage(void) { fprintf(stderr, "%s\n", gzip_version); fprintf(stderr, #ifdef SMALL "usage: %s [-" OPT_LIST "] [ [ ...]]\n", #else "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [ [ ...]]\n" " -1 --fast fastest (worst) compression\n" " -2 .. -8 set compression level\n" " -9 --best best (slowest) compression\n" " -c --stdout write to stdout, keep original files\n" " --to-stdout\n" " -d --decompress uncompress files\n" " --uncompress\n" " -f --force force overwriting & compress links\n" " -h --help display this help\n" " -k --keep don't delete input files during operation\n" " -l --list list compressed file contents\n" " -N --name save or restore original file name and time stamp\n" " -n --no-name don't save original file name or time stamp\n" " -q --quiet output no warnings\n" " -r --recursive recursively compress files in directories\n" " -S .suf use suffix .suf instead of .gz\n" " --suffix .suf\n" " -t --test test compressed file\n" " -V --version display program version\n" " -v --verbose print extra statistics\n", #endif getprogname()); exit(0); } #ifndef SMALL /* display the license information of FreeBSD gzip */ static void display_license(void) { fprintf(stderr, "%s (based on NetBSD gzip 20150113)\n", gzip_version); fprintf(stderr, "%s\n", gzip_copyright); exit(0); } #endif /* display the version of NetBSD gzip */ static void display_version(void) { fprintf(stderr, "%s\n", gzip_version); exit(0); } #ifndef NO_BZIP2_SUPPORT #include "unbzip2.c" #endif #ifndef NO_COMPRESS_SUPPORT #include "zuncompress.c" #endif #ifndef NO_PACK_SUPPORT #include "unpack.c" #endif #ifndef NO_XZ_SUPPORT #include "unxz.c" #endif #ifndef NO_LZ_SUPPORT #include "unlz.c" #endif static ssize_t read_retry(int fd, void *buf, size_t sz) { char *cp = buf; size_t left = MIN(sz, (size_t) SSIZE_MAX); while (left > 0) { ssize_t ret; ret = read(fd, cp, left); if (ret == -1) { return ret; } else if (ret == 0) { break; /* EOF */ } cp += ret; left -= ret; } return sz - left; } static ssize_t write_retry(int fd, const void *buf, size_t sz) { const char *cp = buf; size_t left = MIN(sz, (size_t) SSIZE_MAX); while (left > 0) { ssize_t ret; ret = write(fd, cp, left); if (ret == -1) { return ret; } else if (ret == 0) { abort(); /* Can't happen */ } cp += ret; left -= ret; } return sz - left; } diff --git a/usr.sbin/chown/chown.c b/usr.sbin/chown/chown.c index 8f09b62d10ca..a3fe5d351320 100644 --- a/usr.sbin/chown/chown.c +++ b/usr.sbin/chown/chown.c @@ -1,329 +1,329 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1988, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1988, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 4/4/94"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void a_gid(const char *); static void a_uid(const char *); static void chownerr(const char *); static uid_t id(const char *, const char *); static void usage(void); static void print_info(const FTSENT *, int); static uid_t uid; static gid_t gid; static int ischown; static const char *gname; static volatile sig_atomic_t siginfo; static void siginfo_handler(int sig __unused) { siginfo = 1; } int main(int argc, char **argv) { FTS *ftsp; FTSENT *p; int Hflag, Lflag, Rflag, fflag, hflag, vflag, xflag; int ch, fts_options, rval; char *cp; ischown = (strcmp(basename(argv[0]), "chown") == 0); Hflag = Lflag = Rflag = fflag = hflag = vflag = xflag = 0; while ((ch = getopt(argc, argv, "HLPRfhvx")) != -1) switch (ch) { case 'H': Hflag = 1; Lflag = 0; break; case 'L': Lflag = 1; Hflag = 0; break; case 'P': Hflag = Lflag = 0; break; case 'R': Rflag = 1; break; case 'f': fflag = 1; break; case 'h': hflag = 1; break; case 'v': vflag++; break; case 'x': xflag = 1; break; case '?': default: usage(); } argv += optind; argc -= optind; if (argc < 2) usage(); (void)signal(SIGINFO, siginfo_handler); if (Rflag) { if (hflag && (Hflag || Lflag)) errx(1, "the -R%c and -h options may not be " "specified together", Hflag ? 'H' : 'L'); if (Lflag) { fts_options = FTS_LOGICAL; } else { fts_options = FTS_PHYSICAL; if (Hflag) { fts_options |= FTS_COMFOLLOW; } } } else if (hflag) { fts_options = FTS_PHYSICAL; } else { fts_options = FTS_LOGICAL; } if (xflag) fts_options |= FTS_XDEV; uid = (uid_t)-1; gid = (gid_t)-1; if (ischown) { if ((cp = strchr(*argv, ':')) != NULL) { *cp++ = '\0'; a_gid(cp); } #ifdef SUPPORT_DOT else if ((cp = strchr(*argv, '.')) != NULL) { warnx("separation of user and group with a period is deprecated"); *cp++ = '\0'; a_gid(cp); } #endif a_uid(*argv); } else a_gid(*argv); if ((ftsp = fts_open(++argv, fts_options, NULL)) == NULL) err(1, NULL); - for (rval = 0; (p = fts_read(ftsp)) != NULL;) { + for (rval = 0; errno = 0, (p = fts_read(ftsp)) != NULL;) { int atflag; if ((fts_options & FTS_LOGICAL) || ((fts_options & FTS_COMFOLLOW) && p->fts_level == FTS_ROOTLEVEL)) atflag = 0; else atflag = AT_SYMLINK_NOFOLLOW; switch (p->fts_info) { case FTS_D: /* Change it at FTS_DP. */ if (!Rflag) fts_set(ftsp, p, FTS_SKIP); continue; case FTS_DNR: /* Warn, chown. */ warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; break; case FTS_ERR: /* Warn, continue. */ case FTS_NS: warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; continue; default: break; } if (siginfo) { print_info(p, 2); siginfo = 0; } if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) && (gid == (gid_t)-1 || gid == p->fts_statp->st_gid)) continue; if (fchownat(AT_FDCWD, p->fts_accpath, uid, gid, atflag) == -1 && !fflag) { chownerr(p->fts_path); rval = 1; } else if (vflag) print_info(p, vflag); } if (errno) err(1, "fts_read"); exit(rval); } static void a_gid(const char *s) { struct group *gr; if (*s == '\0') /* Argument was "uid[:.]". */ return; gname = s; gid = ((gr = getgrnam(s)) != NULL) ? gr->gr_gid : id(s, "group"); } static void a_uid(const char *s) { struct passwd *pw; if (*s == '\0') /* Argument was "[:.]gid". */ return; uid = ((pw = getpwnam(s)) != NULL) ? pw->pw_uid : id(s, "user"); } static uid_t id(const char *name, const char *type) { unsigned long val; char *ep; errno = 0; val = strtoul(name, &ep, 10); _Static_assert(UID_MAX >= GID_MAX, "UID MAX less than GID MAX"); if (errno || *ep != '\0' || val > UID_MAX) errx(1, "%s: illegal %s name", name, type); return (val); } static void chownerr(const char *file) { static uid_t euid = -1; static int ngroups = -1; static long ngroups_max; gid_t *groups; /* Check for chown without being root. */ if (errno != EPERM || (uid != (uid_t)-1 && euid == (uid_t)-1 && (euid = geteuid()) != 0)) { warn("%s", file); return; } /* Check group membership; kernel just returns EPERM. */ if (gid != (gid_t)-1 && ngroups == -1 && euid == (uid_t)-1 && (euid = geteuid()) != 0) { ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1; if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL) err(1, "malloc"); ngroups = getgroups(ngroups_max, groups); while (--ngroups >= 0 && gid != groups[ngroups]); free(groups); if (ngroups < 0) { warnx("you are not a member of group %s", gname); return; } } warn("%s", file); } static void usage(void) { if (ischown) (void)fprintf(stderr, "%s\n%s\n", "usage: chown [-fhvx] [-R [-H | -L | -P]] owner[:group]" " file ...", " chown [-fhvx] [-R [-H | -L | -P]] :group file ..."); else (void)fprintf(stderr, "%s\n", "usage: chgrp [-fhvx] [-R [-H | -L | -P]] group file ..."); exit(1); } static void print_info(const FTSENT *p, int vflag) { printf("%s", p->fts_path); if (vflag > 1) { if (ischown) { printf(": %ju:%ju -> %ju:%ju", (uintmax_t)p->fts_statp->st_uid, (uintmax_t)p->fts_statp->st_gid, (uid == (uid_t)-1) ? (uintmax_t)p->fts_statp->st_uid : (uintmax_t)uid, (gid == (gid_t)-1) ? (uintmax_t)p->fts_statp->st_gid : (uintmax_t)gid); } else { printf(": %ju -> %ju", (uintmax_t)p->fts_statp->st_gid, (gid == (gid_t)-1) ? (uintmax_t)p->fts_statp->st_gid : (uintmax_t)gid); } } printf("\n"); } diff --git a/usr.sbin/ckdist/ckdist.c b/usr.sbin/ckdist/ckdist.c index 22bd04819d1e..d3a0bc313a5f 100644 --- a/usr.sbin/ckdist/ckdist.c +++ b/usr.sbin/ckdist/ckdist.c @@ -1,447 +1,447 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 1997 Robert Nordier * 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(S) ``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(S) BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include extern int crc(int fd, uint32_t *cval, off_t *clen); #define DISTMD5 1 /* MD5 format */ #define DISTINF 2 /* .inf format */ #define DISTTYPES 2 /* types supported */ #define E_UNKNOWN 1 /* Unknown format */ #define E_BADMD5 2 /* Invalid MD5 format */ #define E_BADINF 3 /* Invalid .inf format */ #define E_NAME 4 /* Can't derive component name */ #define E_LENGTH 5 /* Length mismatch */ #define E_CHKSUM 6 /* Checksum mismatch */ #define E_ERRNO 7 /* sys_errlist[errno] */ #define isfatal(err) ((err) && (err) <= E_NAME) #define NAMESIZE 256 /* filename buffer size */ #define MDSUMLEN 32 /* length of MD5 message digest */ #define isstdin(path) ((path)[0] == '-' && !(path)[1]) static const char *opt_dir; /* where to look for components */ static const char *opt_name; /* name for accessing components */ static int opt_all; /* report on all components */ static int opt_ignore; /* ignore missing components */ static int opt_recurse; /* search directories recursively */ static int opt_silent; /* silent about inaccessible files */ static int opt_type; /* dist type: md5 or inf */ static int opt_exist; /* just verify existence */ static int ckdist(const char *path, int type); static int chkmd5(FILE * fp, const char *path); static int chkinf(FILE * fp, const char *path); static int report(const char *path, const char *name, int error); static const char *distname(const char *path, const char *name, const char *ext); static const char *stripath(const char *path); static int distfile(const char *path); static int disttype(const char *name); static int fail(const char *path, const char *msg); static void usage(void); int main(int argc, char *argv[]) { static char *arg[2]; struct stat sb; FTS *ftsp; FTSENT *f; int rval, c, type; while ((c = getopt(argc, argv, "ad:in:rst:x")) != -1) switch (c) { case 'a': opt_all = 1; break; case 'd': opt_dir = optarg; break; case 'i': opt_ignore = 1; break; case 'n': opt_name = optarg; break; case 'r': opt_recurse = 1; break; case 's': opt_silent = 1; break; case 't': if ((opt_type = disttype(optarg)) == 0) { warnx("illegal argument to -t option"); usage(); } break; case 'x': opt_exist = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc < 1) usage(); if (opt_dir) { if (stat(opt_dir, &sb)) err(2, "%s", opt_dir); if (!S_ISDIR(sb.st_mode)) errx(2, "%s: not a directory", opt_dir); } rval = 0; do { if (isstdin(*argv)) rval |= ckdist(*argv, opt_type); else if (stat(*argv, &sb)) rval |= fail(*argv, NULL); else if (S_ISREG(sb.st_mode)) rval |= ckdist(*argv, opt_type); else { arg[0] = *argv; if ((ftsp = fts_open(arg, FTS_LOGICAL, NULL)) == NULL) err(2, "fts_open"); - while ((f = fts_read(ftsp)) != NULL) + while (errno = 0, (f = fts_read(ftsp)) != NULL) switch (f->fts_info) { case FTS_DC: rval = fail(f->fts_path, "Directory causes a cycle"); break; case FTS_DNR: case FTS_ERR: case FTS_NS: rval = fail(f->fts_path, sys_errlist[f->fts_errno]); break; case FTS_D: if (!opt_recurse && f->fts_level > FTS_ROOTLEVEL && fts_set(ftsp, f, FTS_SKIP)) err(2, "fts_set"); break; case FTS_F: if ((type = distfile(f->fts_name)) != 0 && (!opt_type || type == opt_type)) rval |= ckdist(f->fts_path, type); break; default: ; } if (errno) err(2, "fts_read"); if (fts_close(ftsp)) err(2, "fts_close"); } } while (*++argv); return rval; } static int ckdist(const char *path, int type) { FILE *fp; int rval, c; if (isstdin(path)) { path = "(stdin)"; fp = stdin; } else if ((fp = fopen(path, "r")) == NULL) return fail(path, NULL); if (!type) { if (fp != stdin) type = distfile(path); if (!type) if ((c = fgetc(fp)) != EOF) { type = c == 'M' ? DISTMD5 : c == 'P' ? DISTINF : 0; (void)ungetc(c, fp); } } switch (type) { case DISTMD5: rval = chkmd5(fp, path); break; case DISTINF: rval = chkinf(fp, path); break; default: rval = report(path, NULL, E_UNKNOWN); } if (ferror(fp)) warn("%s", path); if (fp != stdin && fclose(fp)) err(2, "%s", path); return rval; } static int chkmd5(FILE * fp, const char *path) { char buf[298]; /* "MD5 (NAMESIZE = MDSUMLEN" */ char name[NAMESIZE + 1]; char sum[MDSUMLEN + 1], chk[MDSUMLEN + 1]; const char *dname; char *s; int rval, error, c, fd; char ch; rval = 0; while (fgets(buf, sizeof(buf), fp)) { dname = NULL; error = 0; if (((c = sscanf(buf, "MD5 (%256s = %32s%c", name, sum, &ch)) != 3 && (!feof(fp) || c != 2)) || (c == 3 && ch != '\n') || (s = strrchr(name, ')')) == NULL || strlen(sum) != MDSUMLEN) error = E_BADMD5; else { *s = 0; if ((dname = distname(path, name, NULL)) == NULL) error = E_NAME; else if (opt_exist) { if ((fd = open(dname, O_RDONLY)) == -1) error = E_ERRNO; else if (close(fd)) err(2, "%s", dname); } else if (!MD5File(dname, chk)) error = E_ERRNO; else if (strcmp(chk, sum)) error = E_CHKSUM; } if (opt_ignore && error == E_ERRNO && errno == ENOENT) continue; if (error || opt_all) rval |= report(path, dname, error); if (isfatal(error)) break; } return rval; } static int chkinf(FILE * fp, const char *path) { char buf[30]; /* "cksum.2 = 10 6" */ char ext[3]; struct stat sb; const char *dname; off_t len; u_long sum; intmax_t sumlen; uint32_t chk; int rval, error, c, pieces, cnt, fd; char ch; rval = 0; for (cnt = -1; fgets(buf, sizeof(buf), fp); cnt++) { fd = -1; dname = NULL; error = 0; if (cnt == -1) { if ((c = sscanf(buf, "Pieces = %d%c", &pieces, &ch)) != 2 || ch != '\n' || pieces < 1) error = E_BADINF; } else if (((c = sscanf(buf, "cksum.%2s = %lu %jd%c", ext, &sum, &sumlen, &ch)) != 4 && (!feof(fp) || c != 3)) || (c == 4 && ch != '\n') || ext[0] != 'a' + cnt / 26 || ext[1] != 'a' + cnt % 26) error = E_BADINF; else if ((dname = distname(fp == stdin ? NULL : path, NULL, ext)) == NULL) error = E_NAME; else if ((fd = open(dname, O_RDONLY)) == -1) error = E_ERRNO; else if (fstat(fd, &sb)) error = E_ERRNO; else if (sb.st_size != (off_t)sumlen) error = E_LENGTH; else if (!opt_exist) { if (crc(fd, &chk, &len)) error = E_ERRNO; else if (chk != sum) error = E_CHKSUM; } if (fd != -1 && close(fd)) err(2, "%s", dname); if (opt_ignore && error == E_ERRNO && errno == ENOENT) continue; if (error || (opt_all && cnt >= 0)) rval |= report(path, dname, error); if (isfatal(error)) break; } return rval; } static int report(const char *path, const char *name, int error) { if (name) name = stripath(name); switch (error) { case E_UNKNOWN: printf("%s: Unknown format\n", path); break; case E_BADMD5: printf("%s: Invalid MD5 format\n", path); break; case E_BADINF: printf("%s: Invalid .inf format\n", path); break; case E_NAME: printf("%s: Can't derive component name\n", path); break; case E_LENGTH: printf("%s: %s: Size mismatch\n", path, name); break; case E_CHKSUM: printf("%s: %s: Checksum mismatch\n", path, name); break; case E_ERRNO: printf("%s: %s: %s\n", path, name, sys_errlist[errno]); break; default: printf("%s: %s: OK\n", path, name); } return error != 0; } static const char * distname(const char *path, const char *name, const char *ext) { static char buf[NAMESIZE]; size_t plen, nlen; char *s; if (opt_name) name = opt_name; else if (!name) { if (!path) return NULL; name = stripath(path); } nlen = strlen(name); if (ext && nlen > 4 && name[nlen - 4] == '.' && disttype(name + nlen - 3) == DISTINF) nlen -= 4; if (opt_dir) { path = opt_dir; plen = strlen(path); } else plen = path && (s = strrchr(path, '/')) != NULL ? (size_t)(s - path) : 0; if (plen + (plen > 0) + nlen + (ext ? 3 : 0) >= sizeof(buf)) return NULL; s = buf; if (plen) { memcpy(s, path, plen); s += plen; *s++ = '/'; } memcpy(s, name, nlen); s += nlen; if (ext) { *s++ = '.'; memcpy(s, ext, 2); s += 2; } *s = 0; return buf; } static const char * stripath(const char *path) { const char *s; return ((s = strrchr(path, '/')) != NULL && s[1] ? s + 1 : path); } static int distfile(const char *path) { const char *s; int type; if ((type = disttype(path)) == DISTMD5 || ((s = strrchr(path, '.')) != NULL && s > path && (type = disttype(s + 1)) != 0)) return type; return 0; } static int disttype(const char *name) { static const char dname[DISTTYPES][4] = {"md5", "inf"}; int i; for (i = 0; i < DISTTYPES; i++) if (!strcmp(dname[i], name)) return 1 + i; return 0; } static int fail(const char *path, const char *msg) { if (opt_silent) return 0; warnx("%s: %s", path, msg ? msg : sys_errlist[errno]); return 2; } static void usage(void) { fprintf(stderr, "usage: ckdist [-airsx] [-d dir] [-n name] [-t type] file ...\n"); exit(2); } diff --git a/usr.sbin/fmtree/create.c b/usr.sbin/fmtree/create.c index 204f40abc15c..edfb09ebd2f2 100644 --- a/usr.sbin/fmtree/create.c +++ b/usr.sbin/fmtree/create.c @@ -1,429 +1,431 @@ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static char sccsid[] = "@(#)create.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #ifdef MD5 #include #endif #ifdef SHA1 #include #endif #ifdef RMD160 #include #endif #ifdef SHA256 #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 cl; char *argv[2], host[MAXHOSTNAMELEN]; char dot[] = "."; int indent = 0; if (!nflag) { (void)time(&cl); (void)gethostname(host, sizeof(host)); (void)printf( "#\t user: %s\n#\tmachine: %s\n", getlogin(), host); (void)printf( "#\t tree: %s\n#\t date: %s", fullpath, ctime(&cl)); } argv[0] = dot; argv[1] = NULL; if ((t = fts_open(argv, ftsoptions, dsort)) == NULL) err(1, "fts_open()"); - while ((p = fts_read(t))) { + while (errno = 0, (p = fts_read(t))) { if (iflag) indent = p->fts_level * 4; if (check_excludes(p->fts_name, p->fts_path)) { fts_set(t, p, FTS_SKIP); continue; } switch(p->fts_info) { case FTS_D: if (!dflag) (void)printf("\n"); if (!nflag) (void)printf("# %s\n", p->fts_path); statd(t, p, &uid, &gid, &mode, &flags); statf(indent, p); break; case FTS_DP: if (!nflag && (p->fts_level > 0)) (void)printf("%*s# %s\n", indent, "", p->fts_path); (void)printf("%*s..\n", indent, ""); if (!dflag) (void)printf("\n"); break; case FTS_DNR: case FTS_ERR: case FTS_NS: warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); break; default: if (!dflag) statf(indent, p); break; } } + if (errno != 0) + err(1, "fts_read()"); (void)fts_close(t); if (sflag && keys & F_CKSUM) warnx("%s checksum: %lu", fullpath, (unsigned long)crc_total); } static void statf(int indent, FTSENT *p) { struct group *gr; struct passwd *pw; uint32_t val; off_t len; int fd, offset; char *fflags; char *escaped_name; escaped_name = calloc(1, p->fts_namelen * 4 + 1); if (escaped_name == NULL) errx(1, "statf(): calloc() failed"); strvis(escaped_name, p->fts_name, VIS_WHITE | VIS_OCTAL | VIS_GLOB); if (iflag || S_ISDIR(p->fts_statp->st_mode)) offset = printf("%*s%s", indent, "", escaped_name); else offset = printf("%*s %s", indent, "", escaped_name); free(escaped_name); if (offset > (INDENTNAMELEN + indent)) offset = MAXLINELEN; else offset += printf("%*s", (INDENTNAMELEN + indent) - offset, ""); if (!S_ISREG(p->fts_statp->st_mode) && !dflag) output(indent, &offset, "type=%s", inotype(p->fts_statp->st_mode)); if (p->fts_statp->st_uid != uid) { if (keys & F_UNAME) { pw = getpwuid(p->fts_statp->st_uid); if (pw != NULL) output(indent, &offset, "uname=%s", pw->pw_name); else if (wflag) warnx("Could not get uname for uid=%u", p->fts_statp->st_uid); else errx(1, "Could not get uname for uid=%u", p->fts_statp->st_uid); } if (keys & F_UID) output(indent, &offset, "uid=%u", p->fts_statp->st_uid); } if (p->fts_statp->st_gid != gid) { if (keys & F_GNAME) { gr = getgrgid(p->fts_statp->st_gid); if (gr != NULL) output(indent, &offset, "gname=%s", gr->gr_name); else if (wflag) warnx("Could not get gname for gid=%u", p->fts_statp->st_gid); else errx(1, "Could not get gname for gid=%u", p->fts_statp->st_gid); } if (keys & F_GID) output(indent, &offset, "gid=%u", p->fts_statp->st_gid); } if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode) output(indent, &offset, "mode=%#o", p->fts_statp->st_mode & MBITS); if (keys & F_NLINK && p->fts_statp->st_nlink != 1) output(indent, &offset, "nlink=%ju", (uintmax_t)p->fts_statp->st_nlink); if (keys & F_SIZE && S_ISREG(p->fts_statp->st_mode)) output(indent, &offset, "size=%jd", (intmax_t)p->fts_statp->st_size); if (keys & F_TIME) output(indent, &offset, "time=%ld.%09ld", (long)p->fts_statp->st_mtim.tv_sec, p->fts_statp->st_mtim.tv_nsec); if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) { if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 || crc(fd, &val, &len)) err(1, "%s", p->fts_accpath); (void)close(fd); output(indent, &offset, "cksum=%lu", (unsigned long)val); } #ifdef MD5 if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) { char *digest, buf[33]; digest = MD5File(p->fts_accpath, buf); if (!digest) err(1, "%s", p->fts_accpath); output(indent, &offset, "md5digest=%s", digest); } #endif /* MD5 */ #ifdef SHA1 if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) { char *digest, buf[41]; digest = SHA1_File(p->fts_accpath, buf); if (!digest) err(1, "%s", p->fts_accpath); output(indent, &offset, "sha1digest=%s", digest); } #endif /* SHA1 */ #ifdef RMD160 if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) { char *digest, buf[41]; digest = RIPEMD160_File(p->fts_accpath, buf); if (!digest) err(1, "%s", p->fts_accpath); output(indent, &offset, "ripemd160digest=%s", digest); } #endif /* RMD160 */ #ifdef SHA256 if (keys & F_SHA256 && S_ISREG(p->fts_statp->st_mode)) { char *digest, buf[65]; digest = SHA256_File(p->fts_accpath, buf); if (!digest) err(1, "%s", p->fts_accpath); output(indent, &offset, "sha256digest=%s", digest); } #endif /* SHA256 */ if (keys & F_SLINK && (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) output(indent, &offset, "link=%s", rlink(p->fts_accpath)); if (keys & F_FLAGS && p->fts_statp->st_flags != flags) { fflags = flags_to_string(p->fts_statp->st_flags); output(indent, &offset, "flags=%s", fflags); free(fflags); } (void)putchar('\n'); } #define MAXGID 5000 #define MAXUID 5000 #define MAXMODE MBITS + 1 #define MAXFLAGS 256 #define MAXS 16 static int statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, u_long *pflags) { FTSENT *p; gid_t sgid; uid_t suid; mode_t smode; u_long sflags; struct group *gr; struct passwd *pw; gid_t savegid = *pgid; uid_t saveuid = *puid; mode_t savemode = *pmode; u_long saveflags = *pflags; u_short maxgid, maxuid, maxmode, maxflags; u_short g[MAXGID], u[MAXUID], m[MAXMODE], f[MAXFLAGS]; char *fflags; static int first = 1; if ((p = fts_children(t, 0)) == NULL) { if (errno) err(1, "%s", RP(parent)); return (1); } bzero(g, sizeof(g)); bzero(u, sizeof(u)); bzero(m, sizeof(m)); bzero(f, sizeof(f)); maxuid = maxgid = maxmode = maxflags = 0; for (; p; p = p->fts_link) { if (!dflag || (dflag && S_ISDIR(p->fts_statp->st_mode))) { smode = p->fts_statp->st_mode & MBITS; if (smode < MAXMODE && ++m[smode] > maxmode) { savemode = smode; maxmode = m[smode]; } sgid = p->fts_statp->st_gid; if (sgid < MAXGID && ++g[sgid] > maxgid) { savegid = sgid; maxgid = g[sgid]; } suid = p->fts_statp->st_uid; if (suid < MAXUID && ++u[suid] > maxuid) { saveuid = suid; maxuid = u[suid]; } /* * XXX * note that the below will break when file flags * are extended beyond the first 4 bytes of each * half word of the flags */ #define FLAGS2IDX(f) ((f & 0xf) | ((f >> 12) & 0xf0)) sflags = p->fts_statp->st_flags; if (FLAGS2IDX(sflags) < MAXFLAGS && ++f[FLAGS2IDX(sflags)] > maxflags) { saveflags = sflags; maxflags = f[FLAGS2IDX(sflags)]; } } } /* * If the /set record is the same as the last one we do not need to output * a new one. So first we check to see if anything changed. Note that we * always output a /set record for the first directory. */ if ((((keys & F_UNAME) | (keys & F_UID)) && (*puid != saveuid)) || (((keys & F_GNAME) | (keys & F_GID)) && (*pgid != savegid)) || ((keys & F_MODE) && (*pmode != savemode)) || ((keys & F_FLAGS) && (*pflags != saveflags)) || (first)) { first = 0; if (dflag) (void)printf("/set type=dir"); else (void)printf("/set type=file"); if (keys & F_UNAME) { pw = getpwuid(saveuid); if (pw != NULL) (void)printf(" uname=%s", pw->pw_name); else if (wflag) warnx( "Could not get uname for uid=%u", saveuid); else errx(1, "Could not get uname for uid=%u", saveuid); } if (keys & F_UID) (void)printf(" uid=%lu", (u_long)saveuid); if (keys & F_GNAME) { gr = getgrgid(savegid); if (gr != NULL) (void)printf(" gname=%s", gr->gr_name); else if (wflag) warnx("Could not get gname for gid=%u", savegid); else errx(1, "Could not get gname for gid=%u", savegid); } if (keys & F_GID) (void)printf(" gid=%lu", (u_long)savegid); if (keys & F_MODE) (void)printf(" mode=%#o", savemode); if (keys & F_NLINK) (void)printf(" nlink=1"); if (keys & F_FLAGS) { fflags = flags_to_string(saveflags); (void)printf(" flags=%s", fflags); free(fflags); } (void)printf("\n"); *puid = saveuid; *pgid = savegid; *pmode = savemode; *pflags = saveflags; } return (0); } static int dsort(const FTSENT * const *a, const FTSENT * const *b) { if (S_ISDIR((*a)->fts_statp->st_mode)) { if (!S_ISDIR((*b)->fts_statp->st_mode)) return (1); } else if (S_ISDIR((*b)->fts_statp->st_mode)) return (-1); return (strcmp((*a)->fts_name, (*b)->fts_name)); } #include void output(int indent, int *offset, const char *fmt, ...) { va_list ap; char buf[1024]; va_start(ap, fmt); (void)vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (*offset + strlen(buf) > MAXLINELEN - 3) { (void)printf(" \\\n%*s", INDENTNAMELEN + indent, ""); *offset = INDENTNAMELEN + indent; } *offset += printf(" %s", buf) + 1; } diff --git a/usr.sbin/fmtree/verify.c b/usr.sbin/fmtree/verify.c index c9291c00e218..2f9ca3dae927 100644 --- a/usr.sbin/fmtree/verify.c +++ b/usr.sbin/fmtree/verify.c @@ -1,259 +1,261 @@ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if 0 #ifndef lint static char sccsid[] = "@(#)verify.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #endif #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "mtree.h" #include "extern.h" static NODE *root; static char path[MAXPATHLEN]; static void miss(NODE *, char *); static int vwalk(void); int mtree_verifyspec(FILE *fi) { int rval; root = mtree_readspec(fi); rval = vwalk(); miss(root, path); return (rval); } static int nsort(const FTSENT * const *a, const FTSENT * const *b) { return (strcmp((*a)->fts_name, (*b)->fts_name)); } static int vwalk(void) { FTS *t; FTSENT *p; NODE *ep, *level; int specdepth, rval; char *argv[2]; char dot[] = "."; argv[0] = dot; argv[1] = NULL; if ((t = fts_open(argv, ftsoptions, nsort)) == NULL) err(1, "line %d: fts_open", lineno); level = root; specdepth = rval = 0; - while ((p = fts_read(t))) { + while (errno = 0, (p = fts_read(t))) { if (check_excludes(p->fts_name, p->fts_path)) { fts_set(t, p, FTS_SKIP); continue; } switch(p->fts_info) { case FTS_D: case FTS_SL: break; case FTS_DP: if (specdepth > p->fts_level) { for (level = level->parent; level->prev; level = level->prev); --specdepth; } continue; case FTS_DNR: case FTS_ERR: case FTS_NS: warnx("%s: %s", RP(p), strerror(p->fts_errno)); continue; default: if (dflag) continue; } if (specdepth != p->fts_level) goto extra; for (ep = level; ep; ep = ep->next) if ((ep->flags & F_MAGIC && !fnmatch(ep->name, p->fts_name, FNM_PATHNAME)) || !strcmp(ep->name, p->fts_name)) { ep->flags |= F_VISIT; if ((ep->flags & F_NOCHANGE) == 0 && compare(ep->name, ep, p)) rval = MISMATCHEXIT; if (ep->flags & F_IGN) (void)fts_set(t, p, FTS_SKIP); else if (ep->child && ep->type == F_DIR && p->fts_info == FTS_D) { level = ep->child; ++specdepth; } break; } if (ep) continue; extra: if (!eflag) { (void)printf("%s extra", RP(p)); if (rflag) { if ((S_ISDIR(p->fts_statp->st_mode) ? rmdir : unlink)(p->fts_accpath)) { (void)printf(", not removed: %s", strerror(errno)); } else (void)printf(", removed"); } (void)putchar('\n'); } (void)fts_set(t, p, FTS_SKIP); } + if (errno != 0) + err(1, "fts_read()"); (void)fts_close(t); if (sflag) warnx("%s checksum: %lu", fullpath, (unsigned long)crc_total); return (rval); } static void miss(NODE *p, char *tail) { int create; char *tp; const char *type, *what; int serr; for (; p; p = p->next) { if (p->flags & F_OPT && !(p->flags & F_VISIT)) continue; if (p->type != F_DIR && (dflag || p->flags & F_VISIT)) continue; (void)strcpy(tail, p->name); if (!(p->flags & F_VISIT)) { /* Don't print missing message if file exists as a symbolic link and the -q flag is set. */ struct stat statbuf; if (qflag && stat(path, &statbuf) == 0) p->flags |= F_VISIT; else (void)printf("%s missing", path); } if (p->type != F_DIR && p->type != F_LINK) { putchar('\n'); continue; } create = 0; if (p->type == F_LINK) type = "symlink"; else type = "directory"; if (!(p->flags & F_VISIT) && uflag) { if (!(p->flags & (F_UID | F_UNAME))) (void)printf(" (%s not created: user not specified)", type); else if (!(p->flags & (F_GID | F_GNAME))) (void)printf(" (%s not created: group not specified)", type); else if (p->type == F_LINK) { if (symlink(p->slink, path)) (void)printf(" (symlink not created: %s)\n", strerror(errno)); else (void)printf(" (created)\n"); if (lchown(path, p->st_uid, p->st_gid) == -1) { serr = errno; if (p->st_uid == (uid_t)-1) what = "group"; else if (lchown(path, (uid_t)-1, p->st_gid) == -1) what = "user & group"; else { what = "user"; errno = serr; } (void)printf("%s: %s not modified: %s" "\n", path, what, strerror(errno)); } continue; } else if (!(p->flags & F_MODE)) (void)printf(" (directory not created: mode not specified)"); else if (mkdir(path, S_IRWXU)) (void)printf(" (directory not created: %s)", strerror(errno)); else { create = 1; (void)printf(" (created)"); } } if (!(p->flags & F_VISIT)) (void)putchar('\n'); for (tp = tail; *tp; ++tp); *tp = '/'; miss(p->child, tp + 1); *tp = '\0'; if (!create && !uflag) continue; if (chown(path, p->st_uid, p->st_gid) == -1) { serr = errno; if (p->st_uid == (uid_t)-1) what = "group"; else if (chown(path, (uid_t)-1, p->st_gid) == -1) what = "user & group"; else { what = "user"; errno = serr; } (void)printf("%s: %s not modified: %s\n", path, what, strerror(errno)); } if (chmod(path, p->st_mode)) (void)printf("%s: permissions not set: %s\n", path, strerror(errno)); if ((p->flags & F_FLAGS) && p->st_flags && chflags(path, p->st_flags)) (void)printf("%s: file flags not set: %s\n", path, strerror(errno)); } } diff --git a/usr.sbin/setfmac/setfmac.c b/usr.sbin/setfmac/setfmac.c index dd361aa2ec8b..15dc7ffc4dde 100644 --- a/usr.sbin/setfmac/setfmac.c +++ b/usr.sbin/setfmac/setfmac.c @@ -1,500 +1,502 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2002, 2004 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by NAI Labs, the * Security Research Division of Network Associates, Inc. under * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA * CHATS research program. * * 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct label_spec { struct label_spec_entry { regex_t regex; /* compiled regular expression to match */ char *regexstr; /* uncompiled regular expression */ mode_t mode; /* mode to possibly match */ const char *modestr; /* print-worthy ",-?" mode string */ char *mactext; /* MAC label to apply */ int flags; /* miscellaneous flags */ #define F_DONTLABEL 0x01 #define F_ALWAYSMATCH 0x02 } *entries, /* entries[0..nentries] */ *match; /* cached decision for MAC label to apply */ size_t nentries; /* size of entries list */ STAILQ_ENTRY(label_spec) link; }; struct label_specs { STAILQ_HEAD(label_specs_head, label_spec) head; }; void usage(int) __dead2; struct label_specs *new_specs(void); void add_specs(struct label_specs *, const char *, int); void add_setfmac_specs(struct label_specs *, char *); void add_spec_line(const char *, int, struct label_spec_entry *, char *); int apply_specs(struct label_specs *, FTSENT *, int, int); int specs_empty(struct label_specs *); static int qflag; int main(int argc, char **argv) { FTSENT *ftsent; FTS *fts; struct label_specs *specs; int eflag = 0, xflag = 0, vflag = 0, Rflag = 0, hflag; int ch, is_setfmac; char *bn; bn = basename(argv[0]); if (bn == NULL) err(1, "basename"); is_setfmac = strcmp(bn, "setfmac") == 0; hflag = is_setfmac ? FTS_LOGICAL : FTS_PHYSICAL; specs = new_specs(); while ((ch = getopt(argc, argv, is_setfmac ? "Rhq" : "ef:qs:vx")) != -1) { switch (ch) { case 'R': Rflag = 1; break; case 'e': eflag = 1; break; case 'f': add_specs(specs, optarg, 0); break; case 'h': hflag = FTS_PHYSICAL; break; case 'q': qflag = 1; break; case 's': add_specs(specs, optarg, 1); break; case 'v': vflag++; break; case 'x': xflag = FTS_XDEV; break; default: usage(is_setfmac); } } argc -= optind; argv += optind; if (is_setfmac) { if (argc <= 1) usage(is_setfmac); add_setfmac_specs(specs, *argv); argc--; argv++; } else { if (argc == 0 || specs_empty(specs)) usage(is_setfmac); } fts = fts_open(argv, hflag | xflag, NULL); if (fts == NULL) err(1, "cannot traverse filesystem%s", argc ? "s" : ""); - while ((ftsent = fts_read(fts)) != NULL) { + while (errno = 0, (ftsent = fts_read(fts)) != NULL) { switch (ftsent->fts_info) { case FTS_DP: /* skip post-order */ break; case FTS_D: /* do pre-order */ case FTS_DC: /* do cyclic? */ /* don't ever recurse directories as setfmac(8) */ if (is_setfmac && !Rflag) fts_set(fts, ftsent, FTS_SKIP); case FTS_DEFAULT: /* do default */ case FTS_F: /* do regular */ case FTS_SL: /* do symlink */ case FTS_SLNONE: /* do symlink */ case FTS_W: /* do whiteout */ if (apply_specs(specs, ftsent, hflag, vflag)) { if (eflag) { errx(1, "labeling not supported in %s", ftsent->fts_path); } if (!qflag) warnx("labeling not supported in %s", ftsent->fts_path); fts_set(fts, ftsent, FTS_SKIP); } break; case FTS_DNR: /* die on all errors */ case FTS_ERR: case FTS_NS: err(1, "traversing %s", ftsent->fts_path); default: errx(1, "CANNOT HAPPEN (%d) traversing %s", ftsent->fts_info, ftsent->fts_path); } } + if (errno != 0) + err(1, "fts_read"); fts_close(fts); exit(0); } void usage(int is_setfmac) { if (is_setfmac) fprintf(stderr, "usage: setfmac [-Rhq] label file ...\n"); else fprintf(stderr, "usage: setfsmac [-ehqvx] [-f specfile [...]] [-s specfile [...]] file ...\n"); exit(1); } static int chomp_line(char **line, size_t *linesize) { char *s; int freeme = 0; for (s = *line; (unsigned)(s - *line) < *linesize; s++) { if (!isspace(*s)) break; } if (*s == '#') { **line = '\0'; *linesize = 0; return (freeme); } memmove(*line, s, *linesize - (s - *line)); *linesize -= s - *line; for (s = &(*line)[*linesize - 1]; s >= *line; s--) { if (!isspace(*s)) break; } if (s != &(*line)[*linesize - 1]) { *linesize = s - *line + 1; } else { s = malloc(*linesize + 1); if (s == NULL) err(1, "malloc"); strncpy(s, *line, *linesize); *line = s; freeme = 1; } (*line)[*linesize] = '\0'; return (freeme); } void add_specs(struct label_specs *specs, const char *file, int is_sebsd) { struct label_spec *spec; FILE *fp; char *line; size_t nlines = 0, linesize; int freeline; spec = malloc(sizeof(*spec)); if (spec == NULL) err(1, "malloc"); fp = fopen(file, "r"); if (fp == NULL) err(1, "opening %s", file); while ((line = fgetln(fp, &linesize)) != NULL) { freeline = chomp_line(&line, &linesize); if (linesize > 0) /* only allocate space for non-comments */ nlines++; if (freeline) free(line); } if (ferror(fp)) err(1, "fgetln on %s", file); rewind(fp); spec->entries = calloc(nlines, sizeof(*spec->entries)); if (spec->entries == NULL) err(1, "malloc"); spec->nentries = nlines; while (nlines > 0) { line = fgetln(fp, &linesize); if (line == NULL) { if (feof(fp)) errx(1, "%s ended prematurely", file); else err(1, "failure reading %s", file); } freeline = chomp_line(&line, &linesize); if (linesize == 0) { if (freeline) free(line); continue; } add_spec_line(file, is_sebsd, &spec->entries[--nlines], line); if (freeline) free(line); } fclose(fp); if (!qflag) warnx("%s: read %lu specifications", file, (long)spec->nentries); STAILQ_INSERT_TAIL(&specs->head, spec, link); } void add_setfmac_specs(struct label_specs *specs, char *label) { struct label_spec *spec; spec = malloc(sizeof(*spec)); if (spec == NULL) err(1, "malloc"); spec->nentries = 1; spec->entries = calloc(spec->nentries, sizeof(*spec->entries)); if (spec->entries == NULL) err(1, "malloc"); /* The _only_ thing specified here is the mactext! */ spec->entries->mactext = label; spec->entries->flags |= F_ALWAYSMATCH; STAILQ_INSERT_TAIL(&specs->head, spec, link); } void add_spec_line(const char *file, int is_sebsd, struct label_spec_entry *entry, char *line) { char *regexstr, *modestr, *macstr, *regerrorstr; size_t size; int error; regexstr = strtok(line, " \t"); if (regexstr == NULL) errx(1, "%s: need regular expression", file); modestr = strtok(NULL, " \t"); if (modestr == NULL) errx(1, "%s: need a label", file); macstr = strtok(NULL, " \t"); if (macstr == NULL) { /* the mode is just optional */ macstr = modestr; modestr = NULL; } if (strtok(NULL, " \t") != NULL) errx(1, "%s: extraneous fields at end of line", file); /* assume we need to anchor this regex */ if (asprintf(®exstr, "^%s$", regexstr) == -1) err(1, "%s: processing regular expression", file); entry->regexstr = regexstr; error = regcomp(&entry->regex, regexstr, REG_EXTENDED | REG_NOSUB); if (error) { size = regerror(error, &entry->regex, NULL, 0); regerrorstr = malloc(size); if (regerrorstr == NULL) err(1, "malloc"); (void)regerror(error, &entry->regex, regerrorstr, size); errx(1, "%s: %s: %s", file, entry->regexstr, regerrorstr); } if (!is_sebsd) { entry->mactext = strdup(macstr); if (entry->mactext == NULL) err(1, "strdup"); } else { if (asprintf(&entry->mactext, "sebsd/%s", macstr) == -1) err(1, "asprintf"); if (strcmp(macstr, "<>") == 0) entry->flags |= F_DONTLABEL; } if (modestr != NULL) { if (strlen(modestr) != 2 || modestr[0] != '-') errx(1, "%s: invalid mode string: %s", file, modestr); switch (modestr[1]) { case 'b': entry->mode = S_IFBLK; entry->modestr = ",-b"; break; case 'c': entry->mode = S_IFCHR; entry->modestr = ",-c"; break; case 'd': entry->mode = S_IFDIR; entry->modestr = ",-d"; break; case 'p': entry->mode = S_IFIFO; entry->modestr = ",-p"; break; case 'l': entry->mode = S_IFLNK; entry->modestr = ",-l"; break; case 's': entry->mode = S_IFSOCK; entry->modestr = ",-s"; break; case '-': entry->mode = S_IFREG; entry->modestr = ",--"; break; default: errx(1, "%s: invalid mode string: %s", file, modestr); } } else { entry->modestr = ""; } } int specs_empty(struct label_specs *specs) { return (STAILQ_EMPTY(&specs->head)); } int apply_specs(struct label_specs *specs, FTSENT *ftsent, int hflag, int vflag) { regmatch_t pmatch; struct label_spec *ls; struct label_spec_entry *ent; char *regerrorstr, *macstr; size_t size; mac_t mac; int error, matchedby; /* * Work through file context sources in order of specification * on the command line, and through their entries in reverse * order to find the "last" (hopefully "best") match. */ matchedby = 0; STAILQ_FOREACH(ls, &specs->head, link) { for (ls->match = NULL, ent = ls->entries; ent < &ls->entries[ls->nentries]; ent++) { if (ent->flags & F_ALWAYSMATCH) goto matched; if (ent->mode != 0 && (ftsent->fts_statp->st_mode & S_IFMT) != ent->mode) continue; pmatch.rm_so = 0; pmatch.rm_eo = ftsent->fts_pathlen; error = regexec(&ent->regex, ftsent->fts_path, 1, &pmatch, REG_STARTEND); switch (error) { case REG_NOMATCH: continue; case 0: break; default: size = regerror(error, &ent->regex, NULL, 0); regerrorstr = malloc(size); if (regerrorstr == NULL) err(1, "malloc"); (void)regerror(error, &ent->regex, regerrorstr, size); errx(1, "%s: %s", ent->regexstr, regerrorstr); } matched: ls->match = ent; if (vflag) { if (matchedby == 0) { printf("%s matched by ", ftsent->fts_path); matchedby = 1; } printf("%s(%s%s,%s)", matchedby == 2 ? "," : "", ent->regexstr, ent->modestr, ent->mactext); if (matchedby == 1) matchedby = 2; } break; } } if (vflag && matchedby) printf("\n"); size = 0; STAILQ_FOREACH(ls, &specs->head, link) { /* cached match decision */ if (ls->match && (ls->match->flags & F_DONTLABEL) == 0) /* add length of "x\0"/"y," */ size += strlen(ls->match->mactext) + 1; } if (size == 0) return (0); macstr = malloc(size); if (macstr == NULL) err(1, "malloc"); *macstr = '\0'; STAILQ_FOREACH(ls, &specs->head, link) { /* cached match decision */ if (ls->match && (ls->match->flags & F_DONTLABEL) == 0) { if (*macstr != '\0') strcat(macstr, ","); strcat(macstr, ls->match->mactext); } } if (mac_from_text(&mac, macstr)) err(1, "mac_from_text(%s)", macstr); if ((hflag == FTS_PHYSICAL ? mac_set_link(ftsent->fts_accpath, mac) : mac_set_file(ftsent->fts_accpath, mac)) != 0) { if (errno == EOPNOTSUPP) { mac_free(mac); free(macstr); return (1); } err(1, "mac_set_link(%s, %s)", ftsent->fts_path, macstr); } mac_free(mac); free(macstr); return (0); } struct label_specs * new_specs(void) { struct label_specs *specs; specs = malloc(sizeof(*specs)); if (specs == NULL) err(1, "malloc"); STAILQ_INIT(&specs->head); return (specs); }