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