xref: /onnv-gate/usr/src/cmd/bart/rules.c (revision 13116:4fa15249a57b)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52812Sjc144527  * Common Development and Distribution License (the "License").
62812Sjc144527  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*13116SJan.Parcel@Sun.COM  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate #include <dirent.h>
260Sstevel@tonic-gate #include <fnmatch.h>
27550Shm123892 #include <string.h>
280Sstevel@tonic-gate #include "bart.h"
290Sstevel@tonic-gate 
300Sstevel@tonic-gate static int count_slashes(const char *);
310Sstevel@tonic-gate static struct rule *gen_rulestruct(void);
320Sstevel@tonic-gate static struct tree_modifier *gen_tree_modifier(void);
330Sstevel@tonic-gate static struct dir_component *gen_dir_component(void);
340Sstevel@tonic-gate static void init_rule(uint_t, struct rule *);
350Sstevel@tonic-gate static void add_modifier(struct rule *, char *);
360Sstevel@tonic-gate static struct rule *add_subtree_rule(char *, char *, int, int *);
370Sstevel@tonic-gate static struct rule *add_single_rule(char *);
380Sstevel@tonic-gate static void dirs_cleanup(struct dir_component *);
390Sstevel@tonic-gate static void add_dir(struct dir_component **, char *);
400Sstevel@tonic-gate static char *lex(FILE *);
410Sstevel@tonic-gate static int match_subtree(const char *, char *);
420Sstevel@tonic-gate static struct rule *get_last_entry(boolean_t);
430Sstevel@tonic-gate 
440Sstevel@tonic-gate static int	lex_linenum;	/* line number in current input file	*/
450Sstevel@tonic-gate static struct rule	*first_rule = NULL, *current_rule = NULL;
460Sstevel@tonic-gate 
470Sstevel@tonic-gate /*
480Sstevel@tonic-gate  * This function is responsible for validating whether or not a given file
490Sstevel@tonic-gate  * should be cataloged, based upon the modifiers for a subtree.
500Sstevel@tonic-gate  * For example, a line in the rules file: '/home/nickiso *.c' should only
510Sstevel@tonic-gate  * catalog the C files (based upon pattern matching) in the subtree
520Sstevel@tonic-gate  * '/home/nickiso'.
530Sstevel@tonic-gate  *
54*13116SJan.Parcel@Sun.COM  * exclude_fname depends on having the modifiers be pre-sorted to put
55*13116SJan.Parcel@Sun.COM  * negative directory modifiers first, so that the logic does
56*13116SJan.Parcel@Sun.COM  * not need to save complex state information.  This is valid because
57*13116SJan.Parcel@Sun.COM  * we are only cataloging things that meet all modifiers (AND logic.)
58*13116SJan.Parcel@Sun.COM  *
59*13116SJan.Parcel@Sun.COM  * Returns:
60*13116SJan.Parcel@Sun.COM  * NO_EXCLUDE
61*13116SJan.Parcel@Sun.COM  * EXCLUDE_SKIP
62*13116SJan.Parcel@Sun.COM  * EXCLUDE_PRUNE
630Sstevel@tonic-gate  */
640Sstevel@tonic-gate int
exclude_fname(const char * fname,char fname_type,struct rule * rule_ptr)650Sstevel@tonic-gate exclude_fname(const char *fname, char fname_type, struct rule *rule_ptr)
660Sstevel@tonic-gate {
67*13116SJan.Parcel@Sun.COM 	char	*pattern, *ptr, *fname_ptr, saved_char;
68*13116SJan.Parcel@Sun.COM 	char 	fname_cp[PATH_MAX], pattern_cp[PATH_MAX];
69*13116SJan.Parcel@Sun.COM 	int	num_pattern_slash,  i, num_fname_slash, slashes_to_adv;
700Sstevel@tonic-gate 	struct  tree_modifier   *mod_ptr;
710Sstevel@tonic-gate 
720Sstevel@tonic-gate 	/*
73*13116SJan.Parcel@Sun.COM 	 * If this is create and there are no modifiers, bail.
74*13116SJan.Parcel@Sun.COM 	 * This will have to change once create handles multiple rules
75*13116SJan.Parcel@Sun.COM 	 * during walk.
760Sstevel@tonic-gate 	 */
77*13116SJan.Parcel@Sun.COM 	if (rule_ptr->modifiers == NULL)
78*13116SJan.Parcel@Sun.COM 		if (rule_ptr->attr_list == 0)
79*13116SJan.Parcel@Sun.COM 			return (EXCLUDE_PRUNE);
80*13116SJan.Parcel@Sun.COM 		else
81*13116SJan.Parcel@Sun.COM 			return (NO_EXCLUDE);
820Sstevel@tonic-gate 	/*
830Sstevel@tonic-gate 	 * Walk through all the modifiers until its they are exhausted OR
840Sstevel@tonic-gate 	 * until the file should definitely be excluded.
850Sstevel@tonic-gate 	 */
86*13116SJan.Parcel@Sun.COM 	for (mod_ptr = rule_ptr->modifiers; mod_ptr != NULL;
87*13116SJan.Parcel@Sun.COM 	    mod_ptr = mod_ptr->next) {
88*13116SJan.Parcel@Sun.COM 		/* leading !'s were processed in add_modifier */
89*13116SJan.Parcel@Sun.COM 		pattern = mod_ptr->mod_str;
90*13116SJan.Parcel@Sun.COM 		if (mod_ptr->is_dir == B_FALSE) {
910Sstevel@tonic-gate 			/*
92*13116SJan.Parcel@Sun.COM 			 * Pattern is a file pattern.
93*13116SJan.Parcel@Sun.COM 			 *
940Sstevel@tonic-gate 			 * In the case when a user is trying to filter on
95*13116SJan.Parcel@Sun.COM 			 * a file pattern and the entry is a directory,
96*13116SJan.Parcel@Sun.COM 			 * this is not a match.
97*13116SJan.Parcel@Sun.COM 			 *
98*13116SJan.Parcel@Sun.COM 			 * If a match is required, skip this file.  If
99*13116SJan.Parcel@Sun.COM 			 * a match is forbidden, keep looking at modifiers.
1000Sstevel@tonic-gate 			 */
1010Sstevel@tonic-gate 			if (fname_type == 'D') {
102*13116SJan.Parcel@Sun.COM 				if (mod_ptr->include == B_TRUE)
103*13116SJan.Parcel@Sun.COM 					return (EXCLUDE_SKIP);
1042813Srm88369 				else
105*13116SJan.Parcel@Sun.COM 					continue;
1060Sstevel@tonic-gate 			}
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate 			/*
1090Sstevel@tonic-gate 			 * Match patterns against filenames.
1100Sstevel@tonic-gate 			 * Need to be able to handle multi-level patterns,
1110Sstevel@tonic-gate 			 * eg. "SCCS/<star-wildcard>.c", which means
1120Sstevel@tonic-gate 			 * 'only match C files under SCCS directories.
1130Sstevel@tonic-gate 			 *
1140Sstevel@tonic-gate 			 * Determine the number of levels in the filename and
1150Sstevel@tonic-gate 			 * in the pattern.
1160Sstevel@tonic-gate 			 */
1170Sstevel@tonic-gate 			num_pattern_slash = count_slashes(pattern);
1180Sstevel@tonic-gate 			num_fname_slash = count_slashes(fname);
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate 			/* Check for trivial exclude condition */
1210Sstevel@tonic-gate 			if (num_pattern_slash > num_fname_slash) {
122*13116SJan.Parcel@Sun.COM 				if (mod_ptr->include == B_TRUE)
123*13116SJan.Parcel@Sun.COM 					return (EXCLUDE_SKIP);
1240Sstevel@tonic-gate 			}
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate 			/*
1270Sstevel@tonic-gate 			 * Do an apples to apples comparison, based upon the
1280Sstevel@tonic-gate 			 * number of levels:
1290Sstevel@tonic-gate 			 *
1300Sstevel@tonic-gate 			 * Assume fname is /A/B/C/D/E and the pattern is D/E.
1310Sstevel@tonic-gate 			 * In that case, 'ptr' will point to "D/E" and
1320Sstevel@tonic-gate 			 * 'slashes_to_adv' will be '4'.
1330Sstevel@tonic-gate 			 */
1340Sstevel@tonic-gate 			(void) strlcpy(fname_cp, fname, sizeof (fname_cp));
1350Sstevel@tonic-gate 			ptr = fname_cp;
1360Sstevel@tonic-gate 			slashes_to_adv = num_fname_slash - num_pattern_slash;
1370Sstevel@tonic-gate 			for (i = 0; i < slashes_to_adv; i++)  {
1380Sstevel@tonic-gate 				ptr = strchr(ptr, '/');
1390Sstevel@tonic-gate 				ptr++;
1400Sstevel@tonic-gate 			}
1410Sstevel@tonic-gate 			if ((pattern[0] == '.') && (pattern[1] == '.') &&
1420Sstevel@tonic-gate 			    (pattern[2] == '/')) {
1430Sstevel@tonic-gate 				pattern = strchr(pattern, '/');
1440Sstevel@tonic-gate 				ptr = strchr(ptr, '/');
1450Sstevel@tonic-gate 			}
1460Sstevel@tonic-gate 
1470Sstevel@tonic-gate 
148*13116SJan.Parcel@Sun.COM 			/* OK, now do the fnmatch() compare to the file */
149*13116SJan.Parcel@Sun.COM 			if (fnmatch(pattern, ptr, FNM_PATHNAME) == 0) {
150*13116SJan.Parcel@Sun.COM 				/* matches, is it an exclude? */
151*13116SJan.Parcel@Sun.COM 				if (mod_ptr->include == B_FALSE)
152*13116SJan.Parcel@Sun.COM 					return (EXCLUDE_SKIP);
153*13116SJan.Parcel@Sun.COM 			} else if (mod_ptr->include == B_TRUE) {
154*13116SJan.Parcel@Sun.COM 				/* failed a required filename match */
155*13116SJan.Parcel@Sun.COM 				return (EXCLUDE_SKIP);
156*13116SJan.Parcel@Sun.COM 			}
1570Sstevel@tonic-gate 		} else {
1580Sstevel@tonic-gate 			/*
1590Sstevel@tonic-gate 			 * The rule requires directory matching.
1600Sstevel@tonic-gate 			 *
161*13116SJan.Parcel@Sun.COM 			 * Unlike filename matching, directory matching can
162*13116SJan.Parcel@Sun.COM 			 * prune.
163*13116SJan.Parcel@Sun.COM 			 *
1640Sstevel@tonic-gate 			 * First, make copies, since both the pattern and
1650Sstevel@tonic-gate 			 * filename need to be modified.
1660Sstevel@tonic-gate 			 *
1670Sstevel@tonic-gate 			 * When copying 'fname', ignore the relocatable root
1680Sstevel@tonic-gate 			 * since pattern matching is done for the string AFTER
1690Sstevel@tonic-gate 			 * the relocatable root.  For example, if the
1700Sstevel@tonic-gate 			 * relocatable root is "/dir1/dir2/dir3" and the
1710Sstevel@tonic-gate 			 * pattern is "dir3/", we do NOT want to include every
1720Sstevel@tonic-gate 			 * directory in the relocatable root.  Instead, we
1730Sstevel@tonic-gate 			 * only want to include subtrees that look like:
1740Sstevel@tonic-gate 			 * "/dir1/dir2/dir3/....dir3/....."
1750Sstevel@tonic-gate 			 *
1762812Sjc144527 			 * NOTE: the 'fname_cp' does NOT have a trailing '/':
1770Sstevel@tonic-gate 			 * necessary for fnmatch().
1780Sstevel@tonic-gate 			 */
1790Sstevel@tonic-gate 			(void) strlcpy(fname_cp,
1800Sstevel@tonic-gate 			    (fname+strlen(rule_ptr->subtree)),
1810Sstevel@tonic-gate 			    sizeof (fname_cp));
1820Sstevel@tonic-gate 			(void) strlcpy(pattern_cp, pattern,
1830Sstevel@tonic-gate 			    sizeof (pattern_cp));
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 			/*
186*13116SJan.Parcel@Sun.COM 			 * For non-directory files, remove the trailing
1870Sstevel@tonic-gate 			 * name, e.g., for a file /A/B/C/D where 'D' is
1880Sstevel@tonic-gate 			 * the actual filename, remove the 'D' since it
1890Sstevel@tonic-gate 			 * should *not* be considered in the directory match.
1900Sstevel@tonic-gate 			 */
1910Sstevel@tonic-gate 			if (fname_type != 'D') {
1920Sstevel@tonic-gate 				ptr = strrchr(fname_cp, '/');
1930Sstevel@tonic-gate 				if (ptr != NULL)
1940Sstevel@tonic-gate 					*ptr = '\0';
1950Sstevel@tonic-gate 
196*13116SJan.Parcel@Sun.COM 				/*
197*13116SJan.Parcel@Sun.COM 				 * Trivial case: a simple filename does
198*13116SJan.Parcel@Sun.COM 				 * not match a directory by definition,
199*13116SJan.Parcel@Sun.COM 				 * so skip if match is required,
200*13116SJan.Parcel@Sun.COM 				 * keep analyzing otherwise.
201*13116SJan.Parcel@Sun.COM 				 */
202*13116SJan.Parcel@Sun.COM 
203*13116SJan.Parcel@Sun.COM 				if (strlen(fname_cp) == 0)
204*13116SJan.Parcel@Sun.COM 					if (mod_ptr->include == B_TRUE)
205*13116SJan.Parcel@Sun.COM 						return (EXCLUDE_SKIP);
2060Sstevel@tonic-gate 			}
2070Sstevel@tonic-gate 
2080Sstevel@tonic-gate 			/* Count the # of slashes in the pattern and fname */
2090Sstevel@tonic-gate 			num_pattern_slash = count_slashes(pattern_cp);
2100Sstevel@tonic-gate 			num_fname_slash = count_slashes(fname_cp);
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 			/*
213*13116SJan.Parcel@Sun.COM 			 * fname_cp is too short if this is not a dir
2140Sstevel@tonic-gate 			 */
215*13116SJan.Parcel@Sun.COM 			if ((num_pattern_slash > num_fname_slash) &&
216*13116SJan.Parcel@Sun.COM 			    (fname_type != 'D')) {
217*13116SJan.Parcel@Sun.COM 				if (mod_ptr->include == B_TRUE)
218*13116SJan.Parcel@Sun.COM 					return (EXCLUDE_SKIP);
2190Sstevel@tonic-gate 			}
2200Sstevel@tonic-gate 
2212812Sjc144527 
2222812Sjc144527 			/*
2232812Sjc144527 			 * Take the leading '/' from fname_cp before
2242812Sjc144527 			 * decrementing the number of slashes.
2252812Sjc144527 			 */
2262812Sjc144527 			if (fname_cp[0] == '/') {
2272812Sjc144527 				(void) strlcpy(fname_cp,
2282812Sjc144527 				    strchr(fname_cp, '/') + 1,
2292812Sjc144527 				    sizeof (fname_cp));
2302812Sjc144527 				num_fname_slash--;
2312812Sjc144527 			}
2322812Sjc144527 
2330Sstevel@tonic-gate 			/*
2342812Sjc144527 			 * Begin the loop, walk through the file name until
2352812Sjc144527 			 * it can be determined that there is no match.
2362812Sjc144527 			 * For example: if pattern is C/D/, and fname_cp is
2372812Sjc144527 			 * A/B/C/D/E then compare A/B/ with C/D/, if it doesn't
2382812Sjc144527 			 * match, then walk further so that the next iteration
2392812Sjc144527 			 * checks B/C/ against C/D/, continue until we have
2402812Sjc144527 			 * exhausted options.
2412812Sjc144527 			 * In the above case, the 3rd iteration will match
2422812Sjc144527 			 * C/D/ with C/D/.
2430Sstevel@tonic-gate 			 */
2442812Sjc144527 			while (num_pattern_slash <= num_fname_slash) {
2452812Sjc144527 				/* get a pointer to our filename */
2462812Sjc144527 				fname_ptr = fname_cp;
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate 				/*
2492812Sjc144527 				 * Walk the filename through the slashes
2502812Sjc144527 				 * so that we have a component of the same
2512812Sjc144527 				 * number of slashes as the pattern.
2522812Sjc144527 				 */
2532812Sjc144527 
2542812Sjc144527 				for (i = 0; i < num_pattern_slash; i++) {
2552812Sjc144527 					ptr = strchr(fname_ptr, '/');
2562812Sjc144527 					fname_ptr = ptr + 1;
2572812Sjc144527 				}
2582812Sjc144527 
2592812Sjc144527 				/*
2602812Sjc144527 				 * Save the character after our target slash
2612812Sjc144527 				 * before breaking the string for use with
2622812Sjc144527 				 * fnmatch
2630Sstevel@tonic-gate 				 */
2642812Sjc144527 				saved_char = *(++ptr);
2652812Sjc144527 
2662812Sjc144527 				*ptr = '\0';
2672812Sjc144527 
2682812Sjc144527 				/*
269*13116SJan.Parcel@Sun.COM 				 * Call compare function for the current
270*13116SJan.Parcel@Sun.COM 				 * component with the pattern we are looking
271*13116SJan.Parcel@Sun.COM 				 * for.
2722812Sjc144527 				 */
273*13116SJan.Parcel@Sun.COM 				if (fnmatch(pattern_cp, fname_cp,
274*13116SJan.Parcel@Sun.COM 				    FNM_PATHNAME) == 0) {
275*13116SJan.Parcel@Sun.COM 					if (mod_ptr->include == B_TRUE) {
276*13116SJan.Parcel@Sun.COM 						break;
277*13116SJan.Parcel@Sun.COM 					} else if (fname_type == 'D')
278*13116SJan.Parcel@Sun.COM 						return (EXCLUDE_PRUNE);
279*13116SJan.Parcel@Sun.COM 					else
280*13116SJan.Parcel@Sun.COM 						return (EXCLUDE_SKIP);
281*13116SJan.Parcel@Sun.COM 				} else if (mod_ptr->include == B_TRUE) {
282*13116SJan.Parcel@Sun.COM 					if (fname_type == 'D')
283*13116SJan.Parcel@Sun.COM 						return (EXCLUDE_PRUNE);
284*13116SJan.Parcel@Sun.COM 					else
285*13116SJan.Parcel@Sun.COM 						return (EXCLUDE_SKIP);
2860Sstevel@tonic-gate 				}
2870Sstevel@tonic-gate 				/*
2882812Sjc144527 				 * We didn't match, so restore the saved
2892812Sjc144527 				 * character to the original position.
2900Sstevel@tonic-gate 				 */
2912812Sjc144527 				*ptr = saved_char;
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 				/*
2942812Sjc144527 				 * Break down fname_cp, if it was A/B/C
2952812Sjc144527 				 * then after this operation it will be B/C
2962812Sjc144527 				 * in preparation for the next iteration.
2970Sstevel@tonic-gate 				 */
2982812Sjc144527 				(void) strlcpy(fname_cp,
2992812Sjc144527 				    strchr(fname_cp, '/') + 1,
3002812Sjc144527 				    sizeof (fname_cp));
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 				/*
3032812Sjc144527 				 * Decrement the number of slashes to
3042812Sjc144527 				 * compensate for the one removed above.
3050Sstevel@tonic-gate 				 */
3062812Sjc144527 				num_fname_slash--;
307*13116SJan.Parcel@Sun.COM 			} /* end while loop looking down the path */
3080Sstevel@tonic-gate 
3092812Sjc144527 			/*
3102812Sjc144527 			 * If we didn't get a match above then we may be on the
3112812Sjc144527 			 * last component of our filename.
3122812Sjc144527 			 * This is to handle the following cases
3132812Sjc144527 			 *    - filename is A/B/C/D/E and pattern may be D/E/
3142812Sjc144527 			 *    - filename is D/E and pattern may be D/E/
3152812Sjc144527 			 */
316*13116SJan.Parcel@Sun.COM 			if (num_pattern_slash == (num_fname_slash + 1)) {
3172812Sjc144527 
3182812Sjc144527 				/* strip the trailing slash from the pattern */
3192812Sjc144527 				ptr = strrchr(pattern_cp, '/');
3202812Sjc144527 				*ptr = '\0';
3212812Sjc144527 
322*13116SJan.Parcel@Sun.COM 				/* fnmatch returns 0 for a match */
323*13116SJan.Parcel@Sun.COM 				if (fnmatch(pattern_cp, fname_cp,
324*13116SJan.Parcel@Sun.COM 				    FNM_PATHNAME) == 0) {
325*13116SJan.Parcel@Sun.COM 					if (mod_ptr->include == B_FALSE) {
326*13116SJan.Parcel@Sun.COM 						if (fname_type == 'D')
327*13116SJan.Parcel@Sun.COM 							return (EXCLUDE_PRUNE);
328*13116SJan.Parcel@Sun.COM 						else
329*13116SJan.Parcel@Sun.COM 							return (EXCLUDE_SKIP);
330*13116SJan.Parcel@Sun.COM 					}
331*13116SJan.Parcel@Sun.COM 				} else if (mod_ptr->include == B_TRUE)
332*13116SJan.Parcel@Sun.COM 					return (EXCLUDE_SKIP);
3330Sstevel@tonic-gate 
334*13116SJan.Parcel@Sun.COM 			}
335*13116SJan.Parcel@Sun.COM 
3360Sstevel@tonic-gate 		}
3370Sstevel@tonic-gate 	}
338*13116SJan.Parcel@Sun.COM 	return (NO_EXCLUDE);
3390Sstevel@tonic-gate }
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate static int
count_slashes(const char * in_path)3420Sstevel@tonic-gate count_slashes(const char *in_path)
3430Sstevel@tonic-gate {
3440Sstevel@tonic-gate 	int num_fname_slash = 0;
3450Sstevel@tonic-gate 	const char *p;
3460Sstevel@tonic-gate 	for (p = in_path; *p != '\0'; p++)
3470Sstevel@tonic-gate 		if (*p == '/')
3480Sstevel@tonic-gate 			num_fname_slash++;
3490Sstevel@tonic-gate 	return (num_fname_slash);
3500Sstevel@tonic-gate }
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate static struct rule *
gen_rulestruct(void)3530Sstevel@tonic-gate gen_rulestruct(void)
3540Sstevel@tonic-gate {
3550Sstevel@tonic-gate 	struct rule	*new_rule;
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 	new_rule = (struct rule *)safe_calloc(sizeof (struct rule));
3580Sstevel@tonic-gate 	return (new_rule);
3590Sstevel@tonic-gate }
3600Sstevel@tonic-gate 
3610Sstevel@tonic-gate static struct tree_modifier *
gen_tree_modifier(void)3620Sstevel@tonic-gate gen_tree_modifier(void)
3630Sstevel@tonic-gate {
3640Sstevel@tonic-gate 	struct tree_modifier	*new_modifier;
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate 	new_modifier = (struct tree_modifier *)safe_calloc
3670Sstevel@tonic-gate 	    (sizeof (struct tree_modifier));
3680Sstevel@tonic-gate 	return (new_modifier);
3690Sstevel@tonic-gate }
3700Sstevel@tonic-gate 
3710Sstevel@tonic-gate static struct dir_component *
gen_dir_component(void)3720Sstevel@tonic-gate gen_dir_component(void)
3730Sstevel@tonic-gate {
3740Sstevel@tonic-gate 	struct dir_component	*new_dir;
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 	new_dir = (struct dir_component *)safe_calloc
3770Sstevel@tonic-gate 	    (sizeof (struct dir_component));
3780Sstevel@tonic-gate 	return (new_dir);
3790Sstevel@tonic-gate }
3800Sstevel@tonic-gate 
3810Sstevel@tonic-gate /*
3820Sstevel@tonic-gate  * Set up a default rule when there is no rules file.
3830Sstevel@tonic-gate  */
3840Sstevel@tonic-gate static struct rule *
setup_default_rule(char * reloc_root,uint_t flags)3850Sstevel@tonic-gate setup_default_rule(char *reloc_root, uint_t flags)
3860Sstevel@tonic-gate {
3870Sstevel@tonic-gate 	struct	rule	*new_rule;
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate 	new_rule = add_single_rule(reloc_root[0] == '\0' ? "/" : reloc_root);
3900Sstevel@tonic-gate 	init_rule(flags, new_rule);
3910Sstevel@tonic-gate 	add_modifier(new_rule, "*");
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 	return (new_rule);
3940Sstevel@tonic-gate }
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate /*
3970Sstevel@tonic-gate  * Utility function, used to initialize the flag in a new rule structure.
3980Sstevel@tonic-gate  */
3990Sstevel@tonic-gate static void
init_rule(uint_t flags,struct rule * new_rule)4000Sstevel@tonic-gate init_rule(uint_t flags, struct rule *new_rule)
4010Sstevel@tonic-gate {
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate 	if (new_rule == NULL)
4040Sstevel@tonic-gate 		return;
4050Sstevel@tonic-gate 	new_rule->attr_list = flags;
4060Sstevel@tonic-gate }
4070Sstevel@tonic-gate 
4080Sstevel@tonic-gate /*
4090Sstevel@tonic-gate  * Function to read the rulesfile.  Used by both 'bart create' and
4100Sstevel@tonic-gate  * 'bart compare'.
4110Sstevel@tonic-gate  */
4120Sstevel@tonic-gate int
read_rules(FILE * file,char * reloc_root,uint_t in_flags,int create)4130Sstevel@tonic-gate read_rules(FILE *file, char *reloc_root, uint_t in_flags, int create)
4140Sstevel@tonic-gate {
4150Sstevel@tonic-gate 	char		*s;
4160Sstevel@tonic-gate 	struct rule	*block_begin = NULL, *new_rule, *rp;
4170Sstevel@tonic-gate 	struct attr_keyword *akp;
4180Sstevel@tonic-gate 	int		check_flag, ignore_flag, syntax_err, ret_code;
4192813Srm88369 	int		global_block;
4200Sstevel@tonic-gate 
4210Sstevel@tonic-gate 	ret_code = EXIT;
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate 	lex_linenum = 0;
4240Sstevel@tonic-gate 	check_flag = 0;
4250Sstevel@tonic-gate 	ignore_flag = 0;
4260Sstevel@tonic-gate 	syntax_err = 0;
4272813Srm88369 	global_block = 1;
4280Sstevel@tonic-gate 
4290Sstevel@tonic-gate 	if (file == NULL) {
4300Sstevel@tonic-gate 		(void) setup_default_rule(reloc_root, in_flags);
4310Sstevel@tonic-gate 		return (ret_code);
4320Sstevel@tonic-gate 	} else if (!create) {
4330Sstevel@tonic-gate 		block_begin = setup_default_rule("/", in_flags);
4340Sstevel@tonic-gate 	}
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	while (!feof(file)) {
4370Sstevel@tonic-gate 		/* Read a line from the file */
4380Sstevel@tonic-gate 		s = lex(file);
4390Sstevel@tonic-gate 
4400Sstevel@tonic-gate 		/* skip blank lines and comments */
4410Sstevel@tonic-gate 		if (s == NULL || *s == 0 || *s == '#')
4420Sstevel@tonic-gate 			continue;
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate 		/*
4450Sstevel@tonic-gate 		 * Beginning of a subtree and possibly a new block.
4460Sstevel@tonic-gate 		 *
4470Sstevel@tonic-gate 		 * If this is a new block, keep track of the beginning of
4480Sstevel@tonic-gate 		 * the block. if there are directives later on, we need to
4490Sstevel@tonic-gate 		 * apply that directive to all members of the block.
4500Sstevel@tonic-gate 		 *
4510Sstevel@tonic-gate 		 * If the first stmt in the file was an 'IGNORE all' or
4520Sstevel@tonic-gate 		 * 'IGNORE contents', we need to keep track of it and
4530Sstevel@tonic-gate 		 * automatically switch off contents checking for new
4540Sstevel@tonic-gate 		 * subtrees.
4550Sstevel@tonic-gate 		 */
4560Sstevel@tonic-gate 		if (s[0] == '/') {
4572813Srm88369 			/* subtree definition hence not a global block */
4582813Srm88369 			global_block = 0;
4592813Srm88369 
4600Sstevel@tonic-gate 			new_rule = add_subtree_rule(s, reloc_root, create,
4610Sstevel@tonic-gate 			    &ret_code);
4620Sstevel@tonic-gate 
4630Sstevel@tonic-gate 			s = lex(0);
4640Sstevel@tonic-gate 			while ((s != NULL) && (*s != 0) && (*s != '#')) {
4650Sstevel@tonic-gate 				add_modifier(new_rule, s);
4660Sstevel@tonic-gate 				s = lex(0);
4670Sstevel@tonic-gate 			}
4680Sstevel@tonic-gate 
4690Sstevel@tonic-gate 			/* Found a new block, keep track of the beginning */
4700Sstevel@tonic-gate 			if (block_begin == NULL ||
4710Sstevel@tonic-gate 			    (ignore_flag != 0) || (check_flag != 0)) {
4720Sstevel@tonic-gate 				block_begin = new_rule;
4730Sstevel@tonic-gate 				check_flag = 0;
4740Sstevel@tonic-gate 				ignore_flag = 0;
4750Sstevel@tonic-gate 			}
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate 			/* Apply global settings to this block, if any */
4780Sstevel@tonic-gate 			init_rule(in_flags, new_rule);
4790Sstevel@tonic-gate 		} else if (IGNORE_KEYWORD(s) || CHECK_KEYWORD(s)) {
4800Sstevel@tonic-gate 			int check_kw;
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 			if (IGNORE_KEYWORD(s)) {
4830Sstevel@tonic-gate 				ignore_flag++;
4840Sstevel@tonic-gate 				check_kw = 0;
4850Sstevel@tonic-gate 			} else {
4860Sstevel@tonic-gate 				check_flag++;
4870Sstevel@tonic-gate 				check_kw = 1;
4880Sstevel@tonic-gate 			}
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate 			/* Parse next token */
4910Sstevel@tonic-gate 			s = lex(0);
4920Sstevel@tonic-gate 			while ((s != NULL) && (*s != 0) && (*s != '#')) {
4930Sstevel@tonic-gate 				akp = attr_keylookup(s);
4940Sstevel@tonic-gate 				if (akp == NULL) {
4950Sstevel@tonic-gate 					(void) fprintf(stderr, SYNTAX_ERR, s);
4960Sstevel@tonic-gate 					syntax_err++;
4970Sstevel@tonic-gate 					exit(2);
4980Sstevel@tonic-gate 				}
4990Sstevel@tonic-gate 
5000Sstevel@tonic-gate 				/*
5010Sstevel@tonic-gate 				 * For all the flags, check if this is a global
5022813Srm88369 				 * IGNORE/CHECK. If so, set the global flags.
5030Sstevel@tonic-gate 				 *
5040Sstevel@tonic-gate 				 * NOTE: The only time you can have a
5050Sstevel@tonic-gate 				 * global ignore is when its the
5060Sstevel@tonic-gate 				 * stmt before any blocks have been
5070Sstevel@tonic-gate 				 * spec'd.
5080Sstevel@tonic-gate 				 */
5092813Srm88369 				if (global_block) {
5100Sstevel@tonic-gate 					if (check_kw)
5110Sstevel@tonic-gate 						in_flags |= akp->ak_flags;
5120Sstevel@tonic-gate 					else
5130Sstevel@tonic-gate 						in_flags &= ~(akp->ak_flags);
5140Sstevel@tonic-gate 				} else {
5150Sstevel@tonic-gate 					for (rp = block_begin; rp != NULL;
5160Sstevel@tonic-gate 					    rp = rp->next) {
5170Sstevel@tonic-gate 						if (check_kw)
5180Sstevel@tonic-gate 							rp->attr_list |=
5190Sstevel@tonic-gate 							    akp->ak_flags;
5200Sstevel@tonic-gate 						else
5210Sstevel@tonic-gate 							rp->attr_list &=
5220Sstevel@tonic-gate 							    ~(akp->ak_flags);
5230Sstevel@tonic-gate 					}
5240Sstevel@tonic-gate 				}
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 				/* Parse next token */
5270Sstevel@tonic-gate 				s = lex(0);
5280Sstevel@tonic-gate 			}
5290Sstevel@tonic-gate 		} else {
5300Sstevel@tonic-gate 			(void) fprintf(stderr, SYNTAX_ERR, s);
5310Sstevel@tonic-gate 			s = lex(0);
5320Sstevel@tonic-gate 			while (s != NULL && *s != 0) {
5330Sstevel@tonic-gate 				(void) fprintf(stderr, " %s", s);
5340Sstevel@tonic-gate 				s = lex(0);
5350Sstevel@tonic-gate 			}
5360Sstevel@tonic-gate 			(void) fprintf(stderr, "\n");
5370Sstevel@tonic-gate 			syntax_err++;
5380Sstevel@tonic-gate 		}
5390Sstevel@tonic-gate 	}
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate 	(void) fclose(file);
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 	if (syntax_err) {
5440Sstevel@tonic-gate 		(void) fprintf(stderr, SYNTAX_ABORT);
5450Sstevel@tonic-gate 		exit(2);
5460Sstevel@tonic-gate 	}
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate 	return (ret_code);
5490Sstevel@tonic-gate }
550*13116SJan.Parcel@Sun.COM /*
551*13116SJan.Parcel@Sun.COM  * Add a modifier to the mod_ptr list in each rule, putting negative
552*13116SJan.Parcel@Sun.COM  * directory entries
553*13116SJan.Parcel@Sun.COM  * first to guarantee walks will be appropriately pruned.
554*13116SJan.Parcel@Sun.COM  */
5550Sstevel@tonic-gate static void
add_modifier(struct rule * rule,char * modifier_str)5560Sstevel@tonic-gate add_modifier(struct rule *rule, char *modifier_str)
5570Sstevel@tonic-gate {
558*13116SJan.Parcel@Sun.COM 	int	include, is_dir;
559*13116SJan.Parcel@Sun.COM 	char	*pattern;
5600Sstevel@tonic-gate 	struct tree_modifier	*new_mod_ptr, *curr_mod_ptr;
5610Sstevel@tonic-gate 	struct rule		*this_rule;
5620Sstevel@tonic-gate 
563*13116SJan.Parcel@Sun.COM 	include = B_TRUE;
564*13116SJan.Parcel@Sun.COM 	pattern = modifier_str;
565*13116SJan.Parcel@Sun.COM 
566*13116SJan.Parcel@Sun.COM 	/* see if the pattern is an include or an exclude */
567*13116SJan.Parcel@Sun.COM 	if (pattern[0] == '!') {
568*13116SJan.Parcel@Sun.COM 		include = B_FALSE;
569*13116SJan.Parcel@Sun.COM 		pattern++;
570*13116SJan.Parcel@Sun.COM 	}
571*13116SJan.Parcel@Sun.COM 
572*13116SJan.Parcel@Sun.COM 	is_dir = (pattern[0] != '\0' && pattern[strlen(pattern) - 1] == '/');
573*13116SJan.Parcel@Sun.COM 
574*13116SJan.Parcel@Sun.COM 	for (this_rule = rule; this_rule != NULL; this_rule = this_rule->next) {
5750Sstevel@tonic-gate 		new_mod_ptr = gen_tree_modifier();
576*13116SJan.Parcel@Sun.COM 		new_mod_ptr->include = include;
577*13116SJan.Parcel@Sun.COM 		new_mod_ptr->is_dir = is_dir;
578*13116SJan.Parcel@Sun.COM 		new_mod_ptr->mod_str = safe_strdup(pattern);
5790Sstevel@tonic-gate 
580*13116SJan.Parcel@Sun.COM 		if (is_dir && !include) {
581*13116SJan.Parcel@Sun.COM 			new_mod_ptr->next = this_rule->modifiers;
582*13116SJan.Parcel@Sun.COM 			this_rule->modifiers = new_mod_ptr;
583*13116SJan.Parcel@Sun.COM 		} else if (this_rule->modifiers == NULL)
5840Sstevel@tonic-gate 			this_rule->modifiers = new_mod_ptr;
5850Sstevel@tonic-gate 		else {
5860Sstevel@tonic-gate 			curr_mod_ptr = this_rule->modifiers;
5870Sstevel@tonic-gate 			while (curr_mod_ptr->next != NULL)
5880Sstevel@tonic-gate 				curr_mod_ptr = curr_mod_ptr->next;
5890Sstevel@tonic-gate 
5900Sstevel@tonic-gate 			curr_mod_ptr->next = new_mod_ptr;
5910Sstevel@tonic-gate 		}
5920Sstevel@tonic-gate 	}
5930Sstevel@tonic-gate }
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate /*
5960Sstevel@tonic-gate  * This funtion is invoked when reading rulesfiles.  A subtree may have
5970Sstevel@tonic-gate  * wildcards in it, e.g., '/home/n*', which is expected to match all home
5980Sstevel@tonic-gate  * dirs which start with an 'n'.
5990Sstevel@tonic-gate  *
6000Sstevel@tonic-gate  * This function needs to break down the subtree into its components.  For
6010Sstevel@tonic-gate  * each component, see how many directories match.  Take the subtree list just
6020Sstevel@tonic-gate  * generated and run it through again, this time looking at the next component.
6030Sstevel@tonic-gate  * At each iteration, keep a linked list of subtrees that currently match.
6040Sstevel@tonic-gate  * Once the final list is created, invoke add_single_rule() to create the
6050Sstevel@tonic-gate  * rule struct with the correct information.
6060Sstevel@tonic-gate  *
6070Sstevel@tonic-gate  * This function returns a ptr to the first element in the block of subtrees
6080Sstevel@tonic-gate  * which matched the subtree def'n in the rulesfile.
6090Sstevel@tonic-gate  */
6100Sstevel@tonic-gate static struct rule *
add_subtree_rule(char * rule,char * reloc_root,int create,int * err_code)6110Sstevel@tonic-gate add_subtree_rule(char *rule, char *reloc_root, int create, int *err_code)
6120Sstevel@tonic-gate {
613*13116SJan.Parcel@Sun.COM 	char			full_path[PATH_MAX], pattern[PATH_MAX];
614*13116SJan.Parcel@Sun.COM 	char			new_dirname[PATH_MAX];
615*13116SJan.Parcel@Sun.COM 	char			*beg_pattern, *end_pattern, *curr_dirname;
616*13116SJan.Parcel@Sun.COM 	struct	dir_component	*current_level = NULL, *next_level = NULL;
617*13116SJan.Parcel@Sun.COM 	struct	dir_component	*tmp_ptr;
6180Sstevel@tonic-gate 	DIR			*dir_ptr;
6190Sstevel@tonic-gate 	struct dirent		*dir_entry;
6200Sstevel@tonic-gate 	struct rule		*begin_rule = NULL;
6210Sstevel@tonic-gate 	int			ret;
6220Sstevel@tonic-gate 	struct stat64		statb;
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 	(void) snprintf(full_path, sizeof (full_path),
6250Sstevel@tonic-gate 	    (rule[0] == '/') ? "%s%s" : "%s/%s", reloc_root, rule);
6260Sstevel@tonic-gate 
6270Sstevel@tonic-gate 	/*
6280Sstevel@tonic-gate 	 * In the case of 'bart compare', don't validate
6290Sstevel@tonic-gate 	 * the subtrees, since the machine running the
6300Sstevel@tonic-gate 	 * comparison may not be the machine which generated
6310Sstevel@tonic-gate 	 * the manifest.
6320Sstevel@tonic-gate 	 */
6330Sstevel@tonic-gate 	if (create == 0)
6340Sstevel@tonic-gate 		return (add_single_rule(full_path));
6350Sstevel@tonic-gate 
6360Sstevel@tonic-gate 
6370Sstevel@tonic-gate 	/* Insert 'current_level' into the linked list */
6380Sstevel@tonic-gate 	add_dir(&current_level, NULL);
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 	/* Special case: occurs when -R is "/" and the subtree is "/" */
6410Sstevel@tonic-gate 	if (strcmp(full_path, "/") == 0)
6420Sstevel@tonic-gate 		(void) strcpy(current_level->dirname, "/");
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	beg_pattern = full_path;
6450Sstevel@tonic-gate 
6460Sstevel@tonic-gate 	while (beg_pattern != NULL) {
6470Sstevel@tonic-gate 		/*
6480Sstevel@tonic-gate 		 * Extract the pathname component starting at 'beg_pattern'.
6490Sstevel@tonic-gate 		 * Take those chars and put them into 'pattern'.
6500Sstevel@tonic-gate 		 */
6510Sstevel@tonic-gate 		while (*beg_pattern == '/')
6520Sstevel@tonic-gate 			beg_pattern++;
6530Sstevel@tonic-gate 		if (*beg_pattern == '\0')	/* end of pathname */
6540Sstevel@tonic-gate 			break;
6550Sstevel@tonic-gate 		end_pattern = strchr(beg_pattern, '/');
6560Sstevel@tonic-gate 		if (end_pattern != NULL)
6570Sstevel@tonic-gate 			(void) strlcpy(pattern, beg_pattern,
6580Sstevel@tonic-gate 			    end_pattern - beg_pattern + 1);
6590Sstevel@tonic-gate 		else
6600Sstevel@tonic-gate 			(void) strlcpy(pattern, beg_pattern, sizeof (pattern));
6610Sstevel@tonic-gate 		beg_pattern = end_pattern;
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate 		/*
6640Sstevel@tonic-gate 		 * At this point, search for 'pattern' as a *subdirectory* of
6650Sstevel@tonic-gate 		 * the dirs in the linked list.
6660Sstevel@tonic-gate 		 */
6670Sstevel@tonic-gate 		while (current_level != NULL) {
6680Sstevel@tonic-gate 			/* curr_dirname used to make the code more readable */
6690Sstevel@tonic-gate 			curr_dirname = current_level->dirname;
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate 			/* Initialization case */
6720Sstevel@tonic-gate 			if (strlen(curr_dirname) == 0)
6730Sstevel@tonic-gate 				(void) strcpy(curr_dirname, "/");
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate 			/* Open up the dir for this element in the list */
6760Sstevel@tonic-gate 			dir_ptr = opendir(curr_dirname);
6770Sstevel@tonic-gate 			dir_entry = NULL;
6780Sstevel@tonic-gate 
6790Sstevel@tonic-gate 			if (dir_ptr == NULL) {
6800Sstevel@tonic-gate 				perror(curr_dirname);
6810Sstevel@tonic-gate 				*err_code = WARNING_EXIT;
6820Sstevel@tonic-gate 			} else
6830Sstevel@tonic-gate 				dir_entry = readdir(dir_ptr);
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate 			/*
6860Sstevel@tonic-gate 			 * Now iterate through the subdirs of 'curr_dirname'
6870Sstevel@tonic-gate 			 * In the case of a match against 'pattern',
6880Sstevel@tonic-gate 			 * add the path to the next linked list, which
6890Sstevel@tonic-gate 			 * will be matched on the next iteration.
6900Sstevel@tonic-gate 			 */
6910Sstevel@tonic-gate 			while (dir_entry != NULL) {
6920Sstevel@tonic-gate 				/* Skip the dirs "." and ".." */
6930Sstevel@tonic-gate 				if ((strcmp(dir_entry->d_name, ".") == 0) ||
6940Sstevel@tonic-gate 				    (strcmp(dir_entry->d_name, "..") == 0)) {
6950Sstevel@tonic-gate 					dir_entry = readdir(dir_ptr);
6960Sstevel@tonic-gate 					continue;
6970Sstevel@tonic-gate 				}
6980Sstevel@tonic-gate 				if (fnmatch(pattern, dir_entry->d_name,
6990Sstevel@tonic-gate 				    FNM_PATHNAME) == 0) {
7000Sstevel@tonic-gate 					/*
7010Sstevel@tonic-gate 					 * Build 'new_dirname' which will be
7020Sstevel@tonic-gate 					 * examined on the next iteration.
7030Sstevel@tonic-gate 					 */
7040Sstevel@tonic-gate 					if (curr_dirname[strlen(curr_dirname)-1]
705*13116SJan.Parcel@Sun.COM 					    != '/')
7060Sstevel@tonic-gate 						(void) snprintf(new_dirname,
7070Sstevel@tonic-gate 						    sizeof (new_dirname),
7080Sstevel@tonic-gate 						    "%s/%s", curr_dirname,
7090Sstevel@tonic-gate 						    dir_entry->d_name);
7100Sstevel@tonic-gate 					else
7110Sstevel@tonic-gate 						(void) snprintf(new_dirname,
7120Sstevel@tonic-gate 						    sizeof (new_dirname),
7130Sstevel@tonic-gate 						    "%s%s", curr_dirname,
7140Sstevel@tonic-gate 						    dir_entry->d_name);
7150Sstevel@tonic-gate 
7160Sstevel@tonic-gate 					/* Add to the next lined list */
7170Sstevel@tonic-gate 					add_dir(&next_level, new_dirname);
7180Sstevel@tonic-gate 				}
7190Sstevel@tonic-gate 				dir_entry = readdir(dir_ptr);
7200Sstevel@tonic-gate 			}
7210Sstevel@tonic-gate 
7220Sstevel@tonic-gate 			/* Close directory */
7230Sstevel@tonic-gate 			if (dir_ptr != NULL)
7240Sstevel@tonic-gate 				(void) closedir(dir_ptr);
7250Sstevel@tonic-gate 
7260Sstevel@tonic-gate 			/* Free this entry and move on.... */
7270Sstevel@tonic-gate 			tmp_ptr = current_level;
7280Sstevel@tonic-gate 			current_level = current_level->next;
7290Sstevel@tonic-gate 			free(tmp_ptr);
7300Sstevel@tonic-gate 		}
7310Sstevel@tonic-gate 
7320Sstevel@tonic-gate 		/*
7330Sstevel@tonic-gate 		 * OK, done with this level.  Move to the next level and
7340Sstevel@tonic-gate 		 * advance the ptrs which indicate the component name.
7350Sstevel@tonic-gate 		 */
7360Sstevel@tonic-gate 		current_level = next_level;
7370Sstevel@tonic-gate 		next_level = NULL;
7380Sstevel@tonic-gate 	}
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate 	tmp_ptr = current_level;
7410Sstevel@tonic-gate 
7420Sstevel@tonic-gate 	/* Error case: the subtree doesn't exist! */
7430Sstevel@tonic-gate 	if (current_level == NULL) {
7440Sstevel@tonic-gate 		(void) fprintf(stderr, INVALID_SUBTREE, full_path);
7450Sstevel@tonic-gate 		*err_code = WARNING_EXIT;
7460Sstevel@tonic-gate 	}
7470Sstevel@tonic-gate 
7480Sstevel@tonic-gate 	/*
7490Sstevel@tonic-gate 	 * Iterate through all the dirnames which match the pattern and
7500Sstevel@tonic-gate 	 * add them to to global list of subtrees which must be examined.
7510Sstevel@tonic-gate 	 */
7520Sstevel@tonic-gate 	while (current_level != NULL) {
7530Sstevel@tonic-gate 		/*
7540Sstevel@tonic-gate 		 * Sanity check for 'bart create', make sure the subtree
7550Sstevel@tonic-gate 		 * points to a valid object.
7560Sstevel@tonic-gate 		 */
7570Sstevel@tonic-gate 		ret = lstat64(current_level->dirname, &statb);
7580Sstevel@tonic-gate 		if (ret < 0) {
7590Sstevel@tonic-gate 			(void) fprintf(stderr, INVALID_SUBTREE,
7600Sstevel@tonic-gate 			    current_level->dirname);
7610Sstevel@tonic-gate 			current_level = current_level->next;
7620Sstevel@tonic-gate 			*err_code = WARNING_EXIT;
7630Sstevel@tonic-gate 			continue;
7640Sstevel@tonic-gate 		}
7650Sstevel@tonic-gate 
7660Sstevel@tonic-gate 		if (begin_rule == NULL) {
7670Sstevel@tonic-gate 			begin_rule =
7680Sstevel@tonic-gate 			    add_single_rule(current_level->dirname);
7690Sstevel@tonic-gate 		} else
7700Sstevel@tonic-gate 			(void) add_single_rule(current_level->dirname);
7710Sstevel@tonic-gate 
7720Sstevel@tonic-gate 		current_level = current_level->next;
7730Sstevel@tonic-gate 	}
7740Sstevel@tonic-gate 
7750Sstevel@tonic-gate 	/*
7760Sstevel@tonic-gate 	 * Free up the memory and return a ptr to the first entry in the
7770Sstevel@tonic-gate 	 * subtree block.  This is necessary for the parser, which may need
7780Sstevel@tonic-gate 	 * to add modifier strings to all the elements in this block.
7790Sstevel@tonic-gate 	 */
7800Sstevel@tonic-gate 	dirs_cleanup(tmp_ptr);
7810Sstevel@tonic-gate 
7820Sstevel@tonic-gate 	return (begin_rule);
7830Sstevel@tonic-gate }
7840Sstevel@tonic-gate 
7850Sstevel@tonic-gate 
7860Sstevel@tonic-gate /*
7870Sstevel@tonic-gate  * Add a single entry to the linked list of rules to be read.  Does not do
7880Sstevel@tonic-gate  * the wildcard expansion of 'add_subtree_rule', so is much simpler.
7890Sstevel@tonic-gate  */
7900Sstevel@tonic-gate static struct rule *
add_single_rule(char * path)7910Sstevel@tonic-gate add_single_rule(char *path)
7920Sstevel@tonic-gate {
7930Sstevel@tonic-gate 
7940Sstevel@tonic-gate 	/*
7950Sstevel@tonic-gate 	 * If the rules list does NOT exist, then create it.
7960Sstevel@tonic-gate 	 * If the rules list does exist, then traverse the next element.
7970Sstevel@tonic-gate 	 */
7980Sstevel@tonic-gate 	if (first_rule == NULL) {
7990Sstevel@tonic-gate 		first_rule = gen_rulestruct();
8000Sstevel@tonic-gate 		current_rule = first_rule;
8010Sstevel@tonic-gate 	} else {
8020Sstevel@tonic-gate 		current_rule->next = gen_rulestruct();
8030Sstevel@tonic-gate 		current_rule->next->prev = current_rule;
8040Sstevel@tonic-gate 		current_rule = current_rule->next;
8050Sstevel@tonic-gate 	}
8060Sstevel@tonic-gate 
8070Sstevel@tonic-gate 	/* Setup the rule struct, handle relocatable roots, i.e. '-R' option */
8080Sstevel@tonic-gate 	(void) strlcpy(current_rule->subtree, path,
8090Sstevel@tonic-gate 	    sizeof (current_rule->subtree));
8100Sstevel@tonic-gate 
8110Sstevel@tonic-gate 	return (current_rule);
8120Sstevel@tonic-gate }
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate /*
8150Sstevel@tonic-gate  * Code stolen from filesync utility, used by read_rules() to read in the
8160Sstevel@tonic-gate  * rulesfile.
8170Sstevel@tonic-gate  */
8180Sstevel@tonic-gate static char *
lex(FILE * file)8190Sstevel@tonic-gate lex(FILE *file)
8200Sstevel@tonic-gate {
8210Sstevel@tonic-gate 	char c, delim;
8220Sstevel@tonic-gate 	char *p;
8230Sstevel@tonic-gate 	char *s;
8240Sstevel@tonic-gate 	static char *savep;
8250Sstevel@tonic-gate 	static char namebuf[ BUF_SIZE ];
8260Sstevel@tonic-gate 	static char inbuf[ BUF_SIZE ];
8270Sstevel@tonic-gate 
8280Sstevel@tonic-gate 	if (file) {			/* read a new line		*/
8290Sstevel@tonic-gate 		p = inbuf + sizeof (inbuf);
8300Sstevel@tonic-gate 
8310Sstevel@tonic-gate 		s = inbuf;
8320Sstevel@tonic-gate 		/* read the next input line, with all continuations	*/
8330Sstevel@tonic-gate 		while (savep = fgets(s, p - s, file)) {
8340Sstevel@tonic-gate 			lex_linenum++;
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate 			/* go find the last character of the input line	*/
8370Sstevel@tonic-gate 			while (*s && s[1])
8380Sstevel@tonic-gate 				s++;
8390Sstevel@tonic-gate 			if (*s == '\n')
8400Sstevel@tonic-gate 				s--;
8410Sstevel@tonic-gate 
8420Sstevel@tonic-gate 			/* see whether or not we need a continuation	*/
8430Sstevel@tonic-gate 			if (s < inbuf || *s != '\\')
8440Sstevel@tonic-gate 				break;
8450Sstevel@tonic-gate 
8460Sstevel@tonic-gate 			continue;
8470Sstevel@tonic-gate 		}
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 		if (savep == NULL)
8500Sstevel@tonic-gate 			return (0);
8510Sstevel@tonic-gate 
8520Sstevel@tonic-gate 		s = inbuf;
8530Sstevel@tonic-gate 	} else {			/* continue with old line	*/
8540Sstevel@tonic-gate 		if (savep == NULL)
8550Sstevel@tonic-gate 			return (0);
8560Sstevel@tonic-gate 		s = savep;
8570Sstevel@tonic-gate 	}
8580Sstevel@tonic-gate 	savep = NULL;
8590Sstevel@tonic-gate 
8600Sstevel@tonic-gate 	/* skip over leading white space	*/
8610Sstevel@tonic-gate 	while (isspace(*s))
8620Sstevel@tonic-gate 		s++;
8630Sstevel@tonic-gate 	if (*s == 0)
8640Sstevel@tonic-gate 		return (0);
8650Sstevel@tonic-gate 
8660Sstevel@tonic-gate 	/* see if this is a quoted string	*/
8670Sstevel@tonic-gate 	c = *s;
8680Sstevel@tonic-gate 	if (c == '\'' || c == '"') {
8690Sstevel@tonic-gate 		delim = c;
8700Sstevel@tonic-gate 		s++;
8710Sstevel@tonic-gate 	} else
8720Sstevel@tonic-gate 		delim = 0;
8730Sstevel@tonic-gate 
8740Sstevel@tonic-gate 	/* copy the token into the buffer	*/
8750Sstevel@tonic-gate 	for (p = namebuf; (c = *s) != 0; s++) {
8760Sstevel@tonic-gate 		/* literal escape		*/
8770Sstevel@tonic-gate 		if (c == '\\') {
8780Sstevel@tonic-gate 			s++;
8790Sstevel@tonic-gate 			*p++ = *s;
8800Sstevel@tonic-gate 			continue;
8810Sstevel@tonic-gate 		}
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 		/* closing delimiter		*/
8840Sstevel@tonic-gate 		if (c == delim) {
8850Sstevel@tonic-gate 			s++;
8860Sstevel@tonic-gate 			break;
8870Sstevel@tonic-gate 		}
8880Sstevel@tonic-gate 
8890Sstevel@tonic-gate 		/* delimiting white space	*/
8900Sstevel@tonic-gate 		if (delim == 0 && isspace(c))
8910Sstevel@tonic-gate 			break;
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate 		/* ordinary characters		*/
8940Sstevel@tonic-gate 		*p++ = *s;
8950Sstevel@tonic-gate 	}
8960Sstevel@tonic-gate 
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate 	/* remember where we left off		*/
8990Sstevel@tonic-gate 	savep = *s ? s : 0;
9000Sstevel@tonic-gate 
9010Sstevel@tonic-gate 	/* null terminate and return the buffer	*/
9020Sstevel@tonic-gate 	*p = 0;
9030Sstevel@tonic-gate 	return (namebuf);
9040Sstevel@tonic-gate }
9050Sstevel@tonic-gate 
9060Sstevel@tonic-gate /*
9070Sstevel@tonic-gate  * Iterate through the dir strcutures and free memory.
9080Sstevel@tonic-gate  */
9090Sstevel@tonic-gate static void
dirs_cleanup(struct dir_component * dir)9100Sstevel@tonic-gate dirs_cleanup(struct dir_component *dir)
9110Sstevel@tonic-gate {
9120Sstevel@tonic-gate 	struct	dir_component	*next;
9130Sstevel@tonic-gate 
9140Sstevel@tonic-gate 	while (dir != NULL) {
9150Sstevel@tonic-gate 		next = dir->next;
9160Sstevel@tonic-gate 		free(dir);
9170Sstevel@tonic-gate 		dir = next;
9180Sstevel@tonic-gate 	}
9190Sstevel@tonic-gate }
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate /*
9220Sstevel@tonic-gate  * Create and initialize a new dir structure.  Used by add_subtree_rule() when
9230Sstevel@tonic-gate  * doing expansion of directory names caused by wildcards.
9240Sstevel@tonic-gate  */
9250Sstevel@tonic-gate static void
add_dir(struct dir_component ** dir,char * dirname)9260Sstevel@tonic-gate add_dir(struct dir_component **dir, char *dirname)
9270Sstevel@tonic-gate {
9280Sstevel@tonic-gate 	struct	dir_component	*new, *next_dir;
9290Sstevel@tonic-gate 
9300Sstevel@tonic-gate 	new = gen_dir_component();
9310Sstevel@tonic-gate 	if (dirname != NULL)
9320Sstevel@tonic-gate 		(void) strlcpy(new->dirname, dirname, sizeof (new->dirname));
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate 	if (*dir == NULL)
9350Sstevel@tonic-gate 		*dir = new;
9360Sstevel@tonic-gate 	else {
9370Sstevel@tonic-gate 		next_dir = *dir;
9380Sstevel@tonic-gate 		while (next_dir->next != NULL)
9390Sstevel@tonic-gate 			next_dir = next_dir->next;
9400Sstevel@tonic-gate 
9410Sstevel@tonic-gate 		next_dir->next = new;
9420Sstevel@tonic-gate 	}
9430Sstevel@tonic-gate }
9440Sstevel@tonic-gate 
9450Sstevel@tonic-gate /*
9460Sstevel@tonic-gate  * Traverse the linked list of rules in a REVERSE order.
9470Sstevel@tonic-gate  */
9480Sstevel@tonic-gate static struct rule *
get_last_entry(boolean_t reset)9490Sstevel@tonic-gate get_last_entry(boolean_t reset)
9500Sstevel@tonic-gate {
9510Sstevel@tonic-gate 	static struct rule	*curr_root = NULL;
9520Sstevel@tonic-gate 
9530Sstevel@tonic-gate 	if (reset) {
9540Sstevel@tonic-gate 
9550Sstevel@tonic-gate 		curr_root = first_rule;
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate 		/* RESET: set cur_root to the end of the list */
9580Sstevel@tonic-gate 		while (curr_root != NULL)
9590Sstevel@tonic-gate 			if (curr_root->next == NULL)
9600Sstevel@tonic-gate 				break;
9610Sstevel@tonic-gate 			else
9620Sstevel@tonic-gate 				curr_root = curr_root->next;
9630Sstevel@tonic-gate 	} else
9640Sstevel@tonic-gate 		curr_root = (curr_root->prev);
9650Sstevel@tonic-gate 
9660Sstevel@tonic-gate 	return (curr_root);
9670Sstevel@tonic-gate }
9680Sstevel@tonic-gate 
9690Sstevel@tonic-gate /*
9700Sstevel@tonic-gate  * Traverse the first entry, used by 'bart create' to iterate through
9710Sstevel@tonic-gate  * subtrees or individual filenames.
9720Sstevel@tonic-gate  */
9730Sstevel@tonic-gate struct rule *
get_first_subtree()9740Sstevel@tonic-gate get_first_subtree()
9750Sstevel@tonic-gate {
9760Sstevel@tonic-gate 	return (first_rule);
9770Sstevel@tonic-gate }
9780Sstevel@tonic-gate 
9790Sstevel@tonic-gate /*
9800Sstevel@tonic-gate  * Traverse the next entry, used by 'bart create' to iterate through
9810Sstevel@tonic-gate  * subtrees or individual filenames.
9820Sstevel@tonic-gate  */
9830Sstevel@tonic-gate struct rule *
get_next_subtree(struct rule * entry)9840Sstevel@tonic-gate get_next_subtree(struct rule *entry)
9850Sstevel@tonic-gate {
9860Sstevel@tonic-gate 	return (entry->next);
9870Sstevel@tonic-gate }
9880Sstevel@tonic-gate 
9890Sstevel@tonic-gate char *
safe_strdup(char * s)9900Sstevel@tonic-gate safe_strdup(char *s)
9910Sstevel@tonic-gate {
9920Sstevel@tonic-gate 	char *ret;
9930Sstevel@tonic-gate 	size_t len;
9940Sstevel@tonic-gate 
9950Sstevel@tonic-gate 	len = strlen(s) + 1;
9960Sstevel@tonic-gate 	ret = safe_calloc(len);
9970Sstevel@tonic-gate 	(void) strlcpy(ret, s, len);
9980Sstevel@tonic-gate 	return (ret);
9990Sstevel@tonic-gate }
10000Sstevel@tonic-gate 
10010Sstevel@tonic-gate /*
10020Sstevel@tonic-gate  * Function to match a filename against the subtrees in the link list
10030Sstevel@tonic-gate  * of 'rule' strcutures.  Upon finding a matching rule, see if it should
10040Sstevel@tonic-gate  * be excluded.  Keep going until a match is found OR all rules have been
10050Sstevel@tonic-gate  * exhausted.
10060Sstevel@tonic-gate  * NOTES: Rules are parsed in reverse;
10070Sstevel@tonic-gate  * satisfies the spec that "Last rule wins".  Also, the default rule should
10080Sstevel@tonic-gate  * always match, so this function should NEVER return NULL.
10090Sstevel@tonic-gate  */
10100Sstevel@tonic-gate struct rule *
check_rules(const char * fname,char type)10110Sstevel@tonic-gate check_rules(const char *fname, char type)
10120Sstevel@tonic-gate {
10130Sstevel@tonic-gate 	struct rule		*root;
10140Sstevel@tonic-gate 
10150Sstevel@tonic-gate 	root = get_last_entry(B_TRUE);
10160Sstevel@tonic-gate 	while (root != NULL) {
10170Sstevel@tonic-gate 		if (match_subtree(fname, root->subtree)) {
1018*13116SJan.Parcel@Sun.COM 			if (exclude_fname(fname, type, root) == NO_EXCLUDE)
10190Sstevel@tonic-gate 				break;
10200Sstevel@tonic-gate 		}
10210Sstevel@tonic-gate 		root = get_last_entry(B_FALSE);
10220Sstevel@tonic-gate 	}
10230Sstevel@tonic-gate 
10240Sstevel@tonic-gate 	return (root);
10250Sstevel@tonic-gate }
10260Sstevel@tonic-gate 
10270Sstevel@tonic-gate /*
1028550Shm123892  * Function to determine if an entry in a rules file (see bart_rules(4)) applies
1029550Shm123892  * to a filename. We truncate "fname" such that it has the same number of
1030550Shm123892  * components as "rule" and let fnmatch(3C) do the rest. A "component" is one
1031550Shm123892  * part of an fname as delimited by slashes ('/'). So "/A/B/C/D" has four
1032550Shm123892  * components: "A", "B", "C" and "D".
1033550Shm123892  *
1034550Shm123892  * For example:
1035550Shm123892  *
1036550Shm123892  * 1. the rule "/home/nickiso" applies to fname "/home/nickiso/src/foo.c" so
1037550Shm123892  * should match.
1038550Shm123892  *
1039550Shm123892  * 2. the rule "/home/nickiso/temp/src" does not apply to fname
1040550Shm123892  * "/home/nickiso/foo.c" so should not match.
10410Sstevel@tonic-gate  */
10420Sstevel@tonic-gate static int
match_subtree(const char * fname,char * rule)10430Sstevel@tonic-gate match_subtree(const char *fname, char *rule)
10440Sstevel@tonic-gate {
1045550Shm123892 	int	match, num_rule_slash;
1046550Shm123892 	char	*ptr, fname_cp[PATH_MAX];
10470Sstevel@tonic-gate 
1048550Shm123892 	/* If rule has more components than fname, it cannot match. */
1049550Shm123892 	if ((num_rule_slash = count_slashes(rule)) > count_slashes(fname))
1050550Shm123892 		return (0);
1051550Shm123892 
1052550Shm123892 	/* Create a copy of fname that we can truncate. */
1053550Shm123892 	(void) strlcpy(fname_cp, fname, sizeof (fname_cp));
10540Sstevel@tonic-gate 
10550Sstevel@tonic-gate 	/*
1056550Shm123892 	 * Truncate fname_cp such that it has the same number of components
1057550Shm123892 	 * as rule. If rule ends with '/', so should fname_cp. ie:
10580Sstevel@tonic-gate 	 *
1059550Shm123892 	 * rule		fname			fname_cp	matches
1060550Shm123892 	 * ----		-----			--------	-------
1061550Shm123892 	 * /home/dir*	/home/dir0/dir1/fileA	/home/dir0	yes
1062550Shm123892 	 * /home/dir/	/home/dir0/dir1/fileA	/home/dir0/	no
10630Sstevel@tonic-gate 	 */
1064550Shm123892 	for (ptr = fname_cp; num_rule_slash > 0; num_rule_slash--, ptr++)
1065550Shm123892 		ptr = strchr(ptr, '/');
1066550Shm123892 	if (*(rule + strlen(rule) - 1) != '/') {
1067550Shm123892 		while (*ptr != '\0') {
1068550Shm123892 			if (*ptr == '/')
1069550Shm123892 				break;
1070550Shm123892 			ptr++;
1071550Shm123892 		}
1072550Shm123892 	}
1073550Shm123892 	*ptr = '\0';
10740Sstevel@tonic-gate 
1075550Shm123892 	/* OK, now see if they match. */
1076550Shm123892 	match = fnmatch(rule, fname_cp, FNM_PATHNAME);
10770Sstevel@tonic-gate 
10780Sstevel@tonic-gate 	/* No match, return failure */
10790Sstevel@tonic-gate 	if (match != 0)
10800Sstevel@tonic-gate 		return (0);
10810Sstevel@tonic-gate 	else
10820Sstevel@tonic-gate 		return (1);
10830Sstevel@tonic-gate }
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate void
process_glob_ignores(char * ignore_list,uint_t * flags)10860Sstevel@tonic-gate process_glob_ignores(char *ignore_list, uint_t *flags)
10870Sstevel@tonic-gate {
10880Sstevel@tonic-gate 	char	*cp;
10890Sstevel@tonic-gate 	struct attr_keyword *akp;
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 	if (ignore_list == NULL)
10920Sstevel@tonic-gate 		usage();
10930Sstevel@tonic-gate 
10940Sstevel@tonic-gate 	cp = strtok(ignore_list, ",");
10950Sstevel@tonic-gate 	while (cp != NULL) {
10960Sstevel@tonic-gate 		akp = attr_keylookup(cp);
10970Sstevel@tonic-gate 		if (akp == NULL)
10980Sstevel@tonic-gate 			(void) fprintf(stderr, "ERROR: Invalid keyword %s\n",
10990Sstevel@tonic-gate 			    cp);
11000Sstevel@tonic-gate 		else
11010Sstevel@tonic-gate 			*flags &= ~akp->ak_flags;
11020Sstevel@tonic-gate 		cp = strtok(NULL, ",");
11030Sstevel@tonic-gate 	}
11040Sstevel@tonic-gate }
1105