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