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