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.
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.
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]
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.
28 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
29 /* All Rights Reserved */
32 /* Parts of this product may be derived from */
33 /* Mortice Kern Systems Inc. and Berkeley 4.3 BSD systems. */
34 /* licensed from Mortice Kern Systems Inc. and */
35 /* the University of California. */
38 * Copyright 1985, 1990 by Mortice Kern Systems Inc. All rights reserved.
45 #include <sys/types.h>
47 #include <sys/param.h>
63 #include "getresponse.h"
65 #define A_DAY (long)(60*60*24) /* a day full of seconds */
66 #define A_MIN (long)(60)
68 #define round(x, s) (((x)+(s)-1)&~((s)-1))
72 #define LINEBUF_SIZE LINE_MAX /* input or output lines */
73 #define REMOTE_FS "/etc/dfs/fstypes"
75 #define SHELL_MAXARGS 253 /* see doexec() for description */
78 * This is the list of operations
79 * F_USER and F_GROUP are named to avoid conflict with USER and GROUP defined
86 ACL
, AMIN
, AND
, ATIME
, CMIN
, CPIO
, CSIZE
, CTIME
, DEPTH
, EXEC
, F_GROUP
,
87 F_GROUPACL
, F_USER
, F_USERACL
, FOLLOW
, FSTYPE
, INAME
, INUM
, IPATH
,
88 IREGEX
, LINKS
, LOCAL
, LPAREN
, LS
, MAXDEPTH
, MINDEPTH
, MMIN
, MOUNT
,
89 MTIME
, NAME
, NCPIO
, NEWER
, NOGRP
, NOT
, NOUSER
, OK
, OR
, PATH
, PERM
,
90 PRINT0
, PRUNE
, REGEX
, RPAREN
, SIZE
, TYPE
, VARARGS
, XATTR
, DELETE
95 Unary
, Id
, Num
, Str
, Exec
, Cpio
, Op
106 * Except for pathnames, these are the only legal arguments
108 static struct Args commands
[] =
117 "-atime", ATIME
, Num
,
120 "-ctime", CTIME
, Num
,
121 "-depth", DEPTH
, Unary
,
122 "-delete", DELETE
, Unary
,
124 "-follow", FOLLOW
, Unary
,
125 "-fstype", FSTYPE
, Str
,
126 "-group", F_GROUP
, Num
,
127 "-groupacl", F_GROUPACL
, Num
,
128 "-iname", INAME
, Str
,
130 "-ipath", IPATH
, Str
,
131 "-iregex", IREGEX
, Str
,
132 "-links", LINKS
, Num
,
133 "-local", LOCAL
, Unary
,
135 "-maxdepth", MAXDEPTH
, Num
,
136 "-mindepth", MINDEPTH
, Num
,
138 "-mount", MOUNT
, Unary
,
139 "-mtime", MTIME
, Num
,
141 "-ncpio", NCPIO
, Cpio
,
142 "-newer", NEWER
, Str
,
143 "-nogroup", NOGRP
, Unary
,
145 "-nouser", NOUSER
, Unary
,
151 "-print", PRINT
, Unary
,
152 "-print0", PRINT0
, Unary
,
153 "-prune", PRUNE
, Unary
,
154 "-regex", REGEX
, Str
,
157 "-user", F_USER
, Num
,
158 "-useracl", F_USERACL
, Num
,
159 "-xattr", XATTR
, Unary
,
160 "-xdev", MOUNT
, Unary
,
185 /* if no -print, -exec or -ok replace "expression" with "(expression) -print" */
186 static struct Node PRINT_NODE
= { 0, PRINT
, 0, 0};
187 static struct Node LPAREN_NODE
= { 0, LPAREN
, 0, 0};
191 * Prototype variable size arglist buffer
196 struct Arglist
*next
;
205 static int compile();
206 static int execute();
207 static int doexec(char *, char **, int *);
208 static int dodelete(char *, struct stat
*, struct FTW
*);
209 static struct Args
*lookup();
211 static void usage(void) __NORETURN
;
212 static struct Arglist
*varargs();
214 static char *getgroup();
215 static FILE *cmdopen();
216 static int cmdclose();
217 static char *getshell();
218 static void init_remote_fs();
219 static char *getname();
220 static int readmode();
221 static mode_t
getmode();
222 static char *gettail();
225 static int walkflags
= FTW_CHDIR
|FTW_PHYS
|FTW_ANYERR
|FTW_NOLOOP
;
226 static struct Node
*topnode
;
227 static struct Node
*freenode
; /* next free node we may use later */
228 static char *cpio
[] = { "cpio", "-o", 0 };
229 static char *ncpio
[] = { "cpio", "-oc", 0 };
230 static char *cpiol
[] = { "cpio", "-oL", 0 };
231 static char *ncpiol
[] = { "cpio", "-ocL", 0 };
234 static char *dummyarg
= (char *)-1;
237 static struct Arglist
*lastlist
;
238 static char *cmdname
;
239 static char *remote_fstypes
[N_FSTYPES
+1];
240 static int fstype_index
= 0;
241 static int action_expression
= 0; /* -print, -exec, or -ok */
242 static int error
= 0;
243 static int paren_cnt
= 0; /* keeps track of parentheses */
244 static int Eflag
= 0;
245 static int hflag
= 0;
246 static int lflag
= 0;
247 /* set when doexec()-invoked utility returns non-zero */
248 static int exec_exitcode
= 0;
249 static regex_t
*preg
= NULL
;
250 static int npreg
= 0;
251 static int mindepth
= -1, maxdepth
= -1;
252 extern char **environ
;
255 main(int argc
, char **argv
)
262 (void) setlocale(LC_ALL
, "");
263 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
264 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
266 (void) textdomain(TEXT_DOMAIN
);
269 if (time(&now
) == (time_t)(-1)) {
270 (void) fprintf(stderr
, gettext("%s: time() %s\n"),
271 cmdname
, strerror(errno
));
274 while ((c
= getopt(argc
, argv
, "EHL")) != -1) {
297 (void) fprintf(stderr
,
298 gettext("%s: insufficient number of arguments\n"), cmdname
);
302 for (paths
= 0; (cp
= argv
[paths
]) != 0; ++paths
) {
305 else if ((*cp
== '!' || *cp
== '(') && *(cp
+1) == 0)
309 if (paths
== 0) /* no path-list */
314 /* lflag is the same as -follow */
316 walkflags
&= ~FTW_PHYS
;
318 /* allocate enough space for the compiler */
319 topnode
= malloc((argc
+ 1) * sizeof (struct Node
));
320 (void) memset(topnode
, 0, (argc
+ 1) * sizeof (struct Node
));
322 if (compile(argv
+ paths
, topnode
, &action_expression
) == 0) {
323 /* no expression, default to -print */
324 (void) memcpy(topnode
, &PRINT_NODE
, sizeof (struct Node
));
325 } else if (!action_expression
) {
327 * if no action expression, insert an LPAREN node above topnode,
328 * with a PRINT node as its next node
330 struct Node
*savenode
;
332 if (freenode
== NULL
) {
333 (void) fprintf(stderr
, gettext("%s: can't append -print"
334 " implicitly; try explicit -print option\n"),
339 topnode
= freenode
++;
340 (void) memcpy(topnode
, &LPAREN_NODE
, sizeof (struct Node
));
341 topnode
->next
= freenode
;
342 topnode
->first
.np
= savenode
;
343 (void) memcpy(topnode
->next
, &PRINT_NODE
, sizeof (struct Node
));
353 * If -H is specified, it means we walk the first
354 * level (pathname on command line) logically, following
355 * symlinks, but lower levels are walked physically.
356 * We use our own secret interface to nftw() to change
357 * the from stat to lstat after the top level is walked.
360 if (stat(curpath
, &sb
) < 0 && errno
== ENOENT
)
361 walkflags
&= ~FTW_HOPTION
;
363 walkflags
|= FTW_HOPTION
;
367 * We need this check as nftw needs a CWD and we have no
368 * way of returning back from that code with a meaningful
369 * error related to this
371 if ((cwdpath
= getcwd(NULL
, PATH_MAX
)) == NULL
) {
372 if ((errno
== EACCES
) && (walkflags
& FTW_CHDIR
)) {
374 * A directory above cwd is inaccessible,
375 * so don't do chdir(2)s. Slower, but at least
378 walkflags
&= ~FTW_CHDIR
;
381 (void) fprintf(stderr
,
382 gettext("%s : cannot get the current "
383 "working directory\n"), cmdname
);
390 if (nftw(curpath
, execute
, 1000, walkflags
)) {
391 (void) fprintf(stderr
,
392 gettext("%s: cannot open %s: %s\n"),
393 cmdname
, curpath
, strerror(errno
));
399 /* execute any remaining variable length lists */
401 if (lastlist
->end
!= lastlist
->nextstr
) {
402 *lastlist
->nextvar
= 0;
403 (void) doexec((char *)0, lastlist
->arglist
,
406 lastlist
= lastlist
->next
;
408 if (output
!= stdout
)
409 return (cmdclose(output
));
410 return ((exec_exitcode
!= 0) ? exec_exitcode
: error
);
414 * compile the arguments
418 compile(argv
, np
, actionp
)
425 struct Node
*oldnp
= topnode
;
429 enum Command wasop
= PRINT
;
431 if (init_yes() < 0) {
432 (void) fprintf(stderr
, gettext(ERR_MSG_INIT_YES
),
437 for (av
= argv
; *av
&& (argp
= lookup(*av
)); av
++) {
439 np
->action
= argp
->action
;
440 np
->type
= argp
->type
;
442 if (argp
->type
== Op
) {
443 if (wasop
== NOT
|| (wasop
&& np
->action
!= NOT
)) {
444 (void) fprintf(stderr
,
445 gettext("%s: operand follows operand\n"),
449 if (np
->action
!= NOT
&& oldnp
== 0)
451 wasop
= argp
->action
;
454 if (argp
->type
!= Unary
) {
456 (void) fprintf(stderr
,
457 gettext("%s: incomplete statement\n"),
461 if (argp
->type
== Num
) {
462 if (((argp
->action
== MAXDEPTH
) ||
463 (argp
->action
== MINDEPTH
)) &&
464 ((int)strtol(b
, (char **)NULL
,
467 gettext("%s: value must be positive"),
468 (argp
->action
== MAXDEPTH
) ?
469 "maxdepth" : "mindepth");
470 if ((argp
->action
!= PERM
) ||
472 if (*b
== '+' || *b
== '-') {
480 switch (argp
->action
) {
486 np
->first
.np
= topnode
;
492 struct Node
*save
= topnode
;
495 i
= compile(++av
, topnode
, actionp
);
496 np
->first
.np
= topnode
;
506 if (paren_cnt
<= 0) {
507 (void) fprintf(stderr
,
508 gettext("%s: unmatched ')'\n"),
515 if (oldnp
->type
== Op
) {
516 (void) fprintf(stderr
,
517 gettext("%s: cannot immediately"
518 " follow an operand with ')'\n"),
526 walkflags
&= ~FTW_PHYS
;
529 walkflags
|= FTW_MOUNT
;
532 walkflags
|= FTW_DEPTH
;
535 walkflags
|= (FTW_DEPTH
| FTW_PHYS
);
536 walkflags
&= ~FTW_CHDIR
;
545 * Make it compatible to df -l for
546 * future enhancement. So, anything
547 * that is not remote, then it is
554 if (b
[strlen(b
)-1] == 'c')
558 np
->first
.ll
= atoll(b
);
568 np
->first
.l
= atol(b
);
581 if (argp
->action
== F_USER
||
582 argp
->action
== F_USERACL
) {
583 if ((pw
= getpwnam(b
)) != 0)
584 value
= (long)pw
->pw_uid
;
586 if ((gr
= getgrnam(b
)) != 0)
587 value
= (long)gr
->gr_gid
;
591 value
= strtol(b
, &q
, 10);
592 if (errno
!= 0 || q
== b
|| *q
!= '\0') {
593 (void) fprintf(stderr
, gettext(
594 "%s: cannot find %s name\n"),
605 walkflags
&= ~FTW_CHDIR
;
609 if ((b
= *av
) == 0) {
610 (void) fprintf(stderr
,
611 gettext("%s: incomplete statement\n"),
615 if (strcmp(b
, ";") == 0) {
618 } else if (strcmp(b
, "{}") == 0)
620 else if (strcmp(b
, "+") == 0 &&
621 av
[-1] == dummyarg
&&
622 np
->action
== EXEC
) {
624 np
->first
.vp
= varargs(np
->first
.ap
);
625 np
->action
= VARARGS
;
644 if ((preg
= realloc(preg
, (npreg
+ 1) *
645 sizeof (regex_t
))) == NULL
)
647 if ((error
= regcomp(&preg
[npreg
], b
,
648 ((np
->action
== IREGEX
) ? REG_ICASE
: 0) |
649 ((Eflag
) ? REG_EXTENDED
: 0))) != 0) {
650 errlen
= regerror(error
, &preg
[npreg
], NULL
, 0);
651 if ((errmsg
= malloc(errlen
)) == NULL
)
653 (void) regerror(error
, &preg
[npreg
], errmsg
,
655 errx(1, gettext("RE error: %s"), errmsg
);
664 if (readmode(b
) != NULL
) {
665 (void) fprintf(stderr
, gettext(
666 "find: -perm: Bad permission string\n"));
669 np
->first
.l
= (long)getmode((mode_t
)0);
685 i
== 's' ? S_IFSOCK
:
688 i
== 'D' ? S_IFDOOR
:
694 if (walkflags
& FTW_PHYS
)
703 if (walkflags
& FTW_PHYS
)
709 if ((fd
= fopen(b
, "w")) == NULL
) {
710 (void) fprintf(stderr
,
711 gettext("%s: cannot create %s\n"),
716 np
->first
.l
= (long)cmdopen("cpio", com
, "w", fd
);
718 walkflags
|= FTW_DEPTH
;
729 if (stat(b
, &statb
) < 0) {
730 (void) fprintf(stderr
,
731 gettext("%s: cannot access %s\n"),
735 np
->first
.l
= statb
.st_mtime
;
755 maxdepth
= (int)strtol(b
, (char **)NULL
, 10);
758 mindepth
= (int)strtol(b
, (char **)NULL
, 10);
766 if ((*av
) || (wasop
))
769 if (paren_cnt
!= 0) {
770 (void) fprintf(stderr
, gettext("%s: unmatched '('\n"),
775 /* just before returning, save next free node from the list */
776 freenode
= oldnp
->next
;
781 (void) fprintf(stderr
,
782 gettext("%s: bad option %s\n"), cmdname
, *av
);
784 (void) fprintf(stderr
, gettext("%s: bad option\n"), cmdname
);
790 * print out a usage message
796 (void) fprintf(stderr
,
797 gettext("%s: [-E] [-H | -L] path-list predicate-list\n"), cmdname
);
802 * This is the function that gets executed at each node
806 execute(name
, statb
, type
, state
)
812 struct Node
*np
= topnode
;
821 if (type
== FTW_NS
) {
822 (void) fprintf(stderr
, gettext("%s: stat() error %s: %s\n"),
823 cmdname
, name
, strerror(errno
));
826 } else if (type
== FTW_DNR
) {
827 (void) fprintf(stderr
, gettext("%s: cannot read dir %s: %s\n"),
828 cmdname
, name
, strerror(errno
));
830 } else if (type
== FTW_SLN
&& lflag
== 1) {
831 (void) fprintf(stderr
,
832 gettext("%s: cannot follow symbolic link %s: %s\n"),
833 cmdname
, name
, strerror(errno
));
835 } else if (type
== FTW_DL
) {
836 (void) fprintf(stderr
, gettext("%s: cycle detected for %s\n"),
842 if ((maxdepth
!= -1 && state
->level
> maxdepth
) ||
843 (mindepth
!= -1 && state
->level
< mindepth
))
847 switch (np
->action
) {
858 if (np
->first
.np
== np
) {
860 * handle naked OR (no term on left hand side)
862 (void) fprintf(stderr
,
863 gettext("%s: invalid -o construction\n"),
869 struct Node
*save
= topnode
;
870 topnode
= np
->first
.np
;
871 (void) execute(name
, statb
, type
, state
);
874 if (np
->action
== OR
) {
886 * If file system type matches the remote
887 * file system type, then it is not local.
889 for (nremfs
= 0; nremfs
< fstype_index
; nremfs
++) {
890 if (strcmp(remote_fstypes
[nremfs
],
891 statb
->st_fstype
) == 0) {
900 l
= (long)statb
->st_mode
&S_IFMT
;
904 l
= (long)statb
->st_mode
&07777;
905 if (np
->second
.i
== '-')
906 val
= ((l
&np
->first
.l
) == np
->first
.l
);
908 val
= (l
== np
->first
.l
);
912 ll
= (long long)statb
->st_ino
;
941 ll
= (long long)statb
->st_size
;
944 ll
= (long long)round(statb
->st_size
, BLKSIZ
)/BLKSIZ
;
947 l
= (long)statb
->st_uid
;
950 l
= (long)statb
->st_gid
;
953 l
= (long)statb
->st_nlink
;
956 if (np
->second
.i
== '+')
957 val
= (ll
> np
->first
.ll
);
958 else if (np
->second
.i
== '-')
959 val
= (ll
< np
->first
.ll
);
961 val
= (ll
== np
->first
.ll
);
964 if (np
->second
.i
== '+')
965 val
= (l
> np
->first
.l
);
966 else if (np
->second
.i
== '-')
967 val
= (l
< np
->first
.l
);
969 val
= (l
== np
->first
.l
);
972 val
= ok(name
, np
->first
.ap
);
975 val
= doexec(name
, np
->first
.ap
, NULL
);
978 val
= dodelete(name
, statb
, state
);
982 struct Arglist
*ap
= np
->first
.vp
;
984 cp
= ap
->nextstr
- (strlen(name
)+1);
985 if (cp
>= (char *)(ap
->nextvar
+3)) {
986 /* there is room just copy the name */
988 (void) strcpy(cp
, name
);
992 /* no more room, exec command */
993 *ap
->nextvar
++ = name
;
996 (void) doexec((char *)0, ap
->arglist
,
998 ap
->nextstr
= ap
->end
;
999 ap
->nextvar
= ap
->firstvar
;
1017 if (np
->action
== INAME
|| np
->action
== IPATH
)
1018 fnmflags
= FNM_IGNORECASE
;
1021 * basename(3c) may modify name, so
1022 * we need to pass another string
1024 if ((path
= strdup(name
)) == NULL
) {
1025 (void) fprintf(stderr
,
1026 gettext("%s: cannot strdup() %s: %s\n"),
1027 cmdname
, name
, strerror(errno
));
1031 * XPG4 find should not treat a leading '.' in a
1032 * filename specially for pattern matching.
1033 * /usr/bin/find will not pattern match a leading
1034 * '.' in a filename, unless '.' is explicitly
1038 fnmflags
|= FNM_PERIOD
;
1041 val
= !fnmatch(np
->first
.cp
,
1042 (np
->action
== NAME
|| np
->action
== INAME
)
1043 ? basename(path
) : path
, fnmflags
);
1050 state
->quit
= FTW_PRUNE
;
1054 val
= ((getpwuid(statb
->st_uid
)) == 0);
1057 val
= ((getgrgid(statb
->st_gid
)) == 0);
1060 val
= (strcmp(np
->first
.cp
, statb
->st_fstype
) == 0);
1063 output
= (FILE *)np
->first
.l
;
1064 (void) fprintf(output
, "%s\n", name
);
1069 (void) fprintf(stdout
, "%s%c", name
,
1070 (np
->action
== PRINT
) ? '\n' : '\0');
1074 (void) list(name
, statb
);
1078 filename
= (walkflags
& FTW_CHDIR
) ?
1079 gettail(name
) : name
;
1080 val
= (pathconf(filename
, _PC_XATTR_EXISTS
) == 1);
1084 * Need to get the tail of the file name, since we have
1085 * already chdir()ed into the directory (performed in
1086 * nftw()) of the file
1088 filename
= (walkflags
& FTW_CHDIR
) ?
1089 gettail(name
) : name
;
1090 val
= acl_trivial(filename
);
1100 filename
= (walkflags
& FTW_CHDIR
) ?
1101 gettail(name
) : name
;
1103 if (acl_get(filename
, 0, &acl
) != 0)
1105 for (i
= 0, acl_entry
= acl
->acl_aclp
;
1106 i
!= acl
->acl_cnt
; i
++) {
1107 if (acl
->acl_type
== ACLENT_T
) {
1108 p1
= (aclent_t
*)acl_entry
;
1109 if (p1
->a_id
== np
->first
.l
) {
1115 p2
= (ace_t
*)acl_entry
;
1116 if (p2
->a_who
== np
->first
.l
) {
1122 acl_entry
= ((char *)acl_entry
+
1123 acl
->acl_entry_size
);
1133 if (regexec(&preg
[cnpreg
], name
, 1, &pmatch
, NULL
) == 0)
1134 val
= ((pmatch
.rm_so
== 0) &&
1135 (pmatch
.rm_eo
== strlen(name
)));
1140 if (state
->level
== maxdepth
&& type
== FTW_D
)
1141 state
->quit
= FTW_PRUNE
;
1148 * evaluate 'val' and 'not' (exclusive-or)
1149 * if no inversion (not == 1), return only when val == 0
1150 * (primary not true). Otherwise, invert the primary
1151 * and return when the primary is true.
1152 * 'Lastval' saves the last result (fail or pass) when
1153 * returning back to the calling routine.
1167 * code for the -ok option
1177 char resp
[LINE_MAX
+ 1];
1179 (void) fflush(stdout
); /* to flush possible `-print' */
1181 if ((*argv
!= dummyarg
) && (strcmp(*argv
, name
)))
1182 (void) fprintf(stderr
, "< %s ... %s >? ", *argv
, name
);
1184 (void) fprintf(stderr
, "< {} ... %s >? ", name
);
1186 (void) fflush(stderr
);
1188 while ((c
= getchar()) != '\n') {
1196 if (yes_check(resp
))
1197 return (doexec(name
, argv
, NULL
));
1203 * execute argv with {} replaced by name
1205 * Per XPG6, find must exit non-zero if an invocation through
1206 * -exec, punctuated by a plus sign, exits non-zero, so set
1207 * exitcode if we see a non-zero exit.
1208 * exitcode should be NULL when -exec or -ok is not punctuated
1213 doexec(char *name
, char *argv
[], int *exitcode
)
1217 char *newargs
[1 + SHELL_MAXARGS
+ 1];
1219 int i
, j
, status
, rc
, r
= 0;
1220 int exit_status
= 0;
1223 (void) fflush(stdout
); /* to flush possible `-print' */
1225 while (cp
= *av
++) {
1226 if (cp
== dummyarg
) {
1233 if (argv
[0] == NULL
) /* null command line */
1236 if ((pid
= fork()) == -1) {
1238 if (exitcode
!= NULL
)
1245 /* wait for child to exit */
1246 if ((rc
= wait(&r
)) == -1 && errno
!= EINTR
) {
1247 (void) fprintf(stderr
,
1248 gettext("wait failed %s"), strerror(errno
));
1250 if (exitcode
!= NULL
)
1254 } while (rc
!= pid
);
1257 (void) execvp(argv
[0], argv
);
1262 * We are in a situation where argv[0] points to a
1263 * script without the interpreter line, e.g. #!/bin/sh.
1264 * execvp() will execute either /usr/bin/sh or
1265 * /usr/xpg4/bin/sh against the script, and you will be
1266 * limited to SHELL_MAXARGS arguments. If you try to
1267 * pass more than SHELL_MAXARGS arguments, execvp()
1269 * See usr/src/lib/libc/port/gen/execvp.c.
1271 * In this situation, process the argument list by
1272 * packets of SHELL_MAXARGS arguments with respect of
1273 * the following rules:
1274 * 1. the invocations have to complete before find exits
1275 * 2. only one invocation can be running at a time
1279 newargs
[0] = argv
[0];
1283 while (j
<= SHELL_MAXARGS
&& argv
[i
]) {
1284 newargs
[j
++] = argv
[i
++];
1288 if ((pid1
= fork()) == -1) {
1294 (void) execvp(newargs
[0], newargs
);
1301 /* wait for the child to exit */
1302 if ((rc
= wait(&status
)) == -1 &&
1304 (void) fprintf(stderr
,
1305 gettext("wait failed %s"),
1309 } while (rc
!= pid1
);
1314 /* all the invocations have completed */
1318 if (name
&& dummyseen
) {
1319 for (av
= argv
; cp
= *av
++; ) {
1325 if (r
&& exitcode
!= NULL
)
1326 *exitcode
= 3; /* use to indicate error in cmd invocation */
1332 dodelete(char *name
, struct stat
*statb
, struct FTW
*state
)
1337 /* restrict symlinks */
1338 if ((walkflags
& FTW_PHYS
) == 0) {
1339 (void) fprintf(stderr
,
1340 gettext("-delete is not allowed when symlinks are "
1345 fn
= name
+ state
->base
;
1346 if (strcmp(fn
, ".") == 0) {
1351 if (strchr(fn
, '/') != NULL
) {
1352 (void) fprintf(stderr
,
1353 gettext("-delete with relative path is unsafe."));
1357 if (S_ISDIR(statb
->st_mode
)) {
1358 /* delete directory */
1366 /* operation failed */
1367 (void) fprintf(stderr
, gettext("delete failed %s: %s\n"),
1368 name
, strerror(errno
));
1376 * Table lookup routine
1378 static struct Args
*
1382 struct Args
*argp
= commands
;
1384 if (word
== 0 || *word
== 0)
1387 while (*argp
->name
) {
1388 if (second
== argp
->name
[1] && strcmp(word
, argp
->name
) == 0)
1397 * Get space for variable length argument list
1400 static struct Arglist
*
1408 n
= 2*sizeof (char **);
1409 for (ep
= environ
; *ep
; ep
++)
1410 n
+= (strlen(*ep
)+sizeof (ep
) + 1);
1411 varsize
= sizeof (struct Arglist
)+ARG_MAX
-PATH_MAX
-n
-1;
1413 ap
= (struct Arglist
*)malloc(varsize
+1);
1414 ap
->end
= (char *)ap
+ varsize
;
1415 ap
->nextstr
= ap
->end
;
1416 ap
->nextvar
= ap
->arglist
;
1417 while (*ap
->nextvar
++ = *com
++);
1419 ap
->firstvar
= ap
->nextvar
;
1420 ap
->next
= lastlist
;
1426 * filter command support
1427 * fork and exec cmd(argv) according to mode:
1429 * "r" with fp as stdin of cmd (default stdin), cmd stdout returned
1430 * "w" with fp as stdout of cmd (default stdout), cmd stdin returned
1433 #define CMDERR ((1<<8)-1) /* command error exit code */
1434 #define MAXCMDS 8 /* max # simultaneous cmdopen()'s */
1436 static struct /* info for each cmdopen() */
1438 FILE *fp
; /* returned by cmdopen() */
1439 pid_t pid
; /* pid used by cmdopen() */
1443 cmdopen(cmd
, argv
, mode
, fp
)
1467 for (proc
= 0; proc
< MAXCMDS
; proc
++)
1468 if (!cmdproc
[proc
].fp
)
1470 if (proc
>= MAXCMDS
)
1476 switch (cmdproc
[proc
].pid
= fork()) {
1480 if (fp
&& fileno(fp
) != usrfd
) {
1481 (void) close(usrfd
);
1482 if (dup2(fileno(fp
), usrfd
) != usrfd
)
1484 (void) close(fileno(fp
));
1486 (void) close(cmdfd
);
1487 if (dup2(pio
[cmdfd
], cmdfd
) != cmdfd
)
1489 (void) close(pio
[cmdfd
]);
1490 (void) close(pio
[usrfd
]);
1491 (void) execvp(cmd
, argv
);
1492 if (errno
== ENOEXEC
) {
1497 * assume cmd is a shell script
1502 if (v
= (char **)malloc((p
- argv
+ 1) *
1503 sizeof (char **))) {
1507 while (*p
++ = *argv
++);
1508 (void) execv(getshell(), v
);
1514 (void) close(pio
[cmdfd
]);
1515 return (cmdproc
[proc
].fp
= fdopen(pio
[usrfd
], mode
));
1520 * close a stream opened by cmdopen()
1521 * -1 returned if cmdopen() had a problem
1522 * otherwise exit() status of command is returned
1533 for (i
= 0; i
< MAXCMDS
; i
++)
1534 if (fp
== cmdproc
[i
].fp
) break;
1539 pid
= cmdproc
[i
].pid
;
1540 while ((p
= wait(&status
)) != pid
&& p
!= (pid_t
)-1);
1542 status
= (status
>> 8) & CMDERR
;
1543 if (status
== CMDERR
)
1552 * return pointer to the full path name of the shell
1554 * SHELL is read from the environment and must start with /
1556 * if set-uid or set-gid then the executable and its containing
1557 * directory must not be writable by the real user
1559 * /usr/bin/sh is returned by default
1570 if (((sh
= getenv("SHELL")) != 0) && *sh
== '/') {
1572 if ((u
!= geteuid() || getgid() != getegid()) &&
1575 s
= strrchr(sh
, '/');
1579 if (!j
) goto defshell
;
1584 return ("/usr/bin/sh");
1588 * the following functions implement the added "-ls" option
1592 #include <sys/mkdev.h>
1595 #define NMAX (sizeof (utmpx.ut_name))
1596 #define SCPYN(a, b) (void) strncpy(a, b, NMAX)
1601 static struct ncache
{
1604 } nc
[NUID
], gc
[NGID
];
1607 * This function assumes that the password file is hashed
1608 * (or some such) to allow fast access based on a name key.
1616 #if (((NUID) & ((NUID) - 1)) != 0)
1619 cp
= uid
& ((NUID
) - 1);
1621 if (nc
[cp
].id
== uid
&& nc
[cp
].name
[0])
1622 return (nc
[cp
].name
);
1627 SCPYN(nc
[cp
].name
, pw
->pw_name
);
1628 return (nc
[cp
].name
);
1632 * This function assumes that the group file is hashed
1633 * (or some such) to allow fast access based on a name key.
1641 #if (((NGID) & ((NGID) - 1)) != 0)
1644 cp
= gid
& ((NGID
) - 1);
1646 if (gc
[cp
].id
== gid
&& gc
[cp
].name
[0])
1647 return (gc
[cp
].name
);
1652 SCPYN(gc
[cp
].name
, gr
->gr_name
);
1653 return (gc
[cp
].name
);
1656 #define permoffset(who) ((who) * 3)
1657 #define permission(who, type) ((type) >> permoffset(who))
1658 #define kbytes(bytes) (((bytes) + 1023) / 1024)
1665 char pmode
[32], uname
[32], gname
[32], fsize
[32], ftime
[32];
1669 * Each line below contains the relevant permission (column 1) and character
1670 * shown when the corresponding execute bit is either clear (column 2)
1672 * These permissions are as shown by ls(1b)
1674 static long special
[] = { S_ISUID
, 'S', 's',
1676 S_ISVTX
, 'T', 't' };
1678 static time_t sixmonthsago
= -1;
1680 char flink
[MAXPATHLEN
+ 1];
1688 if (file
== NULL
|| stp
== NULL
)
1692 if (sixmonthsago
== -1)
1693 sixmonthsago
= now
- 6L*30L*24L*60L*60L;
1695 switch (stp
->st_mode
& S_IFMT
) {
1697 case S_IFDIR
: /* directory */
1702 case S_IFCHR
: /* character special */
1707 case S_IFBLK
: /* block special */
1712 case S_IFIFO
: /* fifo special */
1717 case S_IFLNK
: /* symbolic link */
1722 case S_IFSOCK
: /* socket */
1727 case S_IFDOOR
: /* door */
1732 case S_IFREG
: /* regular */
1741 for (who
= 0; who
< 3; who
++) {
1742 int is_exec
= stp
->st_mode
& permission(who
, S_IEXEC
)? 1 : 0;
1744 if (stp
->st_mode
& permission(who
, S_IREAD
))
1745 pmode
[permoffset(who
) + 1] = 'r';
1747 pmode
[permoffset(who
) + 1] = '-';
1749 if (stp
->st_mode
& permission(who
, S_IWRITE
))
1750 pmode
[permoffset(who
) + 2] = 'w';
1752 pmode
[permoffset(who
) + 2] = '-';
1754 if (stp
->st_mode
& special
[who
* 3])
1755 pmode
[permoffset(who
) + 3] =
1756 special
[who
* 3 + 1 + is_exec
];
1758 pmode
[permoffset(who
) + 3] = 'x';
1760 pmode
[permoffset(who
) + 3] = '-';
1764 * Need to get the tail of the file name, since we have
1765 * already chdir()ed into the directory of the file
1768 tailname
= gettail(file
);
1770 trivial
= acl_trivial(tailname
);
1775 pmode
[permoffset(who
) + 1] = '+';
1777 pmode
[permoffset(who
) + 1] = ' ';
1779 pmode
[permoffset(who
) + 2] = '\0';
1782 * Prepare uname and gname. Always add a space afterwards
1783 * to keep columns from running together.
1785 cp
= getname(stp
->st_uid
);
1787 (void) sprintf(uname
, "%-8s ", cp
);
1789 (void) sprintf(uname
, "%-8u ", stp
->st_uid
);
1791 cp
= getgroup(stp
->st_gid
);
1793 (void) sprintf(gname
, "%-8s ", cp
);
1795 (void) sprintf(gname
, "%-8u ", stp
->st_gid
);
1797 if (pmode
[0] == 'b' || pmode
[0] == 'c')
1798 (void) sprintf(fsize
, "%3ld,%4ld",
1799 major(stp
->st_rdev
), minor(stp
->st_rdev
));
1801 (void) sprintf(fsize
, (stp
->st_size
< 100000000) ?
1802 "%8lld" : "%lld", stp
->st_size
);
1804 if (pmode
[0] == 'l') {
1807 who
= readlink(tailname
, flink
, sizeof (flink
) - 1);
1817 cp
= ctime(&stp
->st_mtime
);
1818 if (stp
->st_mtime
< sixmonthsago
|| stp
->st_mtime
> now
)
1819 (void) sprintf(ftime
, "%-7.7s %-4.4s", cp
+ 4, cp
+ 20);
1821 (void) sprintf(ftime
, "%-12.12s", cp
+ 4);
1823 (void) printf((stp
->st_ino
< 100000) ? "%5llu " :
1824 "%llu ", stp
->st_ino
); /* inode # */
1826 ksize
= (long long) kbytes(ldbtob(stp
->st_blocks
)); /* kbytes */
1828 ksize
= (long long) kbytes(stp
->st_size
); /* kbytes */
1830 (void) printf((ksize
< 10000) ? "%4lld " : "%lld ", ksize
);
1831 (void) printf("%s %2ld %s%s%s %s %s%s%s\n",
1832 pmode
, /* protection */
1833 stp
->st_nlink
, /* # of links */
1836 fsize
, /* # of bytes */
1837 ftime
, /* modify time */
1840 (pmode
[0] == 'l') ? " -> " : "",
1841 (pmode
[0] == 'l') ? flink
: "" /* symlink */
1854 char *p
= strdup(s
);
1858 (void) fprintf(stderr
, gettext("%s: out of memory\n"), cmdname
);
1864 * Read remote file system types from REMOTE_FS into the
1865 * remote_fstypes array.
1871 char line_buf
[LINEBUF_SIZE
];
1873 if ((fp
= fopen(REMOTE_FS
, "r")) == NULL
) {
1874 (void) fprintf(stderr
,
1875 gettext("%s: Warning: can't open %s, ignored\n"),
1876 REMOTE_FS
, cmdname
);
1877 /* Use default string name for NFS */
1878 remote_fstypes
[fstype_index
++] = "nfs";
1882 while (fgets(line_buf
, sizeof (line_buf
), fp
) != NULL
) {
1883 char buf
[LINEBUF_SIZE
];
1885 /* LINTED - unbounded string specifier */
1886 (void) sscanf(line_buf
, "%s", buf
);
1887 remote_fstypes
[fstype_index
++] = new_string(buf
);
1889 if (fstype_index
== N_FSTYPES
)
1895 #define NPERM 30 /* Largest machine */
1898 * The PERM struct is the machine that builds permissions. The p_special
1899 * field contains what permissions need to be checked at run-time in
1900 * getmode(). This is one of 'X', 'u', 'g', or 'o'. It contains '\0' to
1901 * indicate normal processing.
1903 typedef struct PERMST
{
1904 ushort_t p_who
; /* Range of permission (e.g. ugo) */
1905 ushort_t p_perm
; /* Bits to turn on, off, assign */
1906 uchar_t p_op
; /* Operation: + - = */
1907 uchar_t p_special
; /* Special handling? */
1911 #define S_ISVTX 0 /* Not .1 */
1915 #define P_A (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) /* allbits */
1916 #define P_U (S_ISUID|S_ISVTX|S_IRWXU) /* user */
1917 #define P_G (S_ISGID|S_ISVTX|S_IRWXG) /* group */
1918 #define P_O (S_ISVTX|S_IRWXO) /* other */
1920 static int iswho(int c
);
1921 static int isop(int c
);
1922 static int isperm(PERMST
*pp
, int c
);
1924 static PERMST machine
[NPERM
]; /* Permission construction machine */
1925 static PERMST
*endp
; /* Last used PERM structure */
1927 static uint_t nowho
; /* No who for this mode (DOS kludge) */
1930 * Read an ASCII string containing the symbolic/octal mode and
1931 * compile an automaton that recognizes it. The return value
1932 * is NULL if everything is OK, otherwise it is -1.
1936 const char *ascmode
;
1938 const char *amode
= ascmode
;
1945 if (*amode
>= '0' && *amode
<= '7') {
1949 while (*amode
>= '0' && *amode
<= '7')
1950 mode
= (mode
<<3) + *amode
++ - '0';
1953 #if S_ISUID != 04000 || S_ISGID != 02000 || \
1954 S_IRUSR != 0400 || S_IWUSR != 0200 || S_IXUSR != 0100 || \
1955 S_IRGRP != 0040 || S_IWGRP != 0020 || S_IXGRP != 0010 || \
1956 S_IROTH != 0004 || S_IWOTH != 0002 || S_IXOTH != 0001
1958 * There is no requirement of the octal mode bits being
1959 * the same as the S_ macros.
1962 mode_t mapping
[] = {
1963 S_IXOTH
, S_IWOTH
, S_IROTH
,
1964 S_IXGRP
, S_IWGRP
, S_IRGRP
,
1965 S_IXUSR
, S_IWUSR
, S_IRUSR
,
1971 for (i
= 0; mapping
[i
] != 0; i
++)
1973 newmode
|= mapping
[i
];
1984 while ((t
= iswho(*amode
)) != 0) {
1990 (void) umask(currmask
= umask((mode_t
)0));
1993 * If no who specified, must use contents of
1994 * umask to determine which bits to flip. This
1995 * is POSIX/V7/BSD behaviour, but not SVID.
1997 who
= (~currmask
)&P_A
;
2002 if (!isop(pp
->p_op
= *amode
++))
2006 while ((t
= isperm(pp
, *amode
)) != 0) {
2007 if (pp
->p_special
== 'X') {
2010 if (pp
->p_perm
!= 0) {
2014 * Remember the 'who' for the previous
2022 /* Keep 'X' separate */
2024 pp
->p_special
= 'X';
2027 } else if (seen_X
) {
2030 /* Remember the 'who' for the X */
2035 /* Keep 'X' separate */
2046 * These returned 0, but were actually parsed, so
2047 * don't look at them again.
2049 switch (pp
->p_special
) {
2077 * Given a character from the mode, return the associated
2078 * value as who (user designation) mask or 0 if this isn't valid.
2104 * Return non-zero if this is a valid op code
2105 * in a symbolic mode.
2124 * Return the permission bits implied by this character or 0
2125 * if it isn't valid. Also returns 0 when the pseudo-permissions 'u', 'g', or
2126 * 'o' are used, and sets pp->p_special to the one used.
2141 return (S_IRUSR
|S_IRGRP
|S_IROTH
);
2144 return (S_IWUSR
|S_IWGRP
|S_IWOTH
);
2147 return (S_IXUSR
|S_IXGRP
|S_IXOTH
);
2155 pp
->p_special
= 'X';
2156 return (S_IXUSR
|S_IXGRP
|S_IXOTH
);
2167 * This change makes:
2169 * set the system bit on dos but means that
2173 * are all like UNIX.
2176 return (nowho
? S_ISGID
: S_ISGID
|S_ISUID
);
2185 * Execute the automaton that is created by readmode()
2186 * to generate the final mode that will be used. This
2187 * code is passed a starting mode that is usually the original
2188 * mode of the file being changed (or 0). Note that this mode must contain
2189 * the file-type bits as well, so that S_ISDIR will succeed on directories.
2192 getmode(mode_t startmode
)
2198 for (pp
= &machine
[0]; pp
<= endp
; ++pp
) {
2201 * For the special modes 'u', 'g' and 'o', the named portion
2202 * of the mode refers to after the previous clause has been
2203 * processed, while the 'X' mode refers to the contents of the
2204 * mode before any clauses have been processed.
2206 * References: P1003.2/D11.2, Section 4.7.7,
2207 * lines 2568-2570, 2578-2583
2209 switch (pp
->p_special
) {
2211 temp
= startmode
& S_IRWXU
;
2212 if (temp
& (S_IRUSR
|S_IRGRP
|S_IROTH
))
2213 perm
|= ((S_IRUSR
|S_IRGRP
|S_IROTH
) &
2215 if (temp
& (S_IWUSR
|S_IWGRP
|S_IWOTH
))
2216 perm
|= ((S_IWUSR
|S_IWGRP
|S_IWOTH
) & pp
->p_who
);
2217 if (temp
& (S_IXUSR
|S_IXGRP
|S_IXOTH
))
2218 perm
|= ((S_IXUSR
|S_IXGRP
|S_IXOTH
) & pp
->p_who
);
2222 temp
= startmode
& S_IRWXG
;
2223 if (temp
& (S_IRUSR
|S_IRGRP
|S_IROTH
))
2224 perm
|= ((S_IRUSR
|S_IRGRP
|S_IROTH
) & pp
->p_who
);
2225 if (temp
& (S_IWUSR
|S_IWGRP
|S_IWOTH
))
2226 perm
|= ((S_IWUSR
|S_IWGRP
|S_IWOTH
) & pp
->p_who
);
2227 if (temp
& (S_IXUSR
|S_IXGRP
|S_IXOTH
))
2228 perm
|= ((S_IXUSR
|S_IXGRP
|S_IXOTH
) & pp
->p_who
);
2232 temp
= startmode
& S_IRWXO
;
2233 if (temp
& (S_IRUSR
|S_IRGRP
|S_IROTH
))
2234 perm
|= ((S_IRUSR
|S_IRGRP
|S_IROTH
) & pp
->p_who
);
2235 if (temp
& (S_IWUSR
|S_IWGRP
|S_IWOTH
))
2236 perm
|= ((S_IWUSR
|S_IWGRP
|S_IWOTH
) & pp
->p_who
);
2237 if (temp
& (S_IXUSR
|S_IXGRP
|S_IXOTH
))
2238 perm
|= ((S_IXUSR
|S_IXGRP
|S_IXOTH
) & pp
->p_who
);
2251 startmode
&= ~(perm
& pp
->p_who
);
2255 startmode
&= ~pp
->p_who
;
2258 startmode
|= (perm
& pp
->p_who
);
2266 * Returns the last component of a path name, unless it is
2267 * an absolute path, in which case it returns the whole path
2270 *gettail(char *fname
)
2274 if (*fname
!= '/') {
2275 if ((base
= strrchr(fname
, '/')) != NULL
)