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(¤t_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