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