function.c revision 54828
1/*- 2 * Copyright (c) 1990, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Cimarron D. Taylor of the University of California, Berkeley. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37#ifndef lint 38static char sccsid[] = "@(#)function.c 8.10 (Berkeley) 5/4/95"; 39static char rcsid[] = "$FreeBSD: head/usr.bin/find/function.c 54828 1999-12-19 15:43:19Z roberto $"; 40#endif /* not lint */ 41 42#include <sys/param.h> 43#include <sys/ucred.h> 44#include <sys/stat.h> 45#include <sys/wait.h> 46#include <sys/mount.h> 47 48#include <err.h> 49#include <errno.h> 50#include <fnmatch.h> 51#include <fts.h> 52#include <grp.h> 53#include <pwd.h> 54#include <stdio.h> 55#include <stdlib.h> 56#include <string.h> 57#include <unistd.h> 58 59#include "find.h" 60 61int string_to_flags __P((char **, u_long *, u_long *)); 62 63#define COMPARE(a, b) { \ 64 switch (plan->flags) { \ 65 case F_EQUAL: \ 66 return (a == b); \ 67 case F_LESSTHAN: \ 68 return (a < b); \ 69 case F_GREATER: \ 70 return (a > b); \ 71 default: \ 72 abort(); \ 73 } \ 74} 75 76static PLAN *palloc __P((enum ntype, int (*) __P((PLAN *, FTSENT *)))); 77 78/* 79 * find_parsenum -- 80 * Parse a string of the form [+-]# and return the value. 81 */ 82static long long 83find_parsenum(plan, option, vp, endch) 84 PLAN *plan; 85 char *option, *vp, *endch; 86{ 87 long long value; 88 char *endchar, *str; /* Pointer to character ending conversion. */ 89 90 /* Determine comparison from leading + or -. */ 91 str = vp; 92 switch (*str) { 93 case '+': 94 ++str; 95 plan->flags = F_GREATER; 96 break; 97 case '-': 98 ++str; 99 plan->flags = F_LESSTHAN; 100 break; 101 default: 102 plan->flags = F_EQUAL; 103 break; 104 } 105 106 /* 107 * Convert the string with strtoq(). Note, if strtoq() returns zero 108 * and endchar points to the beginning of the string we know we have 109 * a syntax error. 110 */ 111 value = strtoq(str, &endchar, 10); 112 if (value == 0 && endchar == str) 113 errx(1, "%s: %s: illegal numeric value", option, vp); 114 if (endchar[0] && (endch == NULL || endchar[0] != *endch)) 115 errx(1, "%s: %s: illegal trailing character", option, vp); 116 if (endch) 117 *endch = endchar[0]; 118 return (value); 119} 120 121/* 122 * The value of n for the inode times (atime, ctime, and mtime) is a range, 123 * i.e. n matches from (n - 1) to n 24 hour periods. This interacts with 124 * -n, such that "-mtime -1" would be less than 0 days, which isn't what the 125 * user wanted. Correct so that -1 is "less than 1". 126 */ 127#define TIME_CORRECT(p, ttype) \ 128 if ((p)->type == ttype && (p)->flags == F_LESSTHAN) \ 129 ++((p)->t_data); 130 131/* 132 * -amin n functions -- 133 * 134 * True if the difference between the file access time and the 135 * current time is n min periods. 136 */ 137int 138f_amin(plan, entry) 139 PLAN *plan; 140 FTSENT *entry; 141{ 142 extern time_t now; 143 144 COMPARE((now - entry->fts_statp->st_atime + 145 60 - 1) / 60, plan->t_data); 146} 147 148PLAN * 149c_amin(arg) 150 char *arg; 151{ 152 PLAN *new; 153 154 ftsoptions &= ~FTS_NOSTAT; 155 156 new = palloc(N_AMIN, f_amin); 157 new->t_data = find_parsenum(new, "-amin", arg, NULL); 158 TIME_CORRECT(new, N_AMIN); 159 return (new); 160} 161 162 163/* 164 * -atime n functions -- 165 * 166 * True if the difference between the file access time and the 167 * current time is n 24 hour periods. 168 */ 169int 170f_atime(plan, entry) 171 PLAN *plan; 172 FTSENT *entry; 173{ 174 extern time_t now; 175 176 COMPARE((now - entry->fts_statp->st_atime + 177 86400 - 1) / 86400, plan->t_data); 178} 179 180PLAN * 181c_atime(arg) 182 char *arg; 183{ 184 PLAN *new; 185 186 ftsoptions &= ~FTS_NOSTAT; 187 188 new = palloc(N_ATIME, f_atime); 189 new->t_data = find_parsenum(new, "-atime", arg, NULL); 190 TIME_CORRECT(new, N_ATIME); 191 return (new); 192} 193 194 195/* 196 * -cmin n functions -- 197 * 198 * True if the difference between the last change of file 199 * status information and the current time is n min periods. 200 */ 201int 202f_cmin(plan, entry) 203 PLAN *plan; 204 FTSENT *entry; 205{ 206 extern time_t now; 207 208 COMPARE((now - entry->fts_statp->st_ctime + 209 60 - 1) / 60, plan->t_data); 210} 211 212PLAN * 213c_cmin(arg) 214 char *arg; 215{ 216 PLAN *new; 217 218 ftsoptions &= ~FTS_NOSTAT; 219 220 new = palloc(N_CMIN, f_cmin); 221 new->t_data = find_parsenum(new, "-cmin", arg, NULL); 222 TIME_CORRECT(new, N_CMIN); 223 return (new); 224} 225 226/* 227 * -ctime n functions -- 228 * 229 * True if the difference between the last change of file 230 * status information and the current time is n 24 hour periods. 231 */ 232int 233f_ctime(plan, entry) 234 PLAN *plan; 235 FTSENT *entry; 236{ 237 extern time_t now; 238 239 COMPARE((now - entry->fts_statp->st_ctime + 240 86400 - 1) / 86400, plan->t_data); 241} 242 243PLAN * 244c_ctime(arg) 245 char *arg; 246{ 247 PLAN *new; 248 249 ftsoptions &= ~FTS_NOSTAT; 250 251 new = palloc(N_CTIME, f_ctime); 252 new->t_data = find_parsenum(new, "-ctime", arg, NULL); 253 TIME_CORRECT(new, N_CTIME); 254 return (new); 255} 256 257 258/* 259 * -depth functions -- 260 * 261 * Always true, causes descent of the directory hierarchy to be done 262 * so that all entries in a directory are acted on before the directory 263 * itself. 264 */ 265int 266f_always_true(plan, entry) 267 PLAN *plan; 268 FTSENT *entry; 269{ 270 return (1); 271} 272 273PLAN * 274c_depth() 275{ 276 isdepth = 1; 277 278 return (palloc(N_DEPTH, f_always_true)); 279} 280 281/* 282 * [-exec | -ok] utility [arg ... ] ; functions -- 283 * 284 * True if the executed utility returns a zero value as exit status. 285 * The end of the primary expression is delimited by a semicolon. If 286 * "{}" occurs anywhere, it gets replaced by the current pathname. 287 * The current directory for the execution of utility is the same as 288 * the current directory when the find utility was started. 289 * 290 * The primary -ok is different in that it requests affirmation of the 291 * user before executing the utility. 292 */ 293int 294f_exec(plan, entry) 295 register PLAN *plan; 296 FTSENT *entry; 297{ 298 extern int dotfd; 299 register int cnt; 300 pid_t pid; 301 int status; 302 303 for (cnt = 0; plan->e_argv[cnt]; ++cnt) 304 if (plan->e_len[cnt]) 305 brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], 306 entry->fts_path, plan->e_len[cnt]); 307 308 if (plan->flags == F_NEEDOK && !queryuser(plan->e_argv)) 309 return (0); 310 311 /* make sure find output is interspersed correctly with subprocesses */ 312 fflush(stdout); 313 314 switch (pid = fork()) { 315 case -1: 316 err(1, "fork"); 317 /* NOTREACHED */ 318 case 0: 319 if (fchdir(dotfd)) { 320 warn("chdir"); 321 _exit(1); 322 } 323 execvp(plan->e_argv[0], plan->e_argv); 324 warn("%s", plan->e_argv[0]); 325 _exit(1); 326 } 327 pid = waitpid(pid, &status, 0); 328 return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); 329} 330 331/* 332 * c_exec -- 333 * build three parallel arrays, one with pointers to the strings passed 334 * on the command line, one with (possibly duplicated) pointers to the 335 * argv array, and one with integer values that are lengths of the 336 * strings, but also flags meaning that the string has to be massaged. 337 */ 338PLAN * 339c_exec(argvp, isok) 340 char ***argvp; 341 int isok; 342{ 343 PLAN *new; /* node returned */ 344 register int cnt; 345 register char **argv, **ap, *p; 346 347 isoutput = 1; 348 349 new = palloc(N_EXEC, f_exec); 350 if (isok) 351 new->flags = F_NEEDOK; 352 353 for (ap = argv = *argvp;; ++ap) { 354 if (!*ap) 355 errx(1, 356 "%s: no terminating \";\"", isok ? "-ok" : "-exec"); 357 if (**ap == ';') 358 break; 359 } 360 361 cnt = ap - *argvp + 1; 362 new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); 363 new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); 364 new->e_len = (int *)emalloc((u_int)cnt * sizeof(int)); 365 366 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 367 new->e_orig[cnt] = *argv; 368 for (p = *argv; *p; ++p) 369 if (p[0] == '{' && p[1] == '}') { 370 new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN); 371 new->e_len[cnt] = MAXPATHLEN; 372 break; 373 } 374 if (!*p) { 375 new->e_argv[cnt] = *argv; 376 new->e_len[cnt] = 0; 377 } 378 } 379 new->e_argv[cnt] = new->e_orig[cnt] = NULL; 380 381 *argvp = argv + 1; 382 return (new); 383} 384 385/* 386 * -execdir utility [arg ... ] ; functions -- 387 * 388 * True if the executed utility returns a zero value as exit status. 389 * The end of the primary expression is delimited by a semicolon. If 390 * "{}" occurs anywhere, it gets replaced by the unqualified pathname. 391 * The current directory for the execution of utility is the same as 392 * the directory where the file lives. 393 */ 394int 395f_execdir(plan, entry) 396 register PLAN *plan; 397 FTSENT *entry; 398{ 399 register int cnt; 400 pid_t pid; 401 int status; 402 char *file; 403 404 /* XXX - if file/dir ends in '/' this will not work -- can it? */ 405 if ((file = strrchr(entry->fts_path, '/'))) 406 file++; 407 else 408 file = entry->fts_path; 409 410 for (cnt = 0; plan->e_argv[cnt]; ++cnt) 411 if (plan->e_len[cnt]) 412 brace_subst(plan->e_orig[cnt], &plan->e_argv[cnt], 413 file, plan->e_len[cnt]); 414 415 /* don't mix output of command with find output */ 416 fflush(stdout); 417 fflush(stderr); 418 419 switch (pid = fork()) { 420 case -1: 421 err(1, "fork"); 422 /* NOTREACHED */ 423 case 0: 424 execvp(plan->e_argv[0], plan->e_argv); 425 warn("%s", plan->e_argv[0]); 426 _exit(1); 427 } 428 pid = waitpid(pid, &status, 0); 429 return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); 430} 431 432/* 433 * c_execdir -- 434 * build three parallel arrays, one with pointers to the strings passed 435 * on the command line, one with (possibly duplicated) pointers to the 436 * argv array, and one with integer values that are lengths of the 437 * strings, but also flags meaning that the string has to be massaged. 438 */ 439PLAN * 440c_execdir(argvp) 441 char ***argvp; 442{ 443 PLAN *new; /* node returned */ 444 register int cnt; 445 register char **argv, **ap, *p; 446 447 ftsoptions &= ~FTS_NOSTAT; 448 isoutput = 1; 449 450 new = palloc(N_EXECDIR, f_execdir); 451 452 for (ap = argv = *argvp;; ++ap) { 453 if (!*ap) 454 errx(1, 455 "-execdir: no terminating \";\""); 456 if (**ap == ';') 457 break; 458 } 459 460 cnt = ap - *argvp + 1; 461 new->e_argv = (char **)emalloc((u_int)cnt * sizeof(char *)); 462 new->e_orig = (char **)emalloc((u_int)cnt * sizeof(char *)); 463 new->e_len = (int *)emalloc((u_int)cnt * sizeof(int)); 464 465 for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { 466 new->e_orig[cnt] = *argv; 467 for (p = *argv; *p; ++p) 468 if (p[0] == '{' && p[1] == '}') { 469 new->e_argv[cnt] = emalloc((u_int)MAXPATHLEN); 470 new->e_len[cnt] = MAXPATHLEN; 471 break; 472 } 473 if (!*p) { 474 new->e_argv[cnt] = *argv; 475 new->e_len[cnt] = 0; 476 } 477 } 478 new->e_argv[cnt] = new->e_orig[cnt] = NULL; 479 480 *argvp = argv + 1; 481 return (new); 482} 483 484/* 485 * -follow functions -- 486 * 487 * Always true, causes symbolic links to be followed on a global 488 * basis. 489 */ 490PLAN * 491c_follow() 492{ 493 ftsoptions &= ~FTS_PHYSICAL; 494 ftsoptions |= FTS_LOGICAL; 495 496 return (palloc(N_FOLLOW, f_always_true)); 497} 498 499/* 500 * -fstype functions -- 501 * 502 * True if the file is of a certain type. 503 */ 504int 505f_fstype(plan, entry) 506 PLAN *plan; 507 FTSENT *entry; 508{ 509 static dev_t curdev; /* need a guaranteed illegal dev value */ 510 static int first = 1; 511 struct statfs sb; 512 static int val_type, val_flags; 513 char *p, save[2]; 514 515 /* Only check when we cross mount point. */ 516 if (first || curdev != entry->fts_statp->st_dev) { 517 curdev = entry->fts_statp->st_dev; 518 519 /* 520 * Statfs follows symlinks; find wants the link's file system, 521 * not where it points. 522 */ 523 if (entry->fts_info == FTS_SL || 524 entry->fts_info == FTS_SLNONE) { 525 if ((p = strrchr(entry->fts_accpath, '/')) != NULL) 526 ++p; 527 else 528 p = entry->fts_accpath; 529 save[0] = p[0]; 530 p[0] = '.'; 531 save[1] = p[1]; 532 p[1] = '\0'; 533 534 } else 535 p = NULL; 536 537 if (statfs(entry->fts_accpath, &sb)) 538 err(1, "%s", entry->fts_accpath); 539 540 if (p) { 541 p[0] = save[0]; 542 p[1] = save[1]; 543 } 544 545 first = 0; 546 547 /* 548 * Further tests may need both of these values, so 549 * always copy both of them. 550 */ 551 val_flags = sb.f_flags; 552 val_type = sb.f_type; 553 } 554 switch (plan->flags) { 555 case F_MTFLAG: 556 return (val_flags & plan->mt_data) != 0; 557 case F_MTTYPE: 558 return (val_type == plan->mt_data); 559 default: 560 abort(); 561 } 562} 563 564#if !defined(__NetBSD__) 565PLAN * 566c_fstype(arg) 567 char *arg; 568{ 569 register PLAN *new; 570 struct vfsconf vfc; 571 572 ftsoptions &= ~FTS_NOSTAT; 573 574 new = palloc(N_FSTYPE, f_fstype); 575 576 /* 577 * Check first for a filesystem name. 578 */ 579 if (getvfsbyname(arg, &vfc) == 0) { 580 new->flags = F_MTTYPE; 581 new->mt_data = vfc.vfc_typenum; 582 return (new); 583 } 584 585 switch (*arg) { 586 case 'l': 587 if (!strcmp(arg, "local")) { 588 new->flags = F_MTFLAG; 589 new->mt_data = MNT_LOCAL; 590 return (new); 591 } 592 break; 593 case 'r': 594 if (!strcmp(arg, "rdonly")) { 595 new->flags = F_MTFLAG; 596 new->mt_data = MNT_RDONLY; 597 return (new); 598 } 599 break; 600 } 601 errx(1, "%s: unknown file type", arg); 602 /* NOTREACHED */ 603} 604#endif 605 606/* 607 * -group gname functions -- 608 * 609 * True if the file belongs to the group gname. If gname is numeric and 610 * an equivalent of the getgrnam() function does not return a valid group 611 * name, gname is taken as a group ID. 612 */ 613int 614f_group(plan, entry) 615 PLAN *plan; 616 FTSENT *entry; 617{ 618 return (entry->fts_statp->st_gid == plan->g_data); 619} 620 621PLAN * 622c_group(gname) 623 char *gname; 624{ 625 PLAN *new; 626 struct group *g; 627 gid_t gid; 628 629 ftsoptions &= ~FTS_NOSTAT; 630 631 g = getgrnam(gname); 632 if (g == NULL) { 633 gid = atoi(gname); 634 if (gid == 0 && gname[0] != '0') 635 errx(1, "-group: %s: no such group", gname); 636 } else 637 gid = g->gr_gid; 638 639 new = palloc(N_GROUP, f_group); 640 new->g_data = gid; 641 return (new); 642} 643 644/* 645 * -inum n functions -- 646 * 647 * True if the file has inode # n. 648 */ 649int 650f_inum(plan, entry) 651 PLAN *plan; 652 FTSENT *entry; 653{ 654 COMPARE(entry->fts_statp->st_ino, plan->i_data); 655} 656 657PLAN * 658c_inum(arg) 659 char *arg; 660{ 661 PLAN *new; 662 663 ftsoptions &= ~FTS_NOSTAT; 664 665 new = palloc(N_INUM, f_inum); 666 new->i_data = find_parsenum(new, "-inum", arg, NULL); 667 return (new); 668} 669 670/* 671 * -links n functions -- 672 * 673 * True if the file has n links. 674 */ 675int 676f_links(plan, entry) 677 PLAN *plan; 678 FTSENT *entry; 679{ 680 COMPARE(entry->fts_statp->st_nlink, plan->l_data); 681} 682 683PLAN * 684c_links(arg) 685 char *arg; 686{ 687 PLAN *new; 688 689 ftsoptions &= ~FTS_NOSTAT; 690 691 new = palloc(N_LINKS, f_links); 692 new->l_data = (nlink_t)find_parsenum(new, "-links", arg, NULL); 693 return (new); 694} 695 696/* 697 * -ls functions -- 698 * 699 * Always true - prints the current entry to stdout in "ls" format. 700 */ 701int 702f_ls(plan, entry) 703 PLAN *plan; 704 FTSENT *entry; 705{ 706 printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); 707 return (1); 708} 709 710PLAN * 711c_ls() 712{ 713 ftsoptions &= ~FTS_NOSTAT; 714 isoutput = 1; 715 716 return (palloc(N_LS, f_ls)); 717} 718 719/* 720 * -mtime n functions -- 721 * 722 * True if the difference between the file modification time and the 723 * current time is n 24 hour periods. 724 */ 725int 726f_mtime(plan, entry) 727 PLAN *plan; 728 FTSENT *entry; 729{ 730 extern time_t now; 731 732 COMPARE((now - entry->fts_statp->st_mtime + 86400 - 1) / 733 86400, plan->t_data); 734} 735 736PLAN * 737c_mtime(arg) 738 char *arg; 739{ 740 PLAN *new; 741 742 ftsoptions &= ~FTS_NOSTAT; 743 744 new = palloc(N_MTIME, f_mtime); 745 new->t_data = find_parsenum(new, "-mtime", arg, NULL); 746 TIME_CORRECT(new, N_MTIME); 747 return (new); 748} 749 750/* 751 * -mmin n functions -- 752 * 753 * True if the difference between the file modification time and the 754 * current time is n min periods. 755 */ 756int 757f_mmin(plan, entry) 758 PLAN *plan; 759 FTSENT *entry; 760{ 761 extern time_t now; 762 763 COMPARE((now - entry->fts_statp->st_mtime + 60 - 1) / 764 60, plan->t_data); 765} 766 767PLAN * 768c_mmin(arg) 769 char *arg; 770{ 771 PLAN *new; 772 773 ftsoptions &= ~FTS_NOSTAT; 774 775 new = palloc(N_MMIN, f_mmin); 776 new->t_data = find_parsenum(new, "-mmin", arg, NULL); 777 TIME_CORRECT(new, N_MMIN); 778 return (new); 779} 780 781 782/* 783 * -name functions -- 784 * 785 * True if the basename of the filename being examined 786 * matches pattern using Pattern Matching Notation S3.14 787 */ 788int 789f_name(plan, entry) 790 PLAN *plan; 791 FTSENT *entry; 792{ 793 return (!fnmatch(plan->c_data, entry->fts_name, 0)); 794} 795 796PLAN * 797c_name(pattern) 798 char *pattern; 799{ 800 PLAN *new; 801 802 new = palloc(N_NAME, f_name); 803 new->c_data = pattern; 804 return (new); 805} 806 807/* 808 * -newer file functions -- 809 * 810 * True if the current file has been modified more recently 811 * then the modification time of the file named by the pathname 812 * file. 813 */ 814int 815f_newer(plan, entry) 816 PLAN *plan; 817 FTSENT *entry; 818{ 819 return (entry->fts_statp->st_mtime > plan->t_data); 820} 821 822PLAN * 823c_newer(filename) 824 char *filename; 825{ 826 PLAN *new; 827 struct stat sb; 828 829 ftsoptions &= ~FTS_NOSTAT; 830 831 if (stat(filename, &sb)) 832 err(1, "%s", filename); 833 new = palloc(N_NEWER, f_newer); 834 new->t_data = sb.st_mtime; 835 return (new); 836} 837 838/* 839 * -nogroup functions -- 840 * 841 * True if file belongs to a user ID for which the equivalent 842 * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. 843 */ 844int 845f_nogroup(plan, entry) 846 PLAN *plan; 847 FTSENT *entry; 848{ 849 char *group_from_gid(); 850 851 return (group_from_gid(entry->fts_statp->st_gid, 1) ? 0 : 1); 852} 853 854PLAN * 855c_nogroup() 856{ 857 ftsoptions &= ~FTS_NOSTAT; 858 859 return (palloc(N_NOGROUP, f_nogroup)); 860} 861 862/* 863 * -nouser functions -- 864 * 865 * True if file belongs to a user ID for which the equivalent 866 * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. 867 */ 868int 869f_nouser(plan, entry) 870 PLAN *plan; 871 FTSENT *entry; 872{ 873 char *user_from_uid(); 874 875 return (user_from_uid(entry->fts_statp->st_uid, 1) ? 0 : 1); 876} 877 878PLAN * 879c_nouser() 880{ 881 ftsoptions &= ~FTS_NOSTAT; 882 883 return (palloc(N_NOUSER, f_nouser)); 884} 885 886/* 887 * -path functions -- 888 * 889 * True if the path of the filename being examined 890 * matches pattern using Pattern Matching Notation S3.14 891 */ 892int 893f_path(plan, entry) 894 PLAN *plan; 895 FTSENT *entry; 896{ 897 return (!fnmatch(plan->c_data, entry->fts_path, 0)); 898} 899 900PLAN * 901c_path(pattern) 902 char *pattern; 903{ 904 PLAN *new; 905 906 new = palloc(N_NAME, f_path); 907 new->c_data = pattern; 908 return (new); 909} 910 911/* 912 * -perm functions -- 913 * 914 * The mode argument is used to represent file mode bits. If it starts 915 * with a leading digit, it's treated as an octal mode, otherwise as a 916 * symbolic mode. 917 */ 918int 919f_perm(plan, entry) 920 PLAN *plan; 921 FTSENT *entry; 922{ 923 mode_t mode; 924 925 mode = entry->fts_statp->st_mode & 926 (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); 927 if (plan->flags == F_ATLEAST) 928 return ((plan->m_data | mode) == mode); 929 else 930 return (mode == plan->m_data); 931 /* NOTREACHED */ 932} 933 934PLAN * 935c_perm(perm) 936 char *perm; 937{ 938 PLAN *new; 939 mode_t *set; 940 941 ftsoptions &= ~FTS_NOSTAT; 942 943 new = palloc(N_PERM, f_perm); 944 945 if (*perm == '-') { 946 new->flags = F_ATLEAST; 947 ++perm; 948 } 949 950 if ((set = setmode(perm)) == NULL) 951 errx(1, "-perm: %s: illegal mode string", perm); 952 953 new->m_data = getmode(set, 0); 954 free(set); 955 return (new); 956} 957 958/* 959 * -flags functions -- 960 * 961 * The flags argument is used to represent file flags bits. 962 */ 963int 964f_flags(plan, entry) 965 PLAN *plan; 966 FTSENT *entry; 967{ 968 u_long flags; 969 970 flags = entry->fts_statp->st_flags & 971 (UF_NODUMP | UF_IMMUTABLE | UF_APPEND | UF_OPAQUE | 972 SF_ARCHIVED | SF_IMMUTABLE | SF_APPEND); 973 if (plan->flags == F_ATLEAST) 974 /* note that plan->fl_flags always is a subset of 975 plan->fl_mask */ 976 return (flags & plan->fl_mask) == plan->fl_flags; 977 else 978 return flags == plan->fl_flags; 979 /* NOTREACHED */ 980} 981 982PLAN * 983c_flags(flags_str) 984 char *flags_str; 985{ 986 PLAN *new; 987 u_long flags, notflags; 988 989 ftsoptions &= ~FTS_NOSTAT; 990 991 new = palloc(N_FLAGS, f_flags); 992 993 if (*flags_str == '-') { 994 new->flags = F_ATLEAST; 995 flags_str++; 996 } 997 if (string_to_flags(&flags_str, &flags, ¬flags) == 1) 998 errx(1, "-flags: %s: illegal flags string", flags_str); 999 1000 new->fl_flags = flags; 1001 new->fl_mask = flags | notflags; 1002#if 0 1003 printf("flags = %08x, mask = %08x (%08x, %08x)\n", 1004 new->fl_flags, new->fl_mask, flags, notflags); 1005#endif 1006 return new; 1007} 1008 1009 /* 1010 1011 1012/* 1013 * -print functions -- 1014 * 1015 * Always true, causes the current pathame to be written to 1016 * standard output. 1017 */ 1018int 1019f_print(plan, entry) 1020 PLAN *plan; 1021 FTSENT *entry; 1022{ 1023 (void)puts(entry->fts_path); 1024 return (1); 1025} 1026 1027PLAN * 1028c_print() 1029{ 1030 isoutput = 1; 1031 1032 return (palloc(N_PRINT, f_print)); 1033} 1034 1035/* 1036 * -print0 functions -- 1037 * 1038 * Always true, causes the current pathame to be written to 1039 * standard output followed by a NUL character 1040 */ 1041int 1042f_print0(plan, entry) 1043 PLAN *plan; 1044 FTSENT *entry; 1045{ 1046 fputs(entry->fts_path, stdout); 1047 fputc('\0', stdout); 1048 return (1); 1049} 1050 1051PLAN * 1052c_print0() 1053{ 1054 isoutput = 1; 1055 1056 return (palloc(N_PRINT0, f_print0)); 1057} 1058 1059/* 1060 * -prune functions -- 1061 * 1062 * Prune a portion of the hierarchy. 1063 */ 1064int 1065f_prune(plan, entry) 1066 PLAN *plan; 1067 FTSENT *entry; 1068{ 1069 extern FTS *tree; 1070 1071 if (fts_set(tree, entry, FTS_SKIP)) 1072 err(1, "%s", entry->fts_path); 1073 return (1); 1074} 1075 1076PLAN * 1077c_prune() 1078{ 1079 return (palloc(N_PRUNE, f_prune)); 1080} 1081 1082/* 1083 * -size n[c] functions -- 1084 * 1085 * True if the file size in bytes, divided by an implementation defined 1086 * value and rounded up to the next integer, is n. If n is followed by 1087 * a c, the size is in bytes. 1088 */ 1089#define FIND_SIZE 512 1090static int divsize = 1; 1091 1092int 1093f_size(plan, entry) 1094 PLAN *plan; 1095 FTSENT *entry; 1096{ 1097 off_t size; 1098 1099 size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / 1100 FIND_SIZE : entry->fts_statp->st_size; 1101 COMPARE(size, plan->o_data); 1102} 1103 1104PLAN * 1105c_size(arg) 1106 char *arg; 1107{ 1108 PLAN *new; 1109 char endch; 1110 1111 ftsoptions &= ~FTS_NOSTAT; 1112 1113 new = palloc(N_SIZE, f_size); 1114 endch = 'c'; 1115 new->o_data = find_parsenum(new, "-size", arg, &endch); 1116 if (endch == 'c') 1117 divsize = 0; 1118 return (new); 1119} 1120 1121/* 1122 * -type c functions -- 1123 * 1124 * True if the type of the file is c, where c is b, c, d, p, f or w 1125 * for block special file, character special file, directory, FIFO, 1126 * regular file or whiteout respectively. 1127 */ 1128int 1129f_type(plan, entry) 1130 PLAN *plan; 1131 FTSENT *entry; 1132{ 1133 return ((entry->fts_statp->st_mode & S_IFMT) == plan->m_data); 1134} 1135 1136PLAN * 1137c_type(typestring) 1138 char *typestring; 1139{ 1140 PLAN *new; 1141 mode_t mask; 1142 1143 ftsoptions &= ~FTS_NOSTAT; 1144 1145 switch (typestring[0]) { 1146 case 'b': 1147 mask = S_IFBLK; 1148 break; 1149 case 'c': 1150 mask = S_IFCHR; 1151 break; 1152 case 'd': 1153 mask = S_IFDIR; 1154 break; 1155 case 'f': 1156 mask = S_IFREG; 1157 break; 1158 case 'l': 1159 mask = S_IFLNK; 1160 break; 1161 case 'p': 1162 mask = S_IFIFO; 1163 break; 1164 case 's': 1165 mask = S_IFSOCK; 1166 break; 1167#ifdef FTS_WHITEOUT 1168 case 'w': 1169 mask = S_IFWHT; 1170 ftsoptions |= FTS_WHITEOUT; 1171 break; 1172#endif /* FTS_WHITEOUT */ 1173 default: 1174 errx(1, "-type: %s: unknown type", typestring); 1175 } 1176 1177 new = palloc(N_TYPE, f_type); 1178 new->m_data = mask; 1179 return (new); 1180} 1181 1182/* 1183 * -delete functions -- 1184 * 1185 * True always. Makes it's best shot and continues on regardless. 1186 */ 1187int 1188f_delete(plan, entry) 1189 PLAN *plan; 1190 FTSENT *entry; 1191{ 1192 /* ignore these from fts */ 1193 if (strcmp(entry->fts_accpath, ".") == 0 || 1194 strcmp(entry->fts_accpath, "..") == 0) 1195 return (1); 1196 1197 /* sanity check */ 1198 if (isdepth == 0 || /* depth off */ 1199 (ftsoptions & FTS_NOSTAT) || /* not stat()ing */ 1200 !(ftsoptions & FTS_PHYSICAL) || /* physical off */ 1201 (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ 1202 errx(1, "-delete: insecure options got turned on"); 1203 1204 /* Potentially unsafe - do not accept relative paths whatsoever */ 1205 if (strchr(entry->fts_accpath, '/') != NULL) 1206 errx(1, "-delete: %s: relative path potentially not safe", 1207 entry->fts_accpath); 1208 1209 /* Turn off user immutable bits if running as root */ 1210 if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && 1211 !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && 1212 geteuid() == 0) 1213 chflags(entry->fts_accpath, 1214 entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); 1215 1216 /* rmdir directories, unlink everything else */ 1217 if (S_ISDIR(entry->fts_statp->st_mode)) { 1218 if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) 1219 warn("-delete: rmdir(%s)", entry->fts_path); 1220 } else { 1221 if (unlink(entry->fts_accpath) < 0) 1222 warn("-delete: unlink(%s)", entry->fts_path); 1223 } 1224 1225 /* "succeed" */ 1226 return (1); 1227} 1228 1229PLAN * 1230c_delete() 1231{ 1232 1233 ftsoptions &= ~FTS_NOSTAT; /* no optimise */ 1234 ftsoptions |= FTS_PHYSICAL; /* disable -follow */ 1235 ftsoptions &= ~FTS_LOGICAL; /* disable -follow */ 1236 isoutput = 1; /* possible output */ 1237 isdepth = 1; /* -depth implied */ 1238 1239 return (palloc(N_DELETE, f_delete)); 1240} 1241 1242/* 1243 * -user uname functions -- 1244 * 1245 * True if the file belongs to the user uname. If uname is numeric and 1246 * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not 1247 * return a valid user name, uname is taken as a user ID. 1248 */ 1249int 1250f_user(plan, entry) 1251 PLAN *plan; 1252 FTSENT *entry; 1253{ 1254 return (entry->fts_statp->st_uid == plan->u_data); 1255} 1256 1257PLAN * 1258c_user(username) 1259 char *username; 1260{ 1261 PLAN *new; 1262 struct passwd *p; 1263 uid_t uid; 1264 1265 ftsoptions &= ~FTS_NOSTAT; 1266 1267 p = getpwnam(username); 1268 if (p == NULL) { 1269 uid = atoi(username); 1270 if (uid == 0 && username[0] != '0') 1271 errx(1, "-user: %s: no such user", username); 1272 } else 1273 uid = p->pw_uid; 1274 1275 new = palloc(N_USER, f_user); 1276 new->u_data = uid; 1277 return (new); 1278} 1279 1280/* 1281 * -xdev functions -- 1282 * 1283 * Always true, causes find not to decend past directories that have a 1284 * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) 1285 */ 1286PLAN * 1287c_xdev() 1288{ 1289 ftsoptions |= FTS_XDEV; 1290 1291 return (palloc(N_XDEV, f_always_true)); 1292} 1293 1294/* 1295 * ( expression ) functions -- 1296 * 1297 * True if expression is true. 1298 */ 1299int 1300f_expr(plan, entry) 1301 PLAN *plan; 1302 FTSENT *entry; 1303{ 1304 register PLAN *p; 1305 register int state; 1306 1307 state = 0; 1308 for (p = plan->p_data[0]; 1309 p && (state = (p->eval)(p, entry)); p = p->next); 1310 return (state); 1311} 1312 1313/* 1314 * N_OPENPAREN and N_CLOSEPAREN nodes are temporary place markers. They are 1315 * eliminated during phase 2 of find_formplan() --- the '(' node is converted 1316 * to a N_EXPR node containing the expression and the ')' node is discarded. 1317 */ 1318PLAN * 1319c_openparen() 1320{ 1321 return (palloc(N_OPENPAREN, (int (*)())-1)); 1322} 1323 1324PLAN * 1325c_closeparen() 1326{ 1327 return (palloc(N_CLOSEPAREN, (int (*)())-1)); 1328} 1329 1330/* 1331 * ! expression functions -- 1332 * 1333 * Negation of a primary; the unary NOT operator. 1334 */ 1335int 1336f_not(plan, entry) 1337 PLAN *plan; 1338 FTSENT *entry; 1339{ 1340 register PLAN *p; 1341 register int state; 1342 1343 state = 0; 1344 for (p = plan->p_data[0]; 1345 p && (state = (p->eval)(p, entry)); p = p->next); 1346 return (!state); 1347} 1348 1349PLAN * 1350c_not() 1351{ 1352 return (palloc(N_NOT, f_not)); 1353} 1354 1355/* 1356 * expression -o expression functions -- 1357 * 1358 * Alternation of primaries; the OR operator. The second expression is 1359 * not evaluated if the first expression is true. 1360 */ 1361int 1362f_or(plan, entry) 1363 PLAN *plan; 1364 FTSENT *entry; 1365{ 1366 register PLAN *p; 1367 register int state; 1368 1369 state = 0; 1370 for (p = plan->p_data[0]; 1371 p && (state = (p->eval)(p, entry)); p = p->next); 1372 1373 if (state) 1374 return (1); 1375 1376 for (p = plan->p_data[1]; 1377 p && (state = (p->eval)(p, entry)); p = p->next); 1378 return (state); 1379} 1380 1381PLAN * 1382c_or() 1383{ 1384 return (palloc(N_OR, f_or)); 1385} 1386 1387static PLAN * 1388palloc(t, f) 1389 enum ntype t; 1390 int (*f) __P((PLAN *, FTSENT *)); 1391{ 1392 PLAN *new; 1393 1394 if ((new = malloc(sizeof(PLAN))) == NULL) 1395 err(1, NULL); 1396 new->type = t; 1397 new->eval = f; 1398 new->flags = 0; 1399 new->next = NULL; 1400 return (new); 1401} 1402