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