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