Annotation of src/usr.bin/find/function.c, Revision 1.53
1.53 ! guenther 1: /* $OpenBSD: function.c,v 1.52 2023/03/07 17:43:59 guenther Exp $ */
1.4 deraadt 2:
1.1 deraadt 3: /*-
4: * Copyright (c) 1990, 1993
5: * The Regents of the University of California. All rights reserved.
6: *
7: * This code is derived from software contributed to Berkeley by
8: * Cimarron D. Taylor of the University of California, Berkeley.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
1.26 millert 18: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 19: * may be used to endorse or promote products derived from this software
20: * without specific prior written permission.
21: *
22: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32: * SUCH DAMAGE.
33: */
34:
35: #include <sys/stat.h>
36: #include <sys/wait.h>
37: #include <sys/mount.h>
38:
1.7 tholo 39: #include <dirent.h>
1.1 deraadt 40: #include <err.h>
41: #include <errno.h>
1.15 millert 42: #include <fcntl.h>
1.1 deraadt 43: #include <fnmatch.h>
44: #include <fts.h>
45: #include <grp.h>
1.10 millert 46: #include <libgen.h>
1.37 pascal 47: #include <limits.h>
1.1 deraadt 48: #include <pwd.h>
49: #include <stdio.h>
50: #include <stdlib.h>
51: #include <string.h>
52: #include <unistd.h>
53:
54: #include "find.h"
1.28 deraadt 55: #include "extern.h"
1.1 deraadt 56:
57: #define COMPARE(a, b) { \
58: switch (plan->flags) { \
59: case F_EQUAL: \
60: return (a == b); \
61: case F_LESSTHAN: \
62: return (a < b); \
63: case F_GREATER: \
64: return (a > b); \
65: default: \
66: abort(); \
67: } \
68: }
69:
1.23 millert 70: static PLAN *palloc(enum ntype, int (*)(PLAN *, FTSENT *));
1.39 deraadt 71: static long long find_parsenum(PLAN *plan, char *option, char *vp, char *endch);
1.37 pascal 72: static void run_f_exec(PLAN *plan);
1.28 deraadt 73: static PLAN *palloc(enum ntype t, int (*f)(PLAN *, FTSENT *));
74:
75: int f_amin(PLAN *, FTSENT *);
76: int f_atime(PLAN *, FTSENT *);
77: int f_cmin(PLAN *, FTSENT *);
78: int f_ctime(PLAN *, FTSENT *);
79: int f_always_true(PLAN *, FTSENT *);
80: int f_empty(PLAN *, FTSENT *);
81: int f_exec(PLAN *, FTSENT *);
82: int f_execdir(PLAN *, FTSENT *);
83: int f_flags(PLAN *, FTSENT *);
84: int f_fstype(PLAN *, FTSENT *);
85: int f_group(PLAN *, FTSENT *);
86: int f_inum(PLAN *, FTSENT *);
87: int f_empty(PLAN *, FTSENT *);
88: int f_links(PLAN *, FTSENT *);
89: int f_ls(PLAN *, FTSENT *);
90: int f_maxdepth(PLAN *, FTSENT *);
91: int f_mindepth(PLAN *, FTSENT *);
92: int f_mtime(PLAN *, FTSENT *);
93: int f_mmin(PLAN *, FTSENT *);
94: int f_name(PLAN *, FTSENT *);
95: int f_iname(PLAN *, FTSENT *);
96: int f_newer(PLAN *, FTSENT *);
97: int f_anewer(PLAN *, FTSENT *);
98: int f_cnewer(PLAN *, FTSENT *);
99: int f_nogroup(PLAN *, FTSENT *);
100: int f_nouser(PLAN *, FTSENT *);
101: int f_path(PLAN *, FTSENT *);
102: int f_perm(PLAN *, FTSENT *);
103: int f_print(PLAN *, FTSENT *);
104: int f_print0(PLAN *, FTSENT *);
105: int f_prune(PLAN *, FTSENT *);
106: int f_size(PLAN *, FTSENT *);
107: int f_type(PLAN *, FTSENT *);
108: int f_user(PLAN *, FTSENT *);
109: int f_expr(PLAN *, FTSENT *);
110: int f_not(PLAN *, FTSENT *);
111: int f_or(PLAN *, FTSENT *);
1.1 deraadt 112:
1.9 millert 113: extern int dotfd;
114: extern time_t now;
115: extern FTS *tree;
116:
1.1 deraadt 117: /*
118: * find_parsenum --
119: * Parse a string of the form [+-]# and return the value.
120: */
1.39 deraadt 121: static long long
1.30 deraadt 122: find_parsenum(PLAN *plan, char *option, char *vp, char *endch)
1.1 deraadt 123: {
1.39 deraadt 124: long long value;
1.1 deraadt 125: char *endchar, *str; /* Pointer to character ending conversion. */
126:
127: /* Determine comparison from leading + or -. */
128: str = vp;
129: switch (*str) {
130: case '+':
131: ++str;
132: plan->flags = F_GREATER;
133: break;
134: case '-':
135: ++str;
136: plan->flags = F_LESSTHAN;
137: break;
138: default:
139: plan->flags = F_EQUAL;
140: break;
141: }
142:
143: /*
1.40 deraadt 144: * Convert the string with strtoll(). Note, if strtoll() returns
145: * zero and endchar points to the beginning of the string we know
146: * we have a syntax error.
1.1 deraadt 147: */
1.39 deraadt 148: value = strtoll(str, &endchar, 10);
1.1 deraadt 149: if (value == 0 && endchar == str)
150: errx(1, "%s: %s: illegal numeric value", option, vp);
151: if (endchar[0] && (endch == NULL || endchar[0] != *endch))
152: errx(1, "%s: %s: illegal trailing character", option, vp);
153: if (endch)
154: *endch = endchar[0];
155: return (value);
156: }
157:
158: /*
159: * The value of n for the inode times (atime, ctime, and mtime) is a range,
160: * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with
161: * -n, such that "-mtime -1" would be less than 0 days, which isn't what the
162: * user wanted. Correct so that -1 is "less than 1".
163: */
164: #define TIME_CORRECT(p, ttype) \
165: if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \
1.17 millert 166: ++((p)->sec_data);
1.1 deraadt 167:
168: /*
1.11 deraadt 169: * -amin n functions --
170: *
171: * True if the difference between the file access time and the
172: * current time is n min periods.
173: */
174: int
1.30 deraadt 175: f_amin(PLAN *plan, FTSENT *entry)
1.11 deraadt 176: {
177: extern time_t now;
178:
179: COMPARE((now - entry->fts_statp->st_atime +
1.17 millert 180: 60 - 1) / 60, plan->sec_data);
1.11 deraadt 181: }
182:
183: PLAN *
1.28 deraadt 184: c_amin(char *arg, char ***ignored, int unused)
1.11 deraadt 185: {
186: PLAN *new;
187:
188: ftsoptions &= ~FTS_NOSTAT;
189:
190: new = palloc(N_AMIN, f_amin);
1.17 millert 191: new->sec_data = find_parsenum(new, "-amin", arg, NULL);
1.11 deraadt 192: TIME_CORRECT(new, N_AMIN);
193: return (new);
194: }
195:
196: /*
1.1 deraadt 197: * -atime n functions --
198: *
199: * True if the difference between the file access time and the
200: * current time is n 24 hour periods.
201: */
202: int
1.30 deraadt 203: f_atime(PLAN *plan, FTSENT *entry)
1.1 deraadt 204: {
205:
206: COMPARE((now - entry->fts_statp->st_atime +
1.17 millert 207: SECSPERDAY - 1) / SECSPERDAY, plan->sec_data);
1.1 deraadt 208: }
209:
210: PLAN *
1.28 deraadt 211: c_atime(char *arg, char ***ignored, int unused)
1.1 deraadt 212: {
213: PLAN *new;
214:
215: ftsoptions &= ~FTS_NOSTAT;
216:
217: new = palloc(N_ATIME, f_atime);
1.17 millert 218: new->sec_data = find_parsenum(new, "-atime", arg, NULL);
1.1 deraadt 219: TIME_CORRECT(new, N_ATIME);
220: return (new);
221: }
1.11 deraadt 222:
223: /*
224: * -cmin n functions --
225: *
226: * True if the difference between the last change of file
227: * status information and the current time is n min periods.
228: */
229: int
1.30 deraadt 230: f_cmin(PLAN *plan, FTSENT *entry)
1.11 deraadt 231: {
232: extern time_t now;
233:
234: COMPARE((now - entry->fts_statp->st_ctime +
1.17 millert 235: 60 - 1) / 60, plan->sec_data);
1.11 deraadt 236: }
237:
238: PLAN *
1.28 deraadt 239: c_cmin(char *arg, char ***ignored, int unused)
1.11 deraadt 240: {
241: PLAN *new;
242:
243: ftsoptions &= ~FTS_NOSTAT;
244:
245: new = palloc(N_CMIN, f_cmin);
1.17 millert 246: new->sec_data = find_parsenum(new, "-cmin", arg, NULL);
1.11 deraadt 247: TIME_CORRECT(new, N_CMIN);
248: return (new);
249: }
250:
1.1 deraadt 251: /*
252: * -ctime n functions --
253: *
254: * True if the difference between the last change of file
255: * status information and the current time is n 24 hour periods.
256: */
257: int
1.30 deraadt 258: f_ctime(PLAN *plan, FTSENT *entry)
1.1 deraadt 259: {
260:
261: COMPARE((now - entry->fts_statp->st_ctime +
1.17 millert 262: SECSPERDAY - 1) / SECSPERDAY, plan->sec_data);
1.1 deraadt 263: }
264:
265: PLAN *
1.28 deraadt 266: c_ctime(char *arg, char ***ignored, int unused)
1.1 deraadt 267: {
268: PLAN *new;
269:
270: ftsoptions &= ~FTS_NOSTAT;
271:
272: new = palloc(N_CTIME, f_ctime);
1.17 millert 273: new->sec_data = find_parsenum(new, "-ctime", arg, NULL);
1.1 deraadt 274: TIME_CORRECT(new, N_CTIME);
275: return (new);
276: }
277:
278: /*
279: * -depth functions --
280: *
281: * Always true, causes descent of the directory hierarchy to be done
282: * so that all entries in a directory are acted on before the directory
283: * itself.
284: */
285: int
1.30 deraadt 286: f_always_true(PLAN *plan, FTSENT *entry)
1.1 deraadt 287: {
288: return (1);
289: }
290:
291: PLAN *
1.28 deraadt 292: c_depth(char *ignore, char ***ignored, int unused)
1.1 deraadt 293: {
294: isdepth = 1;
295:
296: return (palloc(N_DEPTH, f_always_true));
1.45 tedu 297: }
298:
299: /*
300: * -delete functions
301: */
302: int
303: f_delete(PLAN *plan, FTSENT *entry)
304: {
305:
306: /* can't delete these */
307: if (strcmp(entry->fts_accpath, ".") == 0 ||
308: strcmp(entry->fts_accpath, "..") == 0)
309: return 1;
310:
311: /* sanity check */
312: if (isdepth == 0 || /* depth off */
313: (ftsoptions & FTS_NOSTAT)) /* not stat()ing */
314: errx(1, "-delete: insecure options got turned on");
315: if (!(ftsoptions & FTS_PHYSICAL) || /* physical off */
316: (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */
317: errx(1, "-delete: forbidden when symlinks are followed");
318:
319: /* Potentially unsafe - do not accept relative paths whatsoever */
320: if (entry->fts_level > FTS_ROOTLEVEL &&
321: strchr(entry->fts_accpath, '/') != NULL)
322: errx(1, "-delete: %s: relative path potentially not safe",
323: entry->fts_accpath);
324: #if 0
325: /* Turn off user immutable bits if running as root */
326: if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
327: !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
328: geteuid() == 0)
329: lchflags(entry->fts_accpath,
330: entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
331: #endif
332: /* rmdir directories, unlink everything else */
333: if (S_ISDIR(entry->fts_statp->st_mode)) {
1.47 deraadt 334: if (rmdir(entry->fts_accpath) == -1 && errno != ENOTEMPTY)
1.45 tedu 335: warn("-delete: rmdir(%s)", entry->fts_path);
336: } else {
1.47 deraadt 337: if (unlink(entry->fts_accpath) == -1)
1.45 tedu 338: warn("-delete: unlink(%s)", entry->fts_path);
339:
340: }
341:
342: return 1;
343: }
344:
345: PLAN *
346: c_delete(char *ignore, char ***ignored, int unused)
347: {
348: ftsoptions &= ~FTS_NOSTAT;
349: isoutput = 1;
350: isdelete = 1;
351: isdepth = 1;
352:
353: return (palloc(N_DELETE, f_delete));
1.1 deraadt 354: }
355:
1.7 tholo 356: /*
357: * -empty functions --
358: *
359: * True if the file or directory is empty
360: */
361: int
1.30 deraadt 362: f_empty(PLAN *plan, FTSENT *entry)
1.7 tholo 363: {
364: if (S_ISREG(entry->fts_statp->st_mode) && entry->fts_statp->st_size == 0)
365: return (1);
366: if (S_ISDIR(entry->fts_statp->st_mode)) {
367: struct dirent *dp;
368: int empty;
369: DIR *dir;
370:
371: empty = 1;
372: dir = opendir(entry->fts_accpath);
373: if (dir == NULL)
1.36 millert 374: return (0);
1.7 tholo 375: for (dp = readdir(dir); dp; dp = readdir(dir))
376: if (dp->d_name[0] != '.' ||
377: (dp->d_name[1] != '\0' &&
378: (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) {
379: empty = 0;
380: break;
381: }
382: closedir(dir);
383: return (empty);
384: }
385: return (0);
386: }
387:
388: PLAN *
1.28 deraadt 389: c_empty(char *ignore, char ***ignored, int unused)
1.7 tholo 390: {
391: ftsoptions &= ~FTS_NOSTAT;
392:
393: return (palloc(N_EMPTY, f_empty));
394: }
395:
1.1 deraadt 396: /*
397: * [-exec | -ok] utility [arg ... ] ; functions --
1.37 pascal 398: * [-exec | -ok] utility [arg ... ] {} + functions --
1.1 deraadt 399: *
1.37 pascal 400: * If the end of the primary expression is delimited by a
401: * semicolon: true if the executed utility returns a zero value
402: * as exit status. If "{}" occurs anywhere, it gets replaced by
403: * the current pathname.
404: *
405: * If the end of the primary expression is delimited by a plus
406: * sign: always true. Pathnames for which the primary is
407: * evaluated shall be aggregated into sets. The utility will be
408: * executed once per set, with "{}" replaced by the entire set of
409: * pathnames (as if xargs). "{}" must appear last.
410: *
411: * The current directory for the execution of utility is the same
412: * as the current directory when the find utility was started.
1.1 deraadt 413: *
1.37 pascal 414: * The primary -ok is different in that it requests affirmation
415: * of the user before executing the utility.
1.1 deraadt 416: */
417: int
1.30 deraadt 418: f_exec(PLAN *plan, FTSENT *entry)
1.1 deraadt 419: {
1.37 pascal 420: int cnt, l;
1.1 deraadt 421: pid_t pid;
422: int status;
423:
1.37 pascal 424: if (plan->flags & F_PLUSSET) {
425: /*
426: * Confirm sufficient buffer space, then copy the path
427: * to the buffer.
428: */
429: l = strlen(entry->fts_path);
430: if (plan->ep_p + l < plan->ep_ebp) {
431: plan->ep_bxp[plan->ep_narg++] = plan->ep_p;
432: strlcpy(plan->ep_p, entry->fts_path, l + 1);
433: plan->ep_p += l + 1;
434:
435: if (plan->ep_narg == plan->ep_maxargs)
436: run_f_exec(plan);
437: } else {
438: /*
439: * Without sufficient space to copy in the next
440: * argument, run the command to empty out the
441: * buffer before re-attepting the copy.
442: */
443: run_f_exec(plan);
444: if (plan->ep_p + l < plan->ep_ebp) {
445: plan->ep_bxp[plan->ep_narg++] = plan->ep_p;
446: strlcpy(plan->ep_p, entry->fts_path, l + 1);
447: plan->ep_p += l + 1;
448: } else
449: errx(1, "insufficient space for argument");
450: }
451: return (1);
452: } else {
453: for (cnt = 0; plan->e_argv[cnt]; ++cnt)
454: if (plan->e_len[cnt])
455: brace_subst(plan->e_orig[cnt],
456: &plan->e_argv[cnt],
457: entry->fts_path,
458: plan->e_len[cnt]);
459: if (plan->flags & F_NEEDOK && !queryuser(plan->e_argv))
460: return (0);
461:
462: /* don't mix output of command with find output */
463: fflush(stdout);
464: fflush(stderr);
465:
466: switch (pid = vfork()) {
467: case -1:
468: err(1, "fork");
469: /* NOTREACHED */
470: case 0:
471: if (fchdir(dotfd)) {
472: warn("chdir");
473: _exit(1);
474: }
475: execvp(plan->e_argv[0], plan->e_argv);
476: warn("%s", plan->e_argv[0]);
477: _exit(1);
478: }
479: pid = waitpid(pid, &status, 0);
480: return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
481: }
482: }
483:
484: static void
485: run_f_exec(PLAN *plan)
486: {
487: pid_t pid;
488: int rval, status;
1.1 deraadt 489:
1.37 pascal 490: /* Ensure arg list is null terminated. */
491: plan->ep_bxp[plan->ep_narg] = NULL;
1.1 deraadt 492:
1.37 pascal 493: /* Don't mix output of command with find output. */
494: fflush(stdout);
495: fflush(stderr);
1.1 deraadt 496:
497: switch (pid = vfork()) {
498: case -1:
1.37 pascal 499: err(1, "vfork");
1.1 deraadt 500: /* NOTREACHED */
501: case 0:
502: if (fchdir(dotfd)) {
503: warn("chdir");
504: _exit(1);
505: }
506: execvp(plan->e_argv[0], plan->e_argv);
507: warn("%s", plan->e_argv[0]);
508: _exit(1);
509: }
1.37 pascal 510:
511: /* Clear out the argument list. */
512: plan->ep_narg = 0;
513: plan->ep_bxp[plan->ep_narg] = NULL;
514: /* As well as the argument buffer. */
515: plan->ep_p = plan->ep_bbp;
516: *plan->ep_p = '\0';
517:
1.1 deraadt 518: pid = waitpid(pid, &status, 0);
1.37 pascal 519: if (WIFEXITED(status))
520: rval = WEXITSTATUS(status);
521: else
522: rval = -1;
523:
524: /*
525: * If we have a non-zero exit status, preserve it so find(1) can
526: * later exit with it.
527: */
528: if (rval)
529: plan->ep_rval = rval;
1.1 deraadt 530: }
531:
532: /*
533: * c_exec --
534: * build three parallel arrays, one with pointers to the strings passed
535: * on the command line, one with (possibly duplicated) pointers to the
536: * argv array, and one with integer values that are lengths of the
537: * strings, but also flags meaning that the string has to be massaged.
1.37 pascal 538: *
539: * If -exec ... {} +, use only the first array, but make it large
540: * enough to hold 5000 args (cf. src/usr.bin/xargs/xargs.c for a
1.48 jca 541: * discussion), and then allocate space for args.
1.1 deraadt 542: */
543: PLAN *
1.28 deraadt 544: c_exec(char *unused, char ***argvp, int isok)
1.1 deraadt 545: {
546: PLAN *new; /* node returned */
1.37 pascal 547: int cnt, brace, lastbrace;
1.21 mpech 548: char **argv, **ap, *p;
1.35 millert 549:
550: /* make sure the current directory is readable */
551: if (dotfd == -1)
552: errx(1, "%s: cannot open \".\"", isok ? "-ok" : "-exec");
1.1 deraadt 553:
554: isoutput = 1;
555:
556: new = palloc(N_EXEC, f_exec);
557: if (isok)
1.37 pascal 558: new->flags |= F_NEEDOK;
1.1 deraadt 559:
1.37 pascal 560: /*
1.38 sthen 561: * Terminate if we encounter an arg exactly equal to ";", or an
562: * arg exactly equal to "+" following an arg exactly equal to
1.37 pascal 563: * "{}".
564: */
565: for (ap = argv = *argvp, brace = 0;; ++ap) {
1.1 deraadt 566: if (!*ap)
1.50 halex 567: errx(1, "%s: no terminating \";\" or \"{} +\"",
1.37 pascal 568: isok ? "-ok" : "-exec");
569: lastbrace = brace;
570: brace = 0;
571: if (strcmp(*ap, "{}") == 0)
572: brace = 1;
573: if (strcmp(*ap, ";") == 0)
574: break;
575: if (strcmp(*ap, "+") == 0 && lastbrace) {
576: new->flags |= F_PLUSSET;
1.8 millert 577: break;
1.37 pascal 578: }
1.8 millert 579: }
580:
581:
1.37 pascal 582: /*
583: * POSIX says -ok ... {} + "need not be supported," and it does
584: * not make much sense anyway.
585: */
586: if (new->flags & F_NEEDOK && new->flags & F_PLUSSET)
587: errx(1, "-ok: terminating \"+\" not permitted.");
588:
589: if (new->flags & F_PLUSSET) {
1.48 jca 590: long arg_max;
1.49 jca 591: extern char **environ;
592: char **ep;
1.37 pascal 593: u_int c, bufsize;
594:
595: cnt = ap - *argvp - 1; /* units are words */
596: new->ep_maxargs = 5000;
1.41 espie 597: new->e_argv = ereallocarray(NULL,
598: (size_t)(cnt + new->ep_maxargs), sizeof(char **));
1.37 pascal 599:
600: /* We start stuffing arguments after the user's last one. */
601: new->ep_bxp = &new->e_argv[cnt];
602: new->ep_narg = 0;
603:
604: /*
1.48 jca 605: * Compute the maximum space we can use for arguments
606: * passed to execve(2).
607: */
608: arg_max = sysconf(_SC_ARG_MAX);
609: if (arg_max == -1)
1.49 jca 610: err(1, "-exec: sysconf(_SC_ARG_MAX) failed");
611: for (ep = environ; *ep != NULL; ep++) {
612: /* 1 byte for each '\0' */
613: arg_max -= strlen(*ep) + 1 + sizeof(*ep);
614: }
1.48 jca 615:
616: /*
1.37 pascal 617: * Count up the space of the user's arguments, and
618: * subtract that from what we allocate.
619: */
620: for (argv = *argvp, c = 0, cnt = 0;
621: argv < ap;
622: ++argv, ++cnt) {
623: c += strlen(*argv) + 1;
624: new->e_argv[cnt] = *argv;
625: }
1.49 jca 626: if (arg_max < 4 * 1024 + c)
627: errx(1, "-exec: no space left to run child command");
1.48 jca 628: bufsize = arg_max - 4 * 1024 - c;
1.37 pascal 629:
630: /*
631: * Allocate, and then initialize current, base, and
632: * end pointers.
633: */
634: new->ep_p = new->ep_bbp = malloc(bufsize + 1);
635: new->ep_ebp = new->ep_bbp + bufsize - 1;
636: new->ep_rval = 0;
637: } else { /* !F_PLUSSET */
638: cnt = ap - *argvp + 1;
1.41 espie 639: new->e_argv = ereallocarray(NULL, cnt, sizeof(char *));
640: new->e_orig = ereallocarray(NULL, cnt, sizeof(char *));
641: new->e_len = ereallocarray(NULL, cnt, sizeof(int));
1.37 pascal 642:
643: for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
644: new->e_orig[cnt] = *argv;
645: for (p = *argv; *p; ++p)
646: if (p[0] == '{' && p[1] == '}') {
647: new->e_argv[cnt] =
1.42 deraadt 648: emalloc((u_int)PATH_MAX);
649: new->e_len[cnt] = PATH_MAX;
1.37 pascal 650: break;
651: }
652: if (!*p) {
653: new->e_argv[cnt] = *argv;
654: new->e_len[cnt] = 0;
1.8 millert 655: }
656: }
1.37 pascal 657: new->e_orig[cnt] = NULL;
658: }
1.8 millert 659:
1.37 pascal 660: new->e_argv[cnt] = NULL;
1.8 millert 661: *argvp = argv + 1;
662: return (new);
663: }
664:
665: /*
666: * -execdir utility [arg ... ] ; functions --
667: *
668: * True if the executed utility returns a zero value as exit status.
669: * The end of the primary expression is delimited by a semicolon. If
670: * "{}" occurs anywhere, it gets replaced by the unqualified pathname.
671: * The current directory for the execution of utility is the same as
672: * the directory where the file lives.
673: */
674: int
1.30 deraadt 675: f_execdir(PLAN *plan, FTSENT *entry)
1.8 millert 676: {
1.21 mpech 677: int cnt;
1.8 millert 678: pid_t pid;
1.15 millert 679: int status, fd;
1.42 deraadt 680: char base[PATH_MAX];
1.8 millert 681:
1.15 millert 682: /* fts(3) does not chdir for the root level so we do it ourselves. */
683: if (entry->fts_level == FTS_ROOTLEVEL) {
684: if ((fd = open(".", O_RDONLY)) == -1) {
685: warn("cannot open \".\"");
686: return (0);
687: }
688: if (chdir(entry->fts_accpath)) {
1.16 millert 689: (void) close(fd);
1.15 millert 690: return (0);
691: }
692: }
693:
1.10 millert 694: /* Substitute basename(path) for {} since cwd is it's parent dir */
695: (void)strncpy(base, basename(entry->fts_path), sizeof(base) - 1);
696: base[sizeof(base) - 1] = '\0';
1.8 millert 697: for (cnt = 0; plan->e_argv[cnt]; ++cnt)
698: if (plan->e_len[cnt])
699: brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt],
1.10 millert 700: base, plan->e_len[cnt]);
1.8 millert 701:
702: /* don't mix output of command with find output */
703: fflush(stdout);
704: fflush(stderr);
705:
706: switch (pid = vfork()) {
707: case -1:
708: err(1, "fork");
709: /* NOTREACHED */
710: case 0:
711: execvp(plan->e_argv[0], plan->e_argv);
712: warn("%s", plan->e_argv[0]);
713: _exit(1);
714: }
1.15 millert 715:
716: /* Undo the above... */
717: if (entry->fts_level == FTS_ROOTLEVEL) {
718: if (fchdir(fd) == -1) {
719: warn("unable to chdir back to starting directory");
1.16 millert 720: (void) close(fd);
1.15 millert 721: return (0);
722: }
1.16 millert 723: (void) close(fd);
1.15 millert 724: }
725:
1.8 millert 726: pid = waitpid(pid, &status, 0);
727: return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status));
728: }
729:
730: /*
731: * c_execdir --
732: * build three parallel arrays, one with pointers to the strings passed
733: * on the command line, one with (possibly duplicated) pointers to the
734: * argv array, and one with integer values that are lengths of the
735: * strings, but also flags meaning that the string has to be massaged.
736: */
737: PLAN *
1.28 deraadt 738: c_execdir(char *ignored, char ***argvp, int unused)
1.8 millert 739: {
740: PLAN *new; /* node returned */
1.21 mpech 741: int cnt;
742: char **argv, **ap, *p;
1.8 millert 743:
744: ftsoptions &= ~FTS_NOSTAT;
745: isoutput = 1;
746:
747: new = palloc(N_EXECDIR, f_execdir);
748:
749: for (ap = argv = *argvp;; ++ap) {
750: if (!*ap)
751: errx(1,
752: "-execdir: no terminating \";\"");
1.1 deraadt 753: if (**ap == ';')
754: break;
755: }
756:
757: cnt = ap - *argvp + 1;
1.41 espie 758: new->e_argv = ereallocarray(NULL, cnt, sizeof(char *));
759: new->e_orig = ereallocarray(NULL, cnt, sizeof(char *));
760: new->e_len = ereallocarray(NULL, cnt, sizeof(int));
1.1 deraadt 761:
762: for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
763: new->e_orig[cnt] = *argv;
764: for (p = *argv; *p; ++p)
765: if (p[0] == '{' && p[1] == '}') {
1.42 deraadt 766: new->e_argv[cnt] = emalloc((u_int)PATH_MAX);
767: new->e_len[cnt] = PATH_MAX;
1.1 deraadt 768: break;
769: }
770: if (!*p) {
771: new->e_argv[cnt] = *argv;
772: new->e_len[cnt] = 0;
773: }
774: }
775: new->e_argv[cnt] = new->e_orig[cnt] = NULL;
776:
777: *argvp = argv + 1;
778: return (new);
779: }
1.19 millert 780:
781: /*
782: * -flags functions --
783: *
784: * The flags argument is used to represent file flags bits.
785: */
786: int
1.30 deraadt 787: f_flags(PLAN *plan, FTSENT *entry)
1.19 millert 788: {
789: u_int flags;
790:
791: flags = entry->fts_statp->st_flags &
792: (UF_NODUMP | UF_IMMUTABLE | UF_APPEND | UF_OPAQUE |
793: SF_ARCHIVED | SF_IMMUTABLE | SF_APPEND);
794: if (plan->flags == F_ATLEAST)
795: /* note that plan->fl_flags always is a subset of
796: plan->fl_mask */
797: return ((flags & plan->fl_mask) == plan->fl_flags);
798: else
799: return (flags == plan->fl_flags);
800: /* NOTREACHED */
801: }
802:
803: PLAN *
1.28 deraadt 804: c_flags(char *flags_str, char ***ignored, int unused)
1.19 millert 805: {
806: PLAN *new;
1.20 mickey 807: u_int32_t flags, notflags;
1.19 millert 808:
809: ftsoptions &= ~FTS_NOSTAT;
810:
811: new = palloc(N_FLAGS, f_flags);
812:
813: if (*flags_str == '-') {
814: new->flags = F_ATLEAST;
815: ++flags_str;
816: }
817:
1.20 mickey 818: if (strtofflags(&flags_str, &flags, ¬flags) == 1)
1.19 millert 819: errx(1, "-flags: %s: illegal flags string", flags_str);
820:
821: new->fl_flags = flags;
822: new->fl_mask = flags | notflags;
823: return (new);
824: }
1.1 deraadt 825:
826: /*
827: * -follow functions --
828: *
829: * Always true, causes symbolic links to be followed on a global
830: * basis.
831: */
832: PLAN *
1.28 deraadt 833: c_follow(char *ignore, char ***ignored, int unused)
1.1 deraadt 834: {
835: ftsoptions &= ~FTS_PHYSICAL;
836: ftsoptions |= FTS_LOGICAL;
837:
838: return (palloc(N_FOLLOW, f_always_true));
839: }
840:
841: /*
842: * -fstype functions --
843: *
844: * True if the file is of a certain type.
845: */
846: int
1.30 deraadt 847: f_fstype(PLAN *plan, FTSENT *entry)
1.1 deraadt 848: {
849: static dev_t curdev; /* need a guaranteed illegal dev value */
850: static int first = 1;
851: struct statfs sb;
852: static short val;
853: static char fstype[MFSNAMELEN];
854: char *p, save[2];
855:
856: /* Only check when we cross mount point. */
857: if (first || curdev != entry->fts_statp->st_dev) {
858: curdev = entry->fts_statp->st_dev;
859:
860: /*
861: * Statfs follows symlinks; find wants the link's file system,
862: * not where it points.
863: */
864: if (entry->fts_info == FTS_SL ||
865: entry->fts_info == FTS_SLNONE) {
1.9 millert 866: if ((p = strrchr(entry->fts_accpath, '/')))
1.1 deraadt 867: ++p;
868: else
869: p = entry->fts_accpath;
870: save[0] = p[0];
871: p[0] = '.';
872: save[1] = p[1];
873: p[1] = '\0';
874:
875: } else
876: p = NULL;
877:
878: if (statfs(entry->fts_accpath, &sb))
879: err(1, "%s", entry->fts_accpath);
880:
881: if (p) {
882: p[0] = save[0];
883: p[1] = save[1];
884: }
885:
886: first = 0;
887:
888: /*
889: * Further tests may need both of these values, so
890: * always copy both of them.
891: */
892: val = sb.f_flags;
893: strncpy(fstype, sb.f_fstypename, MFSNAMELEN);
894: }
895: switch (plan->flags) {
896: case F_MTFLAG:
897: return (val & plan->mt_data);
898: case F_MTTYPE:
899: return (strncmp(fstype, plan->c_data, MFSNAMELEN) == 0);
900: default:
901: abort();
902: }
903: }
904:
905: PLAN *
1.28 deraadt 906: c_fstype(char *arg, char ***ignored, int unused)
1.1 deraadt 907: {
1.21 mpech 908: PLAN *new;
1.1 deraadt 909:
910: ftsoptions &= ~FTS_NOSTAT;
911:
912: new = palloc(N_FSTYPE, f_fstype);
913: switch (*arg) {
914: case 'l':
915: if (!strcmp(arg, "local")) {
916: new->flags = F_MTFLAG;
917: new->mt_data = MNT_LOCAL;
918: return (new);
919: }
920: break;
921: case 'r':
922: if (!strcmp(arg, "rdonly")) {
923: new->flags = F_MTFLAG;
924: new->mt_data = MNT_RDONLY;
925: return (new);
926: }
927: break;
928: }
929:
930: new->flags = F_MTTYPE;
931: new->c_data = arg;
932: return (new);
933: }
934:
935: /*
936: * -group gname functions --
937: *
938: * True if the file belongs to the group gname. If gname is numeric and
939: * an equivalent of the getgrnam() function does not return a valid group
940: * name, gname is taken as a group ID.
941: */
942: int
1.30 deraadt 943: f_group(PLAN *plan, FTSENT *entry)
1.1 deraadt 944: {
945: return (entry->fts_statp->st_gid == plan->g_data);
946: }
947:
948: PLAN *
1.28 deraadt 949: c_group(char *gname, char ***ignored, int unused)
1.1 deraadt 950: {
951: PLAN *new;
952: gid_t gid;
953:
954: ftsoptions &= ~FTS_NOSTAT;
955:
1.46 millert 956: if (gid_from_group(gname, &gid) == -1) {
1.44 deraadt 957: const char *errstr;
958:
959: gid = strtonum(gname, 0, GID_MAX, &errstr);
960: if (errstr)
1.1 deraadt 961: errx(1, "-group: %s: no such group", gname);
1.46 millert 962: }
1.1 deraadt 963:
964: new = palloc(N_GROUP, f_group);
965: new->g_data = gid;
966: return (new);
967: }
968:
969: /*
970: * -inum n functions --
971: *
972: * True if the file has inode # n.
973: */
974: int
1.30 deraadt 975: f_inum(PLAN *plan, FTSENT *entry)
1.1 deraadt 976: {
977: COMPARE(entry->fts_statp->st_ino, plan->i_data);
978: }
979:
980: PLAN *
1.28 deraadt 981: c_inum(char *arg, char ***ignored, int unused)
1.1 deraadt 982: {
1.39 deraadt 983: long long inum;
1.1 deraadt 984: PLAN *new;
985:
986: ftsoptions &= ~FTS_NOSTAT;
987:
988: new = palloc(N_INUM, f_inum);
1.39 deraadt 989: inum = find_parsenum(new, "-inum", arg, NULL);
990: if (inum != (ino_t)inum)
991: errx(1, "-inum: %s: number too great", arg);
992: new->i_data = inum;
1.1 deraadt 993: return (new);
994: }
995:
996: /*
997: * -links n functions --
998: *
999: * True if the file has n links.
1000: */
1001: int
1.30 deraadt 1002: f_links(PLAN *plan, FTSENT *entry)
1.1 deraadt 1003: {
1004: COMPARE(entry->fts_statp->st_nlink, plan->l_data);
1005: }
1006:
1007: PLAN *
1.28 deraadt 1008: c_links(char *arg, char ***ignored, int unused)
1.1 deraadt 1009: {
1010: PLAN *new;
1.39 deraadt 1011: long long nlink;
1.1 deraadt 1012:
1013: ftsoptions &= ~FTS_NOSTAT;
1014:
1015: new = palloc(N_LINKS, f_links);
1.39 deraadt 1016: nlink = find_parsenum(new, "-links", arg, NULL);
1017: if (nlink != (nlink_t)nlink)
1018: errx(1, "-links: %s: number too great", arg);
1019: new->l_data = nlink;
1.1 deraadt 1020: return (new);
1021: }
1022:
1023: /*
1024: * -ls functions --
1025: *
1026: * Always true - prints the current entry to stdout in "ls" format.
1027: */
1028: int
1.30 deraadt 1029: f_ls(PLAN *plan, FTSENT *entry)
1.1 deraadt 1030: {
1031: printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
1032: return (1);
1033: }
1034:
1035: PLAN *
1.28 deraadt 1036: c_ls(char *ignore, char ***ignored, int unused)
1.1 deraadt 1037: {
1038: ftsoptions &= ~FTS_NOSTAT;
1039: isoutput = 1;
1040:
1041: return (palloc(N_LS, f_ls));
1.5 tholo 1042: }
1043:
1044: /*
1045: * - maxdepth n functions --
1046: *
1047: * True if the current search depth is less than or equal to the
1048: * maximum depth specified
1049: */
1050: int
1.30 deraadt 1051: f_maxdepth(PLAN *plan, FTSENT *entry)
1.5 tholo 1052: {
1053:
1.6 tholo 1054: if (entry->fts_level >= plan->max_data)
1.5 tholo 1055: fts_set(tree, entry, FTS_SKIP);
1.6 tholo 1056: return (entry->fts_level <= plan->max_data);
1.5 tholo 1057: }
1058:
1059: PLAN *
1.28 deraadt 1060: c_maxdepth(char *arg, char ***ignored, int unused)
1.5 tholo 1061: {
1062: PLAN *new;
1.32 millert 1063: const char *errstr = NULL;
1.5 tholo 1064:
1.6 tholo 1065: new = palloc(N_MAXDEPTH, f_maxdepth);
1.33 millert 1066: new->max_data = strtonum(arg, 0, FTS_MAXLEVEL, &errstr);
1.32 millert 1067: if (errstr)
1068: errx(1, "%s: maxdepth value %s", arg, errstr);
1.6 tholo 1069: return (new);
1070: }
1071:
1072: /*
1073: * - mindepth n functions --
1074: *
1075: * True if the current search depth is greater than or equal to the
1076: * minimum depth specified
1077: */
1078: int
1.30 deraadt 1079: f_mindepth(PLAN *plan, FTSENT *entry)
1.6 tholo 1080: {
1081:
1082: return (entry->fts_level >= plan->min_data);
1083: }
1084:
1085: PLAN *
1.28 deraadt 1086: c_mindepth(char *arg, char ***ignored, int unused)
1.6 tholo 1087: {
1088: PLAN *new;
1.44 deraadt 1089: const char *errstr = NULL;
1.6 tholo 1090:
1091: new = palloc(N_MINDEPTH, f_mindepth);
1.44 deraadt 1092: new->min_data = strtonum(arg, 0, INT_MAX, &errstr);
1093: if (errstr)
1094: errx(1, "-mindepth: %s: value %s", arg, errstr);
1.5 tholo 1095: return (new);
1.1 deraadt 1096: }
1097:
1098: /*
1099: * -mtime n functions --
1100: *
1101: * True if the difference between the file modification time and the
1102: * current time is n 24 hour periods.
1103: */
1104: int
1.30 deraadt 1105: f_mtime(PLAN *plan, FTSENT *entry)
1.1 deraadt 1106: {
1107:
1108: COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /
1.17 millert 1109: SECSPERDAY, plan->sec_data);
1.1 deraadt 1110: }
1111:
1112: PLAN *
1.28 deraadt 1113: c_mtime(char *arg, char ***ignored, int unused)
1.1 deraadt 1114: {
1115: PLAN *new;
1116:
1117: ftsoptions &= ~FTS_NOSTAT;
1118:
1119: new = palloc(N_MTIME, f_mtime);
1.17 millert 1120: new->sec_data = find_parsenum(new, "-mtime", arg, NULL);
1.1 deraadt 1121: TIME_CORRECT(new, N_MTIME);
1.11 deraadt 1122: return (new);
1123: }
1124:
1125: /*
1126: * -mmin n functions --
1127: *
1128: * True if the difference between the file modification time and the
1129: * current time is n min periods.
1130: */
1131: int
1.30 deraadt 1132: f_mmin(PLAN *plan, FTSENT *entry)
1.11 deraadt 1133: {
1134: extern time_t now;
1135:
1136: COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) /
1.17 millert 1137: 60, plan->sec_data);
1.11 deraadt 1138: }
1139:
1140: PLAN *
1.28 deraadt 1141: c_mmin(char *arg, char ***ignored, int unused)
1.11 deraadt 1142: {
1143: PLAN *new;
1144:
1145: ftsoptions &= ~FTS_NOSTAT;
1146:
1147: new = palloc(N_MMIN, f_mmin);
1.17 millert 1148: new->sec_data = find_parsenum(new, "-mmin", arg, NULL);
1.11 deraadt 1149: TIME_CORRECT(new, N_MMIN);
1.1 deraadt 1150: return (new);
1151: }
1152:
1153: /*
1154: * -name functions --
1155: *
1156: * True if the basename of the filename being examined
1157: * matches pattern using Pattern Matching Notation S3.14
1158: */
1159: int
1.30 deraadt 1160: f_name(PLAN *plan, FTSENT *entry)
1.1 deraadt 1161: {
1162: return (!fnmatch(plan->c_data, entry->fts_name, 0));
1163: }
1164:
1165: PLAN *
1.28 deraadt 1166: c_name(char *pattern, char ***ignored, int unused)
1.1 deraadt 1167: {
1168: PLAN *new;
1169:
1170: new = palloc(N_NAME, f_name);
1.18 deraadt 1171: new->c_data = pattern;
1172: return (new);
1173: }
1174:
1175: /*
1176: * -iname functions --
1177: *
1178: * Similar to -name, but does case insensitive matching
1179: *
1180: */
1181: int
1.30 deraadt 1182: f_iname(PLAN *plan, FTSENT *entry)
1.18 deraadt 1183: {
1184: return (!fnmatch(plan->c_data, entry->fts_name, FNM_CASEFOLD));
1185: }
1186:
1187: PLAN *
1.28 deraadt 1188: c_iname(char *pattern, char ***ignored, int unused)
1.18 deraadt 1189: {
1190: PLAN *new;
1191:
1192: new = palloc(N_INAME, f_iname);
1.1 deraadt 1193: new->c_data = pattern;
1194: return (new);
1195: }
1196:
1197: /*
1198: * -newer file functions --
1199: *
1200: * True if the current file has been modified more recently
1201: * then the modification time of the file named by the pathname
1202: * file.
1203: */
1204: int
1.30 deraadt 1205: f_newer(PLAN *plan, FTSENT *entry)
1.1 deraadt 1206: {
1.17 millert 1207:
1208: return (entry->fts_statp->st_mtimespec.tv_sec > plan->t_data.tv_sec ||
1209: (entry->fts_statp->st_mtimespec.tv_sec == plan->t_data.tv_sec &&
1210: entry->fts_statp->st_mtimespec.tv_nsec > plan->t_data.tv_nsec));
1.1 deraadt 1211: }
1212:
1213: PLAN *
1.28 deraadt 1214: c_newer(char *filename, char ***ignored, int unused)
1.1 deraadt 1215: {
1216: PLAN *new;
1217: struct stat sb;
1218:
1219: ftsoptions &= ~FTS_NOSTAT;
1220:
1221: if (stat(filename, &sb))
1222: err(1, "%s", filename);
1223: new = palloc(N_NEWER, f_newer);
1.17 millert 1224: memcpy(&new->t_data, &sb.st_mtimespec, sizeof(struct timespec));
1225: return (new);
1226: }
1227:
1228: /*
1229: * -anewer file functions --
1230: *
1231: * True if the current file has been accessed more recently
1232: * then the access time of the file named by the pathname
1233: * file.
1234: */
1235: int
1.30 deraadt 1236: f_anewer(PLAN *plan, FTSENT *entry)
1.17 millert 1237: {
1238:
1239: return (entry->fts_statp->st_atimespec.tv_sec > plan->t_data.tv_sec ||
1240: (entry->fts_statp->st_atimespec.tv_sec == plan->t_data.tv_sec &&
1241: entry->fts_statp->st_atimespec.tv_nsec > plan->t_data.tv_nsec));
1242: }
1243:
1244: PLAN *
1.28 deraadt 1245: c_anewer(char *filename, char ***ignored, int unused)
1.17 millert 1246: {
1247: PLAN *new;
1248: struct stat sb;
1249:
1250: ftsoptions &= ~FTS_NOSTAT;
1251:
1252: if (stat(filename, &sb))
1253: err(1, "%s", filename);
1.24 millert 1254: new = palloc(N_NEWER, f_anewer);
1.17 millert 1255: memcpy(&new->t_data, &sb.st_atimespec, sizeof(struct timespec));
1256: return (new);
1257: }
1258:
1259: /*
1260: * -cnewer file functions --
1261: *
1262: * True if the current file has been changed more recently
1263: * then the inode change time of the file named by the pathname
1264: * file.
1265: */
1266: int
1.30 deraadt 1267: f_cnewer(PLAN *plan, FTSENT *entry)
1.17 millert 1268: {
1269:
1270: return (entry->fts_statp->st_ctimespec.tv_sec > plan->t_data.tv_sec ||
1271: (entry->fts_statp->st_ctimespec.tv_sec == plan->t_data.tv_sec &&
1272: entry->fts_statp->st_ctimespec.tv_nsec > plan->t_data.tv_nsec));
1273: }
1274:
1275: PLAN *
1.28 deraadt 1276: c_cnewer(char *filename, char ***ignored, int unused)
1.17 millert 1277: {
1278: PLAN *new;
1279: struct stat sb;
1280:
1281: ftsoptions &= ~FTS_NOSTAT;
1282:
1283: if (stat(filename, &sb))
1284: err(1, "%s", filename);
1.24 millert 1285: new = palloc(N_NEWER, f_cnewer);
1.17 millert 1286: memcpy(&new->t_data, &sb.st_ctimespec, sizeof(struct timespec));
1.1 deraadt 1287: return (new);
1288: }
1289:
1290: /*
1291: * -nogroup functions --
1292: *
1293: * True if file belongs to a user ID for which the equivalent
1294: * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL.
1295: */
1296: int
1.30 deraadt 1297: f_nogroup(PLAN *plan, FTSENT *entry)
1.1 deraadt 1298: {
1299: return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
1300: }
1301:
1302: PLAN *
1.28 deraadt 1303: c_nogroup(char *ignore, char ***ignored, int unused)
1.1 deraadt 1304: {
1305: ftsoptions &= ~FTS_NOSTAT;
1306:
1307: return (palloc(N_NOGROUP, f_nogroup));
1308: }
1309:
1310: /*
1311: * -nouser functions --
1312: *
1313: * True if file belongs to a user ID for which the equivalent
1314: * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL.
1315: */
1316: int
1.30 deraadt 1317: f_nouser(PLAN *plan, FTSENT *entry)
1.1 deraadt 1318: {
1319: return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
1320: }
1321:
1322: PLAN *
1.28 deraadt 1323: c_nouser(char *ignore, char ***ignored, int unused)
1.1 deraadt 1324: {
1325: ftsoptions &= ~FTS_NOSTAT;
1326:
1327: return (palloc(N_NOUSER, f_nouser));
1328: }
1329:
1330: /*
1331: * -path functions --
1332: *
1333: * True if the path of the filename being examined
1334: * matches pattern using Pattern Matching Notation S3.14
1335: */
1336: int
1.30 deraadt 1337: f_path(PLAN *plan, FTSENT *entry)
1.1 deraadt 1338: {
1339: return (!fnmatch(plan->c_data, entry->fts_path, 0));
1340: }
1341:
1342: PLAN *
1.28 deraadt 1343: c_path(char *pattern, char ***ignored, int unused)
1.1 deraadt 1344: {
1345: PLAN *new;
1346:
1347: new = palloc(N_NAME, f_path);
1348: new->c_data = pattern;
1349: return (new);
1350: }
1351:
1352: /*
1353: * -perm functions --
1354: *
1355: * The mode argument is used to represent file mode bits. If it starts
1356: * with a leading digit, it's treated as an octal mode, otherwise as a
1357: * symbolic mode.
1358: */
1359: int
1.30 deraadt 1360: f_perm(PLAN *plan, FTSENT *entry)
1.1 deraadt 1361: {
1362: mode_t mode;
1363:
1364: mode = entry->fts_statp->st_mode &
1365: (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO);
1366: if (plan->flags == F_ATLEAST)
1367: return ((plan->m_data | mode) == mode);
1368: else
1369: return (mode == plan->m_data);
1370: /* NOTREACHED */
1371: }
1372:
1373: PLAN *
1.28 deraadt 1374: c_perm(char *perm, char ***ignored, int unused)
1.1 deraadt 1375: {
1376: PLAN *new;
1.29 otto 1377: void *set;
1.1 deraadt 1378:
1379: ftsoptions &= ~FTS_NOSTAT;
1380:
1381: new = palloc(N_PERM, f_perm);
1382:
1383: if (*perm == '-') {
1384: new->flags = F_ATLEAST;
1385: ++perm;
1386: }
1387:
1388: if ((set = setmode(perm)) == NULL)
1.19 millert 1389: errx(1, "-perm: %s: illegal mode string", perm);
1.1 deraadt 1390:
1391: new->m_data = getmode(set, 0);
1.12 deraadt 1392: free(set);
1.1 deraadt 1393: return (new);
1394: }
1395:
1396: /*
1397: * -print functions --
1398: *
1.51 jmc 1399: * Always true, causes the current pathname to be written to
1.1 deraadt 1400: * standard output.
1401: */
1402: int
1.30 deraadt 1403: f_print(PLAN *plan, FTSENT *entry)
1.1 deraadt 1404: {
1405: (void)printf("%s\n", entry->fts_path);
1406: return(1);
1407: }
1408:
1.9 millert 1409: int
1.30 deraadt 1410: f_print0(PLAN *plan, FTSENT *entry)
1.1 deraadt 1411: {
1412: (void)fputs(entry->fts_path, stdout);
1413: (void)fputc('\0', stdout);
1414: return(1);
1415: }
1416:
1417: PLAN *
1.28 deraadt 1418: c_print(char *ignore, char ***ignored, int unused)
1.1 deraadt 1419: {
1420: isoutput = 1;
1421:
1422: return(palloc(N_PRINT, f_print));
1423: }
1424:
1425: PLAN *
1.28 deraadt 1426: c_print0(char *ignore, char ***ignored, int unused)
1.1 deraadt 1427: {
1428: isoutput = 1;
1429:
1430: return(palloc(N_PRINT0, f_print0));
1431: }
1432:
1433: /*
1434: * -prune functions --
1435: *
1436: * Prune a portion of the hierarchy.
1437: */
1438: int
1.30 deraadt 1439: f_prune(PLAN *plan, FTSENT *entry)
1.1 deraadt 1440: {
1441:
1442: if (fts_set(tree, entry, FTS_SKIP))
1443: err(1, "%s", entry->fts_path);
1444: return (1);
1445: }
1446:
1447: PLAN *
1.28 deraadt 1448: c_prune(char *ignore, char ***ignored, int unused)
1.1 deraadt 1449: {
1450: return (palloc(N_PRUNE, f_prune));
1451: }
1452:
1453: /*
1454: * -size n[c] functions --
1455: *
1456: * True if the file size in bytes, divided by an implementation defined
1457: * value and rounded up to the next integer, is n. If n is followed by
1458: * a c, the size is in bytes.
1459: */
1460: #define FIND_SIZE 512
1461: static int divsize = 1;
1462:
1463: int
1.30 deraadt 1464: f_size(PLAN *plan, FTSENT *entry)
1.1 deraadt 1465: {
1466: off_t size;
1467:
1468: size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) /
1469: FIND_SIZE : entry->fts_statp->st_size;
1470: COMPARE(size, plan->o_data);
1471: }
1472:
1473: PLAN *
1.28 deraadt 1474: c_size(char *arg, char ***ignored, int unused)
1.1 deraadt 1475: {
1476: PLAN *new;
1477: char endch;
1478:
1479: ftsoptions &= ~FTS_NOSTAT;
1480:
1481: new = palloc(N_SIZE, f_size);
1482: endch = 'c';
1483: new->o_data = find_parsenum(new, "-size", arg, &endch);
1484: if (endch == 'c')
1485: divsize = 0;
1486: return (new);
1487: }
1488:
1489: /*
1490: * -type c functions --
1491: *
1492: * True if the type of the file is c, where c is b, c, d, p, or f for
1493: * block special file, character special file, directory, FIFO, or
1494: * regular file, respectively.
1495: */
1496: int
1.30 deraadt 1497: f_type(PLAN *plan, FTSENT *entry)
1.1 deraadt 1498: {
1499: return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data);
1500: }
1501:
1502: PLAN *
1.28 deraadt 1503: c_type(char *typestring, char ***ignored, int unused)
1.1 deraadt 1504: {
1505: PLAN *new;
1506: mode_t mask;
1507:
1508: ftsoptions &= ~FTS_NOSTAT;
1509:
1510: switch (typestring[0]) {
1511: case 'b':
1512: mask = S_IFBLK;
1513: break;
1514: case 'c':
1515: mask = S_IFCHR;
1516: break;
1517: case 'd':
1518: mask = S_IFDIR;
1519: break;
1520: case 'f':
1521: mask = S_IFREG;
1522: break;
1523: case 'l':
1524: mask = S_IFLNK;
1525: break;
1526: case 'p':
1527: mask = S_IFIFO;
1528: break;
1529: case 's':
1530: mask = S_IFSOCK;
1531: break;
1532: default:
1533: errx(1, "-type: %s: unknown type", typestring);
1534: }
1535:
1536: new = palloc(N_TYPE, f_type);
1537: new->m_data = mask;
1538: return (new);
1539: }
1540:
1541: /*
1542: * -user uname functions --
1543: *
1544: * True if the file belongs to the user uname. If uname is numeric and
1545: * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1546: * return a valid user name, uname is taken as a user ID.
1547: */
1548: int
1.30 deraadt 1549: f_user(PLAN *plan, FTSENT *entry)
1.1 deraadt 1550: {
1551: return (entry->fts_statp->st_uid == plan->u_data);
1552: }
1553:
1554: PLAN *
1.28 deraadt 1555: c_user(char *username, char ***ignored, int unused)
1.1 deraadt 1556: {
1557: PLAN *new;
1558: uid_t uid;
1559:
1560: ftsoptions &= ~FTS_NOSTAT;
1561:
1.46 millert 1562: if (uid_from_user(username, &uid) == -1) {
1.44 deraadt 1563: const char *errstr;
1564:
1565: uid = strtonum(username, 0, UID_MAX, &errstr);
1566: if (errstr)
1.1 deraadt 1567: errx(1, "-user: %s: no such user", username);
1.46 millert 1568: }
1.1 deraadt 1569:
1570: new = palloc(N_USER, f_user);
1571: new->u_data = uid;
1572: return (new);
1573: }
1574:
1575: /*
1576: * -xdev functions --
1577: *
1.51 jmc 1578: * Always true, causes find not to descend past directories that have a
1.1 deraadt 1579: * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1580: */
1581: PLAN *
1.28 deraadt 1582: c_xdev(char *ignore, char ***ignored, int unused)
1.1 deraadt 1583: {
1584: ftsoptions |= FTS_XDEV;
1585:
1586: return (palloc(N_XDEV, f_always_true));
1587: }
1588:
1589: /*
1590: * ( expression ) functions --
1591: *
1592: * True if expression is true.
1593: */
1594: int
1.30 deraadt 1595: f_expr(PLAN *plan, FTSENT *entry)
1.1 deraadt 1596: {
1.21 mpech 1597: PLAN *p;
1598: int state;
1.1 deraadt 1599:
1600: for (p = plan->p_data[0];
1601: p && (state = (p->eval)(p, entry)); p = p->next);
1602: return (state);
1603: }
1604:
1605: /*
1606: * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
1607: * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1608: * to a N_EXPR node containing the expression and the ')' node is discarded.
1609: */
1610: PLAN *
1.28 deraadt 1611: c_openparen(char *ignore, char ***ignored, int unused)
1.1 deraadt 1612: {
1.28 deraadt 1613: return (palloc(N_OPENPAREN, (int (*)(PLAN *, FTSENT *))-1));
1.1 deraadt 1614: }
1615:
1616: PLAN *
1.28 deraadt 1617: c_closeparen(char *ignore, char ***ignored, int unused)
1.1 deraadt 1618: {
1.28 deraadt 1619: return (palloc(N_CLOSEPAREN, (int (*)(PLAN *, FTSENT *))-1));
1.1 deraadt 1620: }
1621:
1622: /*
1623: * ! expression functions --
1624: *
1625: * Negation of a primary; the unary NOT operator.
1626: */
1627: int
1.30 deraadt 1628: f_not(PLAN *plan, FTSENT *entry)
1.1 deraadt 1629: {
1.21 mpech 1630: PLAN *p;
1631: int state;
1.1 deraadt 1632:
1633: for (p = plan->p_data[0];
1634: p && (state = (p->eval)(p, entry)); p = p->next);
1635: return (!state);
1636: }
1637:
1638: PLAN *
1.28 deraadt 1639: c_not(char *ignore, char ***ignored, int unused)
1.1 deraadt 1640: {
1641: return (palloc(N_NOT, f_not));
1642: }
1643:
1644: /*
1645: * expression -o expression functions --
1646: *
1647: * Alternation of primaries; the OR operator. The second expression is
1648: * not evaluated if the first expression is true.
1649: */
1650: int
1.30 deraadt 1651: f_or(PLAN *plan, FTSENT *entry)
1.1 deraadt 1652: {
1.21 mpech 1653: PLAN *p;
1654: int state;
1.1 deraadt 1655:
1656: for (p = plan->p_data[0];
1657: p && (state = (p->eval)(p, entry)); p = p->next);
1658:
1659: if (state)
1660: return (1);
1661:
1662: for (p = plan->p_data[1];
1663: p && (state = (p->eval)(p, entry)); p = p->next);
1664: return (state);
1665: }
1666:
1667: PLAN *
1.28 deraadt 1668: c_or(char *ignore, char ***ignored, int unused)
1.1 deraadt 1669: {
1670: return (palloc(N_OR, f_or));
1671: }
1.37 pascal 1672:
1673:
1674: /*
1675: * plan_cleanup --
1676: * Check and see if the specified plan has any residual state,
1677: * and if so, clean it up as appropriate.
1678: *
1679: * At the moment, only N_EXEC has state. Two kinds: 1)
1680: * lists of files to feed to subprocesses 2) State on exit
1.51 jmc 1681: * status of past subprocesses.
1.37 pascal 1682: */
1683: int
1684: plan_cleanup(PLAN *plan, void *arg)
1685: {
1686: if (plan->type==N_EXEC && plan->ep_narg)
1687: run_f_exec(plan);
1688:
1689: return plan->ep_rval; /* Passed save exit-status up chain */
1690: }
1691:
1.1 deraadt 1692:
1693: static PLAN *
1.28 deraadt 1694: palloc(enum ntype t, int (*f)(PLAN *, FTSENT *))
1.1 deraadt 1695: {
1696: PLAN *new;
1697:
1.17 millert 1698: if ((new = calloc(1, sizeof(PLAN)))) {
1.1 deraadt 1699: new->type = t;
1700: new->eval = f;
1701: return (new);
1702: }
1703: err(1, NULL);
1704: /* NOTREACHED */
1705: }