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