Home | History | Annotate | Download | only in bart
      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(&current_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