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