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