xref: /illumos-gate/usr/src/cmd/filesync/eval.c (revision 2a8bcb4efb45d99ac41c94a75c396b362c414f7f)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 1995-2003 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate /*
28*7c478bd9Sstevel@tonic-gate  * module:
29*7c478bd9Sstevel@tonic-gate  *	eval.c
30*7c478bd9Sstevel@tonic-gate  *
31*7c478bd9Sstevel@tonic-gate  * purpose:
32*7c478bd9Sstevel@tonic-gate  *	routines to ascertain the current status of all of the files
33*7c478bd9Sstevel@tonic-gate  *	described by a set of rules.  Some of the routines that update
34*7c478bd9Sstevel@tonic-gate  *	file status information are also called later (during reconcilation)
35*7c478bd9Sstevel@tonic-gate  *	to reflect the changes that have been made to files.
36*7c478bd9Sstevel@tonic-gate  *
37*7c478bd9Sstevel@tonic-gate  * contents:
38*7c478bd9Sstevel@tonic-gate  *	evaluate	top level - evaluate one side of one base
39*7c478bd9Sstevel@tonic-gate  *	add_file_arg	(static) add a file to the list of files to evaluate
40*7c478bd9Sstevel@tonic-gate  *	eval_file	(static) stat a specific file, recurse on directories
41*7c478bd9Sstevel@tonic-gate  *	walker		(static) node visitor for recursive descent
42*7c478bd9Sstevel@tonic-gate  *	note_info	update a file_info structure from a stat structure
43*7c478bd9Sstevel@tonic-gate  *	do_update	(static) update one file_info structure from another
44*7c478bd9Sstevel@tonic-gate  *	update_info	update the baseline file_info from the prevailng side
45*7c478bd9Sstevel@tonic-gate  *	fakedata	(static) make it look like one side hasn't changed
46*7c478bd9Sstevel@tonic-gate  *	check_inum	(static) sanity check to detect wrong-dir errors
47*7c478bd9Sstevel@tonic-gate  *	add_glob	(static) expand a wildcard in an include rule
48*7c478bd9Sstevel@tonic-gate  *	add_run		(static) run a program to generate an include list
49*7c478bd9Sstevel@tonic-gate  *
50*7c478bd9Sstevel@tonic-gate  * notes:
51*7c478bd9Sstevel@tonic-gate  *	pay careful attention to the use of the LISTED and EVALUATE
52*7c478bd9Sstevel@tonic-gate  *	flags in each file description structure.
53*7c478bd9Sstevel@tonic-gate  */
54*7c478bd9Sstevel@tonic-gate 
55*7c478bd9Sstevel@tonic-gate #include <stdio.h>
56*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
57*7c478bd9Sstevel@tonic-gate #include <libgen.h>
58*7c478bd9Sstevel@tonic-gate #include <unistd.h>
59*7c478bd9Sstevel@tonic-gate #include <string.h>
60*7c478bd9Sstevel@tonic-gate #include <glob.h>
61*7c478bd9Sstevel@tonic-gate #include <ftw.h>
62*7c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
63*7c478bd9Sstevel@tonic-gate #include <errno.h>
64*7c478bd9Sstevel@tonic-gate 
65*7c478bd9Sstevel@tonic-gate #include "filesync.h"
66*7c478bd9Sstevel@tonic-gate #include "database.h"
67*7c478bd9Sstevel@tonic-gate #include "messages.h"
68*7c478bd9Sstevel@tonic-gate #include "debug.h"
69*7c478bd9Sstevel@tonic-gate 
70*7c478bd9Sstevel@tonic-gate /*
71*7c478bd9Sstevel@tonic-gate  * routines:
72*7c478bd9Sstevel@tonic-gate  */
73*7c478bd9Sstevel@tonic-gate static errmask_t eval_file(struct base *, struct file *);
74*7c478bd9Sstevel@tonic-gate static errmask_t add_file_arg(struct base *, char *);
75*7c478bd9Sstevel@tonic-gate static int walker(const char *, const struct stat *, int, struct FTW *);
76*7c478bd9Sstevel@tonic-gate static errmask_t add_glob(struct base *, char *);
77*7c478bd9Sstevel@tonic-gate static errmask_t add_run(struct base *, char *);
78*7c478bd9Sstevel@tonic-gate static void check_inum(struct file *, int);
79*7c478bd9Sstevel@tonic-gate static void fakedata(struct file *, int);
80*7c478bd9Sstevel@tonic-gate 
81*7c478bd9Sstevel@tonic-gate /*
82*7c478bd9Sstevel@tonic-gate  * globals
83*7c478bd9Sstevel@tonic-gate  */
84*7c478bd9Sstevel@tonic-gate static bool_t usingsrc;	/* this pass is on the source side		*/
85*7c478bd9Sstevel@tonic-gate static int walk_errs;	/* errors found in tree walk			*/
86*7c478bd9Sstevel@tonic-gate static struct file *cur_dir;	/* base directory for this pass		*/
87*7c478bd9Sstevel@tonic-gate static struct base *cur_base;	/* base pointer for this pass		*/
88*7c478bd9Sstevel@tonic-gate 
89*7c478bd9Sstevel@tonic-gate /*
90*7c478bd9Sstevel@tonic-gate  * routine:
91*7c478bd9Sstevel@tonic-gate  *	evaluate
92*7c478bd9Sstevel@tonic-gate  *
93*7c478bd9Sstevel@tonic-gate  * purpose:
94*7c478bd9Sstevel@tonic-gate  *	to build up a baseline description for all of the files
95*7c478bd9Sstevel@tonic-gate  *	under one side of one base pair (as specified by the rules
96*7c478bd9Sstevel@tonic-gate  *	for that base pair).
97*7c478bd9Sstevel@tonic-gate  *
98*7c478bd9Sstevel@tonic-gate  * parameters:
99*7c478bd9Sstevel@tonic-gate  *	pointer to the base to be evaluated
100*7c478bd9Sstevel@tonic-gate  *	source/destination indication
101*7c478bd9Sstevel@tonic-gate  *	are we restricted to new rules
102*7c478bd9Sstevel@tonic-gate  *
103*7c478bd9Sstevel@tonic-gate  * returns:
104*7c478bd9Sstevel@tonic-gate  *	error mask
105*7c478bd9Sstevel@tonic-gate  *
106*7c478bd9Sstevel@tonic-gate  * notes:
107*7c478bd9Sstevel@tonic-gate  *	we evaluate source and destination separately, and
108*7c478bd9Sstevel@tonic-gate  *	reinterpret the include rules on each side (since there
109*7c478bd9Sstevel@tonic-gate  *	may be wild cards and programs that must be evaluated
110*7c478bd9Sstevel@tonic-gate  *	in a specific directory context).  Similarly the ignore
111*7c478bd9Sstevel@tonic-gate  *	rules must be interpreted anew for each base.
112*7c478bd9Sstevel@tonic-gate  */
113*7c478bd9Sstevel@tonic-gate errmask_t
evaluate(struct base * bp,side_t srcdst,bool_t newrules)114*7c478bd9Sstevel@tonic-gate evaluate(struct base *bp, side_t srcdst, bool_t newrules)
115*7c478bd9Sstevel@tonic-gate {	errmask_t errs = 0;
116*7c478bd9Sstevel@tonic-gate 	char *dir;
117*7c478bd9Sstevel@tonic-gate 	struct rule *rp;
118*7c478bd9Sstevel@tonic-gate 	struct file *fp;
119*7c478bd9Sstevel@tonic-gate 
120*7c478bd9Sstevel@tonic-gate 	/* see if this base is still relevant		*/
121*7c478bd9Sstevel@tonic-gate 	if ((bp->b_flags & F_LISTED) == 0)
122*7c478bd9Sstevel@tonic-gate 		return (0);
123*7c478bd9Sstevel@tonic-gate 
124*7c478bd9Sstevel@tonic-gate 	/* figure out what this pass is all about	*/
125*7c478bd9Sstevel@tonic-gate 	usingsrc = (srcdst == OPT_SRC);
126*7c478bd9Sstevel@tonic-gate 
127*7c478bd9Sstevel@tonic-gate 	/*
128*7c478bd9Sstevel@tonic-gate 	 * the ignore engine maintains considerable per-base-directory
129*7c478bd9Sstevel@tonic-gate 	 * state, and so must be reset at the start of a new tree.
130*7c478bd9Sstevel@tonic-gate 	 */
131*7c478bd9Sstevel@tonic-gate 	ignore_reset();
132*7c478bd9Sstevel@tonic-gate 
133*7c478bd9Sstevel@tonic-gate 	/* all evaluation must happen from the appropriate directory */
134*7c478bd9Sstevel@tonic-gate 	dir = usingsrc ? bp->b_src_name : bp->b_dst_name;
135*7c478bd9Sstevel@tonic-gate 	if (chdir(dir) < 0) {
136*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, gettext(ERR_chdir), dir);
137*7c478bd9Sstevel@tonic-gate 
138*7c478bd9Sstevel@tonic-gate 		/*
139*7c478bd9Sstevel@tonic-gate 		 * if we have -n -o we are actually willing to
140*7c478bd9Sstevel@tonic-gate 		 * pretend that nothing has changed on the missing
141*7c478bd9Sstevel@tonic-gate 		 * side.  This is actually useful on a disconnected
142*7c478bd9Sstevel@tonic-gate 		 * notebook to ask what has been changed so far.
143*7c478bd9Sstevel@tonic-gate 		 */
144*7c478bd9Sstevel@tonic-gate 		if (opt_onesided == (usingsrc ? OPT_DST : OPT_SRC)) {
145*7c478bd9Sstevel@tonic-gate 			for (fp = bp->b_files; fp; fp = fp->f_next)
146*7c478bd9Sstevel@tonic-gate 				fakedata(fp, srcdst);
147*7c478bd9Sstevel@tonic-gate 
148*7c478bd9Sstevel@tonic-gate 			if (opt_debug & DBG_EVAL)
149*7c478bd9Sstevel@tonic-gate 				fprintf(stderr, "EVAL: FAKE DATA %s dir=%s\n",
150*7c478bd9Sstevel@tonic-gate 					usingsrc ? "SRC" : "DST", dir);
151*7c478bd9Sstevel@tonic-gate 			return (0);
152*7c478bd9Sstevel@tonic-gate 		} else
153*7c478bd9Sstevel@tonic-gate 			return (ERR_NOBASE);
154*7c478bd9Sstevel@tonic-gate 	}
155*7c478bd9Sstevel@tonic-gate 
156*7c478bd9Sstevel@tonic-gate 	if (opt_debug & DBG_EVAL)
157*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, "EVAL: base=%d, %s dir=%s\n",
158*7c478bd9Sstevel@tonic-gate 			bp->b_ident, usingsrc ? "SRC" : "DST", dir);
159*7c478bd9Sstevel@tonic-gate 
160*7c478bd9Sstevel@tonic-gate 	/* assemble the include list			*/
161*7c478bd9Sstevel@tonic-gate 	for (rp = bp->b_includes; rp; rp = rp->r_next) {
162*7c478bd9Sstevel@tonic-gate 
163*7c478bd9Sstevel@tonic-gate 		/* see if we are skipping old rules	*/
164*7c478bd9Sstevel@tonic-gate 		if (newrules && ((rp->r_flags & R_NEW) == 0))
165*7c478bd9Sstevel@tonic-gate 			continue;
166*7c478bd9Sstevel@tonic-gate 
167*7c478bd9Sstevel@tonic-gate 		if (rp->r_flags & R_PROGRAM)
168*7c478bd9Sstevel@tonic-gate 			errs |= add_run(bp, rp->r_file);
169*7c478bd9Sstevel@tonic-gate 		else if (rp->r_flags & R_WILD)
170*7c478bd9Sstevel@tonic-gate 			errs |= add_glob(bp, rp->r_file);
171*7c478bd9Sstevel@tonic-gate 		else
172*7c478bd9Sstevel@tonic-gate 			errs |= add_file_arg(bp, rp->r_file);
173*7c478bd9Sstevel@tonic-gate 	}
174*7c478bd9Sstevel@tonic-gate 
175*7c478bd9Sstevel@tonic-gate 	/* assemble the base-specific exclude list		*/
176*7c478bd9Sstevel@tonic-gate 	for (rp = bp->b_excludes; rp; rp = rp->r_next)
177*7c478bd9Sstevel@tonic-gate 		if (rp->r_flags & R_PROGRAM)
178*7c478bd9Sstevel@tonic-gate 			ignore_pgm(rp->r_file);
179*7c478bd9Sstevel@tonic-gate 		else if (rp->r_flags & R_WILD)
180*7c478bd9Sstevel@tonic-gate 			ignore_expr(rp->r_file);
181*7c478bd9Sstevel@tonic-gate 		else
182*7c478bd9Sstevel@tonic-gate 			ignore_file(rp->r_file);
183*7c478bd9Sstevel@tonic-gate 
184*7c478bd9Sstevel@tonic-gate 	/* add in the global excludes				*/
185*7c478bd9Sstevel@tonic-gate 	for (rp = omnibase.b_excludes; rp; rp = rp->r_next)
186*7c478bd9Sstevel@tonic-gate 		if (rp->r_flags & R_WILD)
187*7c478bd9Sstevel@tonic-gate 			ignore_expr(rp->r_file);
188*7c478bd9Sstevel@tonic-gate 		else
189*7c478bd9Sstevel@tonic-gate 			ignore_file(rp->r_file);
190*7c478bd9Sstevel@tonic-gate 
191*7c478bd9Sstevel@tonic-gate 	/*
192*7c478bd9Sstevel@tonic-gate 	 * because of restriction lists and new-rules, the baseline
193*7c478bd9Sstevel@tonic-gate 	 * may contain many more files than we are actually supposed
194*7c478bd9Sstevel@tonic-gate 	 * to look at during the impending evaluation/analysis phases
195*7c478bd9Sstevel@tonic-gate 	 *
196*7c478bd9Sstevel@tonic-gate 	 * when LIST arguments are encountered within a rule, we turn
197*7c478bd9Sstevel@tonic-gate 	 * on the LISTED flag for the associated files.  We only evaluate
198*7c478bd9Sstevel@tonic-gate 	 * files that have the LISTED flag.  We turn the LISTED flag off
199*7c478bd9Sstevel@tonic-gate 	 * after evaluating them because just because a file was enumerated
200*7c478bd9Sstevel@tonic-gate 	 * in the source doesn't mean that will necessarily be enumerated
201*7c478bd9Sstevel@tonic-gate 	 * in the destination.
202*7c478bd9Sstevel@tonic-gate 	 */
203*7c478bd9Sstevel@tonic-gate 	for (fp = bp->b_files; fp; fp = fp->f_next)
204*7c478bd9Sstevel@tonic-gate 		if (fp->f_flags & F_LISTED) {
205*7c478bd9Sstevel@tonic-gate 			errs |= eval_file(bp, fp);
206*7c478bd9Sstevel@tonic-gate 			fp->f_flags &= ~F_LISTED;
207*7c478bd9Sstevel@tonic-gate 		}
208*7c478bd9Sstevel@tonic-gate 
209*7c478bd9Sstevel@tonic-gate 	/* note that this base has been evaluated	*/
210*7c478bd9Sstevel@tonic-gate 	bp->b_flags |= F_EVALUATE;
211*7c478bd9Sstevel@tonic-gate 
212*7c478bd9Sstevel@tonic-gate 	return (errs);
213*7c478bd9Sstevel@tonic-gate }
214*7c478bd9Sstevel@tonic-gate 
215*7c478bd9Sstevel@tonic-gate /*
216*7c478bd9Sstevel@tonic-gate  * routine:
217*7c478bd9Sstevel@tonic-gate  *	add_file_arg
218*7c478bd9Sstevel@tonic-gate  *
219*7c478bd9Sstevel@tonic-gate  * purpose:
220*7c478bd9Sstevel@tonic-gate  *	to create file node(s) under a specified base for an explictly
221*7c478bd9Sstevel@tonic-gate  *	included file.
222*7c478bd9Sstevel@tonic-gate  *
223*7c478bd9Sstevel@tonic-gate  * parameters:
224*7c478bd9Sstevel@tonic-gate  *	pointer to associated base
225*7c478bd9Sstevel@tonic-gate  *	name of the file
226*7c478bd9Sstevel@tonic-gate  *
227*7c478bd9Sstevel@tonic-gate  * returns:
228*7c478bd9Sstevel@tonic-gate  *	error mask
229*7c478bd9Sstevel@tonic-gate  *
230*7c478bd9Sstevel@tonic-gate  * notes:
231*7c478bd9Sstevel@tonic-gate  *	the trick is that an include LIST argument need not be a file
232*7c478bd9Sstevel@tonic-gate  *	in the base directory, but may be a path passing through
233*7c478bd9Sstevel@tonic-gate  *	several intermediate directories.  If this is the case we
234*7c478bd9Sstevel@tonic-gate  *	need to ensure that all of those directories are added to
235*7c478bd9Sstevel@tonic-gate  *	the tree SPARSELY since it is not intended that they be
236*7c478bd9Sstevel@tonic-gate  *	expanded during the course of evaluation.
237*7c478bd9Sstevel@tonic-gate  *
238*7c478bd9Sstevel@tonic-gate  *	we ignore arguments that end in .. because they have the
239*7c478bd9Sstevel@tonic-gate  *	potential to walk out of the base tree, because it can
240*7c478bd9Sstevel@tonic-gate  *	result in different names for a single file, and because
241*7c478bd9Sstevel@tonic-gate  *	should never be necessary to specify files that way.
242*7c478bd9Sstevel@tonic-gate  */
243*7c478bd9Sstevel@tonic-gate static errmask_t
add_file_arg(struct base * bp,char * path)244*7c478bd9Sstevel@tonic-gate add_file_arg(struct base *bp, char *path)
245*7c478bd9Sstevel@tonic-gate {	int i;
246*7c478bd9Sstevel@tonic-gate 	errmask_t errs = 0;
247*7c478bd9Sstevel@tonic-gate 	struct file *dp = 0;
248*7c478bd9Sstevel@tonic-gate 	struct file *fp;
249*7c478bd9Sstevel@tonic-gate 	char *s, *p;
250*7c478bd9Sstevel@tonic-gate 	char name[ MAX_NAME ];
251*7c478bd9Sstevel@tonic-gate 
252*7c478bd9Sstevel@tonic-gate 	/*
253*7c478bd9Sstevel@tonic-gate 	 * see if someone is trying to feed us a ..
254*7c478bd9Sstevel@tonic-gate 	 */
255*7c478bd9Sstevel@tonic-gate 	if (strcmp(path, "..") == 0 || prefix(path, "../") ||
256*7c478bd9Sstevel@tonic-gate 	    suffix(path, "/..") || contains(path, "/../")) {
257*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, gettext(WARN_ignore), path);
258*7c478bd9Sstevel@tonic-gate 		return (ERR_MISSING);
259*7c478bd9Sstevel@tonic-gate 	}
260*7c478bd9Sstevel@tonic-gate 
261*7c478bd9Sstevel@tonic-gate 	/*
262*7c478bd9Sstevel@tonic-gate 	 * strip off any trailing "/." or "/"
263*7c478bd9Sstevel@tonic-gate 	 *	since noone will miss these, it is safe to actually
264*7c478bd9Sstevel@tonic-gate 	 *	take them off the name.  When we fall out of this
265*7c478bd9Sstevel@tonic-gate 	 *	loop, s will point where the null belongs.  We don't
266*7c478bd9Sstevel@tonic-gate 	 *	actually null the end of string yet because we want
267*7c478bd9Sstevel@tonic-gate 	 *	to leave it pristine for error messages.
268*7c478bd9Sstevel@tonic-gate 	 */
269*7c478bd9Sstevel@tonic-gate 	for (s = path; *s; s++);
270*7c478bd9Sstevel@tonic-gate 	while (s > path) {
271*7c478bd9Sstevel@tonic-gate 		if (s[-1] == '/') {
272*7c478bd9Sstevel@tonic-gate 			s--;
273*7c478bd9Sstevel@tonic-gate 			continue;
274*7c478bd9Sstevel@tonic-gate 		}
275*7c478bd9Sstevel@tonic-gate 		if (s[-1] == '.' && s > &path[1] && s[-2] == '/') {
276*7c478bd9Sstevel@tonic-gate 			s -= 2;
277*7c478bd9Sstevel@tonic-gate 			continue;
278*7c478bd9Sstevel@tonic-gate 		}
279*7c478bd9Sstevel@tonic-gate 		break;
280*7c478bd9Sstevel@tonic-gate 	}
281*7c478bd9Sstevel@tonic-gate 
282*7c478bd9Sstevel@tonic-gate 	/*
283*7c478bd9Sstevel@tonic-gate 	 * skip over leading "/" and "./" (but not over a lone ".")
284*7c478bd9Sstevel@tonic-gate 	 */
285*7c478bd9Sstevel@tonic-gate 	for (p = path; p < s; ) {
286*7c478bd9Sstevel@tonic-gate 		if (*p == '/') {
287*7c478bd9Sstevel@tonic-gate 			p++;
288*7c478bd9Sstevel@tonic-gate 			continue;
289*7c478bd9Sstevel@tonic-gate 		}
290*7c478bd9Sstevel@tonic-gate 		if (*p == '.' && s > &p[1] && p[1] == '/') {
291*7c478bd9Sstevel@tonic-gate 			p += 2;
292*7c478bd9Sstevel@tonic-gate 			continue;
293*7c478bd9Sstevel@tonic-gate 		}
294*7c478bd9Sstevel@tonic-gate 		break;
295*7c478bd9Sstevel@tonic-gate 	}
296*7c478bd9Sstevel@tonic-gate 
297*7c478bd9Sstevel@tonic-gate 	/*
298*7c478bd9Sstevel@tonic-gate 	 * if there is nothing left, we're miffed, but done
299*7c478bd9Sstevel@tonic-gate 	 */
300*7c478bd9Sstevel@tonic-gate 	if (p >= s) {
301*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, gettext(WARN_ignore), path);
302*7c478bd9Sstevel@tonic-gate 		return (ERR_MISSING);
303*7c478bd9Sstevel@tonic-gate 	} else {
304*7c478bd9Sstevel@tonic-gate 		/*
305*7c478bd9Sstevel@tonic-gate 		 * this is actually storing a null into the argument,
306*7c478bd9Sstevel@tonic-gate 		 * but it is OK to do this because the stuff we are
307*7c478bd9Sstevel@tonic-gate 		 * truncating really is garbage that noone will ever
308*7c478bd9Sstevel@tonic-gate 		 * want to see.
309*7c478bd9Sstevel@tonic-gate 		 */
310*7c478bd9Sstevel@tonic-gate 		*s = 0;
311*7c478bd9Sstevel@tonic-gate 		path = p;
312*7c478bd9Sstevel@tonic-gate 	}
313*7c478bd9Sstevel@tonic-gate 
314*7c478bd9Sstevel@tonic-gate 	/*
315*7c478bd9Sstevel@tonic-gate 	 * see if there are any restrictions that would force
316*7c478bd9Sstevel@tonic-gate 	 * us to ignore this argument
317*7c478bd9Sstevel@tonic-gate 	 */
318*7c478bd9Sstevel@tonic-gate 	if (check_restr(bp, path) == 0)
319*7c478bd9Sstevel@tonic-gate 		return (0);
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate 	while (*path) {
322*7c478bd9Sstevel@tonic-gate 		/* lex off the next name component	*/
323*7c478bd9Sstevel@tonic-gate 		for (i = 0; path[i] && path[i] != '/'; i++)
324*7c478bd9Sstevel@tonic-gate 			name[i] = path[i];
325*7c478bd9Sstevel@tonic-gate 		name[i] = 0;
326*7c478bd9Sstevel@tonic-gate 
327*7c478bd9Sstevel@tonic-gate 		/* add it into the database		*/
328*7c478bd9Sstevel@tonic-gate 		fp = (dp == 0)  ? add_file_to_base(bp, name)
329*7c478bd9Sstevel@tonic-gate 				: add_file_to_dir(dp, name);
330*7c478bd9Sstevel@tonic-gate 
331*7c478bd9Sstevel@tonic-gate 		/* see if this was an intermediate directory	*/
332*7c478bd9Sstevel@tonic-gate 		if (path[i] == '/') {
333*7c478bd9Sstevel@tonic-gate 			fp->f_flags |= F_LISTED | F_SPARSE;
334*7c478bd9Sstevel@tonic-gate 			path += i+1;
335*7c478bd9Sstevel@tonic-gate 		} else {
336*7c478bd9Sstevel@tonic-gate 			fp->f_flags |= F_LISTED;
337*7c478bd9Sstevel@tonic-gate 			path += i;
338*7c478bd9Sstevel@tonic-gate 		}
339*7c478bd9Sstevel@tonic-gate 
340*7c478bd9Sstevel@tonic-gate 		dp = fp;
341*7c478bd9Sstevel@tonic-gate 	}
342*7c478bd9Sstevel@tonic-gate 
343*7c478bd9Sstevel@tonic-gate 	return (errs);
344*7c478bd9Sstevel@tonic-gate }
345*7c478bd9Sstevel@tonic-gate 
346*7c478bd9Sstevel@tonic-gate /*
347*7c478bd9Sstevel@tonic-gate  * routine:
348*7c478bd9Sstevel@tonic-gate  *	eval_file
349*7c478bd9Sstevel@tonic-gate  *
350*7c478bd9Sstevel@tonic-gate  * purpose:
351*7c478bd9Sstevel@tonic-gate  *	to evaluate one named file under a particular directory
352*7c478bd9Sstevel@tonic-gate  *
353*7c478bd9Sstevel@tonic-gate  * parameters:
354*7c478bd9Sstevel@tonic-gate  *	pointer to base structure
355*7c478bd9Sstevel@tonic-gate  *	pointer to file structure
356*7c478bd9Sstevel@tonic-gate  *
357*7c478bd9Sstevel@tonic-gate  * returns:
358*7c478bd9Sstevel@tonic-gate  *	error mask
359*7c478bd9Sstevel@tonic-gate  *	filled in evaluations in the baseline
360*7c478bd9Sstevel@tonic-gate  *
361*7c478bd9Sstevel@tonic-gate  * note:
362*7c478bd9Sstevel@tonic-gate  *	due to new rules and other restrictions we may not be expected
363*7c478bd9Sstevel@tonic-gate  *	to evaluate the entire tree.  We should only be called on files
364*7c478bd9Sstevel@tonic-gate  *	that are LISTed, and we should only invoke ourselves recursively
365*7c478bd9Sstevel@tonic-gate  *	on such files.
366*7c478bd9Sstevel@tonic-gate  */
367*7c478bd9Sstevel@tonic-gate static errmask_t
eval_file(struct base * bp,struct file * fp)368*7c478bd9Sstevel@tonic-gate eval_file(struct base *bp, struct file *fp)
369*7c478bd9Sstevel@tonic-gate {	errmask_t errs = 0;
370*7c478bd9Sstevel@tonic-gate 	int rc;
371*7c478bd9Sstevel@tonic-gate 	char *name;
372*7c478bd9Sstevel@tonic-gate 	struct file *cp;
373*7c478bd9Sstevel@tonic-gate 	struct stat statb;
374*7c478bd9Sstevel@tonic-gate 
375*7c478bd9Sstevel@tonic-gate 	if (opt_debug & DBG_EVAL)
376*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, "EVAL: FILE, flags=%s, name=%s\n",
377*7c478bd9Sstevel@tonic-gate 			showflags(fileflags, fp->f_flags), fp->f_name);
378*7c478bd9Sstevel@tonic-gate 
379*7c478bd9Sstevel@tonic-gate 	/* stat the file and fill in the file structure information	*/
380*7c478bd9Sstevel@tonic-gate 	name = get_name(fp);
381*7c478bd9Sstevel@tonic-gate 
382*7c478bd9Sstevel@tonic-gate #ifdef 	DBG_ERRORS
383*7c478bd9Sstevel@tonic-gate 	/* see if we should simulated a stat error on this file	*/
384*7c478bd9Sstevel@tonic-gate 	if (opt_errors && (errno = dbg_chk_error(name, usingsrc ? 's' : 'S')))
385*7c478bd9Sstevel@tonic-gate 		rc = -1;
386*7c478bd9Sstevel@tonic-gate 	else
387*7c478bd9Sstevel@tonic-gate #endif
388*7c478bd9Sstevel@tonic-gate 	rc = lstat(name, &statb);
389*7c478bd9Sstevel@tonic-gate 
390*7c478bd9Sstevel@tonic-gate 	if (rc < 0) {
391*7c478bd9Sstevel@tonic-gate 		if (opt_debug & DBG_EVAL)
392*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "EVAL: FAIL lstat, errno=%d\n", errno);
393*7c478bd9Sstevel@tonic-gate 		switch (errno) {
394*7c478bd9Sstevel@tonic-gate 		    case EACCES:
395*7c478bd9Sstevel@tonic-gate 			fp->f_flags |= F_STAT_ERROR;
396*7c478bd9Sstevel@tonic-gate 			return (ERR_PERM);
397*7c478bd9Sstevel@tonic-gate 		    case EOVERFLOW:
398*7c478bd9Sstevel@tonic-gate 			fp->f_flags |= F_STAT_ERROR;
399*7c478bd9Sstevel@tonic-gate 			return (ERR_UNRESOLVED);
400*7c478bd9Sstevel@tonic-gate 		    default:
401*7c478bd9Sstevel@tonic-gate 			return (ERR_MISSING);
402*7c478bd9Sstevel@tonic-gate 		}
403*7c478bd9Sstevel@tonic-gate 	}
404*7c478bd9Sstevel@tonic-gate 
405*7c478bd9Sstevel@tonic-gate 	/* record the information we've just gained			*/
406*7c478bd9Sstevel@tonic-gate 	note_info(fp, &statb, usingsrc ? OPT_SRC : OPT_DST);
407*7c478bd9Sstevel@tonic-gate 
408*7c478bd9Sstevel@tonic-gate 	/*
409*7c478bd9Sstevel@tonic-gate 	 * checking for ACLs is expensive, so we only do it if we
410*7c478bd9Sstevel@tonic-gate 	 * have been asked to, or if we have reason to believe that
411*7c478bd9Sstevel@tonic-gate 	 * the file has an ACL
412*7c478bd9Sstevel@tonic-gate 	 */
413*7c478bd9Sstevel@tonic-gate 	if (opt_acls || fp->f_info[OPT_BASE].f_numacls)
414*7c478bd9Sstevel@tonic-gate 		(void) get_acls(name,
415*7c478bd9Sstevel@tonic-gate 				&fp->f_info[usingsrc ? OPT_SRC : OPT_DST]);
416*7c478bd9Sstevel@tonic-gate 
417*7c478bd9Sstevel@tonic-gate 
418*7c478bd9Sstevel@tonic-gate 	/* note that this file has been evaluated			*/
419*7c478bd9Sstevel@tonic-gate 	fp->f_flags |= F_EVALUATE;
420*7c478bd9Sstevel@tonic-gate 
421*7c478bd9Sstevel@tonic-gate 	/* if it is not a directory, a simple stat will suffice	*/
422*7c478bd9Sstevel@tonic-gate 	if ((statb.st_mode & S_IFMT) != S_IFDIR)
423*7c478bd9Sstevel@tonic-gate 		return (0);
424*7c478bd9Sstevel@tonic-gate 
425*7c478bd9Sstevel@tonic-gate 	/*
426*7c478bd9Sstevel@tonic-gate 	 * as a sanity check, we look for changes in the I-node
427*7c478bd9Sstevel@tonic-gate 	 * numbers associated with LISTed directories ... on the
428*7c478bd9Sstevel@tonic-gate 	 * assumption that these are high-enough up on the tree
429*7c478bd9Sstevel@tonic-gate 	 * that they aren't likely to change, and so a change
430*7c478bd9Sstevel@tonic-gate 	 * might indicate trouble.
431*7c478bd9Sstevel@tonic-gate 	 */
432*7c478bd9Sstevel@tonic-gate 	if (fp->f_flags & F_LISTED)
433*7c478bd9Sstevel@tonic-gate 		check_inum(fp, usingsrc);
434*7c478bd9Sstevel@tonic-gate 
435*7c478bd9Sstevel@tonic-gate 	/*
436*7c478bd9Sstevel@tonic-gate 	 * sparse directories are on the path between a base and
437*7c478bd9Sstevel@tonic-gate 	 * a listed directory.  As such, we don't walk these
438*7c478bd9Sstevel@tonic-gate 	 * directories.  Rather, we just enumerate the LISTed
439*7c478bd9Sstevel@tonic-gate 	 * files.
440*7c478bd9Sstevel@tonic-gate 	 */
441*7c478bd9Sstevel@tonic-gate 	if (fp->f_flags & F_SPARSE) {
442*7c478bd9Sstevel@tonic-gate 		push_name(fp->f_name);
443*7c478bd9Sstevel@tonic-gate 
444*7c478bd9Sstevel@tonic-gate 		/* this directory isn't supposed to be fully walked	*/
445*7c478bd9Sstevel@tonic-gate 		for (cp = fp->f_files; cp; cp = cp->f_next)
446*7c478bd9Sstevel@tonic-gate 			if (cp->f_flags & F_LISTED) {
447*7c478bd9Sstevel@tonic-gate 				errs |= eval_file(bp, cp);
448*7c478bd9Sstevel@tonic-gate 				cp->f_flags &= ~F_LISTED;
449*7c478bd9Sstevel@tonic-gate 			}
450*7c478bd9Sstevel@tonic-gate 		pop_name();
451*7c478bd9Sstevel@tonic-gate 	} else {
452*7c478bd9Sstevel@tonic-gate 		/* fully walk the tree beneath this directory		*/
453*7c478bd9Sstevel@tonic-gate 		walk_errs = 0;
454*7c478bd9Sstevel@tonic-gate 		cur_base = bp;
455*7c478bd9Sstevel@tonic-gate 		cur_dir = fp;
456*7c478bd9Sstevel@tonic-gate 		nftw(get_name(fp), &walker, MAX_DEPTH, FTW_PHYS|FTW_MOUNT);
457*7c478bd9Sstevel@tonic-gate 		errs |= walk_errs;
458*7c478bd9Sstevel@tonic-gate 	}
459*7c478bd9Sstevel@tonic-gate 
460*7c478bd9Sstevel@tonic-gate 	return (errs);
461*7c478bd9Sstevel@tonic-gate }
462*7c478bd9Sstevel@tonic-gate 
463*7c478bd9Sstevel@tonic-gate /*
464*7c478bd9Sstevel@tonic-gate  * routine:
465*7c478bd9Sstevel@tonic-gate  *	walker
466*7c478bd9Sstevel@tonic-gate  *
467*7c478bd9Sstevel@tonic-gate  * purpose:
468*7c478bd9Sstevel@tonic-gate  *	node visitor for recursive directory enumeration
469*7c478bd9Sstevel@tonic-gate  *
470*7c478bd9Sstevel@tonic-gate  * parameters:
471*7c478bd9Sstevel@tonic-gate  *	name of file
472*7c478bd9Sstevel@tonic-gate  *	pointer to stat buffer for file
473*7c478bd9Sstevel@tonic-gate  *	file type
474*7c478bd9Sstevel@tonic-gate  *	FTW structure (base name offset, walk-depth)
475*7c478bd9Sstevel@tonic-gate  *
476*7c478bd9Sstevel@tonic-gate  * returns:
477*7c478bd9Sstevel@tonic-gate  *	0 	continue
478*7c478bd9Sstevel@tonic-gate  *	-1	stop
479*7c478bd9Sstevel@tonic-gate  *
480*7c478bd9Sstevel@tonic-gate  * notes:
481*7c478bd9Sstevel@tonic-gate  *	Ignoring files is easy, but ignoring directories is harder.
482*7c478bd9Sstevel@tonic-gate  *	Ideally we would just decline to walk the trees beneath
483*7c478bd9Sstevel@tonic-gate  *	ignored directories, but ftw doesn't allow the walker to
484*7c478bd9Sstevel@tonic-gate  *	tell it to "don't enter this directory, but continue".
485*7c478bd9Sstevel@tonic-gate  *
486*7c478bd9Sstevel@tonic-gate  *	Instead, we have to set a global to tell us to ignore
487*7c478bd9Sstevel@tonic-gate  *	everything under that tree.  The variable ignore_level
488*7c478bd9Sstevel@tonic-gate  *	is set to a level, below which, everything should be
489*7c478bd9Sstevel@tonic-gate  *	ignored.  Once the enumeration rises above that level
490*7c478bd9Sstevel@tonic-gate  *	again, we clear it.
491*7c478bd9Sstevel@tonic-gate  */
492*7c478bd9Sstevel@tonic-gate static int
walker(const char * name,const struct stat * sp,int type,struct FTW * ftwx)493*7c478bd9Sstevel@tonic-gate walker(const char *name, const struct stat *sp, int type,
494*7c478bd9Sstevel@tonic-gate 		struct FTW *ftwx)
495*7c478bd9Sstevel@tonic-gate {	const char *path;
496*7c478bd9Sstevel@tonic-gate 	struct file *fp;
497*7c478bd9Sstevel@tonic-gate 	int level;
498*7c478bd9Sstevel@tonic-gate 	int which;
499*7c478bd9Sstevel@tonic-gate 	bool_t restr;
500*7c478bd9Sstevel@tonic-gate 	static struct file *dirstack[ MAX_DEPTH + 1 ];
501*7c478bd9Sstevel@tonic-gate 	static int ignore_level = 0;
502*7c478bd9Sstevel@tonic-gate 
503*7c478bd9Sstevel@tonic-gate 	path = &name[ftwx->base];
504*7c478bd9Sstevel@tonic-gate 	level = ftwx->level;
505*7c478bd9Sstevel@tonic-gate 	which = usingsrc ? OPT_SRC : OPT_DST;
506*7c478bd9Sstevel@tonic-gate 
507*7c478bd9Sstevel@tonic-gate 	/*
508*7c478bd9Sstevel@tonic-gate 	 * see if we are ignoring all files in this sub-tree
509*7c478bd9Sstevel@tonic-gate 	 */
510*7c478bd9Sstevel@tonic-gate 	if (ignore_level > 0 && level >= ignore_level) {
511*7c478bd9Sstevel@tonic-gate 		if (opt_debug & DBG_EVAL)
512*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "EVAL: SKIP file=%s\n", name);
513*7c478bd9Sstevel@tonic-gate 		return (0);
514*7c478bd9Sstevel@tonic-gate 	} else
515*7c478bd9Sstevel@tonic-gate 		ignore_level = 0;	/* we're through ignoring	*/
516*7c478bd9Sstevel@tonic-gate 
517*7c478bd9Sstevel@tonic-gate #ifdef 	DBG_ERRORS
518*7c478bd9Sstevel@tonic-gate 	/* see if we should simulated a stat error on this file	*/
519*7c478bd9Sstevel@tonic-gate 	if (opt_errors && dbg_chk_error(name, usingsrc ? 'n' : 'N'))
520*7c478bd9Sstevel@tonic-gate 		type = FTW_NS;
521*7c478bd9Sstevel@tonic-gate #endif
522*7c478bd9Sstevel@tonic-gate 
523*7c478bd9Sstevel@tonic-gate 	switch (type) {
524*7c478bd9Sstevel@tonic-gate 	case FTW_F:	/* file 		*/
525*7c478bd9Sstevel@tonic-gate 	case FTW_SL:	/* symbolic link	*/
526*7c478bd9Sstevel@tonic-gate 		/*
527*7c478bd9Sstevel@tonic-gate 		 * filter out files of inappropriate types
528*7c478bd9Sstevel@tonic-gate 		 */
529*7c478bd9Sstevel@tonic-gate 		switch (sp->st_mode & S_IFMT) {
530*7c478bd9Sstevel@tonic-gate 			default:	/* anything else we ignore	*/
531*7c478bd9Sstevel@tonic-gate 				return (0);
532*7c478bd9Sstevel@tonic-gate 
533*7c478bd9Sstevel@tonic-gate 			case S_IFCHR:
534*7c478bd9Sstevel@tonic-gate 			case S_IFBLK:
535*7c478bd9Sstevel@tonic-gate 			case S_IFREG:
536*7c478bd9Sstevel@tonic-gate 			case S_IFLNK:
537*7c478bd9Sstevel@tonic-gate 				if (opt_debug & DBG_EVAL)
538*7c478bd9Sstevel@tonic-gate 					fprintf(stderr,
539*7c478bd9Sstevel@tonic-gate 						"EVAL: WALK lvl=%d, file=%s\n",
540*7c478bd9Sstevel@tonic-gate 						level, path);
541*7c478bd9Sstevel@tonic-gate 
542*7c478bd9Sstevel@tonic-gate 				/* see if we were told to ignore this one */
543*7c478bd9Sstevel@tonic-gate 				if (ignore_check(path))
544*7c478bd9Sstevel@tonic-gate 					return (0);
545*7c478bd9Sstevel@tonic-gate 
546*7c478bd9Sstevel@tonic-gate 				fp = add_file_to_dir(dirstack[level-1], path);
547*7c478bd9Sstevel@tonic-gate 				note_info(fp, sp, which);
548*7c478bd9Sstevel@tonic-gate 
549*7c478bd9Sstevel@tonic-gate 				/* note that this file has been evaluated */
550*7c478bd9Sstevel@tonic-gate 				fp->f_flags |= F_EVALUATE;
551*7c478bd9Sstevel@tonic-gate 
552*7c478bd9Sstevel@tonic-gate 				/* see if we should check ACLs		*/
553*7c478bd9Sstevel@tonic-gate 				if ((sp->st_mode & S_IFMT) == S_IFLNK)
554*7c478bd9Sstevel@tonic-gate 					return (0);
555*7c478bd9Sstevel@tonic-gate 
556*7c478bd9Sstevel@tonic-gate 				if (fp->f_info[OPT_BASE].f_numacls || opt_acls)
557*7c478bd9Sstevel@tonic-gate 					(void) get_acls(name,
558*7c478bd9Sstevel@tonic-gate 							&fp->f_info[which]);
559*7c478bd9Sstevel@tonic-gate 
560*7c478bd9Sstevel@tonic-gate 				return (0);
561*7c478bd9Sstevel@tonic-gate 		}
562*7c478bd9Sstevel@tonic-gate 
563*7c478bd9Sstevel@tonic-gate 	case FTW_D:	/* enter directory 		*/
564*7c478bd9Sstevel@tonic-gate 		if (opt_debug & DBG_EVAL)
565*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "EVAL: WALK lvl=%d, dir=%s\n",
566*7c478bd9Sstevel@tonic-gate 				level, name);
567*7c478bd9Sstevel@tonic-gate 
568*7c478bd9Sstevel@tonic-gate 		/*
569*7c478bd9Sstevel@tonic-gate 		 * if we have been told to ignore this directory, we should
570*7c478bd9Sstevel@tonic-gate 		 * ignore all files under it.  Similarly, if we are outside
571*7c478bd9Sstevel@tonic-gate 		 * of our restrictions, we should ignore the entire subtree
572*7c478bd9Sstevel@tonic-gate 		 */
573*7c478bd9Sstevel@tonic-gate 		restr = check_restr(cur_base, name);
574*7c478bd9Sstevel@tonic-gate 		if (restr == FALSE || ignore_check(path)) {
575*7c478bd9Sstevel@tonic-gate 			ignore_level = level + 1;
576*7c478bd9Sstevel@tonic-gate 			return (0);
577*7c478bd9Sstevel@tonic-gate 		}
578*7c478bd9Sstevel@tonic-gate 
579*7c478bd9Sstevel@tonic-gate 		fp = (level == 0) ?  cur_dir :
580*7c478bd9Sstevel@tonic-gate 		    add_file_to_dir(dirstack[level-1], path);
581*7c478bd9Sstevel@tonic-gate 
582*7c478bd9Sstevel@tonic-gate 		note_info(fp, sp, which);
583*7c478bd9Sstevel@tonic-gate 
584*7c478bd9Sstevel@tonic-gate 		/* see if we should be checking ACLs	*/
585*7c478bd9Sstevel@tonic-gate 		if (opt_acls || fp->f_info[OPT_BASE].f_numacls)
586*7c478bd9Sstevel@tonic-gate 			(void) get_acls(name, &fp->f_info[which]);
587*7c478bd9Sstevel@tonic-gate 
588*7c478bd9Sstevel@tonic-gate 		/* note that this file has been evaluated */
589*7c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_EVALUATE;
590*7c478bd9Sstevel@tonic-gate 
591*7c478bd9Sstevel@tonic-gate 		/* note the parent of the children to come	*/
592*7c478bd9Sstevel@tonic-gate 		dirstack[ level ] = fp;
593*7c478bd9Sstevel@tonic-gate 
594*7c478bd9Sstevel@tonic-gate 		/*
595*7c478bd9Sstevel@tonic-gate 		 * PROBLEM: given the information that nftw provides us with,
596*7c478bd9Sstevel@tonic-gate 		 *	    how do we know that we have confirmed the fact
597*7c478bd9Sstevel@tonic-gate 		 *	    that a file no longer exists.  Or to rephrase
598*7c478bd9Sstevel@tonic-gate 		 *	    this in filesync terms, how do we know when to
599*7c478bd9Sstevel@tonic-gate 		 *	    set the EVALUATE flag for a file we didn't find.
600*7c478bd9Sstevel@tonic-gate 		 *
601*7c478bd9Sstevel@tonic-gate 		 * if we are going to fully scan this directory (we
602*7c478bd9Sstevel@tonic-gate 		 * are completely within our restrictions) then we
603*7c478bd9Sstevel@tonic-gate 		 * will be confirming the non-existance of files that
604*7c478bd9Sstevel@tonic-gate 		 * used to be here.  Thus any file that was in the
605*7c478bd9Sstevel@tonic-gate 		 * base line under this directory should be considered
606*7c478bd9Sstevel@tonic-gate 		 * to have been evaluated (whether we found it or not).
607*7c478bd9Sstevel@tonic-gate 		 *
608*7c478bd9Sstevel@tonic-gate 		 * if, however, we are only willing to scan selected
609*7c478bd9Sstevel@tonic-gate 		 * files (due to restrictions), or the file was not
610*7c478bd9Sstevel@tonic-gate 		 * in the baseline, then we should not assume that this
611*7c478bd9Sstevel@tonic-gate 		 * pass will evaluate it.
612*7c478bd9Sstevel@tonic-gate 		 */
613*7c478bd9Sstevel@tonic-gate 		if (restr == TRUE)
614*7c478bd9Sstevel@tonic-gate 			for (fp = fp->f_files; fp; fp = fp->f_next) {
615*7c478bd9Sstevel@tonic-gate 				if ((fp->f_flags & F_IN_BASELINE) == 0)
616*7c478bd9Sstevel@tonic-gate 					continue;
617*7c478bd9Sstevel@tonic-gate 				fp->f_flags |= F_EVALUATE;
618*7c478bd9Sstevel@tonic-gate 			}
619*7c478bd9Sstevel@tonic-gate 
620*7c478bd9Sstevel@tonic-gate 		return (0);
621*7c478bd9Sstevel@tonic-gate 
622*7c478bd9Sstevel@tonic-gate 	case FTW_DP:	/* end of directory	*/
623*7c478bd9Sstevel@tonic-gate 		dirstack[ level ] = 0;
624*7c478bd9Sstevel@tonic-gate 		break;
625*7c478bd9Sstevel@tonic-gate 
626*7c478bd9Sstevel@tonic-gate 	case FTW_DNR:	/* unreadable directory	*/
627*7c478bd9Sstevel@tonic-gate 		walk_errs |= ERR_PERM;
628*7c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH	*/
629*7c478bd9Sstevel@tonic-gate 	case FTW_NS:	/* unstatable file	*/
630*7c478bd9Sstevel@tonic-gate 		if (opt_debug & DBG_EVAL)
631*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "EVAL: walker can't stat/read %s\n",
632*7c478bd9Sstevel@tonic-gate 				name);
633*7c478bd9Sstevel@tonic-gate 		fp = (level == 0) ?  cur_dir :
634*7c478bd9Sstevel@tonic-gate 			add_file_to_dir(dirstack[level-1], path);
635*7c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_STAT_ERROR;
636*7c478bd9Sstevel@tonic-gate 		walk_errs |= ERR_UNRESOLVED;
637*7c478bd9Sstevel@tonic-gate 		break;
638*7c478bd9Sstevel@tonic-gate 	}
639*7c478bd9Sstevel@tonic-gate 
640*7c478bd9Sstevel@tonic-gate 	return (0);
641*7c478bd9Sstevel@tonic-gate }
642*7c478bd9Sstevel@tonic-gate 
643*7c478bd9Sstevel@tonic-gate /*
644*7c478bd9Sstevel@tonic-gate  * routine:
645*7c478bd9Sstevel@tonic-gate  *	note_info
646*7c478bd9Sstevel@tonic-gate  *
647*7c478bd9Sstevel@tonic-gate  * purpose:
648*7c478bd9Sstevel@tonic-gate  * 	to record information about a file in its file node
649*7c478bd9Sstevel@tonic-gate  *
650*7c478bd9Sstevel@tonic-gate  * parameters
651*7c478bd9Sstevel@tonic-gate  *	file node pointer
652*7c478bd9Sstevel@tonic-gate  *	stat buffer
653*7c478bd9Sstevel@tonic-gate  *	which file info structure to fill in (0-2)
654*7c478bd9Sstevel@tonic-gate  *
655*7c478bd9Sstevel@tonic-gate  * returns
656*7c478bd9Sstevel@tonic-gate  *	void
657*7c478bd9Sstevel@tonic-gate  */
658*7c478bd9Sstevel@tonic-gate void
note_info(struct file * fp,const struct stat * sp,side_t which)659*7c478bd9Sstevel@tonic-gate note_info(struct file *fp, const struct stat *sp, side_t which)
660*7c478bd9Sstevel@tonic-gate {	struct fileinfo *ip;
661*7c478bd9Sstevel@tonic-gate 	static int flags[3] = { F_IN_BASELINE, F_IN_SOURCE, F_IN_DEST };
662*7c478bd9Sstevel@tonic-gate 
663*7c478bd9Sstevel@tonic-gate 	ip = &fp->f_info[ which ];
664*7c478bd9Sstevel@tonic-gate 
665*7c478bd9Sstevel@tonic-gate 	ip->f_ino	= sp->st_ino;
666*7c478bd9Sstevel@tonic-gate 	ip->f_d_maj	= major(sp->st_dev);
667*7c478bd9Sstevel@tonic-gate 	ip->f_d_min	= minor(sp->st_dev);
668*7c478bd9Sstevel@tonic-gate 	ip->f_type	= sp->st_mode & S_IFMT;
669*7c478bd9Sstevel@tonic-gate 	ip->f_size	= sp->st_size;
670*7c478bd9Sstevel@tonic-gate 	ip->f_mode	= sp->st_mode & S_IAMB;
671*7c478bd9Sstevel@tonic-gate 	ip->f_uid	= sp->st_uid;
672*7c478bd9Sstevel@tonic-gate 	ip->f_gid	= sp->st_gid;
673*7c478bd9Sstevel@tonic-gate 	ip->f_modtime	= sp->st_mtim.tv_sec;
674*7c478bd9Sstevel@tonic-gate 	ip->f_modns	= sp->st_mtim.tv_nsec;
675*7c478bd9Sstevel@tonic-gate 	ip->f_nlink	= sp->st_nlink;
676*7c478bd9Sstevel@tonic-gate 	ip->f_rd_maj	= major(sp->st_rdev);
677*7c478bd9Sstevel@tonic-gate 	ip->f_rd_min	= minor(sp->st_rdev);
678*7c478bd9Sstevel@tonic-gate 
679*7c478bd9Sstevel@tonic-gate 	/* indicate where this file has been found	*/
680*7c478bd9Sstevel@tonic-gate 	fp->f_flags |= flags[which];
681*7c478bd9Sstevel@tonic-gate 
682*7c478bd9Sstevel@tonic-gate 	if (opt_debug & DBG_STAT)
683*7c478bd9Sstevel@tonic-gate 		fprintf(stderr,
684*7c478bd9Sstevel@tonic-gate 			"STAT: list=%d, file=%s, mod=%08lx.%08lx, nacl=%d\n",
685*7c478bd9Sstevel@tonic-gate 			which, fp->f_name, ip->f_modtime, ip->f_modns,
686*7c478bd9Sstevel@tonic-gate 			ip->f_numacls);
687*7c478bd9Sstevel@tonic-gate }
688*7c478bd9Sstevel@tonic-gate 
689*7c478bd9Sstevel@tonic-gate /*
690*7c478bd9Sstevel@tonic-gate  * routine:
691*7c478bd9Sstevel@tonic-gate  *	do_update
692*7c478bd9Sstevel@tonic-gate  *
693*7c478bd9Sstevel@tonic-gate  * purpose:
694*7c478bd9Sstevel@tonic-gate  * 	to copy information from one side into the baseline in order
695*7c478bd9Sstevel@tonic-gate  *	to reflect the effects of recent reconciliation actions
696*7c478bd9Sstevel@tonic-gate  *
697*7c478bd9Sstevel@tonic-gate  * parameters
698*7c478bd9Sstevel@tonic-gate  *	fileinfo structure to be updated
699*7c478bd9Sstevel@tonic-gate  *	fileinfo structure to be updated from
700*7c478bd9Sstevel@tonic-gate  *
701*7c478bd9Sstevel@tonic-gate  * returns
702*7c478bd9Sstevel@tonic-gate  *	void
703*7c478bd9Sstevel@tonic-gate  *
704*7c478bd9Sstevel@tonic-gate  * note:
705*7c478bd9Sstevel@tonic-gate  *	we play fast and loose with the copying of acl chains
706*7c478bd9Sstevel@tonic-gate  *	here, but noone is going to free or reuse any of this
707*7c478bd9Sstevel@tonic-gate  * 	memory anyway.  None the less, I do feel embarassed.
708*7c478bd9Sstevel@tonic-gate  */
709*7c478bd9Sstevel@tonic-gate static void
do_update(struct fileinfo * np,struct fileinfo * ip)710*7c478bd9Sstevel@tonic-gate do_update(struct fileinfo *np, struct fileinfo *ip)
711*7c478bd9Sstevel@tonic-gate {
712*7c478bd9Sstevel@tonic-gate 	/* get most of the fields from the designated "right" copy */
713*7c478bd9Sstevel@tonic-gate 	np->f_type	= ip->f_type;
714*7c478bd9Sstevel@tonic-gate 	np->f_size	= ip->f_size;
715*7c478bd9Sstevel@tonic-gate 	np->f_mode	= ip->f_mode;
716*7c478bd9Sstevel@tonic-gate 	np->f_uid	= ip->f_uid;
717*7c478bd9Sstevel@tonic-gate 	np->f_gid	= ip->f_gid;
718*7c478bd9Sstevel@tonic-gate 	np->f_rd_maj	= ip->f_rd_maj;
719*7c478bd9Sstevel@tonic-gate 	np->f_rd_min	= ip->f_rd_min;
720*7c478bd9Sstevel@tonic-gate 
721*7c478bd9Sstevel@tonic-gate 	/* see if facls have to be propagated	*/
722*7c478bd9Sstevel@tonic-gate 	np->f_numacls = ip->f_numacls;
723*7c478bd9Sstevel@tonic-gate 	np->f_acls = ip->f_acls;
724*7c478bd9Sstevel@tonic-gate }
725*7c478bd9Sstevel@tonic-gate 
726*7c478bd9Sstevel@tonic-gate /*
727*7c478bd9Sstevel@tonic-gate  * routine:
728*7c478bd9Sstevel@tonic-gate  *	update_info
729*7c478bd9Sstevel@tonic-gate  *
730*7c478bd9Sstevel@tonic-gate  * purpose:
731*7c478bd9Sstevel@tonic-gate  * 	to update the baseline to reflect recent reconcliations
732*7c478bd9Sstevel@tonic-gate  *
733*7c478bd9Sstevel@tonic-gate  * parameters
734*7c478bd9Sstevel@tonic-gate  *	file node pointer
735*7c478bd9Sstevel@tonic-gate  *	which file info structure to trust (1/2)
736*7c478bd9Sstevel@tonic-gate  *
737*7c478bd9Sstevel@tonic-gate  * returns
738*7c478bd9Sstevel@tonic-gate  *	void
739*7c478bd9Sstevel@tonic-gate  *
740*7c478bd9Sstevel@tonic-gate  * note:
741*7c478bd9Sstevel@tonic-gate  *	after we update this I-node we run down the entire
742*7c478bd9Sstevel@tonic-gate  *	change list looking for links and update them too.
743*7c478bd9Sstevel@tonic-gate  *	This is to ensure that when subsequent links get
744*7c478bd9Sstevel@tonic-gate  *	reconciled, they are already found to be up-to-date.
745*7c478bd9Sstevel@tonic-gate  */
746*7c478bd9Sstevel@tonic-gate void
update_info(struct file * fp,side_t which)747*7c478bd9Sstevel@tonic-gate update_info(struct file *fp, side_t which)
748*7c478bd9Sstevel@tonic-gate {
749*7c478bd9Sstevel@tonic-gate 	/* first update the specified fileinfo structure	*/
750*7c478bd9Sstevel@tonic-gate 	do_update(&fp->f_info[ OPT_BASE ], &fp->f_info[ which ]);
751*7c478bd9Sstevel@tonic-gate 
752*7c478bd9Sstevel@tonic-gate 	if (opt_debug & DBG_STAT)
753*7c478bd9Sstevel@tonic-gate 		fprintf(stderr,
754*7c478bd9Sstevel@tonic-gate 			"STAT: UPDATE from=%d, file=%s, mod=%08lx.%08lx\n",
755*7c478bd9Sstevel@tonic-gate 			which, fp->f_name, fp->f_info[ which ].f_modtime,
756*7c478bd9Sstevel@tonic-gate 			fp->f_info[ which ].f_modns);
757*7c478bd9Sstevel@tonic-gate }
758*7c478bd9Sstevel@tonic-gate 
759*7c478bd9Sstevel@tonic-gate /*
760*7c478bd9Sstevel@tonic-gate  * routine:
761*7c478bd9Sstevel@tonic-gate  *	fakedata
762*7c478bd9Sstevel@tonic-gate  *
763*7c478bd9Sstevel@tonic-gate  * purpose:
764*7c478bd9Sstevel@tonic-gate  *	to populate a tree we cannot analyze with information from the baseline
765*7c478bd9Sstevel@tonic-gate  *
766*7c478bd9Sstevel@tonic-gate  * parameters:
767*7c478bd9Sstevel@tonic-gate  *	file to be faked
768*7c478bd9Sstevel@tonic-gate  *	which side to fake
769*7c478bd9Sstevel@tonic-gate  *
770*7c478bd9Sstevel@tonic-gate  * notes:
771*7c478bd9Sstevel@tonic-gate  *	We would never use this for real reconciliation, but it is useful
772*7c478bd9Sstevel@tonic-gate  *	if a disconnected notebook user wants to find out what has been
773*7c478bd9Sstevel@tonic-gate  *	changed so far.  We only do this if we are notouch and oneway.
774*7c478bd9Sstevel@tonic-gate  */
775*7c478bd9Sstevel@tonic-gate static void
fakedata(struct file * fp,int which)776*7c478bd9Sstevel@tonic-gate fakedata(struct file *fp, int which)
777*7c478bd9Sstevel@tonic-gate {	struct file *lp;
778*7c478bd9Sstevel@tonic-gate 
779*7c478bd9Sstevel@tonic-gate 	/* pretend we actually found the file			*/
780*7c478bd9Sstevel@tonic-gate 	fp->f_flags |= (which == OPT_SRC) ? F_IN_SOURCE : F_IN_DEST;
781*7c478bd9Sstevel@tonic-gate 
782*7c478bd9Sstevel@tonic-gate 	/* update the specified side from the baseline		*/
783*7c478bd9Sstevel@tonic-gate 	do_update(&fp->f_info[ which ], &fp->f_info[ OPT_BASE ]);
784*7c478bd9Sstevel@tonic-gate 	fp->f_info[which].f_nlink = (which == OPT_SRC) ? fp->f_s_nlink :
785*7c478bd9Sstevel@tonic-gate 							fp->f_d_nlink;
786*7c478bd9Sstevel@tonic-gate 	fp->f_info[which].f_modtime = (which == OPT_SRC) ? fp->f_s_modtime :
787*7c478bd9Sstevel@tonic-gate 							fp->f_d_modtime;
788*7c478bd9Sstevel@tonic-gate 
789*7c478bd9Sstevel@tonic-gate 	for (lp = fp->f_files; lp; lp = lp->f_next)
790*7c478bd9Sstevel@tonic-gate 		fakedata(lp, which);
791*7c478bd9Sstevel@tonic-gate }
792*7c478bd9Sstevel@tonic-gate 
793*7c478bd9Sstevel@tonic-gate /*
794*7c478bd9Sstevel@tonic-gate  * routine:
795*7c478bd9Sstevel@tonic-gate  *	check_inum
796*7c478bd9Sstevel@tonic-gate  *
797*7c478bd9Sstevel@tonic-gate  * purpose:
798*7c478bd9Sstevel@tonic-gate  *	sanity check inode #s on directories that are unlikely to change
799*7c478bd9Sstevel@tonic-gate  *
800*7c478bd9Sstevel@tonic-gate  * parameters:
801*7c478bd9Sstevel@tonic-gate  *	pointer to file node
802*7c478bd9Sstevel@tonic-gate  *	are we using the source
803*7c478bd9Sstevel@tonic-gate  *
804*7c478bd9Sstevel@tonic-gate  * note:
805*7c478bd9Sstevel@tonic-gate  *	the purpose of this sanity check is to catch a case where we
806*7c478bd9Sstevel@tonic-gate  *	have somehow been pointed at a directory that is not the one
807*7c478bd9Sstevel@tonic-gate  *	we expected to be reconciling against.  It could happen if a
808*7c478bd9Sstevel@tonic-gate  *	variable wasn't properly set, or if we were in a new domain
809*7c478bd9Sstevel@tonic-gate  *	where an old path no longer worked.  This could result in
810*7c478bd9Sstevel@tonic-gate  *	bazillions of inappropriate propagations and deletions.
811*7c478bd9Sstevel@tonic-gate  */
812*7c478bd9Sstevel@tonic-gate void
check_inum(struct file * fp,int src)813*7c478bd9Sstevel@tonic-gate check_inum(struct file *fp, int src)
814*7c478bd9Sstevel@tonic-gate {	struct fileinfo *ip;
815*7c478bd9Sstevel@tonic-gate 
816*7c478bd9Sstevel@tonic-gate 	/*
817*7c478bd9Sstevel@tonic-gate 	 * we validate the inode number and the major device numbers ... minor
818*7c478bd9Sstevel@tonic-gate 	 * device numbers for NFS devices are arbitrary
819*7c478bd9Sstevel@tonic-gate 	 */
820*7c478bd9Sstevel@tonic-gate 	if (src) {
821*7c478bd9Sstevel@tonic-gate 		ip = &fp->f_info[ OPT_SRC ];
822*7c478bd9Sstevel@tonic-gate 		if (ip->f_ino == fp->f_s_inum && ip->f_d_maj == fp->f_s_maj)
823*7c478bd9Sstevel@tonic-gate 			return;
824*7c478bd9Sstevel@tonic-gate 
825*7c478bd9Sstevel@tonic-gate 		/* if file was newly created/deleted, this isn't warnable */
826*7c478bd9Sstevel@tonic-gate 		if (fp->f_s_inum == 0 || ip->f_ino == 0)
827*7c478bd9Sstevel@tonic-gate 			return;
828*7c478bd9Sstevel@tonic-gate 
829*7c478bd9Sstevel@tonic-gate 		if (opt_verbose)
830*7c478bd9Sstevel@tonic-gate 			fprintf(stdout, V_change, fp->f_name, TXT_src,
831*7c478bd9Sstevel@tonic-gate 				fp->f_s_maj, fp->f_s_min, fp->f_s_inum,
832*7c478bd9Sstevel@tonic-gate 				ip->f_d_maj, ip->f_d_min, ip->f_ino);
833*7c478bd9Sstevel@tonic-gate 	} else {
834*7c478bd9Sstevel@tonic-gate 		ip = &fp->f_info[ OPT_DST ];
835*7c478bd9Sstevel@tonic-gate 		if (ip->f_ino == fp->f_d_inum && ip->f_d_maj == fp->f_d_maj)
836*7c478bd9Sstevel@tonic-gate 			return;
837*7c478bd9Sstevel@tonic-gate 
838*7c478bd9Sstevel@tonic-gate 		/* if file was newly created/deleted, this isn't warnable */
839*7c478bd9Sstevel@tonic-gate 		if (fp->f_d_inum == 0 || ip->f_ino == 0)
840*7c478bd9Sstevel@tonic-gate 			return;
841*7c478bd9Sstevel@tonic-gate 
842*7c478bd9Sstevel@tonic-gate 		if (opt_verbose)
843*7c478bd9Sstevel@tonic-gate 			fprintf(stdout, V_change, fp->f_name, TXT_dst,
844*7c478bd9Sstevel@tonic-gate 				fp->f_d_maj, fp->f_d_min, fp->f_d_inum,
845*7c478bd9Sstevel@tonic-gate 				ip->f_d_maj, ip->f_d_min, ip->f_ino);
846*7c478bd9Sstevel@tonic-gate 	}
847*7c478bd9Sstevel@tonic-gate 
848*7c478bd9Sstevel@tonic-gate 	/* note that something has changed	*/
849*7c478bd9Sstevel@tonic-gate 	inum_changes++;
850*7c478bd9Sstevel@tonic-gate }
851*7c478bd9Sstevel@tonic-gate 
852*7c478bd9Sstevel@tonic-gate /*
853*7c478bd9Sstevel@tonic-gate  * routine:
854*7c478bd9Sstevel@tonic-gate  *	add_glob
855*7c478bd9Sstevel@tonic-gate  *
856*7c478bd9Sstevel@tonic-gate  * purpose:
857*7c478bd9Sstevel@tonic-gate  *	to evaluate a wild-carded expression into names, and add them
858*7c478bd9Sstevel@tonic-gate  *	to the evaluation list.
859*7c478bd9Sstevel@tonic-gate  *
860*7c478bd9Sstevel@tonic-gate  * parameters:
861*7c478bd9Sstevel@tonic-gate  *	base
862*7c478bd9Sstevel@tonic-gate  *	expression
863*7c478bd9Sstevel@tonic-gate  *
864*7c478bd9Sstevel@tonic-gate  * returns:
865*7c478bd9Sstevel@tonic-gate  * 	error mask
866*7c478bd9Sstevel@tonic-gate  *
867*7c478bd9Sstevel@tonic-gate  * notes:
868*7c478bd9Sstevel@tonic-gate  *	we don't want to allow any patterns to expand to a . because
869*7c478bd9Sstevel@tonic-gate  *	that could result in re-evaluation of a tree under a different
870*7c478bd9Sstevel@tonic-gate  *	name.  The real thing we are worried about here is ".*" which
871*7c478bd9Sstevel@tonic-gate  *	is meant to pick up . files, but shouldn't pick up . and ..
872*7c478bd9Sstevel@tonic-gate  */
873*7c478bd9Sstevel@tonic-gate static errmask_t
add_glob(struct base * bp,char * expr)874*7c478bd9Sstevel@tonic-gate add_glob(struct base *bp, char *expr)
875*7c478bd9Sstevel@tonic-gate {	int i;
876*7c478bd9Sstevel@tonic-gate 	errmask_t errs = 0;
877*7c478bd9Sstevel@tonic-gate #ifndef BROKEN_GLOB
878*7c478bd9Sstevel@tonic-gate 	glob_t gt;
879*7c478bd9Sstevel@tonic-gate 	char *s;
880*7c478bd9Sstevel@tonic-gate 
881*7c478bd9Sstevel@tonic-gate 	/* expand the regular expression	*/
882*7c478bd9Sstevel@tonic-gate 	i = glob(expr, GLOB_NOSORT, 0, &gt);
883*7c478bd9Sstevel@tonic-gate 	if (i == GLOB_NOMATCH)
884*7c478bd9Sstevel@tonic-gate 		return (ERR_MISSING);
885*7c478bd9Sstevel@tonic-gate 	if (i) {
886*7c478bd9Sstevel@tonic-gate 		/* this shouldn't happen, so it's cryptic message time	*/
887*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, "EVAL: add_glob globfail expr=%s, ret=%d\n",
888*7c478bd9Sstevel@tonic-gate 				expr, i);
889*7c478bd9Sstevel@tonic-gate 		return (ERR_OTHER);
890*7c478bd9Sstevel@tonic-gate 	}
891*7c478bd9Sstevel@tonic-gate 
892*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < gt.gl_pathc; i++) {
893*7c478bd9Sstevel@tonic-gate 		/* make sure we don't let anything expand to a . */
894*7c478bd9Sstevel@tonic-gate 		s = basename(gt.gl_pathv[i]);
895*7c478bd9Sstevel@tonic-gate 		if (strcmp(s, ".") == 0) {
896*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, gettext(WARN_ignore), gt.gl_pathv[i]);
897*7c478bd9Sstevel@tonic-gate 			errs |= ERR_MISSING;
898*7c478bd9Sstevel@tonic-gate 			continue;
899*7c478bd9Sstevel@tonic-gate 		}
900*7c478bd9Sstevel@tonic-gate 
901*7c478bd9Sstevel@tonic-gate 		errs |= add_file_arg(bp, gt.gl_pathv[i]);
902*7c478bd9Sstevel@tonic-gate 	}
903*7c478bd9Sstevel@tonic-gate 
904*7c478bd9Sstevel@tonic-gate 	globfree(&gt);
905*7c478bd9Sstevel@tonic-gate #else
906*7c478bd9Sstevel@tonic-gate 	/*
907*7c478bd9Sstevel@tonic-gate 	 * in 2.4 the glob function was completely broken.  The
908*7c478bd9Sstevel@tonic-gate 	 * easiest way to get around this problem is to just ask
909*7c478bd9Sstevel@tonic-gate 	 * the shell to do the work for us.  This is much slower
910*7c478bd9Sstevel@tonic-gate 	 * but produces virtually identical results.  Given that
911*7c478bd9Sstevel@tonic-gate 	 * the 2.4 version is internal use only, I probably won't
912*7c478bd9Sstevel@tonic-gate 	 * worry about the performance difference (less than 2
913*7c478bd9Sstevel@tonic-gate 	 * seconds for a typical filesync command, and no hit
914*7c478bd9Sstevel@tonic-gate 	 * at all if they don't use regular expressions in
915*7c478bd9Sstevel@tonic-gate 	 * their LIST rules).
916*7c478bd9Sstevel@tonic-gate 	 */
917*7c478bd9Sstevel@tonic-gate 	char cmdbuf[MAX_LINE];
918*7c478bd9Sstevel@tonic-gate 
919*7c478bd9Sstevel@tonic-gate 	sprintf(cmdbuf, "ls -d %s 2> /dev/null", expr);
920*7c478bd9Sstevel@tonic-gate 	errs |= add_run(bp, cmdbuf);
921*7c478bd9Sstevel@tonic-gate #endif
922*7c478bd9Sstevel@tonic-gate 
923*7c478bd9Sstevel@tonic-gate 	return (errs);
924*7c478bd9Sstevel@tonic-gate }
925*7c478bd9Sstevel@tonic-gate 
926*7c478bd9Sstevel@tonic-gate 
927*7c478bd9Sstevel@tonic-gate /*
928*7c478bd9Sstevel@tonic-gate  * routine:
929*7c478bd9Sstevel@tonic-gate  *	add_run
930*7c478bd9Sstevel@tonic-gate  *
931*7c478bd9Sstevel@tonic-gate  * purpose:
932*7c478bd9Sstevel@tonic-gate  *	to run a command and capture the names it outputs in the
933*7c478bd9Sstevel@tonic-gate  *	evaluation list.
934*7c478bd9Sstevel@tonic-gate  *
935*7c478bd9Sstevel@tonic-gate  * parameters
936*7c478bd9Sstevel@tonic-gate  *	base
937*7c478bd9Sstevel@tonic-gate  *	command
938*7c478bd9Sstevel@tonic-gate  *
939*7c478bd9Sstevel@tonic-gate  * returns:
940*7c478bd9Sstevel@tonic-gate  *	error mask
941*7c478bd9Sstevel@tonic-gate  */
942*7c478bd9Sstevel@tonic-gate static errmask_t
add_run(struct base * bp,char * cmd)943*7c478bd9Sstevel@tonic-gate add_run(struct base *bp, char *cmd)
944*7c478bd9Sstevel@tonic-gate {	char *s, *p;
945*7c478bd9Sstevel@tonic-gate 	FILE *fp;
946*7c478bd9Sstevel@tonic-gate 	char inbuf[ MAX_LINE ];
947*7c478bd9Sstevel@tonic-gate 	errmask_t errs = 0;
948*7c478bd9Sstevel@tonic-gate 	int added = 0;
949*7c478bd9Sstevel@tonic-gate 
950*7c478bd9Sstevel@tonic-gate 	if (opt_debug & DBG_EVAL)
951*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, "EVAL: RUN %s\n", cmd);
952*7c478bd9Sstevel@tonic-gate 
953*7c478bd9Sstevel@tonic-gate 	/* run the command and collect its ouput	*/
954*7c478bd9Sstevel@tonic-gate 	fp = popen(cmd, "r");
955*7c478bd9Sstevel@tonic-gate 	if (fp == NULL) {
956*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, gettext(ERR_badrun), cmd);
957*7c478bd9Sstevel@tonic-gate 		return (ERR_OTHER);
958*7c478bd9Sstevel@tonic-gate 	}
959*7c478bd9Sstevel@tonic-gate 
960*7c478bd9Sstevel@tonic-gate 	while (fgets(inbuf, sizeof (inbuf), fp) != 0) {
961*7c478bd9Sstevel@tonic-gate 		/* strip off any trailing newline	*/
962*7c478bd9Sstevel@tonic-gate 		for (s = inbuf; *s && *s != '\n'; s++);
963*7c478bd9Sstevel@tonic-gate 		*s = 0;
964*7c478bd9Sstevel@tonic-gate 
965*7c478bd9Sstevel@tonic-gate 		/* skip any leading white space		*/
966*7c478bd9Sstevel@tonic-gate 		for (s = inbuf; *s == ' ' || *s == '\t'; s++);
967*7c478bd9Sstevel@tonic-gate 
968*7c478bd9Sstevel@tonic-gate 		/* make sure we don't let anything expand to a . */
969*7c478bd9Sstevel@tonic-gate 		p = basename(s);
970*7c478bd9Sstevel@tonic-gate 		if (strcmp(p, ".") == 0) {
971*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, gettext(WARN_ignore), s);
972*7c478bd9Sstevel@tonic-gate 			errs |= ERR_MISSING;
973*7c478bd9Sstevel@tonic-gate 			continue;
974*7c478bd9Sstevel@tonic-gate 		}
975*7c478bd9Sstevel@tonic-gate 
976*7c478bd9Sstevel@tonic-gate 		/* add this file to the list		*/
977*7c478bd9Sstevel@tonic-gate 		if (*s) {
978*7c478bd9Sstevel@tonic-gate 			errs |= add_file_arg(bp, s);
979*7c478bd9Sstevel@tonic-gate 			added++;
980*7c478bd9Sstevel@tonic-gate 		}
981*7c478bd9Sstevel@tonic-gate 	}
982*7c478bd9Sstevel@tonic-gate 
983*7c478bd9Sstevel@tonic-gate 	pclose(fp);
984*7c478bd9Sstevel@tonic-gate 
985*7c478bd9Sstevel@tonic-gate #ifdef	BROKEN_GLOB
986*7c478bd9Sstevel@tonic-gate 	/*
987*7c478bd9Sstevel@tonic-gate 	 * if we are being used to simulate libc glob, and we didn't
988*7c478bd9Sstevel@tonic-gate 	 * return anything, we should probably assume that the regex
989*7c478bd9Sstevel@tonic-gate 	 * was unable to match anything
990*7c478bd9Sstevel@tonic-gate 	 */
991*7c478bd9Sstevel@tonic-gate 	if (added == 0)
992*7c478bd9Sstevel@tonic-gate 		errs |= ERR_MISSING;
993*7c478bd9Sstevel@tonic-gate #endif
994*7c478bd9Sstevel@tonic-gate 	return (errs);
995*7c478bd9Sstevel@tonic-gate }
996