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