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