xref: /illumos-gate/usr/src/cmd/find/find.c (revision 09bb4cf9da831896c9f17460ac8d5d482f7f4de0)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
24  * Copyright (c) 2013 Andrew Stormont.  All rights reserved.
25  * Copyright 2020 Joyent, Inc.
26  * Copyright 2024 Bill Sommerfeld <sommerfeld@hamachi.org>
27  * Copyright 2026 Oxide Computer Company
28  */
29 
30 
31 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
32 /*	  All Rights Reserved	*/
33 
34 
35 /*	Parts of this product may be derived from		*/
36 /*	Mortice Kern Systems Inc. and Berkeley 4.3 BSD systems.	*/
37 /*	licensed from  Mortice Kern Systems Inc. and		*/
38 /*	the University of California.				*/
39 
40 /*
41  * Copyright 1985, 1990 by Mortice Kern Systems Inc.  All rights reserved.
42  */
43 
44 #include <stdio.h>
45 #include <err.h>
46 #include <errno.h>
47 #include <pwd.h>
48 #include <grp.h>
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <sys/param.h>
52 #include <sys/acl.h>
53 #include <aclutils.h>
54 #include <limits.h>
55 #include <spawn.h>
56 #include <unistd.h>
57 #include <stdlib.h>
58 #include <locale.h>
59 #include <string.h>
60 #include <strings.h>
61 #include <ctype.h>
62 #include <wait.h>
63 #include <fnmatch.h>
64 #include <langinfo.h>
65 #include <ftw.h>
66 #include <libgen.h>
67 #include <err.h>
68 #include <regex.h>
69 #include "getresponse.h"
70 
71 #define	A_DAY		(long)(60*60*24)	/* a day full of seconds */
72 #define	A_MIN		(long)(60)
73 #define	BLKSIZ		512
74 #define	round(x, s)	(((x)+(s)-1)&~((s)-1))
75 #ifndef FTW_SLN
76 #define	FTW_SLN		7
77 #endif
78 #define	LINEBUF_SIZE		LINE_MAX	/* input or output lines */
79 #define	REMOTE_FS		"/etc/dfs/fstypes"
80 #define	N_FSTYPES		20
81 
82 /*
83  * This is the list of operations
84  * F_USER and F_GROUP are named to avoid conflict with USER and GROUP defined
85  * in sys/acl.h
86  */
87 
88 enum Command
89 {
90 	PRINT,
91 	ACL, AMIN, AND, ATIME, CMIN, CPIO, CSIZE, CTIME, DEPTH, EXEC, F_GROUP,
92 	GROUPACL, GSID, GSIDACL, F_USER, USERACL, USID, USIDACL, FOLLOW,
93 	FSTYPE, INAME, INUM, IPATH, IREGEX, LINKS, LOCAL, LPAREN, LS, MAXDEPTH,
94 	MINDEPTH, MMIN, MOUNT, MTIME, NAME, NCPIO, NEWER, NOGRP, NOT, NOUSER,
95 	OK, OR, PATH, PERM, PRINT0, PRUNE, REGEX, RPAREN, SIDACL, SIZE, TYPE,
96 	VARARGS, XATTR, DELETE
97 };
98 
99 enum Type
100 {
101 	Unary, Id, Num, Str, Exec, Cpio, Op
102 };
103 
104 struct Args
105 {
106 	char		name[10];
107 	enum Command	action;
108 	enum Type	type;
109 };
110 
111 /*
112  * Except for pathnames, these are the only legal arguments
113  */
114 static const struct Args commands[] =
115 {
116 	"!",		NOT,		Op,
117 	"(",		LPAREN,		Unary,
118 	")",		RPAREN,		Unary,
119 	"-a",		AND,		Op,
120 	"-acl",		ACL,		Unary,
121 	"-amin",	AMIN,		Num,
122 	"-and",		AND,		Op,
123 	"-atime",	ATIME,		Num,
124 	"-cmin",	CMIN,		Num,
125 	"-cpio",	CPIO,		Cpio,
126 	"-ctime",	CTIME,		Num,
127 	"-depth",	DEPTH,		Unary,
128 	"-delete",	DELETE,		Unary,
129 	"-exec",	EXEC,		Exec,
130 	"-follow",	FOLLOW,		Unary,
131 	"-fstype",	FSTYPE,		Str,
132 	"-group",	F_GROUP,	Num,
133 	"-groupacl",	GROUPACL,	Num,
134 	"-gsid",	GSID,		Num,
135 	"-gsidacl",	GSIDACL,	Num,
136 	"-iname",	INAME,		Str,
137 	"-inum",	INUM,		Num,
138 	"-ipath",	IPATH,		Str,
139 	"-iregex",	IREGEX,		Str,
140 	"-links",	LINKS,		Num,
141 	"-local",	LOCAL,		Unary,
142 	"-ls",		LS,		Unary,
143 	"-maxdepth",	MAXDEPTH,	Num,
144 	"-mindepth",	MINDEPTH,	Num,
145 	"-mmin",	MMIN,		Num,
146 	"-mount",	MOUNT,		Unary,
147 	"-mtime",	MTIME,		Num,
148 	"-name",	NAME,		Str,
149 	"-ncpio",	NCPIO,		Cpio,
150 	"-newer",	NEWER,		Str,
151 	"-nogroup",	NOGRP,		Unary,
152 	"-not",		NOT,		Op,
153 	"-nouser",	NOUSER,		Unary,
154 	"-o",		OR,		Op,
155 	"-ok",		OK,		Exec,
156 	"-or",		OR,		Op,
157 	"-path",	PATH,		Str,
158 	"-perm",	PERM,		Num,
159 	"-print",	PRINT,		Unary,
160 	"-print0",	PRINT0,		Unary,
161 	"-prune",	PRUNE,		Unary,
162 	"-regex",	REGEX,		Str,
163 	"-sidacl",	SIDACL,		Num,
164 	"-size",	SIZE,		Num,
165 	"-type",	TYPE,		Num,
166 	"-user",	F_USER,		Num,
167 	"-useracl",	USERACL,	Num,
168 	"-usid",	USID,		Num,
169 	"-usidacl",	USIDACL,	Num,
170 	"-xattr",	XATTR,		Unary,
171 	"-xdev",	MOUNT,		Unary,
172 	0,		0,		0
173 };
174 
175 union Item
176 {
177 	struct Node	*np;
178 	struct Arglist	*vp;
179 	time_t		t;
180 	char		*cp;
181 	char		**ap;
182 	long		l;
183 	int		i;
184 	long long	ll;
185 };
186 
187 struct Node
188 {
189 	struct Node	*next;
190 	enum Command	action;
191 	enum Type	type;
192 	union Item	first;
193 	union Item	second;
194 };
195 
196 /* if no -print, -exec or -ok replace "expression" with "(expression) -print" */
197 static	struct	Node PRINT_NODE = { 0, PRINT, 0, 0};
198 static	struct	Node LPAREN_NODE = { 0, LPAREN, 0, 0};
199 
200 
201 /*
202  * Prototype variable size arglist buffer
203  */
204 
205 struct Arglist
206 {
207 	struct Arglist	*next;
208 	char		*end;
209 	char		*nextstr;
210 	char		**firstvar;
211 	char		**nextvar;
212 	char		*arglist[1];
213 };
214 
215 
216 static int		compile(char **, struct Node *, int *);
217 static int		execute(const char *, const struct stat *, int,
218     struct FTW *);
219 static int		doexec(const char *, char **, int *);
220 static int		dodelete(const char *, const struct stat *,
221     struct FTW *);
222 static const struct Args *lookup(char *);
223 static int		ok(const char *, char *[]);
224 static void		usage(void)	__NORETURN;
225 static struct Arglist	*varargs(char **);
226 static int		list(const char *, const struct stat *);
227 static char		*getgroup(gid_t);
228 static FILE		*cmdopen(char *, char **, char *, FILE *);
229 static int		cmdclose(FILE *);
230 static void		init_remote_fs(void);
231 static char		*getname(uid_t);
232 static int		readmode(const char *);
233 static mode_t		getmode(mode_t);
234 static const char	*gettail(const char *);
235 
236 
237 static int walkflags = FTW_CHDIR|FTW_PHYS|FTW_ANYERR|FTW_NOLOOP;
238 static struct Node	*topnode;
239 static struct Node	*freenode;	/* next free node we may use later */
240 static char		*cpio[] = { "cpio", "-o", 0 };
241 static char		*ncpio[] = { "cpio", "-oc", 0 };
242 static char		*cpiol[] = { "cpio", "-oL", 0 };
243 static char		*ncpiol[] = { "cpio", "-ocL", 0 };
244 static time_t		now;
245 static FILE		*output;
246 static char		*dummyarg = (char *)-1;
247 static int		lastval;
248 static int		varsize;
249 static struct Arglist	*lastlist;
250 static char		*cmdname;
251 static char		*remote_fstypes[N_FSTYPES+1];
252 static int		fstype_index = 0;
253 static int		action_expression = 0;	/* -print, -exec, or -ok */
254 static int		error = 0;
255 static int		paren_cnt = 0;	/* keeps track of parentheses */
256 static int		Eflag = 0;
257 static int		hflag = 0;
258 static int		lflag = 0;
259 /* set when doexec()-invoked utility returns non-zero */
260 static int		exec_exitcode = 0;
261 static regex_t		*preg = NULL;
262 static int		npreg = 0;
263 static int		mindepth = -1, maxdepth = -1;
264 extern char		**environ;
265 
266 int
main(int argc,char ** argv)267 main(int argc, char **argv)
268 {
269 	char *cp;
270 	int c;
271 	int paths;
272 	char *cwdpath;
273 
274 	(void) setlocale(LC_ALL, "");
275 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
276 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
277 #endif
278 	(void) textdomain(TEXT_DOMAIN);
279 
280 	cmdname = argv[0];
281 	if (time(&now) == (time_t)(-1)) {
282 		(void) fprintf(stderr, gettext("%s: time() %s\n"),
283 		    cmdname, strerror(errno));
284 		exit(1);
285 	}
286 	while ((c = getopt(argc, argv, "EHL")) != -1) {
287 		switch (c) {
288 		case 'E':
289 			Eflag = 1;
290 			break;
291 		case 'H':
292 			hflag = 1;
293 			lflag = 0;
294 			break;
295 		case 'L':
296 			hflag = 0;
297 			lflag = 1;
298 			break;
299 		case '?':
300 			usage();
301 			break;
302 		}
303 	}
304 
305 	argc -= optind;
306 	argv += optind;
307 
308 	if (argc < 1) {
309 		(void) fprintf(stderr,
310 		    gettext("%s: insufficient number of arguments\n"), cmdname);
311 		usage();
312 	}
313 
314 	for (paths = 0; (cp = argv[paths]) != 0; ++paths) {
315 		if (*cp == '-')
316 			break;
317 		else if ((*cp == '!' || *cp == '(') && *(cp+1) == 0)
318 			break;
319 	}
320 
321 	if (paths == 0) /* no path-list */
322 		usage();
323 
324 	output = stdout;
325 
326 	/* lflag is the same as -follow */
327 	if (lflag)
328 		walkflags &= ~FTW_PHYS;
329 
330 	/* allocate enough space for the compiler */
331 	topnode = malloc((argc + 1) * sizeof (struct Node));
332 	(void) memset(topnode, 0, (argc + 1) * sizeof (struct Node));
333 
334 	if (compile(argv + paths, topnode, &action_expression) == 0) {
335 		/* no expression, default to -print */
336 		(void) memcpy(topnode, &PRINT_NODE, sizeof (struct Node));
337 	} else if (!action_expression) {
338 		/*
339 		 * if no action expression, insert an LPAREN node above topnode,
340 		 * with a PRINT node as its next node
341 		 */
342 		struct Node *savenode;
343 
344 		if (freenode == NULL) {
345 			(void) fprintf(stderr, gettext("%s: can't append -print"
346 			    " implicitly; try explicit -print option\n"),
347 			    cmdname);
348 			exit(1);
349 		}
350 		savenode = topnode;
351 		topnode = freenode++;
352 		(void) memcpy(topnode, &LPAREN_NODE, sizeof (struct Node));
353 		topnode->next = freenode;
354 		topnode->first.np = savenode;
355 		(void) memcpy(topnode->next, &PRINT_NODE, sizeof (struct Node));
356 	}
357 
358 	while (paths--) {
359 		char *curpath;
360 		struct stat sb;
361 
362 		curpath = *(argv++);
363 
364 		/*
365 		 * If -H is specified, it means we walk the first
366 		 * level (pathname on command line) logically, following
367 		 * symlinks, but lower levels are walked physically.
368 		 * We use our own secret interface to nftw() to change
369 		 * the from stat to lstat after the top level is walked.
370 		 */
371 		if (hflag) {
372 			if (stat(curpath, &sb) < 0 && errno == ENOENT)
373 				walkflags &= ~FTW_HOPTION;
374 			else
375 				walkflags |= FTW_HOPTION;
376 		}
377 
378 		/*
379 		 * We need this check as nftw needs a CWD and we have no
380 		 * way of returning back from that code with a meaningful
381 		 * error related to this
382 		 */
383 		if ((cwdpath = getcwd(NULL, PATH_MAX)) == NULL) {
384 			if ((errno == EACCES) && (walkflags & FTW_CHDIR)) {
385 				/*
386 				 * A directory above cwd is inaccessible,
387 				 * so don't do chdir(2)s. Slower, but at least
388 				 * it works.
389 				 */
390 				walkflags &= ~FTW_CHDIR;
391 				free(cwdpath);
392 			} else {
393 				(void) fprintf(stderr,
394 				    gettext("%s : cannot get the current "
395 				    "working directory\n"), cmdname);
396 				exit(1);
397 			}
398 		} else
399 			free(cwdpath);
400 
401 
402 		if (nftw(curpath, execute, 1000, walkflags)) {
403 			(void) fprintf(stderr,
404 			    gettext("%s: cannot open %s: %s\n"),
405 			    cmdname, curpath, strerror(errno));
406 			error = 1;
407 		}
408 
409 	}
410 
411 	/* execute any remaining variable length lists */
412 	while (lastlist) {
413 		if (lastlist->end != lastlist->nextstr) {
414 			*lastlist->nextvar = 0;
415 			(void) doexec(NULL, lastlist->arglist,
416 			    &exec_exitcode);
417 		}
418 		lastlist = lastlist->next;
419 	}
420 	if (output != stdout)
421 		return (cmdclose(output));
422 	return ((exec_exitcode != 0) ? exec_exitcode : error);
423 }
424 
425 /*
426  * compile the arguments
427  */
428 
429 static int
compile(char ** argv,struct Node * np,int * actionp)430 compile(char **argv, struct Node *np, int *actionp)
431 {
432 	char *b;
433 	char **av;
434 	struct Node *oldnp = topnode;
435 	const struct Args *argp;
436 	char **com;
437 	int i;
438 	enum Command wasop = PRINT;
439 
440 	if (init_yes() < 0) {
441 		(void) fprintf(stderr, gettext(ERR_MSG_INIT_YES),
442 		    strerror(errno));
443 		exit(1);
444 	}
445 
446 	for (av = argv; *av && (argp = lookup(*av)); av++) {
447 		np->next = 0;
448 		np->action = argp->action;
449 		np->type = argp->type;
450 		np->second.i = 0;
451 		if (argp->type == Op) {
452 			if (wasop == NOT || (wasop && np->action != NOT)) {
453 				(void) fprintf(stderr,
454 				    gettext("%s: operand follows operand\n"),
455 				    cmdname);
456 				exit(1);
457 			}
458 			if (np->action != NOT && oldnp == 0)
459 				goto err;
460 			wasop = argp->action;
461 		} else {
462 			wasop = PRINT;
463 			if (argp->type != Unary) {
464 				if (!(b = *++av)) {
465 					(void) fprintf(stderr, gettext(
466 					    "%s: incomplete statement\n"),
467 					    cmdname);
468 					exit(1);
469 				}
470 				if (argp->type == Num) {
471 					if (((argp->action == MAXDEPTH) ||
472 					    (argp->action == MINDEPTH)) &&
473 					    ((int)strtol(b, (char **)NULL,
474 					    10) < 0))
475 						errx(1, gettext(
476 						    "%s: value must be "
477 						    "positive"),
478 						    (argp->action == MAXDEPTH) ?
479 						    "maxdepth" : "mindepth");
480 					if ((argp->action != PERM) ||
481 					    (*b != '+')) {
482 						if (*b == '+' || *b == '-') {
483 							np->second.i = *b;
484 							b++;
485 						}
486 					}
487 				}
488 			}
489 		}
490 		switch (argp->action) {
491 		case AND:
492 			break;
493 		case NOT:
494 			break;
495 		case OR:
496 			np->first.np = topnode;
497 			topnode = np;
498 			oldnp->next = 0;
499 			break;
500 
501 		case LPAREN: {
502 			struct Node *save = topnode;
503 			topnode = np+1;
504 			paren_cnt++;
505 			i = compile(++av, topnode, actionp);
506 			np->first.np = topnode;
507 			topnode = save;
508 			av += i;
509 			oldnp = np;
510 			np += i + 1;
511 			oldnp->next = np;
512 			continue;
513 		}
514 
515 		case RPAREN:
516 			if (paren_cnt <= 0) {
517 				(void) fprintf(stderr,
518 				    gettext("%s: unmatched ')'\n"),
519 				    cmdname);
520 				exit(1);
521 			}
522 			paren_cnt--;
523 			if (oldnp == 0)
524 				goto err;
525 			if (oldnp->type == Op) {
526 				(void) fprintf(stderr,
527 				    gettext("%s: cannot immediately"
528 				    " follow an operand with ')'\n"),
529 				    cmdname);
530 				exit(1);
531 			}
532 			oldnp->next = 0;
533 			return (av-argv);
534 
535 		case FOLLOW:
536 			walkflags &= ~FTW_PHYS;
537 			break;
538 		case MOUNT:
539 			walkflags |= FTW_MOUNT;
540 			break;
541 		case DEPTH:
542 			walkflags |= FTW_DEPTH;
543 			break;
544 		case DELETE:
545 			walkflags |= (FTW_DEPTH | FTW_PHYS);
546 			walkflags &= ~FTW_CHDIR;
547 			(*actionp)++;
548 			break;
549 
550 		case LOCAL:
551 			np->first.l = 0L;
552 			np->first.ll = 0LL;
553 			np->second.i = '+';
554 			/*
555 			 * Make it compatible to df -l for
556 			 * future enhancement. So, anything
557 			 * that is not remote, then it is
558 			 * local.
559 			 */
560 			init_remote_fs();
561 			break;
562 
563 		case SIZE:
564 			if (b[strlen(b)-1] == 'c')
565 				np->action = CSIZE;
566 			/*FALLTHROUGH*/
567 		case INUM:
568 			np->first.ll = atoll(b);
569 			break;
570 
571 		case CMIN:
572 		case CTIME:
573 		case MMIN:
574 		case MTIME:
575 		case AMIN:
576 		case ATIME:
577 		case LINKS:
578 			np->first.l = atol(b);
579 			break;
580 
581 		case F_USER:
582 		case F_GROUP:
583 		case USERACL:
584 		case GROUPACL: {
585 			struct	passwd	*pw;
586 			struct	group *gr;
587 			long value;
588 			char *q;
589 
590 			value = -1;
591 			if (argp->action == F_USER ||
592 			    argp->action == USERACL) {
593 				if ((pw = getpwnam(b)) != 0)
594 					value = (long)pw->pw_uid;
595 			} else {
596 				if ((gr = getgrnam(b)) != 0)
597 					value = (long)gr->gr_gid;
598 			}
599 			if (value == -1) {
600 				errno = 0;
601 				value = strtol(b, &q, 10);
602 				if (errno != 0 || q == b || *q != '\0') {
603 					(void) fprintf(stderr, gettext(
604 					    "%s: cannot find %s name\n"),
605 					    cmdname, *av);
606 					exit(1);
607 				}
608 			}
609 			np->first.l = value;
610 			break;
611 		}
612 
613 		case USID:
614 		case GSID:
615 		case USIDACL:
616 		case GSIDACL: {
617 			uid_t value;
618 			boolean_t need_user = ((argp->action == USID) ||
619 			    (argp->action == USIDACL));
620 			value = -1;
621 			if (sid_to_id(b, need_user, &value)) {
622 				(void) fprintf(stderr, gettext(
623 				    "%s: cannot find %s name\n"),
624 				    cmdname, *av);
625 				exit(1);
626 			}
627 			np->first.l = value;
628 
629 			switch (argp->action) {
630 			case USID:
631 				np->action = F_USER;
632 				break;
633 
634 			case GSID:
635 				np->action = F_GROUP;
636 				break;
637 
638 			case USIDACL:
639 				np->action = USERACL;
640 				break;
641 
642 			case GSIDACL:
643 				np->action = GROUPACL;
644 				break;
645 			}
646 			break;
647 		}
648 
649 		case SIDACL: {
650 			uid_t siduid = -1;
651 			uid_t sidgid = -1;
652 			int nouid = sid_to_id(b, B_TRUE, &siduid);
653 			int nogid = sid_to_id(b, B_FALSE, &sidgid);
654 
655 			if (nouid != 0 && nogid != 0) {
656 				(void) fprintf(stderr, gettext(
657 				    "%s: cannot find uid or gid from %s\n"),
658 				    cmdname, *av);
659 				exit(1);
660 			}
661 			if (nouid != 0) {
662 				np->action = GROUPACL;
663 				np->first.l = sidgid;
664 			} else if (nogid != 0) {
665 				np->action = USERACL;
666 				np->first.l = siduid;
667 			} else {
668 				np->first.l = siduid;
669 				np->second.l = sidgid;
670 			}
671 			break;
672 		}
673 
674 		case EXEC:
675 		case OK:
676 			walkflags &= ~FTW_CHDIR;
677 			np->first.ap = av;
678 			(*actionp)++;
679 			for (;;) {
680 				if ((b = *av) == 0) {
681 					(void) fprintf(stderr, gettext(
682 					    "%s: incomplete statement\n"),
683 					    cmdname);
684 					exit(1);
685 				}
686 				if (strcmp(b, ";") == 0) {
687 					*av = 0;
688 					break;
689 				} else if (strcmp(b, "{}") == 0)
690 					*av = dummyarg;
691 				else if (strcmp(b, "+") == 0 &&
692 				    av[-1] == dummyarg && np->action == EXEC) {
693 					av[-1] = 0;
694 					np->first.vp = varargs(np->first.ap);
695 					np->action = VARARGS;
696 					break;
697 				}
698 				av++;
699 			}
700 			break;
701 
702 		case NAME:
703 		case INAME:
704 		case PATH:
705 		case IPATH:
706 			np->first.cp = b;
707 			break;
708 		case REGEX:
709 		case IREGEX: {
710 			int error;
711 			size_t errlen;
712 			char *errmsg;
713 
714 			if ((preg = realloc(preg, (npreg + 1) *
715 			    sizeof (regex_t))) == NULL)
716 				err(1, "realloc");
717 			if ((error = regcomp(&preg[npreg], b,
718 			    ((np->action == IREGEX) ? REG_ICASE : 0) |
719 			    ((Eflag) ? REG_EXTENDED : 0))) != 0) {
720 				errlen = regerror(error, &preg[npreg], NULL, 0);
721 				if ((errmsg = malloc(errlen)) == NULL)
722 					err(1, "malloc");
723 				(void) regerror(error, &preg[npreg], errmsg,
724 				    errlen);
725 				errx(1, gettext("RE error: %s"), errmsg);
726 			}
727 			npreg++;
728 			break;
729 		}
730 		case PERM:
731 			if (*b == '-')
732 				++b;
733 
734 			if (readmode(b) != 0) {
735 				(void) fprintf(stderr, gettext(
736 				    "find: -perm: Bad permission string\n"));
737 				usage();
738 			}
739 			np->first.l = (long)getmode((mode_t)0);
740 			break;
741 		case TYPE:
742 			i = *b;
743 			np->first.l =
744 			    i == 'd' ? S_IFDIR :
745 			    i == 'b' ? S_IFBLK :
746 			    i == 'c' ? S_IFCHR :
747 #ifdef S_IFIFO
748 			    i == 'p' ? S_IFIFO :
749 #endif
750 			    i == 'f' ? S_IFREG :
751 #ifdef S_IFLNK
752 			    i == 'l' ? S_IFLNK :
753 #endif
754 #ifdef S_IFSOCK
755 			    i == 's' ? S_IFSOCK :
756 #endif
757 #ifdef S_IFDOOR
758 			    i == 'D' ? S_IFDOOR :
759 #endif
760 			    0;
761 			break;
762 
763 		case CPIO:
764 			if (walkflags & FTW_PHYS)
765 				com = cpio;
766 			else
767 				com = cpiol;
768 			goto common;
769 
770 		case NCPIO: {
771 			FILE *fd;
772 
773 			if (walkflags & FTW_PHYS)
774 				com = ncpio;
775 			else
776 				com = ncpiol;
777 		common:
778 			/* set up cpio */
779 			if ((fd = fopen(b, "w")) == NULL) {
780 				(void) fprintf(stderr,
781 				    gettext("%s: cannot create %s\n"),
782 				    cmdname, b);
783 				exit(1);
784 			}
785 
786 			np->first.l = (long)cmdopen("cpio", com, "w", fd);
787 			if (np->first.l == 0)
788 				err(1, gettext("cannot run cpio"));
789 			(void) fclose(fd);
790 			walkflags |= FTW_DEPTH;
791 			np->action = CPIO;
792 		}
793 			/*FALLTHROUGH*/
794 		case PRINT:
795 		case PRINT0:
796 			(*actionp)++;
797 			break;
798 
799 		case NEWER: {
800 			struct stat statb;
801 			if (stat(b, &statb) < 0) {
802 				(void) fprintf(stderr,
803 				    gettext("%s: cannot access %s\n"),
804 				    cmdname, b);
805 				exit(1);
806 			}
807 			np->first.l = statb.st_mtime;
808 			np->second.i = '+';
809 			break;
810 		}
811 
812 		case PRUNE:
813 		case NOUSER:
814 		case NOGRP:
815 			break;
816 		case FSTYPE:
817 			np->first.cp = b;
818 			break;
819 		case LS:
820 			(*actionp)++;
821 			break;
822 		case XATTR:
823 			break;
824 		case ACL:
825 			break;
826 		case MAXDEPTH:
827 			maxdepth = (int)strtol(b, NULL, 10);
828 			break;
829 		case MINDEPTH:
830 			mindepth = (int)strtol(b, NULL, 10);
831 			break;
832 		}
833 
834 		oldnp = np++;
835 		oldnp->next = np;
836 	}
837 
838 	if ((*av) || (wasop))
839 		goto err;
840 
841 	if (paren_cnt != 0) {
842 		(void) fprintf(stderr, gettext("%s: unmatched '('\n"), cmdname);
843 		exit(1);
844 	}
845 
846 	/* just before returning, save next free node from the list */
847 	freenode = oldnp->next;
848 	oldnp->next = 0;
849 	return (av-argv);
850 err:
851 	if (*av)
852 		(void) fprintf(stderr,
853 		    gettext("%s: bad option %s\n"), cmdname, *av);
854 	else
855 		(void) fprintf(stderr, gettext("%s: bad option\n"), cmdname);
856 	usage();
857 	/*NOTREACHED*/
858 }
859 
860 /*
861  * print out a usage message
862  */
863 
864 static void
usage(void)865 usage(void)
866 {
867 	(void) fprintf(stderr,
868 	    gettext("%s: [-E] [-H | -L] path-list predicate-list\n"), cmdname);
869 	exit(1);
870 }
871 
872 /*
873  * ACL matching is complex enough to warrant its own function.
874  */
875 static int
aclmatch(struct Node * np,const char * filename)876 aclmatch(struct Node *np, const char *filename)
877 {
878 	int i, t1, t2;
879 	acl_t *acl;
880 	void *acl_entry;
881 	aclent_t *p1;
882 	ace_t *p2;
883 
884 	if (np->action == USERACL) {
885 		t1 = USER;
886 		t2 = 0;
887 	} else {
888 		t1 = GROUP;
889 		t2 = ACE_IDENTIFIER_GROUP;
890 	}
891 
892 	if (acl_get(filename, 0, &acl) != 0)
893 		return (0);
894 
895 	/* Old acls can't map sids */
896 	if (np->action == SIDACL && acl->acl_type == ACLENT_T)
897 		return (0);
898 
899 	for (i = 0, acl_entry = acl->acl_aclp;
900 	    i != acl->acl_cnt; i++) {
901 		id_t who = np->first.l;
902 
903 		if (acl->acl_type == ACLENT_T) {
904 			p1 = (aclent_t *)acl_entry;
905 			if (np->action != SIDACL &&
906 			    p1->a_id == who && p1->a_type == t1) {
907 				acl_free(acl);
908 				return (1);
909 			}
910 		} else {
911 			p2 = (ace_t *)acl_entry;
912 
913 			if (np->action == SIDACL) {
914 				if (p2->a_flags & ACE_IDENTIFIER_GROUP) {
915 					who = np->second.l;
916 					t2 = ACE_IDENTIFIER_GROUP;
917 				} else {
918 					t2 = 0;
919 				}
920 			}
921 			if (p2->a_who == who &&
922 			    ((p2->a_flags & ACE_TYPE_FLAGS) == t2)) {
923 				acl_free(acl);
924 				return (1);
925 			}
926 		}
927 		acl_entry = ((char *)acl_entry + acl->acl_entry_size);
928 	}
929 	acl_free(acl);
930 	return (0);
931 }
932 
933 /*
934  * This is the function that gets executed at each node
935  */
936 
937 static int
execute(const char * name,const struct stat * statb,int type,struct FTW * state)938 execute(const char *name, const struct stat *statb, int type, struct FTW *state)
939 {
940 	struct Node *np = topnode;
941 	int val;
942 	time_t t;
943 	long l;
944 	long long ll;
945 	int not = 1;
946 	const char *filename;
947 	int cnpreg = 0;
948 
949 	if (type == FTW_NS) {
950 		(void) fprintf(stderr, gettext("%s: stat() error %s: %s\n"),
951 		    cmdname, name, strerror(errno));
952 		error = 1;
953 		return (0);
954 	} else if (type == FTW_DNR) {
955 		(void) fprintf(stderr, gettext("%s: cannot read dir %s: %s\n"),
956 		    cmdname, name, strerror(errno));
957 		error = 1;
958 	} else if (type == FTW_SLN && lflag == 1) {
959 		(void) fprintf(stderr,
960 		    gettext("%s: cannot follow symbolic link %s: %s\n"),
961 		    cmdname, name, strerror(errno));
962 		error = 1;
963 	} else if (type == FTW_DL) {
964 		(void) fprintf(stderr, gettext("%s: cycle detected for %s\n"),
965 		    cmdname, name);
966 		error = 1;
967 		return (0);
968 	}
969 
970 	if ((maxdepth != -1 && state->level > maxdepth) ||
971 	    (mindepth != -1 && state->level < mindepth))
972 		return (0);
973 
974 	while (np) {
975 		switch (np->action) {
976 		case NOT:
977 			not = !not;
978 			np = np->next;
979 			continue;
980 
981 		case AND:
982 			np = np->next;
983 			continue;
984 
985 		case OR:
986 			if (np->first.np == np) {
987 				/*
988 				 * handle naked OR (no term on left hand side)
989 				 */
990 				(void) fprintf(stderr,
991 				    gettext("%s: invalid -o construction\n"),
992 				    cmdname);
993 				exit(2);
994 			}
995 			/* FALLTHROUGH */
996 		case LPAREN: {
997 			struct Node *save = topnode;
998 			topnode = np->first.np;
999 			(void) execute(name, statb, type, state);
1000 			val = lastval;
1001 			topnode = save;
1002 			if (np->action == OR) {
1003 				if (val)
1004 					return (0);
1005 				val = 1;
1006 			}
1007 			break;
1008 		}
1009 
1010 		case LOCAL: {
1011 			int	nremfs;
1012 			val = 1;
1013 			/*
1014 			 * If file system type matches the remote
1015 			 * file system type, then it is not local.
1016 			 */
1017 			for (nremfs = 0; nremfs < fstype_index; nremfs++) {
1018 				if (strcmp(remote_fstypes[nremfs],
1019 				    statb->st_fstype) == 0) {
1020 					val = 0;
1021 					break;
1022 				}
1023 			}
1024 			break;
1025 		}
1026 
1027 		case TYPE:
1028 			l = (long)statb->st_mode&S_IFMT;
1029 			goto num;
1030 
1031 		case PERM:
1032 			l = (long)statb->st_mode&07777;
1033 			if (np->second.i == '-')
1034 				val = ((l&np->first.l) == np->first.l);
1035 			else
1036 				val = (l == np->first.l);
1037 			break;
1038 
1039 		case INUM:
1040 			ll = (long long)statb->st_ino;
1041 			goto llnum;
1042 		case NEWER:
1043 			l = statb->st_mtime;
1044 			goto num;
1045 		case ATIME:
1046 			t = statb->st_atime;
1047 			goto days;
1048 		case CTIME:
1049 			t = statb->st_ctime;
1050 			goto days;
1051 		case MTIME:
1052 			t = statb->st_mtime;
1053 		days:
1054 			l = (now-t)/A_DAY;
1055 			goto num;
1056 		case MMIN:
1057 			t = statb->st_mtime;
1058 			goto mins;
1059 		case AMIN:
1060 			t = statb->st_atime;
1061 			goto mins;
1062 		case CMIN:
1063 			t = statb->st_ctime;
1064 			goto mins;
1065 		mins:
1066 			l = (now-t)/A_MIN;
1067 			goto num;
1068 		case CSIZE:
1069 			ll = (long long)statb->st_size;
1070 			goto llnum;
1071 		case SIZE:
1072 			ll = (long long)round(statb->st_size, BLKSIZ)/BLKSIZ;
1073 			goto llnum;
1074 		case F_USER:
1075 			l = (long)statb->st_uid;
1076 			goto num;
1077 		case F_GROUP:
1078 			l = (long)statb->st_gid;
1079 			goto num;
1080 		case LINKS:
1081 			l = (long)statb->st_nlink;
1082 			goto num;
1083 		llnum:
1084 			if (np->second.i == '+')
1085 				val = (ll > np->first.ll);
1086 			else if (np->second.i == '-')
1087 				val = (ll < np->first.ll);
1088 			else
1089 				val = (ll == np->first.ll);
1090 			break;
1091 		num:
1092 			if (np->second.i == '+')
1093 				val = (l > np->first.l);
1094 			else if (np->second.i == '-')
1095 				val = (l < np->first.l);
1096 			else
1097 				val = (l == np->first.l);
1098 			break;
1099 		case OK:
1100 			val = ok(name, np->first.ap);
1101 			break;
1102 		case EXEC:
1103 			val = doexec(name, np->first.ap, NULL);
1104 			break;
1105 		case DELETE:
1106 			val = dodelete(name, statb, state);
1107 			break;
1108 
1109 		case VARARGS: {
1110 			struct Arglist *ap = np->first.vp;
1111 			char *cp;
1112 			cp = ap->nextstr - (strlen(name)+1);
1113 			if (cp >= (char *)(ap->nextvar+3)) {
1114 				/* there is room just copy the name */
1115 				val = 1;
1116 				(void) strcpy(cp, name);
1117 				*ap->nextvar++ = cp;
1118 				ap->nextstr = cp;
1119 			} else {
1120 				/* no more room, exec command */
1121 				*ap->nextvar++ = (char *)name;
1122 				*ap->nextvar = 0;
1123 				val = 1;
1124 				(void) doexec(NULL, ap->arglist,
1125 				    &exec_exitcode);
1126 				ap->nextstr = ap->end;
1127 				ap->nextvar = ap->firstvar;
1128 			}
1129 			break;
1130 		}
1131 
1132 		case DEPTH:
1133 		case MOUNT:
1134 		case FOLLOW:
1135 			val = 1;
1136 			break;
1137 
1138 		case NAME:
1139 		case INAME:
1140 		case PATH:
1141 		case IPATH: {
1142 			char *path;
1143 			int fnmflags = 0;
1144 
1145 			if (np->action == INAME || np->action == IPATH)
1146 				fnmflags = FNM_IGNORECASE;
1147 
1148 			/*
1149 			 * basename(3c) may modify name, so
1150 			 * we need to pass another string
1151 			 */
1152 			if ((path = strdup(name)) == NULL) {
1153 				(void) fprintf(stderr,
1154 				    gettext("%s: cannot strdup() %s: %s\n"),
1155 				    cmdname, name, strerror(errno));
1156 				exit(2);
1157 			}
1158 			/*
1159 			 * XPG4 find should not treat a leading '.' in a
1160 			 * filename specially for pattern matching.
1161 			 * /usr/bin/find  will not pattern match a leading
1162 			 * '.' in a filename, unless '.' is explicitly
1163 			 * specified.
1164 			 *
1165 			 * The legacy behavior makes no sense for PATH.
1166 			 */
1167 #ifndef XPG4
1168 			if (np->action == NAME || np->action == INAME)
1169 				fnmflags |= FNM_PERIOD;
1170 #endif
1171 
1172 			val = !fnmatch(np->first.cp,
1173 			    (np->action == NAME || np->action == INAME) ?
1174 			    basename(path) : path, fnmflags);
1175 			free(path);
1176 			break;
1177 		}
1178 
1179 		case PRUNE:
1180 			if (type == FTW_D)
1181 				state->quit = FTW_PRUNE;
1182 			val = 1;
1183 			break;
1184 		case NOUSER:
1185 			val = ((getpwuid(statb->st_uid)) == 0);
1186 			break;
1187 		case NOGRP:
1188 			val = ((getgrgid(statb->st_gid)) == 0);
1189 			break;
1190 		case FSTYPE:
1191 			val = (strcmp(np->first.cp, statb->st_fstype) == 0);
1192 			break;
1193 		case CPIO:
1194 			output = (FILE *)np->first.l;
1195 			(void) fprintf(output, "%s\n", name);
1196 			val = 1;
1197 			break;
1198 		case PRINT:
1199 		case PRINT0:
1200 			(void) fprintf(stdout, "%s%c", name,
1201 			    (np->action == PRINT) ? '\n' : '\0');
1202 			val = 1;
1203 			break;
1204 		case LS:
1205 			(void) list(name, statb);
1206 			val = 1;
1207 			break;
1208 		case XATTR:
1209 			filename = (walkflags & FTW_CHDIR) ?
1210 			    gettail(name) : name;
1211 			val = (pathconf(filename, _PC_XATTR_EXISTS) == 1);
1212 			break;
1213 		case ACL:
1214 			/*
1215 			 * Need to get the tail of the file name, since we have
1216 			 * already chdir()ed into the directory (performed in
1217 			 * nftw()) of the file
1218 			 */
1219 			filename = (walkflags & FTW_CHDIR) ?
1220 			    gettail(name) : name;
1221 			val = acl_trivial(filename);
1222 			break;
1223 		case USERACL:
1224 		case GROUPACL:
1225 		case SIDACL: {
1226 			filename = (walkflags & FTW_CHDIR) ?
1227 			    gettail(name) : name;
1228 			val = aclmatch(np, filename);
1229 			break;
1230 		}
1231 		case IREGEX:
1232 		case REGEX: {
1233 			regmatch_t pmatch;
1234 
1235 			val = 0;
1236 			if (regexec(&preg[cnpreg], name, 1, &pmatch, 0) == 0)
1237 				val = ((pmatch.rm_so == 0) &&
1238 				    (pmatch.rm_eo == strlen(name)));
1239 			cnpreg++;
1240 			break;
1241 		}
1242 		case MAXDEPTH:
1243 			if (state->level == maxdepth && type == FTW_D)
1244 				state->quit = FTW_PRUNE;
1245 			/* FALLTHROUGH */
1246 		case MINDEPTH:
1247 			val = 1;
1248 			break;
1249 		}
1250 		/*
1251 		 * evaluate 'val' and 'not' (exclusive-or)
1252 		 * if no inversion (not == 1), return only when val == 0
1253 		 * (primary not true). Otherwise, invert the primary
1254 		 * and return when the primary is true.
1255 		 * 'Lastval' saves the last result (fail or pass) when
1256 		 * returning back to the calling routine.
1257 		 */
1258 		if (val ^ not) {
1259 			lastval = 0;
1260 			return (0);
1261 		}
1262 		lastval = 1;
1263 		not = 1;
1264 		np = np->next;
1265 	}
1266 	return (0);
1267 }
1268 
1269 /*
1270  * code for the -ok option
1271  */
1272 
1273 static int
ok(const char * name,char * argv[])1274 ok(const char *name, char *argv[])
1275 {
1276 	int  c;
1277 	int i = 0;
1278 	char resp[LINE_MAX + 1];
1279 
1280 	(void) fflush(stdout);	/* to flush possible `-print' */
1281 
1282 	if ((*argv != dummyarg) && (strcmp(*argv, name)))
1283 		(void) fprintf(stderr, "< %s ... %s >?   ", *argv, name);
1284 	else
1285 		(void) fprintf(stderr, "< {} ... %s >?   ", name);
1286 
1287 	(void) fflush(stderr);
1288 
1289 	while ((c = getchar()) != '\n') {
1290 		if (c == EOF)
1291 			exit(2);
1292 		if (i < LINE_MAX)
1293 			resp[i++] = c;
1294 	}
1295 	resp[i] = '\0';
1296 
1297 	if (yes_check(resp))
1298 		return (doexec(name, argv, NULL));
1299 	else
1300 		return (0);
1301 }
1302 
1303 /*
1304  * execute argv with {} replaced by name
1305  *
1306  * Per XPG6, find must exit non-zero if an invocation through
1307  * -exec, punctuated by a plus sign, exits non-zero, so set
1308  * exitcode if we see a non-zero exit.
1309  * exitcode should be NULL when -exec or -ok is not punctuated
1310  * by a plus sign.
1311  */
1312 
1313 static int
doexec(const char * name,char * argv[],int * exitcode)1314 doexec(const char *name, char *argv[], int *exitcode)
1315 {
1316 	char *cp;
1317 	char **av = argv;
1318 	int dummyseen = 0;
1319 	int err, rc, r = 0;
1320 	pid_t pid;
1321 
1322 	(void) fflush(stdout);	  /* to flush possible `-print' */
1323 	if (name) {
1324 		while (cp = *av++) {
1325 			if (cp == dummyarg) {
1326 				dummyseen = 1;
1327 				av[-1] = (char *)name;
1328 			}
1329 
1330 		}
1331 	}
1332 	if (argv[0] == NULL)    /* null command line */
1333 		return (r);
1334 
1335 	if ((err = posix_spawnp(&pid, argv[0], NULL, NULL, argv,
1336 	    environ)) != 0) {
1337 		/* The command could not be invoked */
1338 		warnc(err, gettext("cannot execute %s"), argv[0]);
1339 		error = 1;
1340 		r = 1;
1341 	} else {
1342 		do {
1343 			/* wait for child to exit */
1344 			if ((rc = wait(&r)) == -1 && errno != EINTR) {
1345 				(void) fprintf(stderr,
1346 				    gettext("wait failed %s"), strerror(errno));
1347 
1348 				if (exitcode != NULL)
1349 					*exitcode = 1;
1350 				return (0);
1351 			}
1352 		} while (rc != pid);
1353 	}
1354 
1355 	if (name && dummyseen) {
1356 		for (av = argv; cp = *av++; ) {
1357 			if (cp == name)
1358 				av[-1] = dummyarg;
1359 		}
1360 	}
1361 
1362 	if (r && exitcode != NULL)
1363 		*exitcode = 3; /* use to indicate error in cmd invocation */
1364 
1365 	return (!r);
1366 }
1367 
1368 static int
dodelete(const char * name,const struct stat * statb,struct FTW * state)1369 dodelete(const char *name, const struct stat *statb, struct FTW *state)
1370 {
1371 	const char *fn;
1372 	int rc = 0;
1373 
1374 	/* restrict symlinks */
1375 	if ((walkflags & FTW_PHYS) == 0) {
1376 		(void) fprintf(stderr,
1377 		    gettext("-delete is not allowed when symlinks are "
1378 		    "followed.\n"));
1379 		return (1);
1380 	}
1381 
1382 	fn = name + state->base;
1383 	if (strcmp(fn, ".") == 0) {
1384 		/* nothing to do */
1385 		return (1);
1386 	}
1387 
1388 	if (strchr(fn, '/') != NULL) {
1389 		(void) fprintf(stderr,
1390 		    gettext("-delete with relative path is unsafe."));
1391 		return (1);
1392 	}
1393 
1394 	if (S_ISDIR(statb->st_mode)) {
1395 		/* delete directory */
1396 		rc = rmdir(name);
1397 	} else {
1398 		/* delete file */
1399 		rc = unlink(name);
1400 	}
1401 
1402 	if (rc < 0) {
1403 		/* operation failed */
1404 		(void) fprintf(stderr, gettext("delete failed %s: %s\n"),
1405 		    name, strerror(errno));
1406 		return (1);
1407 	}
1408 
1409 	return (1);
1410 }
1411 
1412 /*
1413  *  Table lookup routine
1414  */
1415 static const struct Args *
lookup(char * word)1416 lookup(char *word)
1417 {
1418 	const struct Args *argp = commands;
1419 	int second;
1420 	if (word == 0 || *word == 0)
1421 		return (0);
1422 	second = word[1];
1423 	while (*argp->name) {
1424 		if (second == argp->name[1] && strcmp(word, argp->name) == 0)
1425 			return (argp);
1426 		argp++;
1427 	}
1428 	return (0);
1429 }
1430 
1431 
1432 /*
1433  * Get space for variable length argument list
1434  */
1435 
1436 static struct Arglist *
varargs(char ** com)1437 varargs(char **com)
1438 {
1439 	struct Arglist *ap;
1440 	int n;
1441 	char **ep;
1442 	if (varsize == 0) {
1443 		n = 2*sizeof (char **);
1444 		for (ep = environ; *ep; ep++)
1445 			n += (strlen(*ep)+sizeof (ep) + 1);
1446 		varsize = sizeof (struct Arglist)+ARG_MAX-PATH_MAX-n-1;
1447 	}
1448 	ap = (struct Arglist *)malloc(varsize+1);
1449 	ap->end = (char *)ap + varsize;
1450 	ap->nextstr = ap->end;
1451 	ap->nextvar = ap->arglist;
1452 	while (*ap->nextvar++ = *com++)
1453 		;
1454 	ap->nextvar--;
1455 	ap->firstvar = ap->nextvar;
1456 	ap->next = lastlist;
1457 	lastlist = ap;
1458 	return (ap);
1459 }
1460 
1461 /*
1462  * filter command support
1463  * spawn cmd(argv) according to mode:
1464  *
1465  *	"r"	with fp as stdin of cmd (default stdin), cmd stdout returned
1466  *	"w"	with fp as stdout of cmd (default stdout), cmd stdin returned
1467  */
1468 
1469 #define	CMDERR	((1<<8)-1)	/* command error exit code		*/
1470 #define	MAXCMDS	8		/* max # simultaneous cmdopen()'s	*/
1471 
1472 static struct			/* info for each cmdopen()		*/
1473 {
1474 	FILE	*fp;		/* returned by cmdopen()		*/
1475 	pid_t	pid;		/* pid used by cmdopen()		*/
1476 } cmdproc[MAXCMDS];
1477 
1478 static FILE *
cmdopen(char * cmd,char ** argv,char * mode,FILE * fp)1479 cmdopen(char *cmd, char **argv, char *mode, FILE *fp)
1480 {
1481 	posix_spawn_file_actions_t fact;
1482 	int	proc;
1483 	int	cmdfd;
1484 	int	usrfd;
1485 	int	err;
1486 	int	pio[2];
1487 
1488 	switch (*mode) {
1489 	case 'r':
1490 		cmdfd = 1;
1491 		usrfd = 0;
1492 		break;
1493 	case 'w':
1494 		cmdfd = 0;
1495 		usrfd = 1;
1496 		break;
1497 	default:
1498 		return (0);
1499 	}
1500 
1501 	for (proc = 0; proc < MAXCMDS; proc++)
1502 		if (!cmdproc[proc].fp)
1503 			break;
1504 	if (proc >= MAXCMDS)
1505 		return (0);
1506 
1507 	if (pipe(pio))
1508 		return (0);
1509 
1510 	if ((err = posix_spawn_file_actions_init(&fact)) == 0) {
1511 		if (fp != NULL && fileno(fp) != usrfd) {
1512 			if ((err = posix_spawn_file_actions_adddup2(&fact,
1513 			    fileno(fp), usrfd)) == 0) {
1514 				err = posix_spawn_file_actions_addclose(
1515 				    &fact, fileno(fp));
1516 			}
1517 		}
1518 		if (err == 0) {
1519 			err = posix_spawn_file_actions_adddup2(&fact,
1520 			    pio[cmdfd], cmdfd);
1521 		}
1522 		if (err == 0) {
1523 			err = posix_spawn_file_actions_addclose(&fact,
1524 			    pio[cmdfd]);
1525 		}
1526 		if (err == 0) {
1527 			err = posix_spawn_file_actions_addclose(&fact,
1528 			    pio[usrfd]);
1529 		}
1530 		if (err == 0) {
1531 			/*
1532 			 * A command which is executable but not in a
1533 			 * recognised format is run as a shell script.
1534 			 */
1535 			err = posix_spawnp(&cmdproc[proc].pid, cmd, &fact,
1536 			    NULL, argv, environ);
1537 		}
1538 		(void) posix_spawn_file_actions_destroy(&fact);
1539 	}
1540 
1541 	if (err != 0) {
1542 		(void) close(pio[0]);
1543 		(void) close(pio[1]);
1544 		errno = err;
1545 		return (0);
1546 	}
1547 
1548 	(void) close(pio[cmdfd]);
1549 	return (cmdproc[proc].fp = fdopen(pio[usrfd], mode));
1550 }
1551 
1552 /*
1553  * close a stream opened by cmdopen()
1554  * -1 returned if cmdopen() had a problem
1555  * otherwise exit() status of command is returned
1556  */
1557 
1558 static int
cmdclose(FILE * fp)1559 cmdclose(FILE *fp)
1560 {
1561 	int	i;
1562 	pid_t	p, pid;
1563 	int		status;
1564 
1565 	for (i = 0; i < MAXCMDS; i++)
1566 		if (fp == cmdproc[i].fp) break;
1567 	if (i >= MAXCMDS)
1568 		return (-1);
1569 	(void) fclose(fp);
1570 	cmdproc[i].fp = 0;
1571 	pid = cmdproc[i].pid;
1572 	while ((p = wait(&status)) != pid && p != (pid_t)-1)
1573 		;
1574 	if (p == pid) {
1575 		status = (status >> 8) & CMDERR;
1576 		if (status == CMDERR)
1577 			status = -1;
1578 	}
1579 	else
1580 		status = -1;
1581 	return (status);
1582 }
1583 
1584 /*
1585  * the following functions implement the added "-ls" option
1586  */
1587 
1588 #include <utmpx.h>
1589 #include <sys/mkdev.h>
1590 
1591 struct		utmpx utmpx;
1592 #define	NMAX	(sizeof (utmpx.ut_name))
1593 #define	SCPYN(a, b)	(void) strncpy(a, b, NMAX)
1594 
1595 #define	NUID	64
1596 #define	NGID	64
1597 
1598 static struct ncache {
1599 	int	id;
1600 	char	name[NMAX+1];
1601 } nc[NUID], gc[NGID];
1602 
1603 /*
1604  * This function assumes that the password file is hashed
1605  * (or some such) to allow fast access based on a name key.
1606  */
1607 static char *
getname(uid_t uid)1608 getname(uid_t uid)
1609 {
1610 	struct passwd *pw;
1611 	int cp;
1612 
1613 #if	(((NUID) & ((NUID) - 1)) != 0)
1614 	cp = uid % (NUID);
1615 #else
1616 	cp = uid & ((NUID) - 1);
1617 #endif
1618 	if (nc[cp].id == uid && nc[cp].name[0])
1619 		return (nc[cp].name);
1620 	pw = getpwuid(uid);
1621 	if (!pw)
1622 		return (0);
1623 	nc[cp].id = uid;
1624 	SCPYN(nc[cp].name, pw->pw_name);
1625 	return (nc[cp].name);
1626 }
1627 
1628 /*
1629  * This function assumes that the group file is hashed
1630  * (or some such) to allow fast access based on a name key.
1631  */
1632 static char *
getgroup(gid_t gid)1633 getgroup(gid_t gid)
1634 {
1635 	struct group *gr;
1636 	int cp;
1637 
1638 #if	(((NGID) & ((NGID) - 1)) != 0)
1639 	cp = gid % (NGID);
1640 #else
1641 	cp = gid & ((NGID) - 1);
1642 #endif
1643 	if (gc[cp].id == gid && gc[cp].name[0])
1644 		return (gc[cp].name);
1645 	gr = getgrgid(gid);
1646 	if (!gr)
1647 		return (0);
1648 	gc[cp].id = gid;
1649 	SCPYN(gc[cp].name, gr->gr_name);
1650 	return (gc[cp].name);
1651 }
1652 
1653 #define	permoffset(who)		((who) * 3)
1654 #define	permission(who, type)	((type) >> permoffset(who))
1655 #define	kbytes(bytes)		(((bytes) + 1023) / 1024)
1656 
1657 static int
list(const char * file,const struct stat * stp)1658 list(const char *file, const struct stat *stp)
1659 {
1660 	char pmode[32], uname[32], gname[32], fsize[32], ftime[32];
1661 	int trivial;
1662 
1663 /*
1664  * Each line below contains the relevant permission (column 1) and character
1665  * shown when  the corresponding execute bit is either clear (column 2)
1666  * or set (column 3)
1667  * These permissions are as shown by ls(1b)
1668  */
1669 	static long special[] = {	S_ISUID, 'S', 's',
1670 					S_ISGID, 'S', 's',
1671 					S_ISVTX, 'T', 't' };
1672 
1673 	static time_t sixmonthsago = -1;
1674 #ifdef	S_IFLNK
1675 	char flink[MAXPATHLEN + 1];
1676 #endif
1677 	int who;
1678 	char *cp;
1679 	const char *tailname;
1680 	time_t now;
1681 	long long ksize;
1682 
1683 	if (file == NULL || stp == NULL)
1684 		return (-1);
1685 
1686 	(void) time(&now);
1687 	if (sixmonthsago == -1)
1688 		sixmonthsago = now - 6L*30L*24L*60L*60L;
1689 
1690 	switch (stp->st_mode & S_IFMT) {
1691 #ifdef	S_IFDIR
1692 	case S_IFDIR:	/* directory */
1693 		pmode[0] = 'd';
1694 		break;
1695 #endif
1696 #ifdef	S_IFCHR
1697 	case S_IFCHR:	/* character special */
1698 		pmode[0] = 'c';
1699 		break;
1700 #endif
1701 #ifdef	S_IFBLK
1702 	case S_IFBLK:	/* block special */
1703 		pmode[0] = 'b';
1704 		break;
1705 #endif
1706 #ifdef	S_IFIFO
1707 	case S_IFIFO:	/* fifo special */
1708 		pmode[0] = 'p';
1709 		break;
1710 #endif
1711 #ifdef	S_IFLNK
1712 	case S_IFLNK:	/* symbolic link */
1713 		pmode[0] = 'l';
1714 		break;
1715 #endif
1716 #ifdef	S_IFSOCK
1717 	case S_IFSOCK:	/* socket */
1718 		pmode[0] = 's';
1719 		break;
1720 #endif
1721 #ifdef	S_IFDOOR
1722 	case S_IFDOOR:	/* door */
1723 		pmode[0] = 'D';
1724 		break;
1725 #endif
1726 #ifdef	S_IFREG
1727 	case S_IFREG:	/* regular */
1728 		pmode[0] = '-';
1729 		break;
1730 #endif
1731 	default:
1732 		pmode[0] = '?';
1733 		break;
1734 	}
1735 
1736 	for (who = 0; who < 3; who++) {
1737 		int is_exec =  stp->st_mode & permission(who, S_IEXEC)? 1 : 0;
1738 
1739 		if (stp->st_mode & permission(who, S_IREAD))
1740 			pmode[permoffset(who) + 1] = 'r';
1741 		else
1742 			pmode[permoffset(who) + 1] = '-';
1743 
1744 		if (stp->st_mode & permission(who, S_IWRITE))
1745 			pmode[permoffset(who) + 2] = 'w';
1746 		else
1747 			pmode[permoffset(who) + 2] = '-';
1748 
1749 		if (stp->st_mode & special[who * 3])
1750 			pmode[permoffset(who) + 3] =
1751 			    special[who * 3 + 1 + is_exec];
1752 		else if (is_exec)
1753 			pmode[permoffset(who) + 3] = 'x';
1754 		else
1755 			pmode[permoffset(who) + 3] = '-';
1756 	}
1757 
1758 	/*
1759 	 * Need to get the tail of the file name, since we have
1760 	 * already chdir()ed into the directory of the file
1761 	 */
1762 
1763 	tailname = gettail(file);
1764 
1765 	trivial = acl_trivial(tailname);
1766 	if (trivial == -1)
1767 		trivial =  0;
1768 
1769 	if (trivial == 1)
1770 		pmode[permoffset(who) + 1] = '+';
1771 	else
1772 		pmode[permoffset(who) + 1] = ' ';
1773 
1774 	pmode[permoffset(who) + 2] = '\0';
1775 
1776 	/*
1777 	 * Prepare uname and gname.  Always add a space afterwards
1778 	 * to keep columns from running together.
1779 	 */
1780 	cp = getname(stp->st_uid);
1781 	if (cp != NULL)
1782 		(void) sprintf(uname, "%-8s ", cp);
1783 	else
1784 		(void) sprintf(uname, "%-8u ", stp->st_uid);
1785 
1786 	cp = getgroup(stp->st_gid);
1787 	if (cp != NULL)
1788 		(void) sprintf(gname, "%-8s ", cp);
1789 	else
1790 		(void) sprintf(gname, "%-8u ", stp->st_gid);
1791 
1792 	if (pmode[0] == 'b' || pmode[0] == 'c')
1793 		(void) sprintf(fsize, "%3ld,%4ld",
1794 		    major(stp->st_rdev), minor(stp->st_rdev));
1795 	else {
1796 		(void) sprintf(fsize, (stp->st_size < 100000000) ?
1797 		    "%8lld" : "%lld", stp->st_size);
1798 #ifdef	S_IFLNK
1799 		if (pmode[0] == 'l') {
1800 			who = readlink(tailname, flink, sizeof (flink) - 1);
1801 
1802 			if (who >= 0)
1803 				flink[who] = '\0';
1804 			else
1805 				flink[0] = '\0';
1806 		}
1807 #endif
1808 	}
1809 
1810 	cp = ctime(&stp->st_mtime);
1811 	if (stp->st_mtime < sixmonthsago || stp->st_mtime > now)
1812 		(void) sprintf(ftime, "%-7.7s %-4.4s", cp + 4, cp + 20);
1813 	else
1814 		(void) sprintf(ftime, "%-12.12s", cp + 4);
1815 
1816 	(void) printf((stp->st_ino < 100000) ? "%5llu " :
1817 	    "%llu ", stp->st_ino);  /* inode #	*/
1818 #ifdef	S_IFSOCK
1819 	ksize = (long long) kbytes(ldbtob(stp->st_blocks)); /* kbytes */
1820 #else
1821 	ksize = (long long) kbytes(stp->st_size); /* kbytes */
1822 #endif
1823 	(void) printf((ksize < 10000) ? "%4lld " : "%lld ", ksize);
1824 #ifdef	S_IFLNK
1825 	(void) printf("%s %2ld %s%s%s %s %s%s%s\n",
1826 	    pmode,					/* protection	*/
1827 	    stp->st_nlink,				/* # of links	*/
1828 	    uname,					/* owner	*/
1829 	    gname,					/* group	*/
1830 	    fsize,					/* # of bytes	*/
1831 	    ftime,					/* modify time	*/
1832 	    file,					/* name		*/
1833 	    (pmode[0] == 'l') ? " -> " : "",
1834 	    (pmode[0] == 'l') ? flink  : "");		/* symlink	*/
1835 #else
1836 	(void) printf("%s %2ld %s%s%s %s %s\n",
1837 	    pmode,					/* protection	*/
1838 	    stp->st_nlink,				/* # of links	*/
1839 	    uname,					/* owner	*/
1840 	    gname,					/* group	*/
1841 	    fsize,					/* # of bytes	*/
1842 	    ftime,					/* modify time	*/
1843 	    file);					/* name		*/
1844 #endif
1845 
1846 	return (0);
1847 }
1848 
1849 static char *
new_string(char * s)1850 new_string(char *s)
1851 {
1852 	char *p = strdup(s);
1853 
1854 	if (p)
1855 		return (p);
1856 	(void) fprintf(stderr, gettext("%s: out of memory\n"), cmdname);
1857 	exit(1);
1858 	/*NOTREACHED*/
1859 }
1860 
1861 /*
1862  * Read remote file system types from REMOTE_FS into the
1863  * remote_fstypes array.
1864  */
1865 static void
init_remote_fs(void)1866 init_remote_fs(void)
1867 {
1868 	FILE    *fp;
1869 	char    line_buf[LINEBUF_SIZE];
1870 
1871 	if ((fp = fopen(REMOTE_FS, "r")) == NULL) {
1872 		(void) fprintf(stderr,
1873 		    gettext("%s: Warning: can't open %s, ignored\n"),
1874 		    REMOTE_FS, cmdname);
1875 		/* Use default string name for NFS */
1876 		remote_fstypes[fstype_index++] = "nfs";
1877 		return;
1878 	}
1879 
1880 	while (fgets(line_buf, sizeof (line_buf), fp) != NULL) {
1881 		char buf[LINEBUF_SIZE];
1882 
1883 		/* LINTED - unbounded string specifier */
1884 		(void) sscanf(line_buf, "%s", buf);
1885 		remote_fstypes[fstype_index++] = new_string(buf);
1886 
1887 		if (fstype_index == N_FSTYPES)
1888 			break;
1889 	}
1890 	(void) fclose(fp);
1891 }
1892 
1893 #define	NPERM	30			/* Largest machine */
1894 
1895 /*
1896  * The PERM struct is the machine that builds permissions.  The p_special
1897  * field contains what permissions need to be checked at run-time in
1898  * getmode().  This is one of 'X', 'u', 'g', or 'o'.  It contains '\0' to
1899  * indicate normal processing.
1900  */
1901 typedef	struct	PERMST	{
1902 	ushort_t	p_who;		/* Range of permission (e.g. ugo) */
1903 	ushort_t	p_perm;		/* Bits to turn on, off, assign */
1904 	uchar_t		p_op;		/* Operation: + - = */
1905 	uchar_t		p_special;	/* Special handling? */
1906 }	PERMST;
1907 
1908 #ifndef	S_ISVTX
1909 #define	S_ISVTX	0			/* Not .1 */
1910 #endif
1911 
1912 /* Mask values */
1913 #define	P_A	(S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) /* allbits */
1914 #define	P_U	(S_ISUID|S_ISVTX|S_IRWXU)		/* user */
1915 #define	P_G	(S_ISGID|S_ISVTX|S_IRWXG)		/* group */
1916 #define	P_O	(S_ISVTX|S_IRWXO)			/* other */
1917 
1918 static	int	iswho(int c);
1919 static	int	isop(int c);
1920 static	int	isperm(PERMST *pp, int c);
1921 
1922 static	PERMST	machine[NPERM];		/* Permission construction machine */
1923 static	PERMST	*endp;			/* Last used PERM structure */
1924 
1925 static	uint_t	nowho;			/* No who for this mode (DOS kludge) */
1926 
1927 /*
1928  * Read an ASCII string containing the symbolic/octal mode and
1929  * compile an automaton that recognizes it.  The return value
1930  * is NULL if everything is OK, otherwise it is -1.
1931  */
1932 static int
readmode(const char * ascmode)1933 readmode(const char *ascmode)
1934 {
1935 	const char *amode = ascmode;
1936 	PERMST *pp;
1937 	int seen_X;
1938 
1939 	nowho = 0;
1940 	seen_X = 0;
1941 	pp = &machine[0];
1942 	if (*amode >= '0' && *amode <= '7') {
1943 		int mode;
1944 
1945 		mode = 0;
1946 		while (*amode >= '0' && *amode <= '7')
1947 			mode = (mode<<3) + *amode++ - '0';
1948 		if (*amode != '\0')
1949 			return (-1);
1950 #if	S_ISUID != 04000 || S_ISGID != 02000 || \
1951 	S_IRUSR != 0400 || S_IWUSR != 0200 || S_IXUSR != 0100 || \
1952 	S_IRGRP != 0040 || S_IWGRP != 0020 || S_IXGRP != 0010 || \
1953 	S_IROTH != 0004 || S_IWOTH != 0002 || S_IXOTH != 0001
1954 		/*
1955 		 * There is no requirement of the octal mode bits being
1956 		 * the same as the S_ macros.
1957 		 */
1958 	{
1959 		mode_t mapping[] = {
1960 			S_IXOTH, S_IWOTH, S_IROTH,
1961 			S_IXGRP, S_IWGRP, S_IRGRP,
1962 			S_IXUSR, S_IWUSR, S_IRUSR,
1963 			S_ISGID, S_ISUID,
1964 			0
1965 		};
1966 		int i, newmode = 0;
1967 
1968 		for (i = 0; mapping[i] != 0; i++)
1969 			if (mode & (1<<i))
1970 				newmode |= mapping[i];
1971 		mode = newmode;
1972 	}
1973 #endif
1974 		pp->p_who = P_A;
1975 		pp->p_perm = mode;
1976 		pp->p_op = '=';
1977 	} else	for (;;) {
1978 		int t;
1979 		int who = 0;
1980 
1981 		while ((t = iswho(*amode)) != 0) {
1982 			++amode;
1983 			who |= t;
1984 		}
1985 		if (who == 0) {
1986 			mode_t currmask;
1987 			(void) umask(currmask = umask((mode_t)0));
1988 
1989 			/*
1990 			 * If no who specified, must use contents of
1991 			 * umask to determine which bits to flip.  This
1992 			 * is POSIX/V7/BSD behaviour, but not SVID.
1993 			 */
1994 			who = (~currmask)&P_A;
1995 			++nowho;
1996 		} else
1997 			nowho = 0;
1998 	samewho:
1999 		if (!isop(pp->p_op = *amode++))
2000 			return (-1);
2001 		pp->p_perm = 0;
2002 		pp->p_special = 0;
2003 		while ((t = isperm(pp, *amode)) != 0) {
2004 			if (pp->p_special == 'X') {
2005 				seen_X = 1;
2006 
2007 				if (pp->p_perm != 0) {
2008 					ushort_t op;
2009 
2010 					/*
2011 					 * Remember the 'who' for the previous
2012 					 * transformation.
2013 					 */
2014 					pp->p_who = who;
2015 					pp->p_special = 0;
2016 
2017 					op = pp->p_op;
2018 
2019 					/* Keep 'X' separate */
2020 					++pp;
2021 					pp->p_special = 'X';
2022 					pp->p_op = op;
2023 				}
2024 			} else if (seen_X) {
2025 				ushort_t op;
2026 
2027 				/* Remember the 'who' for the X */
2028 				pp->p_who = who;
2029 
2030 				op = pp->p_op;
2031 
2032 				/* Keep 'X' separate */
2033 				++pp;
2034 				pp->p_perm = 0;
2035 				pp->p_special = 0;
2036 				pp->p_op = op;
2037 			}
2038 			++amode;
2039 			pp->p_perm |= t;
2040 		}
2041 
2042 		/*
2043 		 * These returned 0, but were actually parsed, so
2044 		 * don't look at them again.
2045 		 */
2046 		switch (pp->p_special) {
2047 		case 'u':
2048 		case 'g':
2049 		case 'o':
2050 			++amode;
2051 			break;
2052 		}
2053 		pp->p_who = who;
2054 		switch (*amode) {
2055 		case '\0':
2056 			break;
2057 
2058 		case ',':
2059 			++amode;
2060 			++pp;
2061 			continue;
2062 
2063 		default:
2064 			++pp;
2065 			goto samewho;
2066 		}
2067 		break;
2068 	}
2069 	endp = pp;
2070 	return (0);
2071 }
2072 
2073 /*
2074  * Given a character from the mode, return the associated
2075  * value as who (user designation) mask or 0 if this isn't valid.
2076  */
2077 static int
iswho(int c)2078 iswho(int c)
2079 {
2080 	switch (c) {
2081 	case 'a':
2082 		return (P_A);
2083 
2084 	case 'u':
2085 		return (P_U);
2086 
2087 	case 'g':
2088 		return (P_G);
2089 
2090 	case 'o':
2091 		return (P_O);
2092 
2093 	default:
2094 		return (0);
2095 	}
2096 	/* NOTREACHED */
2097 }
2098 
2099 /*
2100  * Return non-zero if this is a valid op code
2101  * in a symbolic mode.
2102  */
2103 static int
isop(int c)2104 isop(int c)
2105 {
2106 	switch (c) {
2107 	case '+':
2108 	case '-':
2109 	case '=':
2110 		return (1);
2111 
2112 	default:
2113 		return (0);
2114 	}
2115 	/* NOTREACHED */
2116 }
2117 
2118 /*
2119  * Return the permission bits implied by this character or 0
2120  * if it isn't valid.  Also returns 0 when the pseudo-permissions 'u', 'g', or
2121  * 'o' are used, and sets pp->p_special to the one used.
2122  */
2123 static int
isperm(PERMST * pp,int c)2124 isperm(PERMST *pp, int c)
2125 {
2126 	switch (c) {
2127 	case 'u':
2128 	case 'g':
2129 	case 'o':
2130 		pp->p_special = c;
2131 		return (0);
2132 
2133 	case 'r':
2134 		return (S_IRUSR|S_IRGRP|S_IROTH);
2135 
2136 	case 'w':
2137 		return (S_IWUSR|S_IWGRP|S_IWOTH);
2138 
2139 	case 'x':
2140 		return (S_IXUSR|S_IXGRP|S_IXOTH);
2141 
2142 #if S_ISVTX != 0
2143 	case 't':
2144 		return (S_ISVTX);
2145 #endif
2146 
2147 	case 'X':
2148 		pp->p_special = 'X';
2149 		return (S_IXUSR|S_IXGRP|S_IXOTH);
2150 
2151 #if S_ISVTX != 0
2152 	case 'a':
2153 		return (S_ISVTX);
2154 #endif
2155 
2156 	case 'h':
2157 		return (S_ISUID);
2158 
2159 	/*
2160 	 * This change makes:
2161 	 *	chmod +s file
2162 	 * set the system bit on dos but means that
2163 	 *	chmod u+s file
2164 	 *	chmod g+s file
2165 	 *	chmod a+s file
2166 	 * are all like UNIX.
2167 	 */
2168 	case 's':
2169 		return (nowho ? S_ISGID : S_ISGID|S_ISUID);
2170 
2171 	default:
2172 		return (0);
2173 	}
2174 	/* NOTREACHED */
2175 }
2176 
2177 /*
2178  * Execute the automaton that is created by readmode()
2179  * to generate the final mode that will be used.  This
2180  * code is passed a starting mode that is usually the original
2181  * mode of the file being changed (or 0).  Note that this mode must contain
2182  * the file-type bits as well, so that S_ISDIR will succeed on directories.
2183  */
2184 static mode_t
getmode(mode_t startmode)2185 getmode(mode_t startmode)
2186 {
2187 	PERMST *pp;
2188 	mode_t temp;
2189 	mode_t perm;
2190 
2191 	for (pp = &machine[0]; pp <= endp; ++pp) {
2192 		perm = (mode_t)0;
2193 		/*
2194 		 * For the special modes 'u', 'g' and 'o', the named portion
2195 		 * of the mode refers to after the previous clause has been
2196 		 * processed, while the 'X' mode refers to the contents of the
2197 		 * mode before any clauses have been processed.
2198 		 *
2199 		 * References: P1003.2/D11.2, Section 4.7.7,
2200 		 *  lines 2568-2570, 2578-2583
2201 		 */
2202 		switch (pp->p_special) {
2203 		case 'u':
2204 			temp = startmode & S_IRWXU;
2205 			if (temp & (S_IRUSR|S_IRGRP|S_IROTH))
2206 				perm |= ((S_IRUSR|S_IRGRP|S_IROTH) &
2207 				    pp->p_who);
2208 			if (temp & (S_IWUSR|S_IWGRP|S_IWOTH))
2209 				perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who);
2210 			if (temp & (S_IXUSR|S_IXGRP|S_IXOTH))
2211 				perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who);
2212 			break;
2213 
2214 		case 'g':
2215 			temp = startmode & S_IRWXG;
2216 			if (temp & (S_IRUSR|S_IRGRP|S_IROTH))
2217 				perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & pp->p_who);
2218 			if (temp & (S_IWUSR|S_IWGRP|S_IWOTH))
2219 				perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who);
2220 			if (temp & (S_IXUSR|S_IXGRP|S_IXOTH))
2221 				perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who);
2222 			break;
2223 
2224 		case 'o':
2225 			temp = startmode & S_IRWXO;
2226 			if (temp & (S_IRUSR|S_IRGRP|S_IROTH))
2227 				perm |= ((S_IRUSR|S_IRGRP|S_IROTH) & pp->p_who);
2228 			if (temp & (S_IWUSR|S_IWGRP|S_IWOTH))
2229 				perm |= ((S_IWUSR|S_IWGRP|S_IWOTH) & pp->p_who);
2230 			if (temp & (S_IXUSR|S_IXGRP|S_IXOTH))
2231 				perm |= ((S_IXUSR|S_IXGRP|S_IXOTH) & pp->p_who);
2232 			break;
2233 
2234 		case 'X':
2235 			perm = pp->p_perm;
2236 			break;
2237 
2238 		default:
2239 			perm = pp->p_perm;
2240 			break;
2241 		}
2242 		switch (pp->p_op) {
2243 		case '-':
2244 			startmode &= ~(perm & pp->p_who);
2245 			break;
2246 
2247 		case '=':
2248 			startmode &= ~pp->p_who;
2249 			/* FALLTHROUGH */
2250 		case '+':
2251 			startmode |= (perm & pp->p_who);
2252 			break;
2253 		}
2254 	}
2255 	return (startmode);
2256 }
2257 
2258 /*
2259  * Returns the last component of a path name, unless it is
2260  * an absolute path, in which case it returns the whole path
2261  */
2262 static const char *
gettail(const char * fname)2263 gettail(const char *fname)
2264 {
2265 	const char *base = fname;
2266 
2267 	if (*fname != '/') {
2268 		if ((base = strrchr(fname, '/')) != NULL)
2269 			base++;
2270 		else
2271 			base = fname;
2272 	}
2273 	return (base);
2274 }
2275