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