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