1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <dirent.h> 29 #include <fnmatch.h> 30 #include "bart.h" 31 32 static int count_slashes(const char *); 33 static struct rule *gen_rulestruct(void); 34 static struct tree_modifier *gen_tree_modifier(void); 35 static struct dir_component *gen_dir_component(void); 36 static void init_rule(uint_t, struct rule *); 37 static void add_modifier(struct rule *, char *); 38 static struct rule *add_subtree_rule(char *, char *, int, int *); 39 static struct rule *add_single_rule(char *); 40 static void dirs_cleanup(struct dir_component *); 41 static void add_dir(struct dir_component **, char *); 42 static char *lex(FILE *); 43 static int match_subtree(const char *, char *); 44 static struct rule *get_last_entry(boolean_t); 45 46 static int lex_linenum; /* line number in current input file */ 47 static struct rule *first_rule = NULL, *current_rule = NULL; 48 49 /* 50 * This function is responsible for validating whether or not a given file 51 * should be cataloged, based upon the modifiers for a subtree. 52 * For example, a line in the rules file: '/home/nickiso *.c' should only 53 * catalog the C files (based upon pattern matching) in the subtree 54 * '/home/nickiso'. 55 * 56 * Return non-zero if it should be excluded, 0 if it should be cataloged. 57 */ 58 int 59 exclude_fname(const char *fname, char fname_type, struct rule *rule_ptr) 60 { 61 char *pattern, *ptr, component[PATH_MAX], fname_cp[PATH_MAX], 62 pattern_cp[PATH_MAX]; 63 int match, num_pattern_slash, num_fname_slash, i, slashes_to_adv, 64 ret_val = 0; 65 struct tree_modifier *mod_ptr; 66 boolean_t dir_flag; 67 68 /* 69 * For a given entry in the rules struct, the modifiers, e.g., '*.c', 70 * are kept in a linked list. Get a ptr to the head of the list. 71 */ 72 mod_ptr = rule_ptr->modifiers; 73 74 /* 75 * Walk through all the modifiers until its they are exhausted OR 76 * until the file should definitely be excluded. 77 */ 78 while ((mod_ptr != NULL) && !ret_val) { 79 /* First, see if we should be matching files or dirs */ 80 if (mod_ptr->mod_str[(strlen(mod_ptr->mod_str)-1)] == '/') 81 dir_flag = B_TRUE; 82 else 83 dir_flag = B_FALSE; 84 85 if (mod_ptr->mod_str[0] == '!') { 86 pattern = (mod_ptr->mod_str + 1); 87 } else { 88 pattern = mod_ptr->mod_str; 89 } 90 91 if (dir_flag == B_FALSE) { 92 /* 93 * In the case when a user is trying to filter on 94 * FILES and the entry is a directory, its excluded! 95 */ 96 if (fname_type == 'D') { 97 ret_val = 1; 98 break; 99 } 100 101 /* 102 * Match patterns against filenames. 103 * Need to be able to handle multi-level patterns, 104 * eg. "SCCS/<star-wildcard>.c", which means 105 * 'only match C files under SCCS directories. 106 * 107 * Determine the number of levels in the filename and 108 * in the pattern. 109 */ 110 num_pattern_slash = count_slashes(pattern); 111 num_fname_slash = count_slashes(fname); 112 113 /* Check for trivial exclude condition */ 114 if (num_pattern_slash > num_fname_slash) { 115 ret_val = 1; 116 break; 117 } 118 119 /* 120 * Do an apples to apples comparison, based upon the 121 * number of levels: 122 * 123 * Assume fname is /A/B/C/D/E and the pattern is D/E. 124 * In that case, 'ptr' will point to "D/E" and 125 * 'slashes_to_adv' will be '4'. 126 */ 127 (void) strlcpy(fname_cp, fname, sizeof (fname_cp)); 128 ptr = fname_cp; 129 slashes_to_adv = num_fname_slash - num_pattern_slash; 130 for (i = 0; i < slashes_to_adv; i++) { 131 ptr = strchr(ptr, '/'); 132 ptr++; 133 } 134 if ((pattern[0] == '.') && (pattern[1] == '.') && 135 (pattern[2] == '/')) { 136 pattern = strchr(pattern, '/'); 137 ptr = strchr(ptr, '/'); 138 } 139 140 141 /* OK, now do the fnmatch() and set the return value */ 142 match = fnmatch(pattern, ptr, FNM_PATHNAME); 143 144 if (match == 0) 145 ret_val = 0; 146 else 147 ret_val = 1; 148 149 } else { 150 /* 151 * The rule requires directory matching. 152 * 153 * First, make copies, since both the pattern and 154 * filename need to be modified. 155 * 156 * When copying 'fname', ignore the relocatable root 157 * since pattern matching is done for the string AFTER 158 * the relocatable root. For example, if the 159 * relocatable root is "/dir1/dir2/dir3" and the 160 * pattern is "dir3/", we do NOT want to include every 161 * directory in the relocatable root. Instead, we 162 * only want to include subtrees that look like: 163 * "/dir1/dir2/dir3/....dir3/....." 164 * 165 * NOTE: the 'pattern_cp' does NOT have a trailing '/': 166 * necessary for fnmatch(). 167 */ 168 (void) strlcpy(fname_cp, 169 (fname+strlen(rule_ptr->subtree)), 170 sizeof (fname_cp)); 171 (void) strlcpy(pattern_cp, pattern, 172 sizeof (pattern_cp)); 173 174 /* 175 * For non-directory entries, remove the trailing 176 * name, e.g., for a file /A/B/C/D where 'D' is 177 * the actual filename, remove the 'D' since it 178 * should *not* be considered in the directory match. 179 */ 180 if (fname_type != 'D') { 181 ptr = strrchr(fname_cp, '/'); 182 if (ptr != NULL) 183 *ptr = '\0'; 184 185 /* Trivial case: simple filename */ 186 if (strlen(fname_cp) == 0) { 187 ret_val = 1; 188 break; 189 } 190 } 191 192 /* Count the # of slashes in the pattern and fname */ 193 num_pattern_slash = count_slashes(pattern_cp); 194 num_fname_slash = count_slashes(fname_cp); 195 196 /* 197 * fname_cp is too short, bail! 198 */ 199 if (num_pattern_slash > num_fname_slash) { 200 ret_val = 1; 201 break; 202 } 203 204 /* 205 * OK, walk through the filename and check for the 206 * pattern. 207 * This loop will termate when the match is found OR 208 * there fname is too short to possibly match. 209 */ 210 while (strlen(fname_cp) > 0) { 211 num_fname_slash = count_slashes(fname_cp); 212 213 /* 214 * fname is too short, bail! 215 */ 216 if (num_pattern_slash > num_fname_slash) { 217 ret_val = 1; 218 break; 219 } 220 221 /* 222 * The next stanza selects an appropriate 223 * substring of the filename. 224 * For example, if pattern is 'C/D/E' and 225 * filename is '/A/B/C/D/E', this stanza will 226 * set ptr to 'C/D/E'. 227 */ 228 229 component[0] = '\0'; 230 if (num_fname_slash > 0) { 231 ptr = (fname_cp+1); 232 233 for (i = 0; i < (num_pattern_slash-1); 234 i++) { 235 ptr = strchr(ptr, '/'); 236 ptr++; 237 } 238 239 if (ptr != NULL) 240 (void) strlcpy(component, ptr, 241 sizeof (component)); 242 243 } else 244 (void) strlcpy(component, fname_cp, 245 sizeof (component)); 246 247 /* 248 * See if they match. If they do, set exclude 249 * to appropriate value and exit. 250 */ 251 match = fnmatch(pattern_cp, component, 252 FNM_PATHNAME); 253 254 /* 255 * Special case: match "/" and "*" or "?". 256 * Necessary since explicitly NOT matched by 257 * fnmatch() 258 */ 259 if ((match == 1) && (strlen(component) == 1) && 260 (component[0] == '/') && 261 (strlen(pattern_cp) == 1)) { 262 if ((pattern_cp[0] == '?') || 263 (pattern_cp[0] == '*')) 264 match = 0; 265 } 266 267 /* 268 * Test to see if there is a match. 269 * 270 * If it matches we are done for this rule. 271 * 272 * If there is NOT a match, then we need 273 * to iterate down the filename until it 274 * matches OR we determine it cannot match. 275 */ 276 if (match == 0) { 277 ret_val = 0; 278 break; 279 } else { 280 /* 281 * No match. Remove the last 'segment' 282 * of the filename, e.g., if its 283 * "/A/B/C/D/E", then remove "/E". 284 * If nothing left to remove, we are 285 * done. 286 */ 287 ptr = strrchr(fname_cp, '/'); 288 if (ptr != NULL) 289 *ptr = '\0'; 290 else { 291 fname_cp[0] = '\0'; 292 ret_val = 1; 293 } 294 } 295 } 296 } 297 298 /* 299 * Take into account whether or not this rule began with 300 * a '!' 301 */ 302 if (mod_ptr->include == B_FALSE) { 303 if (ret_val == 0) 304 ret_val = 1; 305 else ret_val = 0; 306 } 307 308 /* Advance to the next modifier */ 309 mod_ptr = mod_ptr->next; 310 } 311 312 return (ret_val); 313 } 314 315 static int 316 count_slashes(const char *in_path) 317 { 318 int num_fname_slash = 0; 319 const char *p; 320 for (p = in_path; *p != '\0'; p++) 321 if (*p == '/') 322 num_fname_slash++; 323 return (num_fname_slash); 324 } 325 326 static struct rule * 327 gen_rulestruct(void) 328 { 329 struct rule *new_rule; 330 331 new_rule = (struct rule *)safe_calloc(sizeof (struct rule)); 332 new_rule->traversed = B_FALSE; 333 return (new_rule); 334 } 335 336 static struct tree_modifier * 337 gen_tree_modifier(void) 338 { 339 struct tree_modifier *new_modifier; 340 341 new_modifier = (struct tree_modifier *)safe_calloc 342 (sizeof (struct tree_modifier)); 343 return (new_modifier); 344 } 345 346 static struct dir_component * 347 gen_dir_component(void) 348 { 349 struct dir_component *new_dir; 350 351 new_dir = (struct dir_component *)safe_calloc 352 (sizeof (struct dir_component)); 353 return (new_dir); 354 } 355 356 /* 357 * Set up a default rule when there is no rules file. 358 */ 359 static struct rule * 360 setup_default_rule(char *reloc_root, uint_t flags) 361 { 362 struct rule *new_rule; 363 364 new_rule = add_single_rule(reloc_root[0] == '\0' ? "/" : reloc_root); 365 init_rule(flags, new_rule); 366 add_modifier(new_rule, "*"); 367 368 return (new_rule); 369 } 370 371 /* 372 * Utility function, used to initialize the flag in a new rule structure. 373 */ 374 static void 375 init_rule(uint_t flags, struct rule *new_rule) 376 { 377 378 if (new_rule == NULL) 379 return; 380 new_rule->attr_list = flags; 381 } 382 383 /* 384 * Function to read the rulesfile. Used by both 'bart create' and 385 * 'bart compare'. 386 */ 387 int 388 read_rules(FILE *file, char *reloc_root, uint_t in_flags, int create) 389 { 390 char *s; 391 struct rule *block_begin = NULL, *new_rule, *rp; 392 struct attr_keyword *akp; 393 int check_flag, ignore_flag, syntax_err, ret_code; 394 395 ret_code = EXIT; 396 397 lex_linenum = 0; 398 check_flag = 0; 399 ignore_flag = 0; 400 syntax_err = 0; 401 402 if (file == NULL) { 403 (void) setup_default_rule(reloc_root, in_flags); 404 return (ret_code); 405 } else if (!create) { 406 block_begin = setup_default_rule("/", in_flags); 407 } 408 409 while (!feof(file)) { 410 /* Read a line from the file */ 411 s = lex(file); 412 413 /* skip blank lines and comments */ 414 if (s == NULL || *s == 0 || *s == '#') 415 continue; 416 417 /* 418 * Beginning of a subtree and possibly a new block. 419 * 420 * If this is a new block, keep track of the beginning of 421 * the block. if there are directives later on, we need to 422 * apply that directive to all members of the block. 423 * 424 * If the first stmt in the file was an 'IGNORE all' or 425 * 'IGNORE contents', we need to keep track of it and 426 * automatically switch off contents checking for new 427 * subtrees. 428 */ 429 if (s[0] == '/') { 430 new_rule = add_subtree_rule(s, reloc_root, create, 431 &ret_code); 432 433 s = lex(0); 434 while ((s != NULL) && (*s != 0) && (*s != '#')) { 435 add_modifier(new_rule, s); 436 s = lex(0); 437 } 438 439 /* Found a new block, keep track of the beginning */ 440 if (block_begin == NULL || 441 (ignore_flag != 0) || (check_flag != 0)) { 442 block_begin = new_rule; 443 check_flag = 0; 444 ignore_flag = 0; 445 } 446 447 /* Apply global settings to this block, if any */ 448 init_rule(in_flags, new_rule); 449 } else if (IGNORE_KEYWORD(s) || CHECK_KEYWORD(s)) { 450 int check_kw; 451 452 if (IGNORE_KEYWORD(s)) { 453 ignore_flag++; 454 check_kw = 0; 455 } else { 456 check_flag++; 457 check_kw = 1; 458 } 459 460 /* Parse next token */ 461 s = lex(0); 462 while ((s != NULL) && (*s != 0) && (*s != '#')) { 463 akp = attr_keylookup(s); 464 if (akp == NULL) { 465 (void) fprintf(stderr, SYNTAX_ERR, s); 466 syntax_err++; 467 exit(2); 468 } 469 470 /* 471 * For all the flags, check if this is a global 472 * IGNORE/CHECK. If so, set the global flag. 473 * 474 * NOTE: The only time you can have a 475 * global ignore is when its the 476 * stmt before any blocks have been 477 * spec'd. 478 */ 479 if (block_begin == NULL) { 480 if (check_kw) 481 in_flags |= akp->ak_flags; 482 else 483 in_flags &= ~(akp->ak_flags); 484 } else { 485 for (rp = block_begin; rp != NULL; 486 rp = rp->next) { 487 if (check_kw) 488 rp->attr_list |= 489 akp->ak_flags; 490 else 491 rp->attr_list &= 492 ~(akp->ak_flags); 493 } 494 } 495 496 /* Parse next token */ 497 s = lex(0); 498 } 499 } else { 500 (void) fprintf(stderr, SYNTAX_ERR, s); 501 s = lex(0); 502 while (s != NULL && *s != 0) { 503 (void) fprintf(stderr, " %s", s); 504 s = lex(0); 505 } 506 (void) fprintf(stderr, "\n"); 507 syntax_err++; 508 } 509 } 510 511 (void) fclose(file); 512 513 if (syntax_err) { 514 (void) fprintf(stderr, SYNTAX_ABORT); 515 exit(2); 516 } 517 518 return (ret_code); 519 } 520 521 static void 522 add_modifier(struct rule *rule, char *modifier_str) 523 { 524 struct tree_modifier *new_mod_ptr, *curr_mod_ptr; 525 struct rule *this_rule; 526 527 this_rule = rule; 528 while (this_rule != NULL) { 529 new_mod_ptr = gen_tree_modifier(); 530 new_mod_ptr->mod_str = safe_strdup(modifier_str); 531 /* Next, see if the pattern is an include or an exclude */ 532 if (new_mod_ptr->mod_str[0] == '!') { 533 new_mod_ptr->mod_str = (new_mod_ptr->mod_str + 1); 534 new_mod_ptr->include = B_FALSE; 535 } else { 536 new_mod_ptr->include = B_TRUE; 537 } 538 539 if (this_rule->modifiers == NULL) 540 this_rule->modifiers = new_mod_ptr; 541 else { 542 curr_mod_ptr = this_rule->modifiers; 543 while (curr_mod_ptr->next != NULL) 544 curr_mod_ptr = curr_mod_ptr->next; 545 546 curr_mod_ptr->next = new_mod_ptr; 547 } 548 this_rule = this_rule->next; 549 } 550 } 551 552 /* 553 * This funtion is invoked when reading rulesfiles. A subtree may have 554 * wildcards in it, e.g., '/home/n*', which is expected to match all home 555 * dirs which start with an 'n'. 556 * 557 * This function needs to break down the subtree into its components. For 558 * each component, see how many directories match. Take the subtree list just 559 * generated and run it through again, this time looking at the next component. 560 * At each iteration, keep a linked list of subtrees that currently match. 561 * Once the final list is created, invoke add_single_rule() to create the 562 * rule struct with the correct information. 563 * 564 * This function returns a ptr to the first element in the block of subtrees 565 * which matched the subtree def'n in the rulesfile. 566 */ 567 static struct rule * 568 add_subtree_rule(char *rule, char *reloc_root, int create, int *err_code) 569 { 570 char full_path[PATH_MAX], pattern[PATH_MAX], 571 new_dirname[PATH_MAX], *beg_pattern, 572 *end_pattern, *curr_dirname; 573 struct dir_component *current_level = NULL, *next_level = NULL, 574 *tmp_ptr; 575 DIR *dir_ptr; 576 struct dirent *dir_entry; 577 struct rule *begin_rule = NULL; 578 int ret; 579 struct stat64 statb; 580 581 (void) snprintf(full_path, sizeof (full_path), 582 (rule[0] == '/') ? "%s%s" : "%s/%s", reloc_root, rule); 583 584 /* 585 * In the case of 'bart compare', don't validate 586 * the subtrees, since the machine running the 587 * comparison may not be the machine which generated 588 * the manifest. 589 */ 590 if (create == 0) 591 return (add_single_rule(full_path)); 592 593 594 /* Insert 'current_level' into the linked list */ 595 add_dir(¤t_level, NULL); 596 597 /* Special case: occurs when -R is "/" and the subtree is "/" */ 598 if (strcmp(full_path, "/") == 0) 599 (void) strcpy(current_level->dirname, "/"); 600 601 beg_pattern = full_path; 602 603 while (beg_pattern != NULL) { 604 /* 605 * Extract the pathname component starting at 'beg_pattern'. 606 * Take those chars and put them into 'pattern'. 607 */ 608 while (*beg_pattern == '/') 609 beg_pattern++; 610 if (*beg_pattern == '\0') /* end of pathname */ 611 break; 612 end_pattern = strchr(beg_pattern, '/'); 613 if (end_pattern != NULL) 614 (void) strlcpy(pattern, beg_pattern, 615 end_pattern - beg_pattern + 1); 616 else 617 (void) strlcpy(pattern, beg_pattern, sizeof (pattern)); 618 beg_pattern = end_pattern; 619 620 /* 621 * At this point, search for 'pattern' as a *subdirectory* of 622 * the dirs in the linked list. 623 */ 624 while (current_level != NULL) { 625 /* curr_dirname used to make the code more readable */ 626 curr_dirname = current_level->dirname; 627 628 /* Initialization case */ 629 if (strlen(curr_dirname) == 0) 630 (void) strcpy(curr_dirname, "/"); 631 632 /* Open up the dir for this element in the list */ 633 dir_ptr = opendir(curr_dirname); 634 dir_entry = NULL; 635 636 if (dir_ptr == NULL) { 637 perror(curr_dirname); 638 *err_code = WARNING_EXIT; 639 } else 640 dir_entry = readdir(dir_ptr); 641 642 /* 643 * Now iterate through the subdirs of 'curr_dirname' 644 * In the case of a match against 'pattern', 645 * add the path to the next linked list, which 646 * will be matched on the next iteration. 647 */ 648 while (dir_entry != NULL) { 649 /* Skip the dirs "." and ".." */ 650 if ((strcmp(dir_entry->d_name, ".") == 0) || 651 (strcmp(dir_entry->d_name, "..") == 0)) { 652 dir_entry = readdir(dir_ptr); 653 continue; 654 } 655 if (fnmatch(pattern, dir_entry->d_name, 656 FNM_PATHNAME) == 0) { 657 /* 658 * Build 'new_dirname' which will be 659 * examined on the next iteration. 660 */ 661 if (curr_dirname[strlen(curr_dirname)-1] 662 != '/') 663 (void) snprintf(new_dirname, 664 sizeof (new_dirname), 665 "%s/%s", curr_dirname, 666 dir_entry->d_name); 667 else 668 (void) snprintf(new_dirname, 669 sizeof (new_dirname), 670 "%s%s", curr_dirname, 671 dir_entry->d_name); 672 673 /* Add to the next lined list */ 674 add_dir(&next_level, new_dirname); 675 } 676 dir_entry = readdir(dir_ptr); 677 } 678 679 /* Close directory */ 680 if (dir_ptr != NULL) 681 (void) closedir(dir_ptr); 682 683 /* Free this entry and move on.... */ 684 tmp_ptr = current_level; 685 current_level = current_level->next; 686 free(tmp_ptr); 687 } 688 689 /* 690 * OK, done with this level. Move to the next level and 691 * advance the ptrs which indicate the component name. 692 */ 693 current_level = next_level; 694 next_level = NULL; 695 } 696 697 tmp_ptr = current_level; 698 699 /* Error case: the subtree doesn't exist! */ 700 if (current_level == NULL) { 701 (void) fprintf(stderr, INVALID_SUBTREE, full_path); 702 *err_code = WARNING_EXIT; 703 } 704 705 /* 706 * Iterate through all the dirnames which match the pattern and 707 * add them to to global list of subtrees which must be examined. 708 */ 709 while (current_level != NULL) { 710 /* 711 * Sanity check for 'bart create', make sure the subtree 712 * points to a valid object. 713 */ 714 ret = lstat64(current_level->dirname, &statb); 715 if (ret < 0) { 716 (void) fprintf(stderr, INVALID_SUBTREE, 717 current_level->dirname); 718 current_level = current_level->next; 719 *err_code = WARNING_EXIT; 720 continue; 721 } 722 723 if (begin_rule == NULL) { 724 begin_rule = 725 add_single_rule(current_level->dirname); 726 } else 727 (void) add_single_rule(current_level->dirname); 728 729 current_level = current_level->next; 730 } 731 732 /* 733 * Free up the memory and return a ptr to the first entry in the 734 * subtree block. This is necessary for the parser, which may need 735 * to add modifier strings to all the elements in this block. 736 */ 737 dirs_cleanup(tmp_ptr); 738 739 return (begin_rule); 740 } 741 742 743 /* 744 * Add a single entry to the linked list of rules to be read. Does not do 745 * the wildcard expansion of 'add_subtree_rule', so is much simpler. 746 */ 747 static struct rule * 748 add_single_rule(char *path) 749 { 750 751 /* 752 * If the rules list does NOT exist, then create it. 753 * If the rules list does exist, then traverse the next element. 754 */ 755 if (first_rule == NULL) { 756 first_rule = gen_rulestruct(); 757 current_rule = first_rule; 758 } else { 759 current_rule->next = gen_rulestruct(); 760 current_rule->next->prev = current_rule; 761 current_rule = current_rule->next; 762 } 763 764 /* Setup the rule struct, handle relocatable roots, i.e. '-R' option */ 765 (void) strlcpy(current_rule->subtree, path, 766 sizeof (current_rule->subtree)); 767 768 return (current_rule); 769 } 770 771 /* 772 * Code stolen from filesync utility, used by read_rules() to read in the 773 * rulesfile. 774 */ 775 static char * 776 lex(FILE *file) 777 { 778 char c, delim; 779 char *p; 780 char *s; 781 static char *savep; 782 static char namebuf[ BUF_SIZE ]; 783 static char inbuf[ BUF_SIZE ]; 784 785 if (file) { /* read a new line */ 786 p = inbuf + sizeof (inbuf); 787 788 s = inbuf; 789 /* read the next input line, with all continuations */ 790 while (savep = fgets(s, p - s, file)) { 791 lex_linenum++; 792 793 /* go find the last character of the input line */ 794 while (*s && s[1]) 795 s++; 796 if (*s == '\n') 797 s--; 798 799 /* see whether or not we need a continuation */ 800 if (s < inbuf || *s != '\\') 801 break; 802 803 continue; 804 } 805 806 if (savep == NULL) 807 return (0); 808 809 s = inbuf; 810 } else { /* continue with old line */ 811 if (savep == NULL) 812 return (0); 813 s = savep; 814 } 815 savep = NULL; 816 817 /* skip over leading white space */ 818 while (isspace(*s)) 819 s++; 820 if (*s == 0) 821 return (0); 822 823 /* see if this is a quoted string */ 824 c = *s; 825 if (c == '\'' || c == '"') { 826 delim = c; 827 s++; 828 } else 829 delim = 0; 830 831 /* copy the token into the buffer */ 832 for (p = namebuf; (c = *s) != 0; s++) { 833 /* literal escape */ 834 if (c == '\\') { 835 s++; 836 *p++ = *s; 837 continue; 838 } 839 840 /* closing delimiter */ 841 if (c == delim) { 842 s++; 843 break; 844 } 845 846 /* delimiting white space */ 847 if (delim == 0 && isspace(c)) 848 break; 849 850 /* ordinary characters */ 851 *p++ = *s; 852 } 853 854 855 /* remember where we left off */ 856 savep = *s ? s : 0; 857 858 /* null terminate and return the buffer */ 859 *p = 0; 860 return (namebuf); 861 } 862 863 /* 864 * Iterate through the dir strcutures and free memory. 865 */ 866 static void 867 dirs_cleanup(struct dir_component *dir) 868 { 869 struct dir_component *next; 870 871 while (dir != NULL) { 872 next = dir->next; 873 free(dir); 874 dir = next; 875 } 876 } 877 878 /* 879 * Create and initialize a new dir structure. Used by add_subtree_rule() when 880 * doing expansion of directory names caused by wildcards. 881 */ 882 static void 883 add_dir(struct dir_component **dir, char *dirname) 884 { 885 struct dir_component *new, *next_dir; 886 887 new = gen_dir_component(); 888 if (dirname != NULL) 889 (void) strlcpy(new->dirname, dirname, sizeof (new->dirname)); 890 891 if (*dir == NULL) 892 *dir = new; 893 else { 894 next_dir = *dir; 895 while (next_dir->next != NULL) 896 next_dir = next_dir->next; 897 898 next_dir->next = new; 899 } 900 } 901 902 /* 903 * Traverse the linked list of rules in a REVERSE order. 904 */ 905 static struct rule * 906 get_last_entry(boolean_t reset) 907 { 908 static struct rule *curr_root = NULL; 909 910 if (reset) { 911 912 curr_root = first_rule; 913 914 /* RESET: set cur_root to the end of the list */ 915 while (curr_root != NULL) 916 if (curr_root->next == NULL) 917 break; 918 else 919 curr_root = curr_root->next; 920 } else 921 curr_root = (curr_root->prev); 922 923 return (curr_root); 924 } 925 926 /* 927 * Traverse the first entry, used by 'bart create' to iterate through 928 * subtrees or individual filenames. 929 */ 930 struct rule * 931 get_first_subtree() 932 { 933 return (first_rule); 934 } 935 936 /* 937 * Traverse the next entry, used by 'bart create' to iterate through 938 * subtrees or individual filenames. 939 */ 940 struct rule * 941 get_next_subtree(struct rule *entry) 942 { 943 return (entry->next); 944 } 945 946 char * 947 safe_strdup(char *s) 948 { 949 char *ret; 950 size_t len; 951 952 len = strlen(s) + 1; 953 ret = safe_calloc(len); 954 (void) strlcpy(ret, s, len); 955 return (ret); 956 } 957 958 /* 959 * Function to match a filename against the subtrees in the link list 960 * of 'rule' strcutures. Upon finding a matching rule, see if it should 961 * be excluded. Keep going until a match is found OR all rules have been 962 * exhausted. 963 * NOTES: Rules are parsed in reverse; 964 * satisfies the spec that "Last rule wins". Also, the default rule should 965 * always match, so this function should NEVER return NULL. 966 */ 967 struct rule * 968 check_rules(const char *fname, char type) 969 { 970 struct rule *root; 971 972 root = get_last_entry(B_TRUE); 973 while (root != NULL) { 974 if (match_subtree(fname, root->subtree)) { 975 if (exclude_fname(fname, type, root) == 0) 976 break; 977 } 978 root = get_last_entry(B_FALSE); 979 } 980 981 return (root); 982 } 983 984 /* 985 * Function to match a filename against a subtree. Break both paths into 986 * their components and do the comparison. Comparison is OK when all the 987 * components match until the subtree is exhausted. For example, a 988 * subtree of "/home/nickiso" should match against "/home/nickiso/src/foo.c" 989 */ 990 static int 991 match_subtree(const char *fname, char *rule) 992 { 993 int match; 994 995 /* Trivial case, always match against '/' */ 996 if (strcmp(rule, "/") == 0) 997 return (1); 998 999 /* 1000 * Walk through the rule and fname to see if they match, or not. 1001 * 1002 * A "component" is one part of a fname, e.g., assuming an fname 1003 * of "/A/B/C/D", it has four components: "A", "B", "C", "D". 1004 */ 1005 1006 /* OK, now see if the match */ 1007 match = fnmatch(rule, fname, FNM_PATHNAME); 1008 1009 /* No match, return failure */ 1010 if (match != 0) 1011 return (0); 1012 else 1013 return (1); 1014 } 1015 1016 void 1017 process_glob_ignores(char *ignore_list, uint_t *flags) 1018 { 1019 char *cp; 1020 struct attr_keyword *akp; 1021 1022 if (ignore_list == NULL) 1023 usage(); 1024 1025 cp = strtok(ignore_list, ","); 1026 while (cp != NULL) { 1027 akp = attr_keylookup(cp); 1028 if (akp == NULL) 1029 (void) fprintf(stderr, "ERROR: Invalid keyword %s\n", 1030 cp); 1031 else 1032 *flags &= ~akp->ak_flags; 1033 cp = strtok(NULL, ","); 1034 } 1035 } 1036