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