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