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