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