Index: stable/4/bin/ls/Makefile =================================================================== --- stable/4/bin/ls/Makefile (revision 62193) +++ stable/4/bin/ls/Makefile (revision 62194) @@ -1,9 +1,8 @@ # @(#)Makefile 8.1 (Berkeley) 6/2/93 # $FreeBSD$ PROG= ls -SRCS= cmp.c setflags.c ls.c print.c util.c -.PATH: ${.CURDIR}/../../lib/libc/gen +SRCS= cmp.c ls.c print.c util.c .include Index: stable/4/bin/ls/extern.h =================================================================== --- stable/4/bin/ls/extern.h (revision 62193) +++ stable/4/bin/ls/extern.h (revision 62194) @@ -1,53 +1,52 @@ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)extern.h 8.1 (Berkeley) 5/31/93 * $FreeBSD$ */ int acccmp __P((const FTSENT *, const FTSENT *)); int revacccmp __P((const FTSENT *, const FTSENT *)); int modcmp __P((const FTSENT *, const FTSENT *)); int revmodcmp __P((const FTSENT *, const FTSENT *)); int namecmp __P((const FTSENT *, const FTSENT *)); int revnamecmp __P((const FTSENT *, const FTSENT *)); int statcmp __P((const FTSENT *, const FTSENT *)); int revstatcmp __P((const FTSENT *, const FTSENT *)); -char *flags_to_string __P((u_long, char *)); void prcopy __P((char *, char *, int)); void printcol __P((DISPLAY *)); void printlong __P((DISPLAY *)); void printscol __P((DISPLAY *)); void usage __P((void)); int len_octal __P((char *, int)); int prn_octal __P((char *)); Index: stable/4/bin/ls/ls.c =================================================================== --- stable/4/bin/ls/ls.c (revision 62193) +++ stable/4/bin/ls/ls.c (revision 62194) @@ -1,657 +1,662 @@ /* * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static 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 char sccsid[] = "@(#)ls.c 8.5 (Berkeley) 4/2/94"; #else static const char rcsid[] = "$FreeBSD$"; #endif #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #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) -char *getflags __P((u_long, char *)); - static void display __P((FTSENT *, FTSENT *)); static u_quad_t makenines __P((u_long)); static int mastercmp __P((const FTSENT **, const FTSENT **)); static void traverse __P((int, char **, int)); static void (*printfcn) __P((DISPLAY *)); static int (*sortfcn) __P((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_column; /* columnated format */ int f_flags; /* show flags associated with a file */ int f_inode; /* print inode */ int f_kblocks; /* print size in kilobytes */ int f_listdir; /* list actual directory, not contents */ int f_listdot; /* list files beginning with . */ int f_longform; /* long listing format */ int f_nonprint; /* show unprintables as ? */ int f_nosort; /* don't sort output */ int f_notabs; /* don't use tab-separated multi-col output */ 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 */ int f_recursive; /* ls subdirectories also */ int f_reversesort; /* reverse whatever sort is used */ int f_sectime; /* print the real time for all files */ int f_singlecol; /* use single column output */ int f_size; /* list size in short listing */ int f_statustime; /* use time of last mode change */ int f_timesort; /* sort by time vice name */ int f_type; /* add type character for non-regular files */ int f_whiteout; /* show whiteout entries */ int rval; int main(argc, argv) int argc; char *argv[]; { static char dot[] = ".", *dotav[] = { dot, NULL }; struct winsize win; int ch, fts_options, notused; char *p; (void) setlocale(LC_ALL, ""); /* Terminal defaults to -Cq, non-terminal defaults to -1. */ if (isatty(STDOUT_FILENO)) { if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == -1 || !win.ws_col) { if ((p = getenv("COLUMNS")) != NULL) termwidth = atoi(p); } else termwidth = win.ws_col; f_column = f_nonprint = 1; } else { f_singlecol = 1; /* retrieve environment variable, in case of explicit -C */ if ((p = getenv("COLUMNS"))) termwidth = atoi(p); } /* Root is -A automatically. */ if (!getuid()) f_listdot = 1; fts_options = FTS_PHYSICAL; while ((ch = getopt(argc, argv, "1ABCFHLPRTWabcdfgiklnoqrstu")) != -1) { switch (ch) { /* * The -1, -C and -l options all override each other so shell * aliasing works right. */ case '1': f_singlecol = 1; f_column = f_longform = 0; break; case 'B': f_nonprint = 0; f_octal = 1; f_octal_escape = 0; break; case 'C': f_column = 1; f_longform = f_singlecol = 0; break; case 'l': f_longform = 1; f_column = f_singlecol = 0; break; /* The -c and -u options override each other. */ case 'c': f_statustime = 1; f_accesstime = 0; break; case 'u': f_accesstime = 1; f_statustime = 0; break; case 'F': f_type = 1; break; case 'H': fts_options |= FTS_COMFOLLOW; break; case 'L': fts_options &= ~FTS_PHYSICAL; fts_options |= FTS_LOGICAL; break; case 'P': fts_options &= ~FTS_COMFOLLOW; fts_options &= ~FTS_LOGICAL; fts_options |= FTS_PHYSICAL; break; case 'R': f_recursive = 1; break; case 'a': fts_options |= FTS_SEEDOT; /* FALLTHROUGH */ case 'A': f_listdot = 1; break; /* The -d option turns off the -R option. */ case 'd': f_listdir = 1; f_recursive = 0; break; case 'f': f_nosort = 1; break; case 'g': /* Compatibility with 4.3BSD. */ break; case 'i': f_inode = 1; break; case 'k': f_kblocks = 1; break; case 'n': f_numericonly = 1; break; case 'o': f_flags = 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 'T': f_sectime = 1; break; case 't': f_timesort = 1; break; case 'W': f_whiteout = 1; break; case 'b': f_nonprint = 0; f_octal = 0; f_octal_escape = 1; break; default: case '?': usage(); } } argc -= optind; argv += optind; /* * If not -F, -i, -l, -s or -t options, don't require stat * information. */ if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type) fts_options |= FTS_NOSTAT; /* * If not -F, -d or -l options, follow any symbolic links listed on * the command line. */ if (!f_longform && !f_listdir && !f_type) fts_options |= FTS_COMFOLLOW; /* * If -W, show whiteout entries */ #ifdef FTS_WHITEOUT if (f_whiteout) fts_options |= FTS_WHITEOUT; #endif /* If -l or -s, figure out block size. */ if (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) sortfcn = revnamecmp; else if (f_accesstime) sortfcn = revacccmp; else if (f_statustime) sortfcn = revstatcmp; else /* Use modification time. */ sortfcn = revmodcmp; } else { if (!f_timesort) sortfcn = namecmp; else if (f_accesstime) sortfcn = acccmp; 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 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(argc, argv, options) int argc, options; char *argv[]; { FTS *ftsp; FTSENT *p, *chp; int ch_options; if ((ftsp = fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) err(1, NULL); display(NULL, fts_children(ftsp, 0)); if (f_listdir) return; /* * If not recursing down this tree and don't need stat info, just get * the names. */ ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; while ((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_name, 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) (void)printf("\n%s:\n", p->fts_path); else if (argc > 1) { (void)printf("%s:\n", p->fts_path); output = 1; } chp = fts_children(ftsp, ch_options); display(p, chp); if (!f_recursive && chp != NULL) (void)fts_set(ftsp, p, FTS_SKIP); 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(p, list) FTSENT *p, *list; { struct stat *sp; DISPLAY d; FTSENT *cur; NAMES *np; u_quad_t maxsize; u_long btotal, maxblock, maxinode, maxlen, maxnlink; int bcfile, flen, glen, ulen, maxflags, maxgroup, maxuser; char *initmax; int entries, needstats; char *user, *group, *flags; char buf[STRBUF_SIZEOF(u_quad_t) + 1]; char ngroup[STRBUF_SIZEOF(uid_t) + 1]; char nuser[STRBUF_SIZEOF(gid_t) + 1]; /* * If list is NULL there are two possibilities: that the parent * directory p has no children, or that fts_children() returned an * error. We ignore the error case since it will be replicated * on the next call to fts_read() on the post-order visit to the * directory p, and will be signaled in traverse(). */ if (list == NULL) return; 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. */ if (initmax != NULL && *initmax != '\0') { char *initmax2, *jinitmax; int ninitmax; /* Fill-in "::" as "0:0:0" for the sake of scanf. */ jinitmax = initmax2 = malloc(strlen(initmax) * 2 + 2); if (jinitmax == NULL) err(1, NULL); 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, " %lu : %lu : %lu : %i : %i : %i : %qu : %lu ", &maxinode, &maxblock, &maxnlink, &maxuser, &maxgroup, &maxflags, &maxsize, &maxlen); f_notabs = 1; switch (ninitmax) { case 0: maxinode = 0; case 1: maxblock = 0; case 2: maxnlink = 0; case 3: maxuser = 0; case 4: maxgroup = 0; case 5: maxflags = 0; case 6: maxsize = 0; case 7: maxlen = 0, f_notabs = 0; } maxinode = makenines(maxinode); maxblock = makenines(maxblock); maxnlink = makenines(maxnlink); maxsize = makenines(maxsize); } else if (initmax == NULL || *initmax == '\0') maxblock = maxinode = maxlen = maxnlink = maxuser = maxgroup = maxflags = maxsize = 0; bcfile = 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 (f_nonprint) prcopy(cur->fts_name, cur->fts_name, cur->fts_namelen); if (cur->fts_namelen > maxlen) maxlen = cur->fts_namelen; if (f_octal || f_octal_escape) { int 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); group = group_from_gid(sp->st_gid, 0); } if ((ulen = strlen(user)) > maxuser) maxuser = ulen; if ((glen = strlen(group)) > maxgroup) maxgroup = glen; if (f_flags) { - flags = getflags(sp->st_flags, "-"); + flags = fflagstostr(sp->st_flags); + if (flags != NULL && *flags == '\0') { + free(flags); + flags = strdup("-"); + } + if (flags == NULL) + err(1, NULL); if ((flen = strlen(flags)) > maxflags) maxflags = flen; } else flen = 0; if ((np = malloc(sizeof(NAMES) + ulen + glen + flen + 3)) == NULL) err(1, NULL); 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)) bcfile = 1; if (f_flags) { np->flags = &np->data[ulen + glen + 2]; (void)strcpy(np->flags, flags); + free(flags); } cur->fts_pointer = np; } } ++entries; } if (!entries) return; d.list = list; d.entries = entries; d.maxlen = maxlen; if (needstats) { d.bcfile = bcfile; d.btotal = btotal; (void)snprintf(buf, sizeof(buf), "%lu", maxblock); d.s_block = strlen(buf); d.s_flags = maxflags; d.s_group = maxgroup; (void)snprintf(buf, sizeof(buf), "%lu", maxinode); d.s_inode = strlen(buf); (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); d.s_nlink = strlen(buf); (void)snprintf(buf, sizeof(buf), "%qu", maxsize); d.s_size = strlen(buf); d.s_user = maxuser; } 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(a, b) const FTSENT **a, **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)); } /* * Makenines() returns (10**n)-1. This is useful for converting a width * into a number that wide in decimal. */ static u_quad_t makenines(n) u_long n; { u_long i; u_quad_t reg; reg = 1; /* Use a loop instead of pow(), since all values of n are small. */ for (i = 0; i < n; i++) reg *= 10; reg--; return reg; } Index: stable/4/bin/rm/Makefile =================================================================== --- stable/4/bin/rm/Makefile (revision 62193) +++ stable/4/bin/rm/Makefile (revision 62194) @@ -1,11 +1,10 @@ # @(#)Makefile 8.1 (Berkeley) 5/31/93 # $FreeBSD$ PROG= rm -SRCS= rm.c setflags.c +SRCS= rm.c LINKS= ${BINDIR}/rm ${BINDIR}/unlink MLINKS= rm.1 unlink.1 -.PATH: ${.CURDIR}/../../lib/libc/gen .include Index: stable/4/bin/rm/rm.c =================================================================== --- stable/4/bin/rm/rm.c (revision 62193) +++ stable/4/bin/rm/rm.c (revision 62194) @@ -1,497 +1,495 @@ /*- * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static 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 #if 0 static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94"; #else static const char rcsid[] = "$FreeBSD$"; #endif #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include -char *getflags __P((u_long, char *)); - int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok; uid_t uid; int check __P((char *, char *, struct stat *)); void checkdot __P((char **)); void rm_file __P((char **)); void rm_overwrite __P((char *, struct stat *)); void rm_tree __P((char **)); void usage __P((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(argc, argv) int argc; char *argv[]; { int ch, rflag; char *p; /* * Test for the special case where the utility is called as * "unlink", for which the functionality provided is greatly * simplified. */ if ((p = rindex(argv[0], '/')) == NULL) p = argv[0]; else ++p; if (strcmp(p, "unlink") == 0) { if (argc == 2) { rm_file(&argv[1]); exit(eval); } else usage(); } Pflag = rflag = 0; while ((ch = getopt(argc, argv, "dfiPRrvW")) != -1) switch(ch) { case 'd': dflag = 1; break; case 'f': fflag = 1; iflag = 0; break; case 'i': fflag = 0; iflag = 1; break; case 'P': Pflag = 1; break; case 'R': case 'r': /* Compatibility. */ rflag = 1; break; case 'v': vflag = 1; break; case 'W': Wflag = 1; break; default: usage(); } argc -= optind; argv += optind; if (argc < 1) { if (fflag) return 0; usage(); } checkdot(argv); uid = geteuid(); if (*argv) { stdin_ok = isatty(STDIN_FILENO); if (rflag) rm_tree(argv); else rm_file(argv); } exit (eval); } void rm_tree(argv) 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 (!(fts = fts_open(argv, flags, (int (*)())NULL))) err(1, NULL); while ((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: /* * FTS_NS: assume that if can'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)) && chflags(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 = chflags(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_accpath); continue; } break; case FTS_W: rval = undelete(p->fts_accpath); if (rval == 0 && (fflag && errno == ENOENT)) { if (vflag) (void)printf("%s\n", p->fts_accpath); continue; } break; default: if (Pflag) rm_overwrite(p->fts_accpath, NULL); rval = unlink(p->fts_accpath); if (rval == 0 || (fflag && errno == ENOENT)) { if (rval == 0 && vflag) (void)printf("%s\n", p->fts_accpath); continue; } } } err: warn("%s", p->fts_path); eval = 1; } if (errno) err(1, "fts_read"); } void rm_file(argv) 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 && (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) && !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE))) rval = chflags(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 { if (Pflag) rm_overwrite(f, &sb); rval = unlink(f); } } if (rval && (!fflag || errno != ENOENT)) { warn("%s", f); eval = 1; } if (vflag && rval == 0) (void)printf("%s\n", f); } } /* * rm_overwrite -- * Overwrite the file 3 times with varying bit patterns. * * XXX * This is a cheap way to *really* delete files. Note that only regular * files are deleted, directories (and therefore names) will remain. * Also, this assumes a fixed-block file system (like FFS, or a V7 or a * System V file system). In a logging file system, you'll have to have * kernel support. */ void rm_overwrite(file, sbp) char *file; struct stat *sbp; { struct stat sb; struct statfs fsb; off_t len; int bsize, fd, wlen; char *buf = NULL; fd = -1; if (sbp == NULL) { if (lstat(file, &sb)) goto err; sbp = &sb; } if (!S_ISREG(sbp->st_mode)) return; if ((fd = open(file, O_WRONLY, 0)) == -1) goto err; if (fstatfs(fd, &fsb) == -1) goto err; bsize = MAX(fsb.f_iosize, 1024); if ((buf = malloc(bsize)) == NULL) err(1, "malloc"); #define PASS(byte) { \ memset(buf, byte, bsize); \ for (len = sbp->st_size; len > 0; len -= wlen) { \ wlen = len < bsize ? len : bsize; \ if (write(fd, buf, wlen) != wlen) \ goto err; \ } \ } PASS(0xff); if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) goto err; PASS(0x00); if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) goto err; PASS(0xff); if (!fsync(fd) && !close(fd)) { free(buf); return; } err: eval = 1; if (buf) free(buf); warn("%s", file); } int check(path, name, sp) char *path, *name; struct stat *sp; { int ch, first; - char modep[15], flagsp[128]; + 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); - strcpy(flagsp, getflags(sp->st_flags, NULL)); - if (*flagsp) - strcat(flagsp, " "); - (void)fprintf(stderr, "override %s%s%s/%s %sfor %s? ", + if ((flagsp = fflagstostr(sp->st_flags)) == NULL) + err(1, NULL); + (void)fprintf(stderr, "override %s%s%s/%s %s%sfor %s? ", modep + 1, modep[9] == ' ' ? "" : " ", user_from_uid(sp->st_uid, 0), group_from_gid(sp->st_gid, 0), - *flagsp ? flagsp : "", + *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 ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) void checkdot(argv) 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; } } void usage() { (void)fprintf(stderr, "%s\n%s\n", "usage: rm [-f | -i] [-dPRrvW] file ...", " unlink file"); exit(EX_USAGE); } Index: stable/4/lib/libc/sys/chflags.2 =================================================================== --- stable/4/lib/libc/sys/chflags.2 (revision 62193) +++ stable/4/lib/libc/sys/chflags.2 (revision 62194) @@ -1,171 +1,173 @@ .\" Copyright (c) 1989, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)chflags.2 8.3 (Berkeley) 5/2/95 .\" $FreeBSD$ .\" .Dd May 2, 1995 .Dt CHFLAGS 2 .Os .Sh NAME .Nm chflags , .Nm fchflags .Nd set file flags .Sh LIBRARY .Lb libc .Sh SYNOPSIS .Fd #include .Fd #include .Ft int .Fn chflags "const char *path" "u_long flags" .Ft int .Fn fchflags "int fd" "u_long flags" .Sh DESCRIPTION The file whose name is given by .Fa path or referenced by the descriptor .Fa fd has its flags changed to .Fa flags . .Pp The flags specified are formed by .Em or Ns 'ing the following values .Pp .Bl -tag -width "SF_IMMUTABLE" -compact -offset indent .It UF_NODUMP Do not dump the file. .It UF_IMMUTABLE The file may not be changed. .It UF_APPEND The file may only be appended to. .It UF_NOUNLINK The file may not be renamed or deleted. .It UF_OPAQUE The directory is opaque when viewed through a union stack. .\".It ARCHIVED .\"File is archived. .It SF_IMMUTABLE The file may not be changed. .It SF_APPEND The file may only be appended to. .It SF_NOUNLINK The file may not be renamed or deleted. .El .Pp The .Dq UF_IMMUTABLE , .Dq UF_APPEND and .Dq UF_NOUNLINK flags may be set or unset by either the owner of a file or the super-user. .Pp The .Dq SF_IMMUTABLE , .Dq SF_APPEND and .Dq SF_NOUNLINK flags may only be set or unset by the super-user. Attempts by the non-super-user to set the super-user only flags are silently ignored. These flags may be set at any time, but normally may only be unset when the system is in single-user mode. (See .Xr init 8 for details.) .Sh RETURN VALUES Upon successful completion, a value of 0 is returned. Otherwise, -1 is returned and the global variable .Va errno is set to indicate the error. .Sh ERRORS .Fn Chflags will fail if: .Bl -tag -width Er .It Bq Er ENOTDIR A component of the path prefix is not a directory. .It Bq Er ENAMETOOLONG A component of a pathname exceeded 255 characters, or an entire path name exceeded 1023 characters. .It Bq Er ENOENT The named file does not exist. .It Bq Er EACCES Search permission is denied for a component of the path prefix. .It Bq Er ELOOP Too many symbolic links were encountered in translating the pathname. .It Bq Er EPERM The effective user ID does not match the owner of the file and the effective user ID is not the super-user. .It Bq Er EROFS The named file resides on a read-only file system. .It Bq Er EFAULT .Fa Path points outside the process's allocated address space. .It Bq Er EIO An .Tn I/O error occurred while reading from or writing to the file system. .It Bq Er EOPNOTSUPP The underlying file system does not support file flags. .El .Pp .Fn Fchflags will fail if: .Bl -tag -width Er .It Bq Er EBADF The descriptor is not valid. .It Bq Er EINVAL .Fa Fd refers to a socket, not to a file. .It Bq Er EPERM The effective user ID does not match the owner of the file and the effective user ID is not the super-user. .It Bq Er EROFS The file resides on a read-only file system. .It Bq Er EIO An .Tn I/O error occurred while reading from or writing to the file system. .It Bq Er EOPNOTSUPP The underlying file system does not support file flags. .El .Sh SEE ALSO .Xr chflags 1 , +.Xr fflagstostr 3 , +.Xr strtofflags 3 , .Xr init 8 , .Xr mount_union 8 .Sh HISTORY The .Nm chflags and .Nm fchflags functions first appeared in .Bx 4.4 . Index: stable/4/libexec/ftpd/Makefile =================================================================== --- stable/4/libexec/ftpd/Makefile (revision 62193) +++ stable/4/libexec/ftpd/Makefile (revision 62194) @@ -1,28 +1,28 @@ # @(#)Makefile 8.2 (Berkeley) 4/4/94 # $FreeBSD$ PROG= ftpd MAN8= ftpd.8 SRCS= ftpd.c ftpcmd.y logwtmp.c popen.c skey-stuff.c CFLAGS+=-DSETPROCTITLE -DSKEY -DLOGIN_CAP -DVIRTUAL_HOSTING -Wall \ -I${.CURDIR}/../../contrib-crypto/telnet CFLAGS+=-DINET6 YFLAGS= LDADD= -lskey -lmd -lcrypt -lutil DPADD= ${LIBSKEY} ${LIBMD} ${LIBCRYPT} ${LIBUTIL} LSDIR= ../../bin/ls -.PATH: ${.CURDIR}/${LSDIR} ${.CURDIR}/../../lib/libc/gen -SRCS+= ls.c cmp.c print.c setflags.c util.c +.PATH: ${.CURDIR}/${LSDIR} +SRCS+= ls.c cmp.c print.c util.c CFLAGS+=-Dmain=ls_main -I${.CURDIR}/${LSDIR} .if defined(NOPAM) CFLAGS+=-DNOPAM .else DPADD+= ${LIBPAM} LDADD+= ${MINUSLPAM} .endif .include Index: stable/4/usr.bin/chflags/Makefile =================================================================== --- stable/4/usr.bin/chflags/Makefile (revision 62193) +++ stable/4/usr.bin/chflags/Makefile (revision 62194) @@ -1,11 +1,10 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 # $FreeBSD$ NOSHARED?=yes PROG= chflags CFLAGS+=-Wall -.PATH: ${.CURDIR}/../../lib/libc/gen -SRCS= chflags.c setflags.c +SRCS= chflags.c .include Index: stable/4/usr.bin/chflags/chflags.c =================================================================== --- stable/4/usr.bin/chflags/chflags.c (revision 62193) +++ stable/4/usr.bin/chflags/chflags.c (revision 62194) @@ -1,185 +1,183 @@ /* * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1992, 1993, 1994\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "@(#)chflags.c 8.5 (Berkeley) 4/1/94"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include -int setflags __P((char **, u_long *, u_long *)); - void usage __P((void)); int main(argc, argv) int argc; char *argv[]; { FTS *ftsp; FTSENT *p; u_long clear, set; long val; int Hflag, Lflag, Pflag, Rflag, ch, fts_options, oct, rval; char *flags, *ep; Hflag = Lflag = Pflag = Rflag = 0; while ((ch = getopt(argc, argv, "HLPR")) != -1) switch (ch) { case 'H': Hflag = 1; Lflag = Pflag = 0; break; case 'L': Lflag = 1; Hflag = Pflag = 0; break; case 'P': Pflag = 1; Hflag = Lflag = 0; break; case 'R': Rflag = 1; break; case '?': default: usage(); } argv += optind; argc -= optind; if (argc < 2) usage(); fts_options = FTS_PHYSICAL; if (Rflag) { if (Hflag) fts_options |= FTS_COMFOLLOW; if (Lflag) { fts_options &= ~FTS_PHYSICAL; fts_options |= FTS_LOGICAL; } } 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 (setflags(&flags, &set, &clear)) + 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;) { switch (p->fts_info) { case FTS_D: if (Rflag) /* Change it at FTS_DP. */ continue; fts_set(ftsp, p, FTS_SKIP); break; case FTS_DNR: /* Warn, chflag, continue. */ 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; case FTS_SL: /* Ignore. */ case FTS_SLNONE: /* * The only symlinks that end up here are ones that * don't point to anything and ones that we found * doing a physical walk. */ continue; default: break; } if (oct) { if (!chflags(p->fts_accpath, set)) continue; } else { p->fts_statp->st_flags |= set; p->fts_statp->st_flags &= clear; if (!chflags(p->fts_accpath, p->fts_statp->st_flags)) continue; } warn("%s", p->fts_path); rval = 1; } if (errno) err(1, "fts_read"); exit(rval); } void usage() { (void)fprintf(stderr, "usage: chflags [-R [-H | -L | -P]] flags file ...\n"); exit(1); } Index: stable/4/usr.bin/find/Makefile =================================================================== --- stable/4/usr.bin/find/Makefile (revision 62193) +++ stable/4/usr.bin/find/Makefile (revision 62194) @@ -1,9 +1,8 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 # $FreeBSD$ CFLAGS+= -Wall PROG= find -SRCS= find.c function.c ls.c main.c misc.c operator.c option.c setflags.c -.PATH: ${.CURDIR}/../../lib/libc/gen +SRCS= find.c function.c ls.c main.c misc.c operator.c option.c .include Index: stable/4/usr.bin/find/function.c =================================================================== --- stable/4/usr.bin/find/function.c (revision 62193) +++ stable/4/usr.bin/find/function.c (revision 62194) @@ -1,1464 +1,1462 @@ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Cimarron D. Taylor of the University of California, Berkeley. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static const char sccsid[] = "@(#)function.c 8.10 (Berkeley) 5/4/95"; #else static const char rcsid[] = "$FreeBSD$"; #endif #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "find.h" #define COMPARE(a, b) { \ switch (plan->flags) { \ case F_EQUAL: \ return (a == b); \ case F_LESSTHAN: \ return (a < b); \ case F_GREATER: \ return (a > b); \ default: \ abort(); \ } \ } -u_long setflags __P((char **, u_long *, u_long *)); - static PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *)))); /* * find_parsenum -- * Parse a string of the form [+-]# and return the value. */ static long long find_parsenum(plan, option, vp, endch) PLAN *plan; char *option, *vp, *endch; { long long value; char *endchar, *str; /* Pointer to character ending conversion. */ /* Determine comparison from leading + or -. */ str = vp; switch (*str) { case '+': ++str; plan->flags = F_GREATER; break; case '-': ++str; plan->flags = F_LESSTHAN; break; default: plan->flags = F_EQUAL; break; } /* * Convert the string with strtoq(). Note, if strtoq() returns zero * and endchar points to the beginning of the string we know we have * a syntax error. */ value = strtoq(str, &endchar, 10); if (value == 0 && endchar == str) errx(1, "%s: %s: illegal numeric value", option, vp); if (endchar[0] && (endch == NULL || endchar[0] != *endch)) errx(1, "%s: %s: illegal trailing character", option, vp); if (endch) *endch = endchar[0]; return (value); } /* * The value of n for the inode times (atime, ctime, and mtime) is a range, * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with * -n, such that "-mtime -1" would be less than 0 days, which isn't what the * user wanted. Correct so that -1 is "less than 1". */ #define TIME_CORRECT(p, ttype) \ if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \ ++((p)->t_data); /* * -amin n functions -- * * True if the difference between the file access time and the * current time is n min periods. */ int f_amin(plan, entry) PLAN *plan; FTSENT *entry; { extern time_t now; COMPARE((now - entry->fts_statp->st_atime + 60 - 1) / 60, plan->t_data); } PLAN * c_amin(arg) char *arg; { PLAN *new; ftsoptions &= ~FTS_NOSTAT; new = palloc(N_AMIN, f_amin); new->t_data = find_parsenum(new, "-amin", arg, NULL); TIME_CORRECT(new, N_AMIN); return (new); } /* * -atime n functions -- * * True if the difference between the file access time and the * current time is n 24 hour periods. */ int f_atime(plan, entry) PLAN *plan; FTSENT *entry; { extern time_t now; COMPARE((now - entry->fts_statp->st_atime + 86400 - 1) / 86400, plan->t_data); } PLAN * c_atime(arg) char *arg; { PLAN *new; ftsoptions &= ~FTS_NOSTAT; new = palloc(N_ATIME, f_atime); new->t_data = find_parsenum(new, "-atime", arg, NULL); TIME_CORRECT(new, N_ATIME); return (new); } /* * -cmin n functions -- * * True if the difference between the last change of file * status information and the current time is n min periods. */ int f_cmin(plan, entry) PLAN *plan; FTSENT *entry; { extern time_t now; COMPARE((now - entry->fts_statp->st_ctime + 60 - 1) / 60, plan->t_data); } PLAN * c_cmin(arg) char *arg; { PLAN *new; ftsoptions &= ~FTS_NOSTAT; new = palloc(N_CMIN, f_cmin); new->t_data = find_parsenum(new, "-cmin", arg, NULL); TIME_CORRECT(new, N_CMIN); return (new); } /* * -ctime n functions -- * * True if the difference between the last change of file * status information and the current time is n 24 hour periods. */ int f_ctime(plan, entry) PLAN *plan; FTSENT *entry; { extern time_t now; COMPARE((now - entry->fts_statp->st_ctime + 86400 - 1) / 86400, plan->t_data); } PLAN * c_ctime(arg) char *arg; { PLAN *new; ftsoptions &= ~FTS_NOSTAT; new = palloc(N_CTIME, f_ctime); new->t_data = find_parsenum(new, "-ctime", arg, NULL); TIME_CORRECT(new, N_CTIME); return (new); } /* * -depth functions -- * * Always true, causes descent of the directory hierarchy to be done * so that all entries in a directory are acted on before the directory * itself. */ int f_always_true(plan, entry) PLAN *plan; FTSENT *entry; { return (1); } PLAN * c_depth() { isdepth = 1; return (palloc(N_DEPTH, f_always_true)); } /* * [-exec | -ok] utility [arg ... ] ; functions -- * * True if the executed utility returns a zero value as exit status. * The end of the primary expression is delimited by a semicolon. If * "{}" occurs anywhere, it gets replaced by the current pathname. * The current directory for the execution of utility is the same as * the current directory when the find utility was started. * * The primary -ok is different in that it requests affirmation of the * user before executing the utility. */ int f_exec(plan, entry) register PLAN *plan; FTSENT *entry; { extern int dotfd; register int cnt; pid_t pid; int status; for (cnt = 0; plan->e_argv[cnt]; ++cnt) if (plan->e_len[cnt]) brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], entry->fts_path, plan->e_len[cnt]); if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv)) return (0); /* make sure find output is interspersed correctly with subprocesses */ fflush(stdout); switch (pid = fork()) { case -1: err(1, "fork"); /* NOTREACHED */ case 0: if (fchdir(dotfd)) { warn("chdir"); _exit(1); } execvp(plan->e_argv[0], plan->e_argv); warn("%s", plan->e_argv[0]); _exit(1); } pid = waitpid(pid, &status, 0); return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); } /* * c_exec -- * build three parallel arrays, one with pointers to the strings passed * on the command line, one with (possibly duplicated) pointers to the * argv array, and one with integer values that are lengths of the * strings, but also flags meaning that the string has to be massaged. */ PLAN * c_exec(argvp, isok) char ***argvp; int isok; { PLAN *new; /* node returned */ register int cnt; register char **argv, **ap, *p; isoutput = 1; new = palloc(N_EXEC, f_exec); if (isok) new->flags = F_NEEDOK; for (ap = argv = *argvp;; ++ap) { if (!*ap) errx(1, "%s: no terminating \";\"", isok ? "-ok" : "-exec"); if (**ap == ';') break; } cnt = ap - *argvp + 1; new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); new->e_len = (int *)emalloc((u_int)cnt * sizeof(int)); for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { new->e_orig[cnt] = *argv; for (p = *argv; *p; ++p) if (p[0] == '{' && p[1] == '}') { new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN); new->e_len[cnt] = MAXPATHLEN; break; } if (!*p) { new->e_argv[cnt] = *argv; new->e_len[cnt] = 0; } } new->e_argv[cnt] = new->e_orig[cnt] = NULL; *argvp = argv + 1; return (new); } /* * -execdir utility [arg ... ] ; functions -- * * True if the executed utility returns a zero value as exit status. * The end of the primary expression is delimited by a semicolon. If * "{}" occurs anywhere, it gets replaced by the unqualified pathname. * The current directory for the execution of utility is the same as * the directory where the file lives. */ int f_execdir(plan, entry) register PLAN *plan; FTSENT *entry; { register int cnt; pid_t pid; int status; char *file; /* XXX - if file/dir ends in '/' this will not work -- can it? */ if ((file = strrchr(entry->fts_path, '/'))) file++; else file = entry->fts_path; for (cnt = 0; plan->e_argv[cnt]; ++cnt) if (plan->e_len[cnt]) brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], file, plan->e_len[cnt]); /* don't mix output of command with find output */ fflush(stdout); fflush(stderr); switch (pid = fork()) { case -1: err(1, "fork"); /* NOTREACHED */ case 0: execvp(plan->e_argv[0], plan->e_argv); warn("%s", plan->e_argv[0]); _exit(1); } pid = waitpid(pid, &status, 0); return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); } /* * c_execdir -- * build three parallel arrays, one with pointers to the strings passed * on the command line, one with (possibly duplicated) pointers to the * argv array, and one with integer values that are lengths of the * strings, but also flags meaning that the string has to be massaged. */ PLAN * c_execdir(argvp) char ***argvp; { PLAN *new; /* node returned */ register int cnt; register char **argv, **ap, *p; ftsoptions &= ~FTS_NOSTAT; isoutput = 1; new = palloc(N_EXECDIR, f_execdir); for (ap = argv = *argvp;; ++ap) { if (!*ap) errx(1, "-execdir: no terminating \";\""); if (**ap == ';') break; } cnt = ap - *argvp + 1; new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); new->e_len = (int *)emalloc((u_int)cnt * sizeof(int)); for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { new->e_orig[cnt] = *argv; for (p = *argv; *p; ++p) if (p[0] == '{' && p[1] == '}') { new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN); new->e_len[cnt] = MAXPATHLEN; break; } if (!*p) { new->e_argv[cnt] = *argv; new->e_len[cnt] = 0; } } new->e_argv[cnt] = new->e_orig[cnt] = NULL; *argvp = argv + 1; return (new); } /* * -follow functions -- * * Always true, causes symbolic links to be followed on a global * basis. */ PLAN * c_follow() { ftsoptions &= ~FTS_PHYSICAL; ftsoptions |= FTS_LOGICAL; return (palloc(N_FOLLOW, f_always_true)); } /* * -fstype functions -- * * True if the file is of a certain type. */ int f_fstype(plan, entry) PLAN *plan; FTSENT *entry; { static dev_t curdev; /* need a guaranteed illegal dev value */ static int first = 1; struct statfs sb; static int val_type, val_flags; char *p, save[2]; /* Only check when we cross mount point. */ if (first || curdev != entry->fts_statp->st_dev) { curdev = entry->fts_statp->st_dev; /* * Statfs follows symlinks; find wants the link's file system, * not where it points. */ if (entry->fts_info == FTS_SL || entry->fts_info == FTS_SLNONE) { if ((p = strrchr(entry->fts_accpath, '/')) != NULL) ++p; else p = entry->fts_accpath; save[0] = p[0]; p[0] = '.'; save[1] = p[1]; p[1] = '\0'; } else p = NULL; if (statfs(entry->fts_accpath, &sb)) err(1, "%s", entry->fts_accpath); if (p) { p[0] = save[0]; p[1] = save[1]; } first = 0; /* * Further tests may need both of these values, so * always copy both of them. */ val_flags = sb.f_flags; val_type = sb.f_type; } switch (plan->flags) { case F_MTFLAG: return (val_flags & plan->mt_data) != 0; case F_MTTYPE: return (val_type == plan->mt_data); default: abort(); } } #if !defined(__NetBSD__) PLAN * c_fstype(arg) char *arg; { register PLAN *new; struct vfsconf vfc; ftsoptions &= ~FTS_NOSTAT; new = palloc(N_FSTYPE, f_fstype); /* * Check first for a filesystem name. */ if (getvfsbyname(arg, &vfc) == 0) { new->flags = F_MTTYPE; new->mt_data = vfc.vfc_typenum; return (new); } switch (*arg) { case 'l': if (!strcmp(arg, "local")) { new->flags = F_MTFLAG; new->mt_data = MNT_LOCAL; return (new); } break; case 'r': if (!strcmp(arg, "rdonly")) { new->flags = F_MTFLAG; new->mt_data = MNT_RDONLY; return (new); } break; } errx(1, "%s: unknown file type", arg); /* NOTREACHED */ } #endif /* * -group gname functions -- * * True if the file belongs to the group gname. If gname is numeric and * an equivalent of the getgrnam() function does not return a valid group * name, gname is taken as a group ID. */ int f_group(plan, entry) PLAN *plan; FTSENT *entry; { return (entry->fts_statp->st_gid == plan->g_data); } PLAN * c_group(gname) char *gname; { PLAN *new; struct group *g; gid_t gid; ftsoptions &= ~FTS_NOSTAT; g = getgrnam(gname); if (g == NULL) { gid = atoi(gname); if (gid == 0 && gname[0] != '0') errx(1, "-group: %s: no such group", gname); } else gid = g->gr_gid; new = palloc(N_GROUP, f_group); new->g_data = gid; return (new); } /* * -inum n functions -- * * True if the file has inode # n. */ int f_inum(plan, entry) PLAN *plan; FTSENT *entry; { COMPARE(entry->fts_statp->st_ino, plan->i_data); } PLAN * c_inum(arg) char *arg; { PLAN *new; ftsoptions &= ~FTS_NOSTAT; new = palloc(N_INUM, f_inum); new->i_data = find_parsenum(new, "-inum", arg, NULL); return (new); } /* * -links n functions -- * * True if the file has n links. */ int f_links(plan, entry) PLAN *plan; FTSENT *entry; { COMPARE(entry->fts_statp->st_nlink, plan->l_data); } PLAN * c_links(arg) char *arg; { PLAN *new; ftsoptions &= ~FTS_NOSTAT; new = palloc(N_LINKS, f_links); new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL); return (new); } /* * -ls functions -- * * Always true - prints the current entry to stdout in "ls" format. */ int f_ls(plan, entry) PLAN *plan; FTSENT *entry; { printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); return (1); } PLAN * c_ls() { ftsoptions &= ~FTS_NOSTAT; isoutput = 1; return (palloc(N_LS, f_ls)); } /* * -maxdepth n functions -- * * Does the same as -prune if the level of the current file is greater * than the specified maximum depth. * * Note that -maxdepth and -mindepth are handled specially in * find_execute() so their f_* functions here do nothing. */ int f_maxdepth(plan, entry) PLAN *plan; FTSENT *entry; { return (1); } PLAN * c_maxdepth(arg) char *arg; { PLAN *new; if (*arg == '-') /* all other errors handled by find_parsenum() */ errx(1, "-maxdepth: %s: value must be positive", arg); new = palloc(N_MAXDEPTH, f_maxdepth); maxdepth = find_parsenum(new, "-maxdepth", arg, NULL); return (new); } /* * -mindepth n functions -- * * True if the current file is at or deeper than the specified minimum * depth. */ int f_mindepth(plan, entry) PLAN *plan; FTSENT *entry; { return (1); } PLAN * c_mindepth(arg) char *arg; { PLAN *new; if (*arg == '-') /* all other errors handled by find_parsenum() */ errx(1, "-maxdepth: %s: value must be positive", arg); new = palloc(N_MINDEPTH, f_mindepth); mindepth = find_parsenum(new, "-mindepth", arg, NULL); return (new); } /* * -mtime n functions -- * * True if the difference between the file modification time and the * current time is n 24 hour periods. */ int f_mtime(plan, entry) PLAN *plan; FTSENT *entry; { extern time_t now; COMPARE((now - entry->fts_statp->st_mtime + 86400 - 1) / 86400, plan->t_data); } PLAN * c_mtime(arg) char *arg; { PLAN *new; ftsoptions &= ~FTS_NOSTAT; new = palloc(N_MTIME, f_mtime); new->t_data = find_parsenum(new, "-mtime", arg, NULL); TIME_CORRECT(new, N_MTIME); return (new); } /* * -mmin n functions -- * * True if the difference between the file modification time and the * current time is n min periods. */ int f_mmin(plan, entry) PLAN *plan; FTSENT *entry; { extern time_t now; COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) / 60, plan->t_data); } PLAN * c_mmin(arg) char *arg; { PLAN *new; ftsoptions &= ~FTS_NOSTAT; new = palloc(N_MMIN, f_mmin); new->t_data = find_parsenum(new, "-mmin", arg, NULL); TIME_CORRECT(new, N_MMIN); return (new); } /* * -name functions -- * * True if the basename of the filename being examined * matches pattern using Pattern Matching Notation S3.14 */ int f_name(plan, entry) PLAN *plan; FTSENT *entry; { return (!fnmatch(plan->c_data, entry->fts_name, 0)); } PLAN * c_name(pattern) char *pattern; { PLAN *new; new = palloc(N_NAME, f_name); new->c_data = pattern; return (new); } /* * -newer file functions -- * * True if the current file has been modified more recently * then the modification time of the file named by the pathname * file. */ int f_newer(plan, entry) PLAN *plan; FTSENT *entry; { return (entry->fts_statp->st_mtime > plan->t_data); } PLAN * c_newer(filename) char *filename; { PLAN *new; struct stat sb; ftsoptions &= ~FTS_NOSTAT; if (stat(filename, &sb)) err(1, "%s", filename); new = palloc(N_NEWER, f_newer); new->t_data = sb.st_mtime; return (new); } /* * -nogroup functions -- * * True if file belongs to a user ID for which the equivalent * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. */ int f_nogroup(plan, entry) PLAN *plan; FTSENT *entry; { return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1); } PLAN * c_nogroup() { ftsoptions &= ~FTS_NOSTAT; return (palloc(N_NOGROUP, f_nogroup)); } /* * -nouser functions -- * * True if file belongs to a user ID for which the equivalent * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. */ int f_nouser(plan, entry) PLAN *plan; FTSENT *entry; { return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1); } PLAN * c_nouser() { ftsoptions &= ~FTS_NOSTAT; return (palloc(N_NOUSER, f_nouser)); } /* * -path functions -- * * True if the path of the filename being examined * matches pattern using Pattern Matching Notation S3.14 */ int f_path(plan, entry) PLAN *plan; FTSENT *entry; { return (!fnmatch(plan->c_data, entry->fts_path, 0)); } PLAN * c_path(pattern) char *pattern; { PLAN *new; new = palloc(N_NAME, f_path); new->c_data = pattern; return (new); } /* * -perm functions -- * * The mode argument is used to represent file mode bits. If it starts * with a leading digit, it's treated as an octal mode, otherwise as a * symbolic mode. */ int f_perm(plan, entry) PLAN *plan; FTSENT *entry; { mode_t mode; mode = entry->fts_statp->st_mode & (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); if (plan->flags == F_ATLEAST) return ((plan->m_data | mode) == mode); else if (plan->flags == F_ANY ) return (plan->m_data & mode); else return (mode == plan->m_data); /* NOTREACHED */ } PLAN * c_perm(perm) char *perm; { PLAN *new; mode_t *set; ftsoptions &= ~FTS_NOSTAT; new = palloc(N_PERM, f_perm); if (*perm == '-') { new->flags = F_ATLEAST; ++perm; } else if (*perm == '+') { new->flags = F_ANY; ++perm; } if ((set = setmode(perm)) == NULL) errx(1, "-perm: %s: illegal mode string", perm); new->m_data = getmode(set, 0); free(set); return (new); } /* * -flags functions -- * * The flags argument is used to represent file flags bits. */ int f_flags(plan, entry) PLAN *plan; FTSENT *entry; { u_long flags; flags = entry->fts_statp->st_flags & (UF_NODUMP | UF_IMMUTABLE | UF_APPEND | UF_OPAQUE | SF_ARCHIVED | SF_IMMUTABLE | SF_APPEND); if (plan->flags == F_ATLEAST) /* note that plan->fl_flags always is a subset of plan->fl_mask */ return (flags & plan->fl_mask) == plan->fl_flags; else return flags == plan->fl_flags; /* NOTREACHED */ } PLAN * c_flags(flags_str) char *flags_str; { PLAN *new; u_long flags, notflags; ftsoptions &= ~FTS_NOSTAT; new = palloc(N_FLAGS, f_flags); if (*flags_str == '-') { new->flags = F_ATLEAST; flags_str++; } - if (setflags(&flags_str, &flags, ¬flags) == 1) + if (strtofflags(&flags_str, &flags, ¬flags) == 1) errx(1, "-flags: %s: illegal flags string", flags_str); new->fl_flags = flags; new->fl_mask = flags | notflags; #if 0 printf("flags = %08x, mask = %08x (%08x, %08x)\n", new->fl_flags, new->fl_mask, flags, notflags); #endif return new; } /* * -print functions -- * * Always true, causes the current pathame to be written to * standard output. */ int f_print(plan, entry) PLAN *plan; FTSENT *entry; { (void)puts(entry->fts_path); return (1); } PLAN * c_print() { isoutput = 1; return (palloc(N_PRINT, f_print)); } /* * -print0 functions -- * * Always true, causes the current pathame to be written to * standard output followed by a NUL character */ int f_print0(plan, entry) PLAN *plan; FTSENT *entry; { fputs(entry->fts_path, stdout); fputc('\0', stdout); return (1); } PLAN * c_print0() { isoutput = 1; return (palloc(N_PRINT0, f_print0)); } /* * -prune functions -- * * Prune a portion of the hierarchy. */ int f_prune(plan, entry) PLAN *plan; FTSENT *entry; { extern FTS *tree; if (fts_set(tree, entry, FTS_SKIP)) err(1, "%s", entry->fts_path); return (1); } PLAN * c_prune() { return (palloc(N_PRUNE, f_prune)); } /* * -size n[c] functions -- * * True if the file size in bytes, divided by an implementation defined * value and rounded up to the next integer, is n. If n is followed by * a c, the size is in bytes. */ #define FIND_SIZE 512 static int divsize = 1; int f_size(plan, entry) PLAN *plan; FTSENT *entry; { off_t size; size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / FIND_SIZE : entry->fts_statp->st_size; COMPARE(size, plan->o_data); } PLAN * c_size(arg) char *arg; { PLAN *new; char endch; ftsoptions &= ~FTS_NOSTAT; new = palloc(N_SIZE, f_size); endch = 'c'; new->o_data = find_parsenum(new, "-size", arg, &endch); if (endch == 'c') divsize = 0; return (new); } /* * -type c functions -- * * True if the type of the file is c, where c is b, c, d, p, f or w * for block special file, character special file, directory, FIFO, * regular file or whiteout respectively. */ int f_type(plan, entry) PLAN *plan; FTSENT *entry; { return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data); } PLAN * c_type(typestring) char *typestring; { PLAN *new; mode_t mask; ftsoptions &= ~FTS_NOSTAT; switch (typestring[0]) { case 'b': mask = S_IFBLK; break; case 'c': mask = S_IFCHR; break; case 'd': mask = S_IFDIR; break; case 'f': mask = S_IFREG; break; case 'l': mask = S_IFLNK; break; case 'p': mask = S_IFIFO; break; case 's': mask = S_IFSOCK; break; #ifdef FTS_WHITEOUT case 'w': mask = S_IFWHT; ftsoptions |= FTS_WHITEOUT; break; #endif /* FTS_WHITEOUT */ default: errx(1, "-type: %s: unknown type", typestring); } new = palloc(N_TYPE, f_type); new->m_data = mask; return (new); } /* * -delete functions -- * * True always. Makes it's best shot and continues on regardless. */ int f_delete(plan, entry) PLAN *plan; FTSENT *entry; { /* ignore these from fts */ if (strcmp(entry->fts_accpath, ".") == 0 || strcmp(entry->fts_accpath, "..") == 0) return (1); /* sanity check */ if (isdepth == 0 || /* depth off */ (ftsoptions & FTS_NOSTAT) || /* not stat()ing */ !(ftsoptions & FTS_PHYSICAL) || /* physical off */ (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ errx(1, "-delete: insecure options got turned on"); /* Potentially unsafe - do not accept relative paths whatsoever */ if (strchr(entry->fts_accpath, '/') != NULL) errx(1, "-delete: %s: relative path potentially not safe", entry->fts_accpath); /* Turn off user immutable bits if running as root */ if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && geteuid() == 0) chflags(entry->fts_accpath, entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); /* rmdir directories, unlink everything else */ if (S_ISDIR(entry->fts_statp->st_mode)) { if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) warn("-delete: rmdir(%s)", entry->fts_path); } else { if (unlink(entry->fts_accpath) < 0) warn("-delete: unlink(%s)", entry->fts_path); } /* "succeed" */ return (1); } PLAN * c_delete() { ftsoptions &= ~FTS_NOSTAT; /* no optimise */ ftsoptions |= FTS_PHYSICAL; /* disable -follow */ ftsoptions &= ~FTS_LOGICAL; /* disable -follow */ isoutput = 1; /* possible output */ isdepth = 1; /* -depth implied */ return (palloc(N_DELETE, f_delete)); } /* * -user uname functions -- * * True if the file belongs to the user uname. If uname is numeric and * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not * return a valid user name, uname is taken as a user ID. */ int f_user(plan, entry) PLAN *plan; FTSENT *entry; { return (entry->fts_statp->st_uid == plan->u_data); } PLAN * c_user(username) char *username; { PLAN *new; struct passwd *p; uid_t uid; ftsoptions &= ~FTS_NOSTAT; p = getpwnam(username); if (p == NULL) { uid = atoi(username); if (uid == 0 && username[0] != '0') errx(1, "-user: %s: no such user", username); } else uid = p->pw_uid; new = palloc(N_USER, f_user); new->u_data = uid; return (new); } /* * -xdev functions -- * * Always true, causes find not to decend past directories that have a * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) */ PLAN * c_xdev() { ftsoptions |= FTS_XDEV; return (palloc(N_XDEV, f_always_true)); } /* * ( expression ) functions -- * * True if expression is true. */ int f_expr(plan, entry) PLAN *plan; FTSENT *entry; { register PLAN *p; register int state; state = 0; for (p = plan->p_data[0]; p && (state = (p->eval)(p, entry)); p = p->next); return (state); } /* * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are * eliminated during phase 2 of find_formplan() --- the '(' node is converted * to a N_EXPR node containing the expression and the ')' node is discarded. */ PLAN * c_openparen() { return (palloc(N_OPENPAREN, (int (*)())-1)); } PLAN * c_closeparen() { return (palloc(N_CLOSEPAREN, (int (*)())-1)); } /* * ! expression functions -- * * Negation of a primary; the unary NOT operator. */ int f_not(plan, entry) PLAN *plan; FTSENT *entry; { register PLAN *p; register int state; state = 0; for (p = plan->p_data[0]; p && (state = (p->eval)(p, entry)); p = p->next); return (!state); } PLAN * c_not() { return (palloc(N_NOT, f_not)); } /* * expression -o expression functions -- * * Alternation of primaries; the OR operator. The second expression is * not evaluated if the first expression is true. */ int f_or(plan, entry) PLAN *plan; FTSENT *entry; { register PLAN *p; register int state; state = 0; for (p = plan->p_data[0]; p && (state = (p->eval)(p, entry)); p = p->next); if (state) return (1); for (p = plan->p_data[1]; p && (state = (p->eval)(p, entry)); p = p->next); return (state); } PLAN * c_or() { return (palloc(N_OR, f_or)); } static PLAN * palloc(t, f) enum ntype t; int (*f) __P((PLAN *, FTSENT *)); { PLAN *new; if ((new = malloc(sizeof(PLAN))) == NULL) err(1, NULL); new->type = t; new->eval = f; new->flags = 0; new->next = NULL; return (new); } Index: stable/4/usr.bin/xinstall/Makefile =================================================================== --- stable/4/usr.bin/xinstall/Makefile (revision 62193) +++ stable/4/usr.bin/xinstall/Makefile (revision 62194) @@ -1,13 +1,12 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 # $FreeBSD$ PROG= xinstall -SRCS= setflags.c xinstall.c -.PATH: ${.CURDIR}/../../lib/libc/gen +SRCS= xinstall.c MAN1= install.1 install: maninstall ${INSTALL} ${COPY} ${STRIP} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ ${PROG} ${DESTDIR}${BINDIR}/install .include Index: stable/4/usr.bin/xinstall/xinstall.c =================================================================== --- stable/4/usr.bin/xinstall/xinstall.c (revision 62193) +++ stable/4/usr.bin/xinstall/xinstall.c (revision 62194) @@ -1,738 +1,738 @@ /* * Copyright (c) 1987, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static const char copyright[] = "@(#) Copyright (c) 1987, 1993\n\ The Regents of the University of California. All rights reserved.\n"; #endif /* not lint */ #ifndef lint #if 0 static char sccsid[] = "From: @(#)xinstall.c 8.1 (Berkeley) 7/21/93"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ /*- * Todo: * o for -C, compare original files except in -s case. * o for -C, don't change anything if nothing needs be changed. In * particular, don't toggle the immutable flags just to allow null * attribute changes and don't clear the dump flag. (I think inode * ctimes are not updated for null attribute changes, but this is a * bug.) * o independent of -C, if a copy must be made, then copy to a tmpfile, * set all attributes except the immutable flags, then rename, then * set the immutable flags. It's annoying that the immutable flags * defeat the atomicicity of rename - it seems that there must be * a window where the target is not immutable. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pathnames.h" /* Bootstrap aid - this doesn't exist in most older releases */ #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1) /* from */ #endif int debug, docompare, docopy, dodir, dopreserve, dostrip, nommap, verbose; int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; char *group, *owner, pathbuf[MAXPATHLEN]; char pathbuf2[MAXPATHLEN]; #define DIRECTORY 0x01 /* Tell install it's a directory. */ #define SETFLAGS 0x02 /* Tell install to set flags. */ #define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) void copy __P((int, char *, int, char *, off_t)); int compare __P((int, const char *, int, const char *, const struct stat *, const struct stat *)); void install __P((char *, char *, u_long, u_int)); void install_dir __P((char *)); void strip __P((char *)); void usage __P((void)); int trymmap __P((int)); #define ALLOW_NUMERIC_IDS 1 #ifdef ALLOW_NUMERIC_IDS uid_t uid = -1; gid_t gid = -1; uid_t resolve_uid __P((char *)); gid_t resolve_gid __P((char *)); u_long numeric_id __P((char *, char *)); #else struct passwd *pp; struct group *gp; #endif /* ALLOW_NUMERIC_IDS */ int main(argc, argv) int argc; char *argv[]; { struct stat from_sb, to_sb; mode_t *set; u_long fset; u_int iflags; int ch, no_target; char *flags, *to_name; iflags = 0; while ((ch = getopt(argc, argv, "CcdDf:g:m:Mo:psv")) != -1) switch((char)ch) { case 'C': docompare = docopy = 1; break; case 'c': docopy = 1; break; case 'D': debug++; break; case 'd': dodir = 1; break; case 'f': flags = optarg; - if (setflags(&flags, &fset, NULL)) + if (strtofflags(&flags, &fset, NULL)) errx(EX_USAGE, "%s: invalid flag", flags); iflags |= SETFLAGS; break; case 'g': group = optarg; break; case 'm': if (!(set = setmode(optarg))) errx(EX_USAGE, "invalid file mode: %s", optarg); mode = getmode(set, 0); free(set); break; case 'M': nommap = 1; break; case 'o': owner = optarg; break; case 'p': docompare = docopy = dopreserve = 1; break; case 's': dostrip = 1; break; case 'v': verbose = 1; break; case '?': default: usage(); } argc -= optind; argv += optind; /* some options make no sense when creating directories */ if (dostrip && dodir) usage(); /* must have at least two arguments, except when creating directories */ if (argc < 2 && !dodir) usage(); #ifdef ALLOW_NUMERIC_IDS if (owner) uid = resolve_uid(owner); if (group) gid = resolve_gid(group); #else /* get group and owner id's */ if (owner && !(pp = getpwnam(owner))) errx(EX_NOUSER, "unknown user %s", owner); if (group && !(gp = getgrnam(group))) errx(EX_NOUSER, "unknown group %s", group); #endif /* ALLOW_NUMERIC_IDS */ if (dodir) { for (; *argv != NULL; ++argv) install_dir(*argv); exit(EX_OK); /* NOTREACHED */ } no_target = stat(to_name = argv[argc - 1], &to_sb); if (!no_target && (to_sb.st_mode & S_IFMT) == S_IFDIR) { for (; *argv != to_name; ++argv) install(*argv, to_name, fset, iflags | DIRECTORY); exit(EX_OK); /* NOTREACHED */ } /* can't do file1 file2 directory/file */ if (argc != 2) usage(); if (!no_target) { if (stat(*argv, &from_sb)) err(EX_OSERR, "%s", *argv); if (!S_ISREG(to_sb.st_mode)) { errno = EFTYPE; err(EX_OSERR, "%s", to_name); } if (to_sb.st_dev == from_sb.st_dev && to_sb.st_ino == from_sb.st_ino) errx(EX_USAGE, "%s and %s are the same file", *argv, to_name); /* * XXX - It's not at all clear why this code was here, since it completely * duplicates code install(). The version in install() handles the -C flag * correctly, so we'll just disable this for now. */ #if 0 /* * Unlink now... avoid ETXTBSY errors later. Try and turn * off the append/immutable bits -- if we fail, go ahead, * it might work. */ if (to_sb.st_flags & NOCHANGEBITS) (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS)); (void)unlink(to_name); #endif } install(*argv, to_name, fset, iflags); exit(EX_OK); /* NOTREACHED */ } #ifdef ALLOW_NUMERIC_IDS uid_t resolve_uid(s) char *s; { struct passwd *pw; return ((pw = getpwnam(s)) == NULL) ? (uid_t) numeric_id(s, "user") : pw->pw_uid; } gid_t resolve_gid(s) char *s; { struct group *gr; return ((gr = getgrnam(s)) == NULL) ? (gid_t) numeric_id(s, "group") : gr->gr_gid; } u_long numeric_id(name, type) char *name, *type; { u_long val; char *ep; /* * XXX * We know that uid_t's and gid_t's are unsigned longs. */ errno = 0; val = strtoul(name, &ep, 10); if (errno) err(EX_NOUSER, "%s", name); if (*ep != '\0') errx(EX_NOUSER, "unknown %s %s", type, name); return (val); } #endif /* ALLOW_NUMERIC_IDS */ /* * install -- * build a path name and install the file */ void install(from_name, to_name, fset, flags) char *from_name, *to_name; u_long fset; u_int flags; { struct stat from_sb, to_sb; int devnull, from_fd, to_fd, serrno; char *p, *old_to_name = 0; if (debug >= 2 && !docompare) fprintf(stderr, "install: invoked without -C for %s to %s\n", from_name, to_name); /* If try to install NULL file to a directory, fails. */ if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { if (stat(from_name, &from_sb)) err(EX_OSERR, "%s", from_name); if (!S_ISREG(from_sb.st_mode)) { errno = EFTYPE; err(EX_OSERR, "%s", from_name); } /* Build the target path. */ if (flags & DIRECTORY) { (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", to_name, (p = strrchr(from_name, '/')) ? ++p : from_name); to_name = pathbuf; } devnull = 0; } else { from_sb.st_flags = 0; /* XXX */ devnull = 1; } if (docompare) { old_to_name = to_name; /* * Make a new temporary file in the same file system * (actually, in in the same directory) as the target so * that the temporary file can be renamed to the target. */ snprintf(pathbuf2, sizeof pathbuf2, "%s", to_name); p = strrchr(pathbuf2, '/'); p = (p == NULL ? pathbuf2 : p + 1); snprintf(p, &pathbuf2[sizeof pathbuf2] - p, "INS@XXXX"); to_fd = mkstemp(pathbuf2); if (to_fd < 0) /* XXX should fall back to not comparing. */ err(EX_OSERR, "mkstemp: %s for %s", pathbuf2, to_name); to_name = pathbuf2; } else { /* * Unlink now... avoid errors later. Try to turn off the * append/immutable bits -- if we fail, go ahead, it might * work. */ if (stat(to_name, &to_sb) == 0 && to_sb.st_flags & NOCHANGEBITS) (void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS); unlink(to_name); /* Create target. */ to_fd = open(to_name, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR); if (to_fd < 0) err(EX_OSERR, "%s", to_name); } if (!devnull) { if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) { serrno = errno; (void)unlink(to_name); errno = serrno; err(EX_OSERR, "%s", from_name); } copy(from_fd, from_name, to_fd, to_name, from_sb.st_size); (void)close(from_fd); } if (dostrip) { (void)close(to_fd); strip(to_name); /* Reopen target. */ to_fd = open(to_name, O_RDWR, 0); if (to_fd < 0) err(EX_OSERR, "%s", to_name); } /* * Unfortunately, because we strip the installed file and not the * original one, it is impossible to do the comparison without * first laboriously copying things over and then comparing. * It may be possible to better optimize the !dostrip case, however. * For further study. */ if (docompare) { struct stat old_sb, new_sb, timestamp_sb; int old_fd; struct utimbuf utb; old_fd = open(old_to_name, O_RDONLY, 0); if (old_fd < 0 && errno == ENOENT) goto different; if (old_fd < 0) err(EX_OSERR, "%s", old_to_name); fstat(old_fd, &old_sb); if (old_sb.st_flags & NOCHANGEBITS) (void)fchflags(old_fd, old_sb.st_flags & ~NOCHANGEBITS); fstat(to_fd, &new_sb); if (compare(old_fd, old_to_name, to_fd, to_name, &old_sb, &new_sb)) { different: if (debug != 0) fprintf(stderr, "install: renaming for %s: %s to %s\n", from_name, to_name, old_to_name); if (verbose != 0) printf("install: %s -> %s\n", from_name, old_to_name); if (dopreserve && stat(from_name, ×tamp_sb) == 0) { utb.actime = from_sb.st_atime; utb.modtime = from_sb.st_mtime; (void)utime(to_name, &utb); } moveit: if (rename(to_name, old_to_name) < 0) { serrno = errno; unlink(to_name); unlink(old_to_name); errno = serrno; err(EX_OSERR, "rename: %s to %s", to_name, old_to_name); } close(old_fd); } else { if (old_sb.st_nlink != 1) { /* * Replace the target, although it hasn't * changed, to snap the extra links. But * preserve the target file times. */ if (fstat(old_fd, ×tamp_sb) == 0) { utb.actime = timestamp_sb.st_atime; utb.modtime = timestamp_sb.st_mtime; (void)utime(to_name, &utb); } goto moveit; } if (unlink(to_name) < 0) err(EX_OSERR, "unlink: %s", to_name); close(to_fd); to_fd = old_fd; } to_name = old_to_name; } /* * Set owner, group, mode for target; do the chown first, * chown may lose the setuid bits. */ if ((group || owner) && #ifdef ALLOW_NUMERIC_IDS fchown(to_fd, owner ? uid : -1, group ? gid : -1)) { #else fchown(to_fd, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1)) { #endif serrno = errno; (void)unlink(to_name); errno = serrno; err(EX_OSERR,"%s: chown/chgrp", to_name); } if (fchmod(to_fd, mode)) { serrno = errno; (void)unlink(to_name); errno = serrno; err(EX_OSERR, "%s: chmod", to_name); } /* * If provided a set of flags, set them, otherwise, preserve the * flags, except for the dump flag. * NFS does not support flags. Ignore EOPNOTSUPP flags if we're just * trying to turn off UF_NODUMP. If we're trying to set real flags, * then warn if the the fs doesn't support it, otherwise fail. */ if (fchflags(to_fd, flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { if (flags & SETFLAGS) { if (errno == EOPNOTSUPP) warn("%s: chflags", to_name); else { serrno = errno; (void)unlink(to_name); errno = serrno; err(EX_OSERR, "%s: chflags", to_name); } } } (void)close(to_fd); if (!docopy && !devnull && unlink(from_name)) err(EX_OSERR, "%s", from_name); } /* * compare -- * compare two files; non-zero means files differ */ int compare(int from_fd, const char *from_name, int to_fd, const char *to_name, const struct stat *from_sb, const struct stat *to_sb) { char *p, *q; int rv; size_t tsize; int done_compare; rv = 0; if (from_sb->st_size != to_sb->st_size) return 1; tsize = (size_t)from_sb->st_size; if (tsize <= 8 * 1024 * 1024) { done_compare = 0; if (trymmap(from_fd) && trymmap(to_fd)) { p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0); if (p == (char *)MAP_FAILED) goto out; q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0); if (q == (char *)MAP_FAILED) { munmap(p, tsize); goto out; } rv = memcmp(p, q, tsize); munmap(p, tsize); munmap(q, tsize); done_compare = 1; } out: if (!done_compare) { char buf1[MAXBSIZE]; char buf2[MAXBSIZE]; int n1, n2; rv = 0; lseek(from_fd, 0, SEEK_SET); lseek(to_fd, 0, SEEK_SET); while (rv == 0) { n1 = read(from_fd, buf1, sizeof(buf1)); if (n1 == 0) break; /* EOF */ else if (n1 > 0) { n2 = read(to_fd, buf2, n1); if (n2 == n1) rv = memcmp(buf1, buf2, n1); else rv = 1; /* out of sync */ } else rv = 1; /* read failure */ } lseek(from_fd, 0, SEEK_SET); lseek(to_fd, 0, SEEK_SET); } } else rv = 1; /* don't bother in this case */ return rv; } /* * copy -- * copy from one file to another */ void copy(from_fd, from_name, to_fd, to_name, size) register int from_fd, to_fd; char *from_name, *to_name; off_t size; { register int nr, nw; int serrno; char *p, buf[MAXBSIZE]; int done_copy; /* * Mmap and write if less than 8M (the limit is so we don't totally * trash memory on big files. This is really a minor hack, but it * wins some CPU back. */ done_copy = 0; if (size <= 8 * 1048576 && trymmap(from_fd)) { if ((p = mmap(NULL, (size_t)size, PROT_READ, MAP_SHARED, from_fd, (off_t)0)) == (char *)MAP_FAILED) goto out; if ((nw = write(to_fd, p, size)) != size) { serrno = errno; (void)unlink(to_name); errno = nw > 0 ? EIO : serrno; err(EX_OSERR, "%s", to_name); } done_copy = 1; out: } if (!done_copy) { while ((nr = read(from_fd, buf, sizeof(buf))) > 0) if ((nw = write(to_fd, buf, nr)) != nr) { serrno = errno; (void)unlink(to_name); errno = nw > 0 ? EIO : serrno; err(EX_OSERR, "%s", to_name); } if (nr != 0) { serrno = errno; (void)unlink(to_name); errno = serrno; err(EX_OSERR, "%s", from_name); } } } /* * strip -- * use strip(1) to strip the target file */ void strip(to_name) char *to_name; { int serrno, status; switch (fork()) { case -1: serrno = errno; (void)unlink(to_name); errno = serrno; err(EX_TEMPFAIL, "fork"); case 0: execlp("strip", "strip", to_name, NULL); err(EX_OSERR, "exec(strip)"); default: if (wait(&status) == -1 || status) { (void)unlink(to_name); exit(EX_SOFTWARE); /* NOTREACHED */ } } } /* * install_dir -- * build directory heirarchy */ void install_dir(path) char *path; { register char *p; struct stat sb; int ch; for (p = path;; ++p) if (!*p || (p != path && *p == '/')) { ch = *p; *p = '\0'; if (stat(path, &sb)) { if (errno != ENOENT || mkdir(path, 0755) < 0) { err(EX_OSERR, "mkdir %s", path); /* NOTREACHED */ } } else if (!S_ISDIR(sb.st_mode)) errx(EX_OSERR, "%s exists but is not a directory", path); if (!(*p = ch)) break; } if ((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid)) warn("chown %u:%u %s", uid, gid, path); if (chmod(path, mode)) warn("chmod %o %s", mode, path); } /* * usage -- * print a usage message and die */ void usage() { (void)fprintf(stderr,"\ usage: install [-CcDpsv] [-f flags] [-g group] [-m mode] [-o owner] file1 file2\n\ install [-CcDpsv] [-f flags] [-g group] [-m mode] [-o owner] file1 ...\n\ fileN directory\n\ install -d [-v] [-g group] [-m mode] [-o owner] directory ...\n"); exit(EX_USAGE); /* NOTREACHED */ } /* * trymmap -- * return true (1) if mmap should be tried, false (0) if not. */ int trymmap(fd) int fd; { /* * The ifdef is for bootstrapping - f_fstypename doesn't exist in * pre-Lite2-merge systems. */ #ifdef MFSNAMELEN struct statfs stfs; if (nommap || fstatfs(fd, &stfs) != 0) return (0); if (strcmp(stfs.f_fstypename, "ufs") == 0 || strcmp(stfs.f_fstypename, "cd9660") == 0) return (1); #endif return (0); } Index: stable/4/usr.sbin/mtree/Makefile =================================================================== --- stable/4/usr.sbin/mtree/Makefile (revision 62193) +++ stable/4/usr.sbin/mtree/Makefile (revision 62194) @@ -1,16 +1,15 @@ # From: @(#)Makefile 8.1 (Berkeley) 6/6/93 # $FreeBSD$ PROG= mtree -SRCS= compare.c crc.c create.c excludes.c misc.c mtree.c spec.c verify.c \ - setflags.c +SRCS= compare.c crc.c create.c excludes.c misc.c mtree.c spec.c verify.c MAN8= mtree.8 -.PATH: ${.CURDIR}/../../usr.bin/cksum ${.CURDIR}/../../lib/libc/gen +.PATH: ${.CURDIR}/../../usr.bin/cksum .if !defined(WORLD) DPADD+= ${LIBMD} LDADD+= -lmd CFLAGS+= -DMD5 -DSHA1 -DRMD160 .endif .include Index: stable/4/usr.sbin/mtree/compare.c =================================================================== --- stable/4/usr.sbin/mtree/compare.c (revision 62193) +++ stable/4/usr.sbin/mtree/compare.c (revision 62194) @@ -1,364 +1,369 @@ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)compare.c 8.1 (Berkeley) 6/6/93"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #ifdef MD5 #include #endif #ifdef SHA1 #include #endif #ifdef RMD160 #include #endif #include #include #include #include "mtree.h" #include "extern.h" extern int uflag; extern int lineno; static char *ftype __P((u_int)); #define INDENTNAMELEN 8 #define LABEL \ if (!label++) { \ len = printf("%s: ", RP(p)); \ if (len > INDENTNAMELEN) { \ tab = "\t"; \ (void)printf("\n"); \ } else { \ tab = ""; \ (void)printf("%*s", INDENTNAMELEN - (int)len, ""); \ } \ } int compare(name, s, p) char *name; register NODE *s; register FTSENT *p; { extern int uflag; u_long len, val; int fd, label; char *cp, *tab = ""; + char *fflags; label = 0; switch(s->type) { case F_BLOCK: if (!S_ISBLK(p->fts_statp->st_mode)) goto typeerr; break; case F_CHAR: if (!S_ISCHR(p->fts_statp->st_mode)) goto typeerr; break; case F_DIR: if (!S_ISDIR(p->fts_statp->st_mode)) goto typeerr; break; case F_FIFO: if (!S_ISFIFO(p->fts_statp->st_mode)) goto typeerr; break; case F_FILE: if (!S_ISREG(p->fts_statp->st_mode)) goto typeerr; break; case F_LINK: if (!S_ISLNK(p->fts_statp->st_mode)) goto typeerr; break; case F_SOCK: if (!S_ISSOCK(p->fts_statp->st_mode)) { typeerr: LABEL; (void)printf("\ttype (%s, %s)\n", ftype(s->type), inotype(p->fts_statp->st_mode)); } break; } /* Set the uid/gid first, then set the mode. */ if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) { LABEL; (void)printf("%suser (%lu, %lu", tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid); if (uflag) if (chown(p->fts_accpath, s->st_uid, -1)) (void)printf(", not modified: %s)\n", strerror(errno)); else (void)printf(", modified)\n"); else (void)printf(")\n"); tab = "\t"; } if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) { LABEL; (void)printf("%sgid (%lu, %lu", tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid); if (uflag) if (chown(p->fts_accpath, -1, s->st_gid)) (void)printf(", not modified: %s)\n", strerror(errno)); else (void)printf(", modified)\n"); else (void)printf(")\n"); tab = "\t"; } if (s->flags & F_MODE && s->st_mode != (p->fts_statp->st_mode & MBITS)) { LABEL; (void)printf("%spermissions (%#o, %#o", tab, s->st_mode, p->fts_statp->st_mode & MBITS); if (uflag) if (chmod(p->fts_accpath, s->st_mode)) (void)printf(", not modified: %s)\n", strerror(errno)); else (void)printf(", modified)\n"); else (void)printf(")\n"); tab = "\t"; } if (s->flags & F_NLINK && s->type != F_DIR && s->st_nlink != p->fts_statp->st_nlink) { LABEL; (void)printf("%slink count (%u, %u)\n", tab, s->st_nlink, p->fts_statp->st_nlink); tab = "\t"; } if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) { LABEL; (void)printf("%ssize (%qd, %qd)\n", tab, s->st_size, p->fts_statp->st_size); tab = "\t"; } /* * XXX * Catches nano-second differences, but doesn't display them. */ if ((s->flags & F_TIME) && ((s->st_mtimespec.tv_sec != p->fts_statp->st_mtimespec.tv_sec) || (s->st_mtimespec.tv_nsec != p->fts_statp->st_mtimespec.tv_nsec))) { LABEL; (void)printf("%smodification time (%.24s, ", tab, ctime(&s->st_mtimespec.tv_sec)); (void)printf("%.24s)\n", ctime(&p->fts_statp->st_mtimespec.tv_sec)); tab = "\t"; } if (s->flags & F_CKSUM) { if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) { LABEL; (void)printf("%scksum: %s: %s\n", tab, p->fts_accpath, strerror(errno)); tab = "\t"; } else if (crc(fd, &val, &len)) { (void)close(fd); LABEL; (void)printf("%scksum: %s: %s\n", tab, p->fts_accpath, strerror(errno)); tab = "\t"; } else { (void)close(fd); if (s->cksum != val) { LABEL; (void)printf("%scksum (%lu, %lu)\n", tab, s->cksum, val); } tab = "\t"; } } /* * XXX * since chflags(2) will reset file times, the utimes() above * may have been useless! oh well, we'd rather have correct * flags, rather than times? */ if ((s->flags & F_FLAGS) && s->st_flags != p->fts_statp->st_flags) { LABEL; - (void)printf("%sflags (\"%s\" is not ", tab, - getflags(s->st_flags, "none")); - (void)printf("\"%s\"", - getflags(p->fts_statp->st_flags, "none")); + fflags = flags_to_string(s->st_flags); + (void)printf("%sflags (\"%s\" is not ", tab, fflags); + free(fflags); + + fflags = flags_to_string(p->fts_statp->st_flags); + (void)printf("\"%s\"", fflags); + free(fflags); + if (uflag) if (chflags(p->fts_accpath, s->st_flags)) (void)printf(", not modified: %s)\n", strerror(errno)); else (void)printf(", modified)\n"); else (void)printf(")\n"); tab = "\t"; } #ifdef MD5 if (s->flags & F_MD5) { char *new_digest, buf[33]; new_digest = MD5File(p->fts_accpath, buf); if (!new_digest) { LABEL; printf("%sMD5File: %s: %s\n", tab, p->fts_accpath, strerror(errno)); tab = "\t"; } else if (strcmp(new_digest, s->md5digest)) { LABEL; printf("%sMD5 (%s, %s)\n", tab, s->md5digest, new_digest); tab = "\t"; } } #endif /* MD5 */ #ifdef SHA1 if (s->flags & F_SHA1) { char *new_digest, buf[41]; new_digest = SHA1_File(p->fts_accpath, buf); if (!new_digest) { LABEL; printf("%sSHA1_File: %s: %s\n", tab, p->fts_accpath, strerror(errno)); tab = "\t"; } else if (strcmp(new_digest, s->sha1digest)) { LABEL; printf("%sSHA-1 (%s, %s)\n", tab, s->sha1digest, new_digest); tab = "\t"; } } #endif /* SHA1 */ #ifdef RMD160 if (s->flags & F_RMD160) { char *new_digest, buf[41]; new_digest = RIPEMD160_File(p->fts_accpath, buf); if (!new_digest) { LABEL; printf("%sRIPEMD160_File: %s: %s\n", tab, p->fts_accpath, strerror(errno)); tab = "\t"; } else if (strcmp(new_digest, s->rmd160digest)) { LABEL; printf("%sRIPEMD160 (%s, %s)\n", tab, s->rmd160digest, new_digest); tab = "\t"; } } #endif /* RMD160 */ if (s->flags & F_SLINK && strcmp(cp = rlink(name), s->slink)) { LABEL; (void)printf("%slink ref (%s, %s)\n", tab, cp, s->slink); } return (label); } char * inotype(type) u_int type; { switch(type & S_IFMT) { case S_IFBLK: return ("block"); case S_IFCHR: return ("char"); case S_IFDIR: return ("dir"); case S_IFIFO: return ("fifo"); case S_IFREG: return ("file"); case S_IFLNK: return ("link"); case S_IFSOCK: return ("socket"); default: return ("unknown"); } /* NOTREACHED */ } static char * ftype(type) u_int type; { switch(type) { case F_BLOCK: return ("block"); case F_CHAR: return ("char"); case F_DIR: return ("dir"); case F_FIFO: return ("fifo"); case F_FILE: return ("file"); case F_LINK: return ("link"); case F_SOCK: return ("socket"); default: return ("unknown"); } /* NOTREACHED */ } char * rlink(name) char *name; { static char lbuf[MAXPATHLEN]; register int len; if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1) err(1, "line %d: %s", lineno, name); lbuf[len] = '\0'; return (lbuf); } Index: stable/4/usr.sbin/mtree/create.c =================================================================== --- stable/4/usr.sbin/mtree/create.c (revision 62193) +++ stable/4/usr.sbin/mtree/create.c (revision 62194) @@ -1,433 +1,439 @@ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)create.c 8.1 (Berkeley) 6/6/93"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #ifdef MD5 #include #endif #ifdef SHA1 #include #endif #ifdef RMD160 #include #endif #include #include #include #include #include #include "mtree.h" #include "extern.h" #define INDENTNAMELEN 15 #define MAXLINELEN 80 extern long int crc_total; extern int ftsoptions; extern int dflag, iflag, nflag, sflag; extern u_int keys; extern char fullpath[MAXPATHLEN]; extern int lineno; static gid_t gid; static uid_t uid; static mode_t mode; static u_long flags; static int dsort __P((const FTSENT **, const FTSENT **)); static void output __P((int, int *, const char *, ...)); static int statd __P((FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, u_long *)); static void statf __P((int, FTSENT *)); void cwalk() { register FTS *t; register FTSENT *p; time_t clock; char *argv[2], host[MAXHOSTNAMELEN]; int indent = 0; (void)time(&clock); (void)gethostname(host, sizeof(host)); (void)printf( "#\t user: %s\n#\tmachine: %s\n#\t tree: %s\n#\t date: %s", getlogin(), host, fullpath, ctime(&clock)); argv[0] = "."; argv[1] = NULL; if ((t = fts_open(argv, ftsoptions, dsort)) == NULL) err(1, "line %d: fts_open", lineno); while ((p = fts_read(t))) { if (iflag) indent = p->fts_level * 4; if (check_excludes(p->fts_name, p->fts_path)) { fts_set(t, p, FTS_SKIP); continue; } switch(p->fts_info) { case FTS_D: if (!dflag) (void)printf("\n"); if (!nflag) (void)printf("# %s\n", p->fts_path); statd(t, p, &uid, &gid, &mode, &flags); statf(indent, p); break; case FTS_DP: if (!nflag && (p->fts_level > 0)) (void)printf("%*s# %s\n", indent, "", p->fts_path); (void)printf("%*s..\n", indent, ""); if (!dflag) (void)printf("\n"); break; case FTS_DNR: case FTS_ERR: case FTS_NS: warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); break; default: if (!dflag) statf(indent, p); break; } } (void)fts_close(t); if (sflag && keys & F_CKSUM) warnx("%s checksum: %lu", fullpath, crc_total); } static void statf(indent, p) int indent; FTSENT *p; { struct group *gr; struct passwd *pw; u_long len, val; int fd, offset; + char *fflags; char *escaped_name; escaped_name = calloc(1, p->fts_namelen * 4 + 1); if (escaped_name == NULL) errx(1, "statf(): calloc() failed"); strvis(escaped_name, p->fts_name, VIS_WHITE | VIS_OCTAL); if (iflag || S_ISDIR(p->fts_statp->st_mode)) offset = printf("%*s%s", indent, "", escaped_name); else offset = printf("%*s %s", indent, "", escaped_name); free(escaped_name); if (offset > (INDENTNAMELEN + indent)) offset = MAXLINELEN; else offset += printf("%*s", (INDENTNAMELEN + indent) - offset, ""); if (!S_ISREG(p->fts_statp->st_mode) && !dflag) output(indent, &offset, "type=%s", inotype(p->fts_statp->st_mode)); if (p->fts_statp->st_uid != uid) { if (keys & F_UNAME) { if ((pw = getpwuid(p->fts_statp->st_uid)) != NULL) { output(indent, &offset, "uname=%s", pw->pw_name); } else { errx(1, "line %d: could not get uname for uid=%u", lineno, p->fts_statp->st_uid); } } if (keys & F_UID) output(indent, &offset, "uid=%u", p->fts_statp->st_uid); } if (p->fts_statp->st_gid != gid) { if (keys & F_GNAME) { if ((gr = getgrgid(p->fts_statp->st_gid)) != NULL) { output(indent, &offset, "gname=%s", gr->gr_name); } else { errx(1, "line %d: could not get gname for gid=%u", lineno, p->fts_statp->st_gid); } } if (keys & F_GID) output(indent, &offset, "gid=%u", p->fts_statp->st_gid); } if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode) output(indent, &offset, "mode=%#o", p->fts_statp->st_mode & MBITS); if (keys & F_NLINK && p->fts_statp->st_nlink != 1) output(indent, &offset, "nlink=%u", p->fts_statp->st_nlink); if (keys & F_SIZE) output(indent, &offset, "size=%qd", p->fts_statp->st_size); if (keys & F_TIME) output(indent, &offset, "time=%ld.%ld", p->fts_statp->st_mtimespec.tv_sec, p->fts_statp->st_mtimespec.tv_nsec); if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) { if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 || crc(fd, &val, &len)) err(1, "line %d: %s", lineno, p->fts_accpath); (void)close(fd); output(indent, &offset, "cksum=%lu", val); } #ifdef MD5 if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) { char *digest, buf[33]; digest = MD5File(p->fts_accpath, buf); if (!digest) { err(1, "line %d: %s", lineno, p->fts_accpath); } else { output(indent, &offset, "md5digest=%s", digest); } } #endif /* MD5 */ #ifdef SHA1 if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) { char *digest, buf[41]; digest = SHA1_File(p->fts_accpath, buf); if (!digest) { err(1, "line %d: %s", lineno, p->fts_accpath); } else { output(indent, &offset, "sha1digest=%s", digest); } } #endif /* SHA1 */ #ifdef RMD160 if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) { char *digest, buf[41]; digest = RIPEMD160_File(p->fts_accpath, buf); if (!digest) { err(1, "line %d: %s", lineno, p->fts_accpath); } else { output(indent, &offset, "ripemd160digest=%s", digest); } } #endif /* RMD160 */ if (keys & F_SLINK && (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) output(indent, &offset, "link=%s", rlink(p->fts_accpath)); - if (keys & F_FLAGS && p->fts_statp->st_flags != flags) - output(indent, &offset, "flags=%s", - getflags(p->fts_statp->st_flags, "none")); + 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(t, parent, puid, pgid, pmode, pflags) FTS *t; FTSENT *parent; uid_t *puid; gid_t *pgid; mode_t *pmode; u_long *pflags; { register FTSENT *p; register gid_t sgid; register uid_t suid; register mode_t smode; register u_long sflags; struct group *gr; struct passwd *pw; gid_t savegid = *pgid; uid_t saveuid = *puid; mode_t savemode = *pmode; u_long saveflags = 0; u_short maxgid, maxuid, maxmode, maxflags; u_short g[MAXGID], u[MAXUID], m[MAXMODE], f[MAXFLAGS]; + char *fflags; static int first = 1; if ((p = fts_children(t, 0)) == NULL) { if (errno) err(1, "line %d: %s", lineno, RP(parent)); return (1); } bzero(g, sizeof(g)); bzero(u, sizeof(u)); bzero(m, sizeof(m)); bzero(f, sizeof(f)); maxuid = maxgid = maxmode = maxflags = 0; for (; p; p = p->fts_link) { if (!dflag || (dflag && S_ISDIR(p->fts_statp->st_mode))) { smode = p->fts_statp->st_mode & MBITS; if (smode < MAXMODE && ++m[smode] > maxmode) { savemode = smode; maxmode = m[smode]; } sgid = p->fts_statp->st_gid; if (sgid < MAXGID && ++g[sgid] > maxgid) { savegid = sgid; maxgid = g[sgid]; } suid = p->fts_statp->st_uid; if (suid < MAXUID && ++u[suid] > maxuid) { saveuid = suid; maxuid = u[suid]; } /* * XXX * note that the below will break when file flags * are extended beyond the first 4 bytes of each * half word of the flags */ #define FLAGS2IDX(f) ((f & 0xf) | ((f >> 12) & 0xf0)) sflags = p->fts_statp->st_flags; if (FLAGS2IDX(sflags) < MAXFLAGS && ++f[FLAGS2IDX(sflags)] > maxflags) { saveflags = sflags; maxflags = u[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)) || (first)) { first = 0; if (dflag) (void)printf("/set type=dir"); else (void)printf("/set type=file"); if (keys & F_UNAME) { if ((pw = getpwuid(saveuid)) != NULL) (void)printf(" uname=%s", pw->pw_name); else errx(1, "line %d: could not get uname for uid=%u", lineno, saveuid); } if (keys & F_UID) (void)printf(" uid=%lu", (u_long)saveuid); if (keys & F_GNAME) { if ((gr = getgrgid(savegid)) != NULL) (void)printf(" gname=%s", gr->gr_name); else errx(1, "line %d: could not get gname for gid=%u", lineno, 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 && saveflags) - (void)printf(" flags=%s", - getflags(saveflags, "none")); + if (keys & F_FLAGS && saveflags) { + 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(a, b) const FTSENT **a, **b; { if (S_ISDIR((*a)->fts_statp->st_mode)) { if (!S_ISDIR((*b)->fts_statp->st_mode)) return (1); } else if (S_ISDIR((*b)->fts_statp->st_mode)) return (-1); return (strcmp((*a)->fts_name, (*b)->fts_name)); } #if __STDC__ #include #else #include #endif void #if __STDC__ output(int indent, int *offset, const char *fmt, ...) #else output(indent, offset, fmt, va_alist) int indent; int *offset; char *fmt; va_dcl #endif { va_list ap; char buf[1024]; #if __STDC__ va_start(ap, fmt); #else va_start(ap); #endif (void)vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); if (*offset + strlen(buf) > MAXLINELEN - 3) { (void)printf(" \\\n%*s", INDENTNAMELEN + indent, ""); *offset = INDENTNAMELEN + indent; } *offset += printf(" %s", buf) + 1; } Index: stable/4/usr.sbin/mtree/extern.h =================================================================== --- stable/4/usr.sbin/mtree/extern.h (revision 62193) +++ stable/4/usr.sbin/mtree/extern.h (revision 62194) @@ -1,51 +1,50 @@ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)extern.h 8.1 (Berkeley) 6/6/93 * $FreeBSD$ */ int compare __P((char *, NODE *, FTSENT *)); int crc __P((int, u_long *, u_long *)); void cwalk __P((void)); -char *flags_to_string __P((u_long, char *)); -int string_to_flags __P((char **, u_long *, u_long *)); +char *flags_to_string __P((u_long)); char *inotype __P((u_int)); u_int parsekey __P((char *, int *)); char *rlink __P((char *)); NODE *spec __P((void)); int verify __P((void)); int check_excludes __P((const char *, const char *)); void init_excludes __P((void)); void read_excludes_file __P((const char *)); Index: stable/4/usr.sbin/mtree/misc.c =================================================================== --- stable/4/usr.sbin/mtree/misc.c (revision 62193) +++ stable/4/usr.sbin/mtree/misc.c (revision 62194) @@ -1,111 +1,129 @@ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /*not lint */ #include #include #include #include #include +#include #include "mtree.h" #include "extern.h" extern int lineno; typedef struct _key { char *name; /* key name */ u_int val; /* value */ #define NEEDVALUE 0x01 u_int flags; } KEY; /* NB: the following table must be sorted lexically. */ static KEY keylist[] = { {"cksum", F_CKSUM, NEEDVALUE}, {"flags", F_FLAGS, NEEDVALUE}, {"gid", F_GID, NEEDVALUE}, {"gname", F_GNAME, NEEDVALUE}, {"ignore", F_IGN, 0}, {"link", F_SLINK, NEEDVALUE}, #ifdef MD5 {"md5digest", F_MD5, NEEDVALUE}, #endif {"mode", F_MODE, NEEDVALUE}, {"nlink", F_NLINK, NEEDVALUE}, {"nochange", F_NOCHANGE, 0}, #ifdef RMD160 {"ripemd160digest", F_RMD160, NEEDVALUE}, #endif #ifdef SHA1 {"sha1digest", F_SHA1, NEEDVALUE}, #endif {"size", F_SIZE, NEEDVALUE}, {"time", F_TIME, NEEDVALUE}, {"type", F_TYPE, NEEDVALUE}, {"uid", F_UID, NEEDVALUE}, {"uname", F_UNAME, NEEDVALUE}, }; u_int parsekey(name, needvaluep) char *name; int *needvaluep; { KEY *k, tmp; int keycompare __P((const void *, const void *)); tmp.name = name; k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY), sizeof(KEY), keycompare); if (k == NULL) errx(1, "line %d: unknown keyword %s", lineno, name); if (needvaluep) *needvaluep = k->flags & NEEDVALUE ? 1 : 0; return (k->val); } int keycompare(a, b) const void *a, *b; { return (strcmp(((KEY *)a)->name, ((KEY *)b)->name)); +} + +char * +flags_to_string(fflags) + u_long fflags; +{ + char *string; + + string = fflagstostr(fflags); + if (string != NULL && *string == '\0') { + free(string); + string = strdup("none"); + } + if (string == NULL) + err(1, NULL); + + return string; } Index: stable/4/usr.sbin/mtree/spec.c =================================================================== --- stable/4/usr.sbin/mtree/spec.c (revision 62193) +++ stable/4/usr.sbin/mtree/spec.c (revision 62194) @@ -1,323 +1,323 @@ /*- * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint #if 0 static char sccsid[] = "@(#)spec.c 8.1 (Berkeley) 6/6/93"; #endif static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include "mtree.h" #include "extern.h" int lineno; /* Current spec line number. */ static void set __P((char *, NODE *)); static void unset __P((char *, NODE *)); NODE * spec() { register NODE *centry, *last; register char *p; NODE ginfo, *root; int c_cur, c_next; char buf[2048]; centry = last = root = NULL; bzero(&ginfo, sizeof(ginfo)); c_cur = c_next = 0; for (lineno = 1; fgets(buf, sizeof(buf), stdin); ++lineno, c_cur = c_next, c_next = 0) { /* Skip empty lines. */ if (buf[0] == '\n') continue; /* Find end of line. */ if ((p = index(buf, '\n')) == NULL) errx(1, "line %d too long", lineno); /* See if next line is continuation line. */ if (p[-1] == '\\') { --p; c_next = 1; } /* Null-terminate the line. */ *p = '\0'; /* Skip leading whitespace. */ for (p = buf; *p && isspace(*p); ++p); /* If nothing but whitespace or comment char, continue. */ if (!*p || *p == '#') continue; #ifdef DEBUG (void)fprintf(stderr, "line %d: {%s}\n", lineno, p); #endif if (c_cur) { set(p, centry); continue; } /* Grab file name, "$", "set", or "unset". */ if ((p = strtok(p, "\n\t ")) == NULL) errx(1, "line %d: missing field", lineno); if (p[0] == '/') switch(p[1]) { case 's': if (strcmp(p + 1, "set")) break; set(NULL, &ginfo); continue; case 'u': if (strcmp(p + 1, "unset")) break; unset(NULL, &ginfo); continue; } if (index(p, '/')) errx(1, "line %d: slash character in file name", lineno); if (!strcmp(p, "..")) { /* Don't go up, if haven't gone down. */ if (!root) goto noparent; if (last->type != F_DIR || last->flags & F_DONE) { if (last == root) goto noparent; last = last->parent; } last->flags |= F_DONE; continue; noparent: errx(1, "line %d: no parent node", lineno); } if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) errx(1, "calloc"); *centry = ginfo; #define MAGIC "?*[" if (strpbrk(p, MAGIC)) centry->flags |= F_MAGIC; if (strunvis(centry->name, p) == -1) { warnx("filename %s is ill-encoded and literally used", p); strcpy(centry->name, p); } set(NULL, centry); if (!root) { last = root = centry; root->parent = root; } else if (last->type == F_DIR && !(last->flags & F_DONE)) { centry->parent = last; last = last->child = centry; } else { centry->parent = last->parent; centry->prev = last; last = last->next = centry; } } return (root); } static void set(t, ip) char *t; NODE *ip; { register int type; char *kw, *val = NULL; struct group *gr; struct passwd *pw; mode_t *m; int value; char *ep; for (; (kw = strtok(t, "= \t\n")); t = NULL) { ip->flags |= type = parsekey(kw, &value); if (value && (val = strtok(NULL, " \t\n")) == NULL) errx(1, "line %d: missing value", lineno); switch(type) { case F_CKSUM: ip->cksum = strtoul(val, &ep, 10); if (*ep) errx(1, "line %d: invalid checksum %s", lineno, val); break; case F_MD5: ip->md5digest = strdup(val); if(!ip->md5digest) { errx(1, "strdup"); } break; case F_SHA1: ip->sha1digest = strdup(val); if(!ip->sha1digest) { errx(1, "strdup"); } break; case F_RMD160: ip->rmd160digest = strdup(val); if(!ip->rmd160digest) { errx(1, "strdup"); } break; case F_FLAGS: if (strcmp("none", val) == 0) ip->st_flags = 0; - else if (setflags(&val, &ip->st_flags, NULL) != 0) + else if (strtofflags(&val, &ip->st_flags, NULL) != 0) errx(1, "line %d: invalid flag %s",lineno, val); break; case F_GID: ip->st_gid = strtoul(val, &ep, 10); if (*ep) errx(1, "line %d: invalid gid %s", lineno, val); break; case F_GNAME: if ((gr = getgrnam(val)) == NULL) errx(1, "line %d: unknown group %s", lineno, val); ip->st_gid = gr->gr_gid; break; case F_IGN: /* just set flag bit */ break; case F_MODE: if ((m = setmode(val)) == NULL) errx(1, "line %d: invalid file mode %s", lineno, val); ip->st_mode = getmode(m, 0); free(m); break; case F_NLINK: ip->st_nlink = strtoul(val, &ep, 10); if (*ep) errx(1, "line %d: invalid link count %s", lineno, val); break; case F_SIZE: ip->st_size = strtoq(val, &ep, 10); if (*ep) errx(1, "line %d: invalid size %s", lineno, val); break; case F_SLINK: if ((ip->slink = strdup(val)) == NULL) errx(1, "strdup"); break; case F_TIME: ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10); if (*ep != '.') errx(1, "line %d: invalid time %s", lineno, val); val = ep + 1; ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10); if (*ep) errx(1, "line %d: invalid time %s", lineno, val); break; case F_TYPE: switch(*val) { case 'b': if (!strcmp(val, "block")) ip->type = F_BLOCK; break; case 'c': if (!strcmp(val, "char")) ip->type = F_CHAR; break; case 'd': if (!strcmp(val, "dir")) ip->type = F_DIR; break; case 'f': if (!strcmp(val, "file")) ip->type = F_FILE; if (!strcmp(val, "fifo")) ip->type = F_FIFO; break; case 'l': if (!strcmp(val, "link")) ip->type = F_LINK; break; case 's': if (!strcmp(val, "socket")) ip->type = F_SOCK; break; default: errx(1, "line %d: unknown file type %s", lineno, val); } break; case F_UID: ip->st_uid = strtoul(val, &ep, 10); if (*ep) errx(1, "line %d: invalid uid %s", lineno, val); break; case F_UNAME: if ((pw = getpwnam(val)) == NULL) errx(1, "line %d: unknown user %s", lineno, val); ip->st_uid = pw->pw_uid; break; } } } static void unset(t, ip) char *t; register NODE *ip; { register char *p; while ((p = strtok(t, "\n\t "))) ip->flags &= ~parsekey(p, NULL); }