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 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 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 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 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 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 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 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 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 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 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 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