Bug Summary

File:src/usr.bin/find/function.c
Warning:line 712, column 3
This function call is prohibited after a successful vfork

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name function.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/find/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/find/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.bin/find/function.c
1/* $OpenBSD: function.c,v 1.50 2020/11/23 06:21:52 halex Exp $ */
2
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.
18 * 3. Neither the name of the University nor the names of its contributors
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
39#include <dirent.h>
40#include <err.h>
41#include <errno(*__errno()).h>
42#include <fcntl.h>
43#include <fnmatch.h>
44#include <fts.h>
45#include <grp.h>
46#include <libgen.h>
47#include <limits.h>
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"
55#include "extern.h"
56
57#define COMPARE(a, b){ switch (plan->flags) { case 1: return (a == b); case 2: return
(a < b); case 3: return (a > b); default: abort(); } }
{ \
58 switch (plan->flags) { \
59 case F_EQUAL1: \
60 return (a == b); \
61 case F_LESSTHAN2: \
62 return (a < b); \
63 case F_GREATER3: \
64 return (a > b); \
65 default: \
66 abort(); \
67 } \
68}
69
70static PLAN *palloc(enum ntype, int (*)(PLAN *, FTSENT *));
71static long long find_parsenum(PLAN *plan, char *option, char *vp, char *endch);
72static void run_f_exec(PLAN *plan);
73static PLAN *palloc(enum ntype t, int (*f)(PLAN *, FTSENT *));
74
75int f_amin(PLAN *, FTSENT *);
76int f_atime(PLAN *, FTSENT *);
77int f_cmin(PLAN *, FTSENT *);
78int f_ctime(PLAN *, FTSENT *);
79int f_always_true(PLAN *, FTSENT *);
80int f_empty(PLAN *, FTSENT *);
81int f_exec(PLAN *, FTSENT *);
82int f_execdir(PLAN *, FTSENT *);
83int f_flags(PLAN *, FTSENT *);
84int f_fstype(PLAN *, FTSENT *);
85int f_group(PLAN *, FTSENT *);
86int f_inum(PLAN *, FTSENT *);
87int f_empty(PLAN *, FTSENT *);
88int f_links(PLAN *, FTSENT *);
89int f_ls(PLAN *, FTSENT *);
90int f_maxdepth(PLAN *, FTSENT *);
91int f_mindepth(PLAN *, FTSENT *);
92int f_mtime(PLAN *, FTSENT *);
93int f_mmin(PLAN *, FTSENT *);
94int f_name(PLAN *, FTSENT *);
95int f_iname(PLAN *, FTSENT *);
96int f_newer(PLAN *, FTSENT *);
97int f_anewer(PLAN *, FTSENT *);
98int f_cnewer(PLAN *, FTSENT *);
99int f_nogroup(PLAN *, FTSENT *);
100int f_nouser(PLAN *, FTSENT *);
101int f_path(PLAN *, FTSENT *);
102int f_perm(PLAN *, FTSENT *);
103int f_print(PLAN *, FTSENT *);
104int f_print0(PLAN *, FTSENT *);
105int f_prune(PLAN *, FTSENT *);
106int f_size(PLAN *, FTSENT *);
107int f_type(PLAN *, FTSENT *);
108int f_user(PLAN *, FTSENT *);
109int f_expr(PLAN *, FTSENT *);
110int f_not(PLAN *, FTSENT *);
111int f_or(PLAN *, FTSENT *);
112
113extern int dotfd;
114extern time_t now;
115extern FTS *tree;
116
117/*
118 * find_parsenum --
119 * Parse a string of the form [+-]# and return the value.
120 */
121static long long
122find_parsenum(PLAN *plan, char *option, char *vp, char *endch)
123{
124 long long value;
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_GREATER3;
133 break;
134 case '-':
135 ++str;
136 plan->flags = F_LESSTHAN2;
137 break;
138 default:
139 plan->flags = F_EQUAL1;
140 break;
141 }
142
143 /*
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.
147 */
148 value = strtoll(str, &endchar, 10);
149 if (value == 0 && endchar == str)
150 errx(1, "%s: %s: illegal numeric value", option, vp);
151 if (endchar[0] && (endch == NULL((void *)0) || 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)if ((p)->type == ttype && (p)->flags == 2) ++((
p)->p_un._t_data.tv_sec);
\
165 if ((p)->type == ttype && (p)->flags == F_LESSTHAN2) \
166 ++((p)->sec_datap_un._t_data.tv_sec);
167
168/*
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 */
174int
175f_amin(PLAN *plan, FTSENT *entry)
176{
177 extern time_t now;
178
179 COMPARE((now - entry->fts_statp->st_atime +{ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_atim.tv_sec + 60 - 1) / 60 == plan->p_un.
_t_data.tv_sec); case 2: return ((now - entry->fts_statp->
st_atim.tv_sec + 60 - 1) / 60 < plan->p_un._t_data.tv_sec
); case 3: return ((now - entry->fts_statp->st_atim.tv_sec
+ 60 - 1) / 60 > plan->p_un._t_data.tv_sec); default: abort
(); } }
180 60 - 1) / 60, plan->sec_data){ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_atim.tv_sec + 60 - 1) / 60 == plan->p_un.
_t_data.tv_sec); case 2: return ((now - entry->fts_statp->
st_atim.tv_sec + 60 - 1) / 60 < plan->p_un._t_data.tv_sec
); case 3: return ((now - entry->fts_statp->st_atim.tv_sec
+ 60 - 1) / 60 > plan->p_un._t_data.tv_sec); default: abort
(); } }
;
181}
182
183PLAN *
184c_amin(char *arg, char ***ignored, int unused)
185{
186 PLAN *new;
187
188 ftsoptions &= ~FTS_NOSTAT0x0008;
189
190 new = palloc(N_AMIN, f_amin);
191 new->sec_datap_un._t_data.tv_sec = find_parsenum(new, "-amin", arg, NULL((void *)0));
192 TIME_CORRECT(new, N_AMIN)if ((new)->type == N_AMIN && (new)->flags == 2)
++((new)->p_un._t_data.tv_sec);
;
193 return (new);
194}
195
196/*
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 */
202int
203f_atime(PLAN *plan, FTSENT *entry)
204{
205
206 COMPARE((now - entry->fts_statp->st_atime +{ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_atim.tv_sec + (24 * 60 * 60) - 1) / (24 * 60
* 60) == plan->p_un._t_data.tv_sec); case 2: return ((now
- entry->fts_statp->st_atim.tv_sec + (24 * 60 * 60) - 1
) / (24 * 60 * 60) < plan->p_un._t_data.tv_sec); case 3
: return ((now - entry->fts_statp->st_atim.tv_sec + (24
* 60 * 60) - 1) / (24 * 60 * 60) > plan->p_un._t_data.
tv_sec); default: abort(); } }
207 SECSPERDAY - 1) / SECSPERDAY, plan->sec_data){ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_atim.tv_sec + (24 * 60 * 60) - 1) / (24 * 60
* 60) == plan->p_un._t_data.tv_sec); case 2: return ((now
- entry->fts_statp->st_atim.tv_sec + (24 * 60 * 60) - 1
) / (24 * 60 * 60) < plan->p_un._t_data.tv_sec); case 3
: return ((now - entry->fts_statp->st_atim.tv_sec + (24
* 60 * 60) - 1) / (24 * 60 * 60) > plan->p_un._t_data.
tv_sec); default: abort(); } }
;
208}
209
210PLAN *
211c_atime(char *arg, char ***ignored, int unused)
212{
213 PLAN *new;
214
215 ftsoptions &= ~FTS_NOSTAT0x0008;
216
217 new = palloc(N_ATIME, f_atime);
218 new->sec_datap_un._t_data.tv_sec = find_parsenum(new, "-atime", arg, NULL((void *)0));
219 TIME_CORRECT(new, N_ATIME)if ((new)->type == N_ATIME && (new)->flags == 2
) ++((new)->p_un._t_data.tv_sec);
;
220 return (new);
221}
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 */
229int
230f_cmin(PLAN *plan, FTSENT *entry)
231{
232 extern time_t now;
233
234 COMPARE((now - entry->fts_statp->st_ctime +{ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_ctim.tv_sec + 60 - 1) / 60 == plan->p_un.
_t_data.tv_sec); case 2: return ((now - entry->fts_statp->
st_ctim.tv_sec + 60 - 1) / 60 < plan->p_un._t_data.tv_sec
); case 3: return ((now - entry->fts_statp->st_ctim.tv_sec
+ 60 - 1) / 60 > plan->p_un._t_data.tv_sec); default: abort
(); } }
235 60 - 1) / 60, plan->sec_data){ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_ctim.tv_sec + 60 - 1) / 60 == plan->p_un.
_t_data.tv_sec); case 2: return ((now - entry->fts_statp->
st_ctim.tv_sec + 60 - 1) / 60 < plan->p_un._t_data.tv_sec
); case 3: return ((now - entry->fts_statp->st_ctim.tv_sec
+ 60 - 1) / 60 > plan->p_un._t_data.tv_sec); default: abort
(); } }
;
236}
237
238PLAN *
239c_cmin(char *arg, char ***ignored, int unused)
240{
241 PLAN *new;
242
243 ftsoptions &= ~FTS_NOSTAT0x0008;
244
245 new = palloc(N_CMIN, f_cmin);
246 new->sec_datap_un._t_data.tv_sec = find_parsenum(new, "-cmin", arg, NULL((void *)0));
247 TIME_CORRECT(new, N_CMIN)if ((new)->type == N_CMIN && (new)->flags == 2)
++((new)->p_un._t_data.tv_sec);
;
248 return (new);
249}
250
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 */
257int
258f_ctime(PLAN *plan, FTSENT *entry)
259{
260
261 COMPARE((now - entry->fts_statp->st_ctime +{ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_ctim.tv_sec + (24 * 60 * 60) - 1) / (24 * 60
* 60) == plan->p_un._t_data.tv_sec); case 2: return ((now
- entry->fts_statp->st_ctim.tv_sec + (24 * 60 * 60) - 1
) / (24 * 60 * 60) < plan->p_un._t_data.tv_sec); case 3
: return ((now - entry->fts_statp->st_ctim.tv_sec + (24
* 60 * 60) - 1) / (24 * 60 * 60) > plan->p_un._t_data.
tv_sec); default: abort(); } }
262 SECSPERDAY - 1) / SECSPERDAY, plan->sec_data){ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_ctim.tv_sec + (24 * 60 * 60) - 1) / (24 * 60
* 60) == plan->p_un._t_data.tv_sec); case 2: return ((now
- entry->fts_statp->st_ctim.tv_sec + (24 * 60 * 60) - 1
) / (24 * 60 * 60) < plan->p_un._t_data.tv_sec); case 3
: return ((now - entry->fts_statp->st_ctim.tv_sec + (24
* 60 * 60) - 1) / (24 * 60 * 60) > plan->p_un._t_data.
tv_sec); default: abort(); } }
;
263}
264
265PLAN *
266c_ctime(char *arg, char ***ignored, int unused)
267{
268 PLAN *new;
269
270 ftsoptions &= ~FTS_NOSTAT0x0008;
271
272 new = palloc(N_CTIME, f_ctime);
273 new->sec_datap_un._t_data.tv_sec = find_parsenum(new, "-ctime", arg, NULL((void *)0));
274 TIME_CORRECT(new, N_CTIME)if ((new)->type == N_CTIME && (new)->flags == 2
) ++((new)->p_un._t_data.tv_sec);
;
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 */
285int
286f_always_true(PLAN *plan, FTSENT *entry)
287{
288 return (1);
289}
290
291PLAN *
292c_depth(char *ignore, char ***ignored, int unused)
293{
294 isdepth = 1;
295
296 return (palloc(N_DEPTH, f_always_true));
297}
298
299/*
300 * -delete functions
301 */
302int
303f_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_NOSTAT0x0008)) /* not stat()ing */
314 errx(1, "-delete: insecure options got turned on");
315 if (!(ftsoptions & FTS_PHYSICAL0x0010) || /* physical off */
316 (ftsoptions & FTS_LOGICAL0x0002)) /* 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_ROOTLEVEL0 &&
321 strchr(entry->fts_accpath, '/') != NULL((void *)0))
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_APPEND0x00000004|UF_IMMUTABLE0x00000002)) &&
327 !(entry->fts_statp->st_flags & (SF_APPEND0x00040000|SF_IMMUTABLE0x00020000)) &&
328 geteuid() == 0)
329 lchflags(entry->fts_accpath,
330 entry->fts_statp->st_flags &= ~(UF_APPEND0x00000004|UF_IMMUTABLE0x00000002));
331#endif
332 /* rmdir directories, unlink everything else */
333 if (S_ISDIR(entry->fts_statp->st_mode)((entry->fts_statp->st_mode & 0170000) == 0040000)) {
334 if (rmdir(entry->fts_accpath) == -1 && errno(*__errno()) != ENOTEMPTY66)
335 warn("-delete: rmdir(%s)", entry->fts_path);
336 } else {
337 if (unlink(entry->fts_accpath) == -1)
338 warn("-delete: unlink(%s)", entry->fts_path);
339
340 }
341
342 return 1;
343}
344
345PLAN *
346c_delete(char *ignore, char ***ignored, int unused)
347{
348 ftsoptions &= ~FTS_NOSTAT0x0008;
349 isoutput = 1;
350 isdelete = 1;
351 isdepth = 1;
352
353 return (palloc(N_DELETE, f_delete));
354}
355
356/*
357 * -empty functions --
358 *
359 * True if the file or directory is empty
360 */
361int
362f_empty(PLAN *plan, FTSENT *entry)
363{
364 if (S_ISREG(entry->fts_statp->st_mode)((entry->fts_statp->st_mode & 0170000) == 0100000) && entry->fts_statp->st_size == 0)
365 return (1);
366 if (S_ISDIR(entry->fts_statp->st_mode)((entry->fts_statp->st_mode & 0170000) == 0040000)) {
367 struct dirent *dp;
368 int empty;
369 DIR *dir;
370
371 empty = 1;
372 dir = opendir(entry->fts_accpath);
373 if (dir == NULL((void *)0))
374 return (0);
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
388PLAN *
389c_empty(char *ignore, char ***ignored, int unused)
390{
391 ftsoptions &= ~FTS_NOSTAT0x0008;
392
393 return (palloc(N_EMPTY, f_empty));
394}
395
396/*
397 * [-exec | -ok] utility [arg ... ] ; functions --
398 * [-exec | -ok] utility [arg ... ] {} + functions --
399 *
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.
413 *
414 * The primary -ok is different in that it requests affirmation
415 * of the user before executing the utility.
416 */
417int
418f_exec(PLAN *plan, FTSENT *entry)
419{
420 int cnt, l;
421 pid_t pid;
422 int status;
423
424 if (plan->flags & F_PLUSSET2) {
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_pp_un.ex._ep_p + l < plan->ep_ebpp_un.ex._ep_ebp) {
431 plan->ep_bxpp_un.ex._ep_bxp[plan->ep_nargp_un.ex._ep_narg++] = plan->ep_pp_un.ex._ep_p;
432 strlcpy(plan->ep_pp_un.ex._ep_p, entry->fts_path, l + 1);
433 plan->ep_pp_un.ex._ep_p += l + 1;
434
435 if (plan->ep_nargp_un.ex._ep_narg == plan->ep_maxargsp_un.ex._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_pp_un.ex._ep_p + l < plan->ep_ebpp_un.ex._ep_ebp) {
445 plan->ep_bxpp_un.ex._ep_bxp[plan->ep_nargp_un.ex._ep_narg++] = plan->ep_pp_un.ex._ep_p;
446 strlcpy(plan->ep_pp_un.ex._ep_p, entry->fts_path, l + 1);
447 plan->ep_pp_un.ex._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_argvp_un.ex._e_argv[cnt]; ++cnt)
454 if (plan->e_lenp_un.ex._e_len[cnt])
455 brace_subst(plan->e_origp_un.ex._e_orig[cnt],
456 &plan->e_argvp_un.ex._e_argv[cnt],
457 entry->fts_path,
458 plan->e_lenp_un.ex._e_len[cnt]);
459 if (plan->flags & F_NEEDOK1 && !queryuser(plan->e_argvp_un.ex._e_argv))
460 return (0);
461
462 /* don't mix output of command with find output */
463 fflush(stdout(&__sF[1]));
464 fflush(stderr(&__sF[2]));
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_argvp_un.ex._e_argv[0], plan->e_argvp_un.ex._e_argv);
476 warn("%s", plan->e_argvp_un.ex._e_argv[0]);
477 _exit(1);
478 }
479 pid = waitpid(pid, &status, 0);
480 return (pid != -1 && WIFEXITED(status)(((status) & 0177) == 0) && !WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff));
481 }
482}
483
484static void
485run_f_exec(PLAN *plan)
486{
487 pid_t pid;
488 int rval, status;
489
490 /* Ensure arg list is null terminated. */
491 plan->ep_bxpp_un.ex._ep_bxp[plan->ep_nargp_un.ex._ep_narg] = NULL((void *)0);
492
493 /* Don't mix output of command with find output. */
494 fflush(stdout(&__sF[1]));
495 fflush(stderr(&__sF[2]));
496
497 switch (pid = vfork()) {
498 case -1:
499 err(1, "vfork");
500 /* NOTREACHED */
501 case 0:
502 if (fchdir(dotfd)) {
503 warn("chdir");
504 _exit(1);
505 }
506 execvp(plan->e_argvp_un.ex._e_argv[0], plan->e_argvp_un.ex._e_argv);
507 warn("%s", plan->e_argvp_un.ex._e_argv[0]);
508 _exit(1);
509 }
510
511 /* Clear out the argument list. */
512 plan->ep_nargp_un.ex._ep_narg = 0;
513 plan->ep_bxpp_un.ex._ep_bxp[plan->ep_nargp_un.ex._ep_narg] = NULL((void *)0);
514 /* As well as the argument buffer. */
515 plan->ep_pp_un.ex._ep_p = plan->ep_bbpp_un.ex._ep_bbp;
516 *plan->ep_pp_un.ex._ep_p = '\0';
517
518 pid = waitpid(pid, &status, 0);
519 if (WIFEXITED(status)(((status) & 0177) == 0))
520 rval = WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff);
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_rvalp_un.ex._ep_rval = rval;
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.
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
541 * discussion), and then allocate space for args.
542 */
543PLAN *
544c_exec(char *unused, char ***argvp, int isok)
545{
546 PLAN *new; /* node returned */
547 int cnt, brace, lastbrace;
548 char **argv, **ap, *p;
549
550 /* make sure the current directory is readable */
551 if (dotfd == -1)
552 errx(1, "%s: cannot open \".\"", isok ? "-ok" : "-exec");
553
554 isoutput = 1;
555
556 new = palloc(N_EXEC, f_exec);
557 if (isok)
558 new->flags |= F_NEEDOK1;
559
560 /*
561 * Terminate if we encounter an arg exactly equal to ";", or an
562 * arg exactly equal to "+" following an arg exactly equal to
563 * "{}".
564 */
565 for (ap = argv = *argvp, brace = 0;; ++ap) {
566 if (!*ap)
567 errx(1, "%s: no terminating \";\" or \"{} +\"",
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_PLUSSET2;
577 break;
578 }
579 }
580
581
582 /*
583 * POSIX says -ok ... {} + "need not be supported," and it does
584 * not make much sense anyway.
585 */
586 if (new->flags & F_NEEDOK1 && new->flags & F_PLUSSET2)
587 errx(1, "-ok: terminating \"+\" not permitted.");
588
589 if (new->flags & F_PLUSSET2) {
590 long arg_max;
591 extern char **environ;
592 char **ep;
593 u_int c, bufsize;
594
595 cnt = ap - *argvp - 1; /* units are words */
596 new->ep_maxargsp_un.ex._ep_maxargs = 5000;
597 new->e_argvp_un.ex._e_argv = ereallocarray(NULL((void *)0),
598 (size_t)(cnt + new->ep_maxargsp_un.ex._ep_maxargs), sizeof(char **));
599
600 /* We start stuffing arguments after the user's last one. */
601 new->ep_bxpp_un.ex._ep_bxp = &new->e_argvp_un.ex._e_argv[cnt];
602 new->ep_nargp_un.ex._ep_narg = 0;
603
604 /*
605 * Compute the maximum space we can use for arguments
606 * passed to execve(2).
607 */
608 arg_max = sysconf(_SC_ARG_MAX1);
609 if (arg_max == -1)
610 err(1, "-exec: sysconf(_SC_ARG_MAX) failed");
611 for (ep = environ; *ep != NULL((void *)0); ep++) {
612 /* 1 byte for each '\0' */
613 arg_max -= strlen(*ep) + 1 + sizeof(*ep);
614 }
615
616 /*
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_argvp_un.ex._e_argv[cnt] = *argv;
625 }
626 if (arg_max < 4 * 1024 + c)
627 errx(1, "-exec: no space left to run child command");
628 bufsize = arg_max - 4 * 1024 - c;
629
630 /*
631 * Allocate, and then initialize current, base, and
632 * end pointers.
633 */
634 new->ep_pp_un.ex._ep_p = new->ep_bbpp_un.ex._ep_bbp = malloc(bufsize + 1);
635 new->ep_ebpp_un.ex._ep_ebp = new->ep_bbpp_un.ex._ep_bbp + bufsize - 1;
636 new->ep_rvalp_un.ex._ep_rval = 0;
637 } else { /* !F_PLUSSET */
638 cnt = ap - *argvp + 1;
639 new->e_argvp_un.ex._e_argv = ereallocarray(NULL((void *)0), cnt, sizeof(char *));
640 new->e_origp_un.ex._e_orig = ereallocarray(NULL((void *)0), cnt, sizeof(char *));
641 new->e_lenp_un.ex._e_len = ereallocarray(NULL((void *)0), cnt, sizeof(int));
642
643 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
644 new->e_origp_un.ex._e_orig[cnt] = *argv;
645 for (p = *argv; *p; ++p)
646 if (p[0] == '{' && p[1] == '}') {
647 new->e_argvp_un.ex._e_argv[cnt] =
648 emalloc((u_int)PATH_MAX1024);
649 new->e_lenp_un.ex._e_len[cnt] = PATH_MAX1024;
650 break;
651 }
652 if (!*p) {
653 new->e_argvp_un.ex._e_argv[cnt] = *argv;
654 new->e_lenp_un.ex._e_len[cnt] = 0;
655 }
656 }
657 new->e_origp_un.ex._e_orig[cnt] = NULL((void *)0);
658 }
659
660 new->e_argvp_un.ex._e_argv[cnt] = NULL((void *)0);
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 */
674int
675f_execdir(PLAN *plan, FTSENT *entry)
676{
677 int cnt;
678 pid_t pid;
679 int status, fd;
680 char base[PATH_MAX1024];
681
682 /* fts(3) does not chdir for the root level so we do it ourselves. */
683 if (entry->fts_level == FTS_ROOTLEVEL0) {
1
Assuming field 'fts_level' is not equal to FTS_ROOTLEVEL
2
Taking false branch
684 if ((fd = open(".", O_RDONLY0x0000)) == -1) {
685 warn("cannot open \".\"");
686 return (0);
687 }
688 if (chdir(entry->fts_accpath)) {
689 (void) close(fd);
690 return (0);
691 }
692 }
693
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';
697 for (cnt = 0; plan->e_argvp_un.ex._e_argv[cnt]; ++cnt)
3
Loop condition is false. Execution continues on line 703
698 if (plan->e_lenp_un.ex._e_len[cnt])
699 brace_subst(plan->e_origp_un.ex._e_orig[cnt], &plan->e_argvp_un.ex._e_argv[cnt],
700 base, plan->e_lenp_un.ex._e_len[cnt]);
701
702 /* don't mix output of command with find output */
703 fflush(stdout(&__sF[1]));
704 fflush(stderr(&__sF[2]));
705
706 switch (pid = vfork()) {
4
Control jumps to 'case 0:' at line 710
707 case -1:
708 err(1, "fork");
709 /* NOTREACHED */
710 case 0:
711 execvp(plan->e_argvp_un.ex._e_argv[0], plan->e_argvp_un.ex._e_argv);
712 warn("%s", plan->e_argvp_un.ex._e_argv[0]);
5
This function call is prohibited after a successful vfork
713 _exit(1);
714 }
715
716 /* Undo the above... */
717 if (entry->fts_level == FTS_ROOTLEVEL0) {
718 if (fchdir(fd) == -1) {
719 warn("unable to chdir back to starting directory");
720 (void) close(fd);
721 return (0);
722 }
723 (void) close(fd);
724 }
725
726 pid = waitpid(pid, &status, 0);
727 return (pid != -1 && WIFEXITED(status)(((status) & 0177) == 0) && !WEXITSTATUS(status)(int)(((unsigned)(status) >> 8) & 0xff));
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 */
737PLAN *
738c_execdir(char *ignored, char ***argvp, int unused)
739{
740 PLAN *new; /* node returned */
741 int cnt;
742 char **argv, **ap, *p;
743
744 ftsoptions &= ~FTS_NOSTAT0x0008;
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 \";\"");
753 if (**ap == ';')
754 break;
755 }
756
757 cnt = ap - *argvp + 1;
758 new->e_argvp_un.ex._e_argv = ereallocarray(NULL((void *)0), cnt, sizeof(char *));
759 new->e_origp_un.ex._e_orig = ereallocarray(NULL((void *)0), cnt, sizeof(char *));
760 new->e_lenp_un.ex._e_len = ereallocarray(NULL((void *)0), cnt, sizeof(int));
761
762 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) {
763 new->e_origp_un.ex._e_orig[cnt] = *argv;
764 for (p = *argv; *p; ++p)
765 if (p[0] == '{' && p[1] == '}') {
766 new->e_argvp_un.ex._e_argv[cnt] = emalloc((u_int)PATH_MAX1024);
767 new->e_lenp_un.ex._e_len[cnt] = PATH_MAX1024;
768 break;
769 }
770 if (!*p) {
771 new->e_argvp_un.ex._e_argv[cnt] = *argv;
772 new->e_lenp_un.ex._e_len[cnt] = 0;
773 }
774 }
775 new->e_argvp_un.ex._e_argv[cnt] = new->e_origp_un.ex._e_orig[cnt] = NULL((void *)0);
776
777 *argvp = argv + 1;
778 return (new);
779}
780
781/*
782 * -flags functions --
783 *
784 * The flags argument is used to represent file flags bits.
785 */
786int
787f_flags(PLAN *plan, FTSENT *entry)
788{
789 u_int flags;
790
791 flags = entry->fts_statp->st_flags &
792 (UF_NODUMP0x00000001 | UF_IMMUTABLE0x00000002 | UF_APPEND0x00000004 | UF_OPAQUE0x00000008 |
793 SF_ARCHIVED0x00010000 | SF_IMMUTABLE0x00020000 | SF_APPEND0x00040000);
794 if (plan->flags == F_ATLEAST1)
795 /* note that plan->fl_flags always is a subset of
796 plan->fl_mask */
797 return ((flags & plan->fl_maskp_un.fl._f_mask) == plan->fl_flagsp_un.fl._f_flags);
798 else
799 return (flags == plan->fl_flagsp_un.fl._f_flags);
800 /* NOTREACHED */
801}
802
803PLAN *
804c_flags(char *flags_str, char ***ignored, int unused)
805{
806 PLAN *new;
807 u_int32_t flags, notflags;
808
809 ftsoptions &= ~FTS_NOSTAT0x0008;
810
811 new = palloc(N_FLAGS, f_flags);
812
813 if (*flags_str == '-') {
814 new->flags = F_ATLEAST1;
815 ++flags_str;
816 }
817
818 if (strtofflags(&flags_str, &flags, &notflags) == 1)
819 errx(1, "-flags: %s: illegal flags string", flags_str);
820
821 new->fl_flagsp_un.fl._f_flags = flags;
822 new->fl_maskp_un.fl._f_mask = flags | notflags;
823 return (new);
824}
825
826/*
827 * -follow functions --
828 *
829 * Always true, causes symbolic links to be followed on a global
830 * basis.
831 */
832PLAN *
833c_follow(char *ignore, char ***ignored, int unused)
834{
835 ftsoptions &= ~FTS_PHYSICAL0x0010;
836 ftsoptions |= FTS_LOGICAL0x0002;
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 */
846int
847f_fstype(PLAN *plan, FTSENT *entry)
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[MFSNAMELEN16];
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_SL12 ||
865 entry->fts_info == FTS_SLNONE13) {
866 if ((p = strrchr(entry->fts_accpath, '/')))
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((void *)0);
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, MFSNAMELEN16);
894 }
895 switch (plan->flags) {
896 case F_MTFLAG1:
897 return (val & plan->mt_datap_un._mt_data);
898 case F_MTTYPE2:
899 return (strncmp(fstype, plan->c_datap_un._c_data, MFSNAMELEN16) == 0);
900 default:
901 abort();
902 }
903}
904
905PLAN *
906c_fstype(char *arg, char ***ignored, int unused)
907{
908 PLAN *new;
909
910 ftsoptions &= ~FTS_NOSTAT0x0008;
911
912 new = palloc(N_FSTYPE, f_fstype);
913 switch (*arg) {
914 case 'l':
915 if (!strcmp(arg, "local")) {
916 new->flags = F_MTFLAG1;
917 new->mt_datap_un._mt_data = MNT_LOCAL0x00001000;
918 return (new);
919 }
920 break;
921 case 'r':
922 if (!strcmp(arg, "rdonly")) {
923 new->flags = F_MTFLAG1;
924 new->mt_datap_un._mt_data = MNT_RDONLY0x00000001;
925 return (new);
926 }
927 break;
928 }
929
930 new->flags = F_MTTYPE2;
931 new->c_datap_un._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 */
942int
943f_group(PLAN *plan, FTSENT *entry)
944{
945 return (entry->fts_statp->st_gid == plan->g_datap_un._g_data);
946}
947
948PLAN *
949c_group(char *gname, char ***ignored, int unused)
950{
951 PLAN *new;
952 gid_t gid;
953
954 ftsoptions &= ~FTS_NOSTAT0x0008;
955
956 if (gid_from_group(gname, &gid) == -1) {
957 const char *errstr;
958
959 gid = strtonum(gname, 0, GID_MAX(2147483647 *2U +1U), &errstr);
960 if (errstr)
961 errx(1, "-group: %s: no such group", gname);
962 }
963
964 new = palloc(N_GROUP, f_group);
965 new->g_datap_un._g_data = gid;
966 return (new);
967}
968
969/*
970 * -inum n functions --
971 *
972 * True if the file has inode # n.
973 */
974int
975f_inum(PLAN *plan, FTSENT *entry)
976{
977 COMPARE(entry->fts_statp->st_ino, plan->i_data){ switch (plan->flags) { case 1: return (entry->fts_statp
->st_ino == plan->p_un._i_data); case 2: return (entry->
fts_statp->st_ino < plan->p_un._i_data); case 3: return
(entry->fts_statp->st_ino > plan->p_un._i_data);
default: abort(); } }
;
978}
979
980PLAN *
981c_inum(char *arg, char ***ignored, int unused)
982{
983 long long inum;
984 PLAN *new;
985
986 ftsoptions &= ~FTS_NOSTAT0x0008;
987
988 new = palloc(N_INUM, f_inum);
989 inum = find_parsenum(new, "-inum", arg, NULL((void *)0));
990 if (inum != (ino_t)inum)
991 errx(1, "-inum: %s: number too great", arg);
992 new->i_datap_un._i_data = inum;
993 return (new);
994}
995
996/*
997 * -links n functions --
998 *
999 * True if the file has n links.
1000 */
1001int
1002f_links(PLAN *plan, FTSENT *entry)
1003{
1004 COMPARE(entry->fts_statp->st_nlink, plan->l_data){ switch (plan->flags) { case 1: return (entry->fts_statp
->st_nlink == plan->p_un._l_data); case 2: return (entry
->fts_statp->st_nlink < plan->p_un._l_data); case
3: return (entry->fts_statp->st_nlink > plan->p_un
._l_data); default: abort(); } }
;
1005}
1006
1007PLAN *
1008c_links(char *arg, char ***ignored, int unused)
1009{
1010 PLAN *new;
1011 long long nlink;
1012
1013 ftsoptions &= ~FTS_NOSTAT0x0008;
1014
1015 new = palloc(N_LINKS, f_links);
1016 nlink = find_parsenum(new, "-links", arg, NULL((void *)0));
1017 if (nlink != (nlink_t)nlink)
1018 errx(1, "-links: %s: number too great", arg);
1019 new->l_datap_un._l_data = nlink;
1020 return (new);
1021}
1022
1023/*
1024 * -ls functions --
1025 *
1026 * Always true - prints the current entry to stdout in "ls" format.
1027 */
1028int
1029f_ls(PLAN *plan, FTSENT *entry)
1030{
1031 printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp);
1032 return (1);
1033}
1034
1035PLAN *
1036c_ls(char *ignore, char ***ignored, int unused)
1037{
1038 ftsoptions &= ~FTS_NOSTAT0x0008;
1039 isoutput = 1;
1040
1041 return (palloc(N_LS, f_ls));
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 */
1050int
1051f_maxdepth(PLAN *plan, FTSENT *entry)
1052{
1053
1054 if (entry->fts_level >= plan->max_datap_un._max_data)
1055 fts_set(tree, entry, FTS_SKIP4);
1056 return (entry->fts_level <= plan->max_datap_un._max_data);
1057}
1058
1059PLAN *
1060c_maxdepth(char *arg, char ***ignored, int unused)
1061{
1062 PLAN *new;
1063 const char *errstr = NULL((void *)0);
1064
1065 new = palloc(N_MAXDEPTH, f_maxdepth);
1066 new->max_datap_un._max_data = strtonum(arg, 0, FTS_MAXLEVEL0x7fffffff, &errstr);
1067 if (errstr)
1068 errx(1, "%s: maxdepth value %s", arg, errstr);
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 */
1078int
1079f_mindepth(PLAN *plan, FTSENT *entry)
1080{
1081
1082 return (entry->fts_level >= plan->min_datap_un._min_data);
1083}
1084
1085PLAN *
1086c_mindepth(char *arg, char ***ignored, int unused)
1087{
1088 PLAN *new;
1089 const char *errstr = NULL((void *)0);
1090
1091 new = palloc(N_MINDEPTH, f_mindepth);
1092 new->min_datap_un._min_data = strtonum(arg, 0, INT_MAX2147483647, &errstr);
1093 if (errstr)
1094 errx(1, "-mindepth: %s: value %s", arg, errstr);
1095 return (new);
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 */
1104int
1105f_mtime(PLAN *plan, FTSENT *entry)
1106{
1107
1108 COMPARE((now - entry->fts_statp->st_mtime + SECSPERDAY - 1) /{ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_mtim.tv_sec + (24 * 60 * 60) - 1) / (24 * 60
* 60) == plan->p_un._t_data.tv_sec); case 2: return ((now
- entry->fts_statp->st_mtim.tv_sec + (24 * 60 * 60) - 1
) / (24 * 60 * 60) < plan->p_un._t_data.tv_sec); case 3
: return ((now - entry->fts_statp->st_mtim.tv_sec + (24
* 60 * 60) - 1) / (24 * 60 * 60) > plan->p_un._t_data.
tv_sec); default: abort(); } }
1109 SECSPERDAY, plan->sec_data){ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_mtim.tv_sec + (24 * 60 * 60) - 1) / (24 * 60
* 60) == plan->p_un._t_data.tv_sec); case 2: return ((now
- entry->fts_statp->st_mtim.tv_sec + (24 * 60 * 60) - 1
) / (24 * 60 * 60) < plan->p_un._t_data.tv_sec); case 3
: return ((now - entry->fts_statp->st_mtim.tv_sec + (24
* 60 * 60) - 1) / (24 * 60 * 60) > plan->p_un._t_data.
tv_sec); default: abort(); } }
;
1110}
1111
1112PLAN *
1113c_mtime(char *arg, char ***ignored, int unused)
1114{
1115 PLAN *new;
1116
1117 ftsoptions &= ~FTS_NOSTAT0x0008;
1118
1119 new = palloc(N_MTIME, f_mtime);
1120 new->sec_datap_un._t_data.tv_sec = find_parsenum(new, "-mtime", arg, NULL((void *)0));
1121 TIME_CORRECT(new, N_MTIME)if ((new)->type == N_MTIME && (new)->flags == 2
) ++((new)->p_un._t_data.tv_sec);
;
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 */
1131int
1132f_mmin(PLAN *plan, FTSENT *entry)
1133{
1134 extern time_t now;
1135
1136 COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) /{ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_mtim.tv_sec + 60 - 1) / 60 == plan->p_un.
_t_data.tv_sec); case 2: return ((now - entry->fts_statp->
st_mtim.tv_sec + 60 - 1) / 60 < plan->p_un._t_data.tv_sec
); case 3: return ((now - entry->fts_statp->st_mtim.tv_sec
+ 60 - 1) / 60 > plan->p_un._t_data.tv_sec); default: abort
(); } }
1137 60, plan->sec_data){ switch (plan->flags) { case 1: return ((now - entry->
fts_statp->st_mtim.tv_sec + 60 - 1) / 60 == plan->p_un.
_t_data.tv_sec); case 2: return ((now - entry->fts_statp->
st_mtim.tv_sec + 60 - 1) / 60 < plan->p_un._t_data.tv_sec
); case 3: return ((now - entry->fts_statp->st_mtim.tv_sec
+ 60 - 1) / 60 > plan->p_un._t_data.tv_sec); default: abort
(); } }
;
1138}
1139
1140PLAN *
1141c_mmin(char *arg, char ***ignored, int unused)
1142{
1143 PLAN *new;
1144
1145 ftsoptions &= ~FTS_NOSTAT0x0008;
1146
1147 new = palloc(N_MMIN, f_mmin);
1148 new->sec_datap_un._t_data.tv_sec = find_parsenum(new, "-mmin", arg, NULL((void *)0));
1149 TIME_CORRECT(new, N_MMIN)if ((new)->type == N_MMIN && (new)->flags == 2)
++((new)->p_un._t_data.tv_sec);
;
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 */
1159int
1160f_name(PLAN *plan, FTSENT *entry)
1161{
1162 return (!fnmatch(plan->c_datap_un._c_data, entry->fts_name, 0));
1163}
1164
1165PLAN *
1166c_name(char *pattern, char ***ignored, int unused)
1167{
1168 PLAN *new;
1169
1170 new = palloc(N_NAME, f_name);
1171 new->c_datap_un._c_data = pattern;
1172 return (new);
1173}
1174
1175/*
1176 * -iname functions --
1177 *
1178 * Similar to -name, but does case insensitive matching
1179 *
1180 */
1181int
1182f_iname(PLAN *plan, FTSENT *entry)
1183{
1184 return (!fnmatch(plan->c_datap_un._c_data, entry->fts_name, FNM_CASEFOLD0x10));
1185}
1186
1187PLAN *
1188c_iname(char *pattern, char ***ignored, int unused)
1189{
1190 PLAN *new;
1191
1192 new = palloc(N_INAME, f_iname);
1193 new->c_datap_un._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 */
1204int
1205f_newer(PLAN *plan, FTSENT *entry)
1206{
1207
1208 return (entry->fts_statp->st_mtimespecst_mtim.tv_sec > plan->t_datap_un._t_data.tv_sec ||
1209 (entry->fts_statp->st_mtimespecst_mtim.tv_sec == plan->t_datap_un._t_data.tv_sec &&
1210 entry->fts_statp->st_mtimespecst_mtim.tv_nsec > plan->t_datap_un._t_data.tv_nsec));
1211}
1212
1213PLAN *
1214c_newer(char *filename, char ***ignored, int unused)
1215{
1216 PLAN *new;
1217 struct stat sb;
1218
1219 ftsoptions &= ~FTS_NOSTAT0x0008;
1220
1221 if (stat(filename, &sb))
1222 err(1, "%s", filename);
1223 new = palloc(N_NEWER, f_newer);
1224 memcpy(&new->t_datap_un._t_data, &sb.st_mtimespecst_mtim, 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 */
1235int
1236f_anewer(PLAN *plan, FTSENT *entry)
1237{
1238
1239 return (entry->fts_statp->st_atimespecst_atim.tv_sec > plan->t_datap_un._t_data.tv_sec ||
1240 (entry->fts_statp->st_atimespecst_atim.tv_sec == plan->t_datap_un._t_data.tv_sec &&
1241 entry->fts_statp->st_atimespecst_atim.tv_nsec > plan->t_datap_un._t_data.tv_nsec));
1242}
1243
1244PLAN *
1245c_anewer(char *filename, char ***ignored, int unused)
1246{
1247 PLAN *new;
1248 struct stat sb;
1249
1250 ftsoptions &= ~FTS_NOSTAT0x0008;
1251
1252 if (stat(filename, &sb))
1253 err(1, "%s", filename);
1254 new = palloc(N_NEWER, f_anewer);
1255 memcpy(&new->t_datap_un._t_data, &sb.st_atimespecst_atim, 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 */
1266int
1267f_cnewer(PLAN *plan, FTSENT *entry)
1268{
1269
1270 return (entry->fts_statp->st_ctimespecst_ctim.tv_sec > plan->t_datap_un._t_data.tv_sec ||
1271 (entry->fts_statp->st_ctimespecst_ctim.tv_sec == plan->t_datap_un._t_data.tv_sec &&
1272 entry->fts_statp->st_ctimespecst_ctim.tv_nsec > plan->t_datap_un._t_data.tv_nsec));
1273}
1274
1275PLAN *
1276c_cnewer(char *filename, char ***ignored, int unused)
1277{
1278 PLAN *new;
1279 struct stat sb;
1280
1281 ftsoptions &= ~FTS_NOSTAT0x0008;
1282
1283 if (stat(filename, &sb))
1284 err(1, "%s", filename);
1285 new = palloc(N_NEWER, f_cnewer);
1286 memcpy(&new->t_datap_un._t_data, &sb.st_ctimespecst_ctim, sizeof(struct timespec));
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 */
1296int
1297f_nogroup(PLAN *plan, FTSENT *entry)
1298{
1299 return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1);
1300}
1301
1302PLAN *
1303c_nogroup(char *ignore, char ***ignored, int unused)
1304{
1305 ftsoptions &= ~FTS_NOSTAT0x0008;
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 */
1316int
1317f_nouser(PLAN *plan, FTSENT *entry)
1318{
1319 return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1);
1320}
1321
1322PLAN *
1323c_nouser(char *ignore, char ***ignored, int unused)
1324{
1325 ftsoptions &= ~FTS_NOSTAT0x0008;
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 */
1336int
1337f_path(PLAN *plan, FTSENT *entry)
1338{
1339 return (!fnmatch(plan->c_datap_un._c_data, entry->fts_path, 0));
1340}
1341
1342PLAN *
1343c_path(char *pattern, char ***ignored, int unused)
1344{
1345 PLAN *new;
1346
1347 new = palloc(N_NAME, f_path);
1348 new->c_datap_un._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 */
1359int
1360f_perm(PLAN *plan, FTSENT *entry)
1361{
1362 mode_t mode;
1363
1364 mode = entry->fts_statp->st_mode &
1365 (S_ISUID0004000|S_ISGID0002000|S_ISTXT0001000|S_IRWXU0000700|S_IRWXG0000070|S_IRWXO0000007);
1366 if (plan->flags == F_ATLEAST1)
1367 return ((plan->m_datap_un._m_data | mode) == mode);
1368 else
1369 return (mode == plan->m_datap_un._m_data);
1370 /* NOTREACHED */
1371}
1372
1373PLAN *
1374c_perm(char *perm, char ***ignored, int unused)
1375{
1376 PLAN *new;
1377 void *set;
1378
1379 ftsoptions &= ~FTS_NOSTAT0x0008;
1380
1381 new = palloc(N_PERM, f_perm);
1382
1383 if (*perm == '-') {
1384 new->flags = F_ATLEAST1;
1385 ++perm;
1386 }
1387
1388 if ((set = setmode(perm)) == NULL((void *)0))
1389 errx(1, "-perm: %s: illegal mode string", perm);
1390
1391 new->m_datap_un._m_data = getmode(set, 0);
1392 free(set);
1393 return (new);
1394}
1395
1396/*
1397 * -print functions --
1398 *
1399 * Always true, causes the current pathame to be written to
1400 * standard output.
1401 */
1402int
1403f_print(PLAN *plan, FTSENT *entry)
1404{
1405 (void)printf("%s\n", entry->fts_path);
1406 return(1);
1407}
1408
1409/* ARGSUSED */
1410int
1411f_print0(PLAN *plan, FTSENT *entry)
1412{
1413 (void)fputs(entry->fts_path, stdout(&__sF[1]));
1414 (void)fputc('\0', stdout(&__sF[1]));
1415 return(1);
1416}
1417
1418PLAN *
1419c_print(char *ignore, char ***ignored, int unused)
1420{
1421 isoutput = 1;
1422
1423 return(palloc(N_PRINT, f_print));
1424}
1425
1426PLAN *
1427c_print0(char *ignore, char ***ignored, int unused)
1428{
1429 isoutput = 1;
1430
1431 return(palloc(N_PRINT0, f_print0));
1432}
1433
1434/*
1435 * -prune functions --
1436 *
1437 * Prune a portion of the hierarchy.
1438 */
1439int
1440f_prune(PLAN *plan, FTSENT *entry)
1441{
1442
1443 if (fts_set(tree, entry, FTS_SKIP4))
1444 err(1, "%s", entry->fts_path);
1445 return (1);
1446}
1447
1448PLAN *
1449c_prune(char *ignore, char ***ignored, int unused)
1450{
1451 return (palloc(N_PRUNE, f_prune));
1452}
1453
1454/*
1455 * -size n[c] functions --
1456 *
1457 * True if the file size in bytes, divided by an implementation defined
1458 * value and rounded up to the next integer, is n. If n is followed by
1459 * a c, the size is in bytes.
1460 */
1461#define FIND_SIZE512 512
1462static int divsize = 1;
1463
1464int
1465f_size(PLAN *plan, FTSENT *entry)
1466{
1467 off_t size;
1468
1469 size = divsize ? (entry->fts_statp->st_size + FIND_SIZE512 - 1) /
1470 FIND_SIZE512 : entry->fts_statp->st_size;
1471 COMPARE(size, plan->o_data){ switch (plan->flags) { case 1: return (size == plan->
p_un._o_data); case 2: return (size < plan->p_un._o_data
); case 3: return (size > plan->p_un._o_data); default:
abort(); } }
;
1472}
1473
1474PLAN *
1475c_size(char *arg, char ***ignored, int unused)
1476{
1477 PLAN *new;
1478 char endch;
1479
1480 ftsoptions &= ~FTS_NOSTAT0x0008;
1481
1482 new = palloc(N_SIZE, f_size);
1483 endch = 'c';
1484 new->o_datap_un._o_data = find_parsenum(new, "-size", arg, &endch);
1485 if (endch == 'c')
1486 divsize = 0;
1487 return (new);
1488}
1489
1490/*
1491 * -type c functions --
1492 *
1493 * True if the type of the file is c, where c is b, c, d, p, or f for
1494 * block special file, character special file, directory, FIFO, or
1495 * regular file, respectively.
1496 */
1497int
1498f_type(PLAN *plan, FTSENT *entry)
1499{
1500 return ((entry->fts_statp->st_mode & S_IFMT0170000) == plan->m_datap_un._m_data);
1501}
1502
1503PLAN *
1504c_type(char *typestring, char ***ignored, int unused)
1505{
1506 PLAN *new;
1507 mode_t mask;
1508
1509 ftsoptions &= ~FTS_NOSTAT0x0008;
1510
1511 switch (typestring[0]) {
1512 case 'b':
1513 mask = S_IFBLK0060000;
1514 break;
1515 case 'c':
1516 mask = S_IFCHR0020000;
1517 break;
1518 case 'd':
1519 mask = S_IFDIR0040000;
1520 break;
1521 case 'f':
1522 mask = S_IFREG0100000;
1523 break;
1524 case 'l':
1525 mask = S_IFLNK0120000;
1526 break;
1527 case 'p':
1528 mask = S_IFIFO0010000;
1529 break;
1530 case 's':
1531 mask = S_IFSOCK0140000;
1532 break;
1533 default:
1534 errx(1, "-type: %s: unknown type", typestring);
1535 }
1536
1537 new = palloc(N_TYPE, f_type);
1538 new->m_datap_un._m_data = mask;
1539 return (new);
1540}
1541
1542/*
1543 * -user uname functions --
1544 *
1545 * True if the file belongs to the user uname. If uname is numeric and
1546 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not
1547 * return a valid user name, uname is taken as a user ID.
1548 */
1549int
1550f_user(PLAN *plan, FTSENT *entry)
1551{
1552 return (entry->fts_statp->st_uid == plan->u_datap_un._u_data);
1553}
1554
1555PLAN *
1556c_user(char *username, char ***ignored, int unused)
1557{
1558 PLAN *new;
1559 uid_t uid;
1560
1561 ftsoptions &= ~FTS_NOSTAT0x0008;
1562
1563 if (uid_from_user(username, &uid) == -1) {
1564 const char *errstr;
1565
1566 uid = strtonum(username, 0, UID_MAX(2147483647 *2U +1U), &errstr);
1567 if (errstr)
1568 errx(1, "-user: %s: no such user", username);
1569 }
1570
1571 new = palloc(N_USER, f_user);
1572 new->u_datap_un._u_data = uid;
1573 return (new);
1574}
1575
1576/*
1577 * -xdev functions --
1578 *
1579 * Always true, causes find not to decend past directories that have a
1580 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1])
1581 */
1582PLAN *
1583c_xdev(char *ignore, char ***ignored, int unused)
1584{
1585 ftsoptions |= FTS_XDEV0x0040;
1586
1587 return (palloc(N_XDEV, f_always_true));
1588}
1589
1590/*
1591 * ( expression ) functions --
1592 *
1593 * True if expression is true.
1594 */
1595int
1596f_expr(PLAN *plan, FTSENT *entry)
1597{
1598 PLAN *p;
1599 int state;
1600
1601 for (p = plan->p_datap_un._p_data[0];
1602 p && (state = (p->eval)(p, entry)); p = p->next);
1603 return (state);
1604}
1605
1606/*
1607 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are
1608 * eliminated during phase 2 of find_formplan() --- the '(' node is converted
1609 * to a N_EXPR node containing the expression and the ')' node is discarded.
1610 */
1611PLAN *
1612c_openparen(char *ignore, char ***ignored, int unused)
1613{
1614 return (palloc(N_OPENPAREN, (int (*)(PLAN *, FTSENT *))-1));
1615}
1616
1617PLAN *
1618c_closeparen(char *ignore, char ***ignored, int unused)
1619{
1620 return (palloc(N_CLOSEPAREN, (int (*)(PLAN *, FTSENT *))-1));
1621}
1622
1623/*
1624 * ! expression functions --
1625 *
1626 * Negation of a primary; the unary NOT operator.
1627 */
1628int
1629f_not(PLAN *plan, FTSENT *entry)
1630{
1631 PLAN *p;
1632 int state;
1633
1634 for (p = plan->p_datap_un._p_data[0];
1635 p && (state = (p->eval)(p, entry)); p = p->next);
1636 return (!state);
1637}
1638
1639PLAN *
1640c_not(char *ignore, char ***ignored, int unused)
1641{
1642 return (palloc(N_NOT, f_not));
1643}
1644
1645/*
1646 * expression -o expression functions --
1647 *
1648 * Alternation of primaries; the OR operator. The second expression is
1649 * not evaluated if the first expression is true.
1650 */
1651int
1652f_or(PLAN *plan, FTSENT *entry)
1653{
1654 PLAN *p;
1655 int state;
1656
1657 for (p = plan->p_datap_un._p_data[0];
1658 p && (state = (p->eval)(p, entry)); p = p->next);
1659
1660 if (state)
1661 return (1);
1662
1663 for (p = plan->p_datap_un._p_data[1];
1664 p && (state = (p->eval)(p, entry)); p = p->next);
1665 return (state);
1666}
1667
1668PLAN *
1669c_or(char *ignore, char ***ignored, int unused)
1670{
1671 return (palloc(N_OR, f_or));
1672}
1673
1674
1675/*
1676 * plan_cleanup --
1677 * Check and see if the specified plan has any residual state,
1678 * and if so, clean it up as appropriate.
1679 *
1680 * At the moment, only N_EXEC has state. Two kinds: 1)
1681 * lists of files to feed to subprocesses 2) State on exit
1682 * statusses of past subprocesses.
1683 */
1684/* ARGSUSED1 */
1685int
1686plan_cleanup(PLAN *plan, void *arg)
1687{
1688 if (plan->type==N_EXEC && plan->ep_nargp_un.ex._ep_narg)
1689 run_f_exec(plan);
1690
1691 return plan->ep_rvalp_un.ex._ep_rval; /* Passed save exit-status up chain */
1692}
1693
1694
1695static PLAN *
1696palloc(enum ntype t, int (*f)(PLAN *, FTSENT *))
1697{
1698 PLAN *new;
1699
1700 if ((new = calloc(1, sizeof(PLAN)))) {
1701 new->type = t;
1702 new->eval = f;
1703 return (new);
1704 }
1705 err(1, NULL((void *)0));
1706 /* NOTREACHED */
1707}