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
exclude_fname(const char * fname,char fname_type,struct rule * rule_ptr)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
count_slashes(const char * in_path)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 *
gen_rulestruct(void)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 *
gen_tree_modifier(void)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 *
gen_dir_component(void)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 *
setup_default_rule(char * reloc_root,uint_t flags)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
init_rule(uint_t flags,struct rule * new_rule)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
read_rules(FILE * file,char * reloc_root,uint_t in_flags,int create)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
add_modifier(struct rule * rule,char * modifier_str)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 *
add_subtree_rule(char * rule,char * reloc_root,int create,int * err_code)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 *
add_single_rule(char * path)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 *
lex(FILE * file)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
dirs_cleanup(struct dir_component * dir)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
add_dir(struct dir_component ** dir,char * dirname)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 *
get_last_entry(boolean_t reset)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 *
get_first_subtree()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 *
get_next_subtree(struct rule * entry)984 get_next_subtree(struct rule *entry)
985 {
986 return (entry->next);
987 }
988
989 char *
safe_strdup(char * s)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 *
check_rules(const char * fname,char type)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
match_subtree(const char * fname,char * rule)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
process_glob_ignores(char * ignore_list,uint_t * flags)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