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 (c) 1995 Sun Microsystems, Inc. All Rights Reserved 24*7c478bd9Sstevel@tonic-gate * 25*7c478bd9Sstevel@tonic-gate * module: 26*7c478bd9Sstevel@tonic-gate * anal.c 27*7c478bd9Sstevel@tonic-gate * 28*7c478bd9Sstevel@tonic-gate * purpose: 29*7c478bd9Sstevel@tonic-gate * routines to analyze the file trees and figure out what has changed 30*7c478bd9Sstevel@tonic-gate * and queue files for reconciliation. It also contains tree enumeration 31*7c478bd9Sstevel@tonic-gate * routines to for other purposes (pruning and link location). 32*7c478bd9Sstevel@tonic-gate * 33*7c478bd9Sstevel@tonic-gate * contents: 34*7c478bd9Sstevel@tonic-gate * 35*7c478bd9Sstevel@tonic-gate * change analysis: 36*7c478bd9Sstevel@tonic-gate * analyze .... (top level) analyze all files in the tree for changes 37*7c478bd9Sstevel@tonic-gate * summary .... print out change/reconciliation statistics for each base 38*7c478bd9Sstevel@tonic-gate * check_file . (static) look for changes and queue file for reconciliation 39*7c478bd9Sstevel@tonic-gate * check_changes (static) figure out if a particular file has changed 40*7c478bd9Sstevel@tonic-gate * queue_file . (static) add a file to the reconciliation list 41*7c478bd9Sstevel@tonic-gate * 42*7c478bd9Sstevel@tonic-gate * other tree enumeration functions: 43*7c478bd9Sstevel@tonic-gate * prune_file . (static) recursive descent and actual pruning 44*7c478bd9Sstevel@tonic-gate * prune ...... (top level) initiate pruning analysis for nonexistant files 45*7c478bd9Sstevel@tonic-gate * find_link .. look for other files to which a file may be a link 46*7c478bd9Sstevel@tonic-gate * link_update. propagate changed stat info to all other links 47*7c478bd9Sstevel@tonic-gate * same_name .. (static) figure out if two nodes describe same file 48*7c478bd9Sstevel@tonic-gate * 49*7c478bd9Sstevel@tonic-gate * misc: 50*7c478bd9Sstevel@tonic-gate * push_name .. maintain a running full pathname as we descend 51*7c478bd9Sstevel@tonic-gate * pop_name ... maintain a running full pathname as we pop back 52*7c478bd9Sstevel@tonic-gate * get_name ... return full pathname for the current file 53*7c478bd9Sstevel@tonic-gate * 54*7c478bd9Sstevel@tonic-gate * notes: 55*7c478bd9Sstevel@tonic-gate * analysis is limited to files that were evaluated in the previous 56*7c478bd9Sstevel@tonic-gate * pass ... since we don't have complete information about files that 57*7c478bd9Sstevel@tonic-gate * were not evaluated in the previous pass. 58*7c478bd9Sstevel@tonic-gate */ 59*7c478bd9Sstevel@tonic-gate #ident "%W% %E% SMI" 60*7c478bd9Sstevel@tonic-gate 61*7c478bd9Sstevel@tonic-gate #include <stdio.h> 62*7c478bd9Sstevel@tonic-gate #include <stdlib.h> 63*7c478bd9Sstevel@tonic-gate #include <strings.h> 64*7c478bd9Sstevel@tonic-gate 65*7c478bd9Sstevel@tonic-gate #include "messages.h" 66*7c478bd9Sstevel@tonic-gate #include "filesync.h" 67*7c478bd9Sstevel@tonic-gate #include "database.h" 68*7c478bd9Sstevel@tonic-gate #include "debug.h" 69*7c478bd9Sstevel@tonic-gate 70*7c478bd9Sstevel@tonic-gate /* 71*7c478bd9Sstevel@tonic-gate * routines: 72*7c478bd9Sstevel@tonic-gate */ 73*7c478bd9Sstevel@tonic-gate void push_name(const char *); 74*7c478bd9Sstevel@tonic-gate void pop_name(); 75*7c478bd9Sstevel@tonic-gate char *get_name(struct file *); 76*7c478bd9Sstevel@tonic-gate static errmask_t check_file(struct file *fp); 77*7c478bd9Sstevel@tonic-gate static diffmask_t check_changes(struct file *fp, int first, int second); 78*7c478bd9Sstevel@tonic-gate static int prune_file(struct file *fp); 79*7c478bd9Sstevel@tonic-gate static void queue_file(struct file *fp); 80*7c478bd9Sstevel@tonic-gate 81*7c478bd9Sstevel@tonic-gate /* 82*7c478bd9Sstevel@tonic-gate * globals 83*7c478bd9Sstevel@tonic-gate */ 84*7c478bd9Sstevel@tonic-gate static struct file *changes; /* list of files to be reconciled */ 85*7c478bd9Sstevel@tonic-gate 86*7c478bd9Sstevel@tonic-gate static long total_files; /* total number of files being considered */ 87*7c478bd9Sstevel@tonic-gate static long est_deletes; /* estimated number of files to be deleted */ 88*7c478bd9Sstevel@tonic-gate static long est_rmdirs; /* est rmdirs of non-empty directories */ 89*7c478bd9Sstevel@tonic-gate 90*7c478bd9Sstevel@tonic-gate int inum_changes; /* LISTed directories whose I#s changed */ 91*7c478bd9Sstevel@tonic-gate 92*7c478bd9Sstevel@tonic-gate /* 93*7c478bd9Sstevel@tonic-gate * routine: 94*7c478bd9Sstevel@tonic-gate * analyze 95*7c478bd9Sstevel@tonic-gate * 96*7c478bd9Sstevel@tonic-gate * purpose: 97*7c478bd9Sstevel@tonic-gate * top level routine for the analysis/reconciliation process 98*7c478bd9Sstevel@tonic-gate * 99*7c478bd9Sstevel@tonic-gate * parameters: 100*7c478bd9Sstevel@tonic-gate * none 101*7c478bd9Sstevel@tonic-gate * 102*7c478bd9Sstevel@tonic-gate * returns: 103*7c478bd9Sstevel@tonic-gate * error mask 104*7c478bd9Sstevel@tonic-gate * 105*7c478bd9Sstevel@tonic-gate * notes: 106*7c478bd9Sstevel@tonic-gate * a critical side effect of this routine is the creation of 107*7c478bd9Sstevel@tonic-gate * the reconciliation list, an ordered list of files that 108*7c478bd9Sstevel@tonic-gate * needed to be processed in the subsequent reconciliation pass 109*7c478bd9Sstevel@tonic-gate */ 110*7c478bd9Sstevel@tonic-gate errmask_t 111*7c478bd9Sstevel@tonic-gate analyze() 112*7c478bd9Sstevel@tonic-gate { struct base *bp; 113*7c478bd9Sstevel@tonic-gate struct file *fp; 114*7c478bd9Sstevel@tonic-gate int errs = 0; 115*7c478bd9Sstevel@tonic-gate int err; 116*7c478bd9Sstevel@tonic-gate int percentage; 117*7c478bd9Sstevel@tonic-gate bool_t aborted = FALSE; 118*7c478bd9Sstevel@tonic-gate char msgbuf[MAX_LINE]; 119*7c478bd9Sstevel@tonic-gate 120*7c478bd9Sstevel@tonic-gate /* 121*7c478bd9Sstevel@tonic-gate * run through all bases and directories looking for files 122*7c478bd9Sstevel@tonic-gate * that have been renamed. This must be done before the 123*7c478bd9Sstevel@tonic-gate * difference analysis because a directory rename can introduce 124*7c478bd9Sstevel@tonic-gate * radical restructuring into a name-based tree. 125*7c478bd9Sstevel@tonic-gate */ 126*7c478bd9Sstevel@tonic-gate for (bp = bases; bp; bp = bp->b_next) { 127*7c478bd9Sstevel@tonic-gate for (fp = bp->b_files; fp; fp = fp->f_next) 128*7c478bd9Sstevel@tonic-gate if (fp->f_flags & F_EVALUATE) 129*7c478bd9Sstevel@tonic-gate errs |= find_renames(fp); 130*7c478bd9Sstevel@tonic-gate } 131*7c478bd9Sstevel@tonic-gate 132*7c478bd9Sstevel@tonic-gate /* 133*7c478bd9Sstevel@tonic-gate * run through all bases and files looking for candidates 134*7c478bd9Sstevel@tonic-gate * note, however that we only descend into trees that have 135*7c478bd9Sstevel@tonic-gate * the evaluate flag turned on. As a result of new rules or 136*7c478bd9Sstevel@tonic-gate * restriction arguments, we may be deliberatly ignoring 137*7c478bd9Sstevel@tonic-gate * large amounts of the baseline. This means we won't do 138*7c478bd9Sstevel@tonic-gate * any stats to update the information in those nodes, and 139*7c478bd9Sstevel@tonic-gate * they will be written back just as they were. 140*7c478bd9Sstevel@tonic-gate * 141*7c478bd9Sstevel@tonic-gate * note that there is code to prune out baseline nodes for 142*7c478bd9Sstevel@tonic-gate * files that no longer exist, but that code is in reconcile 143*7c478bd9Sstevel@tonic-gate * and will never get a chance to run on nodes that aren't 144*7c478bd9Sstevel@tonic-gate * analyzed. 145*7c478bd9Sstevel@tonic-gate * 146*7c478bd9Sstevel@tonic-gate * we also want to run though all nodes with STAT errors 147*7c478bd9Sstevel@tonic-gate * so that we can put them on the reconciliation list. 148*7c478bd9Sstevel@tonic-gate */ 149*7c478bd9Sstevel@tonic-gate for (bp = bases; bp; bp = bp->b_next) { 150*7c478bd9Sstevel@tonic-gate for (fp = bp->b_files; fp; fp = fp->f_next) 151*7c478bd9Sstevel@tonic-gate if (fp->f_flags & (F_EVALUATE|F_STAT_ERROR)) 152*7c478bd9Sstevel@tonic-gate errs |= check_file(fp); 153*7c478bd9Sstevel@tonic-gate } 154*7c478bd9Sstevel@tonic-gate 155*7c478bd9Sstevel@tonic-gate /* 156*7c478bd9Sstevel@tonic-gate * my greatest fear is that someday, somehow, by messing with 157*7c478bd9Sstevel@tonic-gate * variables or baselines or who-knows-what, that someone will 158*7c478bd9Sstevel@tonic-gate * run a reconciliation against a large tree that doesn't correspond 159*7c478bd9Sstevel@tonic-gate * to the baseline, and I will infer that a bazillion files have 160*7c478bd9Sstevel@tonic-gate * been deleted and will propagate the slaughter before anyone 161*7c478bd9Sstevel@tonic-gate * can say somebody stop that maniac. 162*7c478bd9Sstevel@tonic-gate * 163*7c478bd9Sstevel@tonic-gate * in order to prevent such a possibility, we have a few different 164*7c478bd9Sstevel@tonic-gate * sanity checks. There is, of course, a tradeoff here between 165*7c478bd9Sstevel@tonic-gate * danger and irritation. The current set of heuristics for whether 166*7c478bd9Sstevel@tonic-gate * or not to generate a warning are (any of) 167*7c478bd9Sstevel@tonic-gate * 168*7c478bd9Sstevel@tonic-gate * at least CONFIRM_MIN files have been deleted AND 169*7c478bd9Sstevel@tonic-gate * CONFIRM_PCT of all files have been deleted 170*7c478bd9Sstevel@tonic-gate * 171*7c478bd9Sstevel@tonic-gate * the inode number on a LISTed directory has changed 172*7c478bd9Sstevel@tonic-gate * 173*7c478bd9Sstevel@tonic-gate * a non-empty directory has been deleted. 174*7c478bd9Sstevel@tonic-gate */ 175*7c478bd9Sstevel@tonic-gate msgbuf[0] = 0; 176*7c478bd9Sstevel@tonic-gate 177*7c478bd9Sstevel@tonic-gate percentage = (est_deletes * 100) / (total_files ? total_files : 1); 178*7c478bd9Sstevel@tonic-gate if (est_deletes >= CONFIRM_MIN && percentage >= CONFIRM_PCT) 179*7c478bd9Sstevel@tonic-gate sprintf(msgbuf, gettext(WARN_deletes), est_deletes); 180*7c478bd9Sstevel@tonic-gate else if (inum_changes > 0) 181*7c478bd9Sstevel@tonic-gate sprintf(msgbuf, gettext(WARN_ichange), inum_changes); 182*7c478bd9Sstevel@tonic-gate else if (est_rmdirs) 183*7c478bd9Sstevel@tonic-gate sprintf(msgbuf, gettext(WARN_rmdirs), est_rmdirs); 184*7c478bd9Sstevel@tonic-gate 185*7c478bd9Sstevel@tonic-gate if (msgbuf[0]) 186*7c478bd9Sstevel@tonic-gate confirm(msgbuf); 187*7c478bd9Sstevel@tonic-gate 188*7c478bd9Sstevel@tonic-gate /* 189*7c478bd9Sstevel@tonic-gate * TRICK: 190*7c478bd9Sstevel@tonic-gate * the change list contains both files that have changed 191*7c478bd9Sstevel@tonic-gate * (and probably warrant reconciliation) and files that 192*7c478bd9Sstevel@tonic-gate * we couldn't get up-to-date stat information on. The 193*7c478bd9Sstevel@tonic-gate * latter files should just be flagged as being in conflict 194*7c478bd9Sstevel@tonic-gate * so they can be reported in the summary. The same is 195*7c478bd9Sstevel@tonic-gate * true of all subsequent files if we abort reconciliation. 196*7c478bd9Sstevel@tonic-gate */ 197*7c478bd9Sstevel@tonic-gate for (fp = changes; fp; fp = fp->f_rnext) 198*7c478bd9Sstevel@tonic-gate if (aborted || (fp->f_flags & F_STAT_ERROR)) { 199*7c478bd9Sstevel@tonic-gate fp->f_flags |= F_CONFLICT; 200*7c478bd9Sstevel@tonic-gate /* if it isn't in the baseline yet, don't add it */ 201*7c478bd9Sstevel@tonic-gate if ((fp->f_flags & F_IN_BASELINE) == 0) 202*7c478bd9Sstevel@tonic-gate fp->f_flags |= F_REMOVE; 203*7c478bd9Sstevel@tonic-gate fp->f_problem = aborted ? PROB_aborted : PROB_restat; 204*7c478bd9Sstevel@tonic-gate (fp->f_base)->b_unresolved++; 205*7c478bd9Sstevel@tonic-gate errs |= ERR_UNRESOLVED; 206*7c478bd9Sstevel@tonic-gate if (opt_verbose) 207*7c478bd9Sstevel@tonic-gate fprintf(stdout, 208*7c478bd9Sstevel@tonic-gate gettext(aborted ? V_suppressed 209*7c478bd9Sstevel@tonic-gate : V_nostat), 210*7c478bd9Sstevel@tonic-gate fp->f_fullname); 211*7c478bd9Sstevel@tonic-gate } else { 212*7c478bd9Sstevel@tonic-gate err = reconcile(fp); 213*7c478bd9Sstevel@tonic-gate errs |= err; 214*7c478bd9Sstevel@tonic-gate if (opt_halt && (err & ERR_ABORT)) { 215*7c478bd9Sstevel@tonic-gate fprintf(stderr, gettext(ERR_abort_h)); 216*7c478bd9Sstevel@tonic-gate aborted = TRUE; 217*7c478bd9Sstevel@tonic-gate } 218*7c478bd9Sstevel@tonic-gate } 219*7c478bd9Sstevel@tonic-gate 220*7c478bd9Sstevel@tonic-gate return (errs); 221*7c478bd9Sstevel@tonic-gate } 222*7c478bd9Sstevel@tonic-gate 223*7c478bd9Sstevel@tonic-gate /* 224*7c478bd9Sstevel@tonic-gate * routine: 225*7c478bd9Sstevel@tonic-gate * prune_file 226*7c478bd9Sstevel@tonic-gate * 227*7c478bd9Sstevel@tonic-gate * purpose: 228*7c478bd9Sstevel@tonic-gate * to look for file entries that should be pruned from baseline 229*7c478bd9Sstevel@tonic-gate * prune the current file if it needs pruning, and recursively 230*7c478bd9Sstevel@tonic-gate * descend if it is a directory. 231*7c478bd9Sstevel@tonic-gate * 232*7c478bd9Sstevel@tonic-gate * parameters: 233*7c478bd9Sstevel@tonic-gate * pointer to file node 234*7c478bd9Sstevel@tonic-gate */ 235*7c478bd9Sstevel@tonic-gate static int 236*7c478bd9Sstevel@tonic-gate prune_file(struct file *fp) 237*7c478bd9Sstevel@tonic-gate { struct file *cp; 238*7c478bd9Sstevel@tonic-gate int prunes = 0; 239*7c478bd9Sstevel@tonic-gate 240*7c478bd9Sstevel@tonic-gate /* if node hasn't been evaluated, mark it for removal */ 241*7c478bd9Sstevel@tonic-gate if ((fp->f_flags & (F_EVALUATE|F_STAT_ERROR)) == 0) { 242*7c478bd9Sstevel@tonic-gate fp->f_flags |= F_REMOVE; 243*7c478bd9Sstevel@tonic-gate prunes++; 244*7c478bd9Sstevel@tonic-gate if (opt_debug & DBG_ANAL) 245*7c478bd9Sstevel@tonic-gate fprintf(stderr, "ANAL: PRUNE %s\n", fp->f_name); 246*7c478bd9Sstevel@tonic-gate } 247*7c478bd9Sstevel@tonic-gate 248*7c478bd9Sstevel@tonic-gate /* now check our children */ 249*7c478bd9Sstevel@tonic-gate for (cp = fp->f_files; cp; cp = cp->f_next) 250*7c478bd9Sstevel@tonic-gate prunes += prune_file(cp); 251*7c478bd9Sstevel@tonic-gate 252*7c478bd9Sstevel@tonic-gate return (prunes); 253*7c478bd9Sstevel@tonic-gate } 254*7c478bd9Sstevel@tonic-gate 255*7c478bd9Sstevel@tonic-gate /* 256*7c478bd9Sstevel@tonic-gate * routine: 257*7c478bd9Sstevel@tonic-gate * prune 258*7c478bd9Sstevel@tonic-gate * 259*7c478bd9Sstevel@tonic-gate * purpose: 260*7c478bd9Sstevel@tonic-gate * to prune the baseline of entries that no longer correspond to 261*7c478bd9Sstevel@tonic-gate * existing rules. 262*7c478bd9Sstevel@tonic-gate * 263*7c478bd9Sstevel@tonic-gate * notes: 264*7c478bd9Sstevel@tonic-gate * This routine just calls prune_file on the top of each base tree. 265*7c478bd9Sstevel@tonic-gate */ 266*7c478bd9Sstevel@tonic-gate int 267*7c478bd9Sstevel@tonic-gate prune() 268*7c478bd9Sstevel@tonic-gate { struct base *bp; 269*7c478bd9Sstevel@tonic-gate struct file *fp; 270*7c478bd9Sstevel@tonic-gate int prunes = 0; 271*7c478bd9Sstevel@tonic-gate 272*7c478bd9Sstevel@tonic-gate for (bp = bases; bp; bp = bp->b_next) { 273*7c478bd9Sstevel@tonic-gate for (fp = bp->b_files; fp; fp = fp->f_next) 274*7c478bd9Sstevel@tonic-gate prunes += prune_file(fp); 275*7c478bd9Sstevel@tonic-gate 276*7c478bd9Sstevel@tonic-gate if ((bp->b_flags & F_EVALUATE) == 0) 277*7c478bd9Sstevel@tonic-gate bp->b_flags |= F_REMOVE; 278*7c478bd9Sstevel@tonic-gate } 279*7c478bd9Sstevel@tonic-gate 280*7c478bd9Sstevel@tonic-gate return (prunes); 281*7c478bd9Sstevel@tonic-gate } 282*7c478bd9Sstevel@tonic-gate 283*7c478bd9Sstevel@tonic-gate /* 284*7c478bd9Sstevel@tonic-gate * routine: 285*7c478bd9Sstevel@tonic-gate * summary 286*7c478bd9Sstevel@tonic-gate * 287*7c478bd9Sstevel@tonic-gate * purpose: 288*7c478bd9Sstevel@tonic-gate * to print out statics and conflict lists 289*7c478bd9Sstevel@tonic-gate */ 290*7c478bd9Sstevel@tonic-gate void 291*7c478bd9Sstevel@tonic-gate summary() 292*7c478bd9Sstevel@tonic-gate { struct base *bp; 293*7c478bd9Sstevel@tonic-gate struct file *fp; 294*7c478bd9Sstevel@tonic-gate extern bool_t need_super; 295*7c478bd9Sstevel@tonic-gate 296*7c478bd9Sstevel@tonic-gate (void) fflush(stdout); 297*7c478bd9Sstevel@tonic-gate 298*7c478bd9Sstevel@tonic-gate for (bp = bases; bp; bp = bp->b_next) { 299*7c478bd9Sstevel@tonic-gate 300*7c478bd9Sstevel@tonic-gate /* see if this base was irrelevent */ 301*7c478bd9Sstevel@tonic-gate if ((bp->b_flags & F_EVALUATE) == 0) 302*7c478bd9Sstevel@tonic-gate continue; 303*7c478bd9Sstevel@tonic-gate 304*7c478bd9Sstevel@tonic-gate /* print out a summary for this base */ 305*7c478bd9Sstevel@tonic-gate fprintf(stderr, gettext(SUM_hd), 306*7c478bd9Sstevel@tonic-gate bp->b_src_spec, bp->b_dst_spec, bp->b_totfiles); 307*7c478bd9Sstevel@tonic-gate fprintf(stderr, gettext(SUM_dst), 308*7c478bd9Sstevel@tonic-gate bp->b_dst_copies, bp->b_dst_deletes, bp->b_dst_misc); 309*7c478bd9Sstevel@tonic-gate fprintf(stderr, gettext(SUM_src), 310*7c478bd9Sstevel@tonic-gate bp->b_src_copies, bp->b_src_deletes, bp->b_src_misc); 311*7c478bd9Sstevel@tonic-gate if (bp->b_unresolved) 312*7c478bd9Sstevel@tonic-gate fprintf(stderr, gettext(SUM_unresolved), 313*7c478bd9Sstevel@tonic-gate bp->b_unresolved); 314*7c478bd9Sstevel@tonic-gate 315*7c478bd9Sstevel@tonic-gate 316*7c478bd9Sstevel@tonic-gate /* print out a list of unreconciled files for this base */ 317*7c478bd9Sstevel@tonic-gate for (fp = changes; fp; fp = fp->f_rnext) { 318*7c478bd9Sstevel@tonic-gate if (fp->f_base != bp) 319*7c478bd9Sstevel@tonic-gate continue; 320*7c478bd9Sstevel@tonic-gate if ((fp->f_flags & F_CONFLICT) == 0) 321*7c478bd9Sstevel@tonic-gate continue; 322*7c478bd9Sstevel@tonic-gate fprintf(stderr, "\t\t%s (%s)\n", fp->f_fullname, 323*7c478bd9Sstevel@tonic-gate fp->f_problem ? fp->f_problem : "???"); 324*7c478bd9Sstevel@tonic-gate } 325*7c478bd9Sstevel@tonic-gate 326*7c478bd9Sstevel@tonic-gate fprintf(stderr, "\n"); 327*7c478bd9Sstevel@tonic-gate } 328*7c478bd9Sstevel@tonic-gate 329*7c478bd9Sstevel@tonic-gate if (need_super) 330*7c478bd9Sstevel@tonic-gate fprintf(stderr, gettext(WARN_super)); 331*7c478bd9Sstevel@tonic-gate } 332*7c478bd9Sstevel@tonic-gate 333*7c478bd9Sstevel@tonic-gate /* 334*7c478bd9Sstevel@tonic-gate * routine: 335*7c478bd9Sstevel@tonic-gate * check_file 336*7c478bd9Sstevel@tonic-gate * 337*7c478bd9Sstevel@tonic-gate * purpose: 338*7c478bd9Sstevel@tonic-gate * figure out if a file requires reconciliation and recursively 339*7c478bd9Sstevel@tonic-gate * descend into all sub-files and directories 340*7c478bd9Sstevel@tonic-gate * 341*7c478bd9Sstevel@tonic-gate * parameters: 342*7c478bd9Sstevel@tonic-gate * base pointer 343*7c478bd9Sstevel@tonic-gate * file pointer 344*7c478bd9Sstevel@tonic-gate * 345*7c478bd9Sstevel@tonic-gate * returns: 346*7c478bd9Sstevel@tonic-gate * error mask 347*7c478bd9Sstevel@tonic-gate * built up changes needed list 348*7c478bd9Sstevel@tonic-gate * updated statistics 349*7c478bd9Sstevel@tonic-gate * 350*7c478bd9Sstevel@tonic-gate * notes: 351*7c478bd9Sstevel@tonic-gate * this routine builds up a path name as it descends through 352*7c478bd9Sstevel@tonic-gate * the tree (see push_name, pop_name, get_name). 353*7c478bd9Sstevel@tonic-gate */ 354*7c478bd9Sstevel@tonic-gate static errmask_t 355*7c478bd9Sstevel@tonic-gate check_file(struct file *fp) 356*7c478bd9Sstevel@tonic-gate { struct file *cp; 357*7c478bd9Sstevel@tonic-gate int errs = 0; 358*7c478bd9Sstevel@tonic-gate 359*7c478bd9Sstevel@tonic-gate if ((fp->f_flags & F_STAT_ERROR) == 0) { 360*7c478bd9Sstevel@tonic-gate /* see if the source has changed */ 361*7c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_modtime = fp->f_s_modtime; 362*7c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_ino = fp->f_s_inum; 363*7c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_d_maj = fp->f_s_maj; 364*7c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_d_min = fp->f_s_min; 365*7c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_nlink = fp->f_s_nlink; 366*7c478bd9Sstevel@tonic-gate fp->f_srcdiffs |= check_changes(fp, OPT_BASE, OPT_SRC); 367*7c478bd9Sstevel@tonic-gate 368*7c478bd9Sstevel@tonic-gate /* see if the destination has changed */ 369*7c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_modtime = fp->f_d_modtime; 370*7c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_ino = fp->f_d_inum; 371*7c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_d_maj = fp->f_d_maj; 372*7c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_d_min = fp->f_d_min; 373*7c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_nlink = fp->f_d_nlink; 374*7c478bd9Sstevel@tonic-gate fp->f_dstdiffs |= check_changes(fp, OPT_BASE, OPT_DST); 375*7c478bd9Sstevel@tonic-gate 376*7c478bd9Sstevel@tonic-gate /* if nobody thinks the file exists, baseline needs pruning */ 377*7c478bd9Sstevel@tonic-gate if ((fp->f_flags & (F_IN_SOURCE|F_IN_DEST)) == 0) { 378*7c478bd9Sstevel@tonic-gate fp->f_srcdiffs |= D_DELETE; 379*7c478bd9Sstevel@tonic-gate fp->f_dstdiffs |= D_DELETE; 380*7c478bd9Sstevel@tonic-gate } 381*7c478bd9Sstevel@tonic-gate 382*7c478bd9Sstevel@tonic-gate /* keep track of possible deletions to look for trouble */ 383*7c478bd9Sstevel@tonic-gate if ((fp->f_dstdiffs | fp->f_srcdiffs) & D_DELETE) { 384*7c478bd9Sstevel@tonic-gate est_deletes++; 385*7c478bd9Sstevel@tonic-gate 386*7c478bd9Sstevel@tonic-gate /* see if file is (or has been) a non-empty directory */ 387*7c478bd9Sstevel@tonic-gate if (fp->f_files) 388*7c478bd9Sstevel@tonic-gate est_rmdirs++; 389*7c478bd9Sstevel@tonic-gate } 390*7c478bd9Sstevel@tonic-gate } 391*7c478bd9Sstevel@tonic-gate 392*7c478bd9Sstevel@tonic-gate /* if we found differences, queue the file for reconciliation */ 393*7c478bd9Sstevel@tonic-gate if (fp->f_srcdiffs || fp->f_dstdiffs || fp->f_flags & F_STAT_ERROR) { 394*7c478bd9Sstevel@tonic-gate queue_file(fp); 395*7c478bd9Sstevel@tonic-gate 396*7c478bd9Sstevel@tonic-gate if (opt_debug & DBG_ANAL) { 397*7c478bd9Sstevel@tonic-gate fprintf(stderr, "ANAL: src=%s", 398*7c478bd9Sstevel@tonic-gate showflags(diffmap, fp->f_srcdiffs)); 399*7c478bd9Sstevel@tonic-gate fprintf(stderr, " dst=%s", 400*7c478bd9Sstevel@tonic-gate showflags(diffmap, fp->f_dstdiffs)); 401*7c478bd9Sstevel@tonic-gate fprintf(stderr, " flgs=%s", 402*7c478bd9Sstevel@tonic-gate showflags(fileflags, fp->f_flags)); 403*7c478bd9Sstevel@tonic-gate fprintf(stderr, " name=%s\n", fp->f_fullname); 404*7c478bd9Sstevel@tonic-gate } 405*7c478bd9Sstevel@tonic-gate } 406*7c478bd9Sstevel@tonic-gate 407*7c478bd9Sstevel@tonic-gate /* bump the total file count */ 408*7c478bd9Sstevel@tonic-gate fp->f_base->b_totfiles++; 409*7c478bd9Sstevel@tonic-gate total_files++; 410*7c478bd9Sstevel@tonic-gate 411*7c478bd9Sstevel@tonic-gate /* if this is not a directory, we're done */ 412*7c478bd9Sstevel@tonic-gate if (fp->f_files == 0) 413*7c478bd9Sstevel@tonic-gate return (errs); 414*7c478bd9Sstevel@tonic-gate 415*7c478bd9Sstevel@tonic-gate /* 416*7c478bd9Sstevel@tonic-gate * If this is a directory, we need to recursively analyze 417*7c478bd9Sstevel@tonic-gate * our children, but only children who have been evaluated. 418*7c478bd9Sstevel@tonic-gate * If a node has not been evaluated, then we don't have 419*7c478bd9Sstevel@tonic-gate * updated stat information and there is nothing to analyze. 420*7c478bd9Sstevel@tonic-gate * 421*7c478bd9Sstevel@tonic-gate * we also want to run though all nodes with STAT errors 422*7c478bd9Sstevel@tonic-gate * so that we can put them on the reconciliation list. 423*7c478bd9Sstevel@tonic-gate * If a directory is unreadable on one side, all files 424*7c478bd9Sstevel@tonic-gate * under that directory (ON BOTH SIDES) must be marked as 425*7c478bd9Sstevel@tonic-gate * blocked by stat errors. 426*7c478bd9Sstevel@tonic-gate */ 427*7c478bd9Sstevel@tonic-gate push_name(fp->f_name); 428*7c478bd9Sstevel@tonic-gate 429*7c478bd9Sstevel@tonic-gate for (cp = fp->f_files; cp; cp = cp->f_next) { 430*7c478bd9Sstevel@tonic-gate if (fp->f_flags & F_STAT_ERROR) 431*7c478bd9Sstevel@tonic-gate cp->f_flags |= F_STAT_ERROR; 432*7c478bd9Sstevel@tonic-gate if (cp->f_flags & (F_EVALUATE|F_STAT_ERROR)) 433*7c478bd9Sstevel@tonic-gate errs |= check_file(cp); 434*7c478bd9Sstevel@tonic-gate } 435*7c478bd9Sstevel@tonic-gate 436*7c478bd9Sstevel@tonic-gate pop_name(); 437*7c478bd9Sstevel@tonic-gate 438*7c478bd9Sstevel@tonic-gate return (errs); 439*7c478bd9Sstevel@tonic-gate } 440*7c478bd9Sstevel@tonic-gate 441*7c478bd9Sstevel@tonic-gate /* 442*7c478bd9Sstevel@tonic-gate * routine: 443*7c478bd9Sstevel@tonic-gate * check_changes 444*7c478bd9Sstevel@tonic-gate * 445*7c478bd9Sstevel@tonic-gate * purpose: 446*7c478bd9Sstevel@tonic-gate * to figure out what has changed for a specific file 447*7c478bd9Sstevel@tonic-gate * 448*7c478bd9Sstevel@tonic-gate * parameters: 449*7c478bd9Sstevel@tonic-gate * file pointer 450*7c478bd9Sstevel@tonic-gate * the reference info 451*7c478bd9Sstevel@tonic-gate * the info to be checked for changes 452*7c478bd9Sstevel@tonic-gate * 453*7c478bd9Sstevel@tonic-gate * returns: 454*7c478bd9Sstevel@tonic-gate * diff mask 455*7c478bd9Sstevel@tonic-gate * 456*7c478bd9Sstevel@tonic-gate * notes: 457*7c478bd9Sstevel@tonic-gate * this routine doesn't pretend to understand what happened. 458*7c478bd9Sstevel@tonic-gate * it merely enumerates the ways in which the files differ. 459*7c478bd9Sstevel@tonic-gate */ 460*7c478bd9Sstevel@tonic-gate static diffmask_t 461*7c478bd9Sstevel@tonic-gate check_changes(struct file *fp, int ref, int new) 462*7c478bd9Sstevel@tonic-gate { struct fileinfo *rp, *np; 463*7c478bd9Sstevel@tonic-gate int mask = 0; 464*7c478bd9Sstevel@tonic-gate int type; 465*7c478bd9Sstevel@tonic-gate 466*7c478bd9Sstevel@tonic-gate rp = &fp->f_info[ref]; 467*7c478bd9Sstevel@tonic-gate np = &fp->f_info[new]; 468*7c478bd9Sstevel@tonic-gate 469*7c478bd9Sstevel@tonic-gate if (np->f_uid != rp->f_uid) 470*7c478bd9Sstevel@tonic-gate mask |= D_UID; 471*7c478bd9Sstevel@tonic-gate 472*7c478bd9Sstevel@tonic-gate if (np->f_gid != rp->f_gid) 473*7c478bd9Sstevel@tonic-gate mask |= D_GID; 474*7c478bd9Sstevel@tonic-gate 475*7c478bd9Sstevel@tonic-gate if (np->f_mode != rp->f_mode) 476*7c478bd9Sstevel@tonic-gate mask |= D_PROT; 477*7c478bd9Sstevel@tonic-gate 478*7c478bd9Sstevel@tonic-gate type = np->f_type; 479*7c478bd9Sstevel@tonic-gate if (type != rp->f_type) { 480*7c478bd9Sstevel@tonic-gate if (type == 0) 481*7c478bd9Sstevel@tonic-gate mask |= D_DELETE; 482*7c478bd9Sstevel@tonic-gate else if (rp->f_type == 0) 483*7c478bd9Sstevel@tonic-gate mask |= D_CREATE; 484*7c478bd9Sstevel@tonic-gate else 485*7c478bd9Sstevel@tonic-gate mask |= D_TYPE; 486*7c478bd9Sstevel@tonic-gate } else if (type == S_IFBLK || type == S_IFCHR) { 487*7c478bd9Sstevel@tonic-gate /* 488*7c478bd9Sstevel@tonic-gate * for special files, we only look at the maj/min 489*7c478bd9Sstevel@tonic-gate */ 490*7c478bd9Sstevel@tonic-gate if (np->f_rd_maj != rp->f_rd_maj) 491*7c478bd9Sstevel@tonic-gate mask |= D_SIZE; 492*7c478bd9Sstevel@tonic-gate if (np->f_rd_min != rp->f_rd_min) 493*7c478bd9Sstevel@tonic-gate mask |= D_SIZE; 494*7c478bd9Sstevel@tonic-gate } else if (type != S_IFDIR) { 495*7c478bd9Sstevel@tonic-gate /* 496*7c478bd9Sstevel@tonic-gate * for directories, we don't look directly at 497*7c478bd9Sstevel@tonic-gate * the contents, so these fields don't mean 498*7c478bd9Sstevel@tonic-gate * anything. If the directories have changed 499*7c478bd9Sstevel@tonic-gate * in any interesting way, we'll find it by 500*7c478bd9Sstevel@tonic-gate * walking the tree. 501*7c478bd9Sstevel@tonic-gate */ 502*7c478bd9Sstevel@tonic-gate if (np->f_modtime > rp->f_modtime) 503*7c478bd9Sstevel@tonic-gate mask |= D_MTIME; 504*7c478bd9Sstevel@tonic-gate 505*7c478bd9Sstevel@tonic-gate if (np->f_size != rp->f_size) 506*7c478bd9Sstevel@tonic-gate mask |= D_SIZE; 507*7c478bd9Sstevel@tonic-gate 508*7c478bd9Sstevel@tonic-gate if (np->f_nlink != rp->f_nlink) 509*7c478bd9Sstevel@tonic-gate mask |= D_LINKS; 510*7c478bd9Sstevel@tonic-gate } 511*7c478bd9Sstevel@tonic-gate 512*7c478bd9Sstevel@tonic-gate if (cmp_acls(rp, np) == 0) 513*7c478bd9Sstevel@tonic-gate mask |= D_FACLS; 514*7c478bd9Sstevel@tonic-gate 515*7c478bd9Sstevel@tonic-gate return (mask); 516*7c478bd9Sstevel@tonic-gate } 517*7c478bd9Sstevel@tonic-gate 518*7c478bd9Sstevel@tonic-gate /* 519*7c478bd9Sstevel@tonic-gate * routine: 520*7c478bd9Sstevel@tonic-gate * same_name 521*7c478bd9Sstevel@tonic-gate * 522*7c478bd9Sstevel@tonic-gate * purpose: 523*7c478bd9Sstevel@tonic-gate * to figure out whether or not two databsae nodes actually refer to 524*7c478bd9Sstevel@tonic-gate * the same file. 525*7c478bd9Sstevel@tonic-gate * 526*7c478bd9Sstevel@tonic-gate * parameters: 527*7c478bd9Sstevel@tonic-gate * pointers to two file description nodes 528*7c478bd9Sstevel@tonic-gate * which side we should check 529*7c478bd9Sstevel@tonic-gate * 530*7c478bd9Sstevel@tonic-gate * returns: 531*7c478bd9Sstevel@tonic-gate * TRUE/FALSE 532*7c478bd9Sstevel@tonic-gate * 533*7c478bd9Sstevel@tonic-gate * notes: 534*7c478bd9Sstevel@tonic-gate * if a single directory is specified in multiple base pairs, it 535*7c478bd9Sstevel@tonic-gate * is possible to have multiple nodes in the database describing 536*7c478bd9Sstevel@tonic-gate * the same file. This routine is supposed to detect those cases. 537*7c478bd9Sstevel@tonic-gate * 538*7c478bd9Sstevel@tonic-gate * what should be a trivial string comparison is complicated by 539*7c478bd9Sstevel@tonic-gate * the possibility that the two nodes might describe the same file 540*7c478bd9Sstevel@tonic-gate * from base directories at different depths. Thus, rather than 541*7c478bd9Sstevel@tonic-gate * comparing two strings, we really want to compare the concatenation 542*7c478bd9Sstevel@tonic-gate * of two pairs of strings. Unfortunately calling full_name would 543*7c478bd9Sstevel@tonic-gate * be awkward right now, so instead we have our own comparison 544*7c478bd9Sstevel@tonic-gate * routine that automatically skips from the first string to 545*7c478bd9Sstevel@tonic-gate * the second. 546*7c478bd9Sstevel@tonic-gate */ 547*7c478bd9Sstevel@tonic-gate static bool_t 548*7c478bd9Sstevel@tonic-gate same_name(struct file *f1, struct file *f2, side_t srcdst) 549*7c478bd9Sstevel@tonic-gate { 550*7c478bd9Sstevel@tonic-gate char *s1, *s2, *x1, *x2; 551*7c478bd9Sstevel@tonic-gate 552*7c478bd9Sstevel@tonic-gate if (srcdst == OPT_SRC) { 553*7c478bd9Sstevel@tonic-gate s1 = (f1->f_base)->b_src_name; 554*7c478bd9Sstevel@tonic-gate s2 = (f2->f_base)->b_src_name; 555*7c478bd9Sstevel@tonic-gate } else { 556*7c478bd9Sstevel@tonic-gate s1 = (f1->f_base)->b_dst_name; 557*7c478bd9Sstevel@tonic-gate s2 = (f2->f_base)->b_dst_name; 558*7c478bd9Sstevel@tonic-gate } 559*7c478bd9Sstevel@tonic-gate x1 = f1->f_fullname; 560*7c478bd9Sstevel@tonic-gate x2 = f2->f_fullname; 561*7c478bd9Sstevel@tonic-gate 562*7c478bd9Sstevel@tonic-gate /* 563*7c478bd9Sstevel@tonic-gate * Compare the two names, and if they differ before they end 564*7c478bd9Sstevel@tonic-gate * this is a non-match. If they both end at the same time, 565*7c478bd9Sstevel@tonic-gate * this is a match. 566*7c478bd9Sstevel@tonic-gate * 567*7c478bd9Sstevel@tonic-gate * The trick here is that each string is actually the logical 568*7c478bd9Sstevel@tonic-gate * concatenation of two strings, and we need to automatically 569*7c478bd9Sstevel@tonic-gate * wrap from the first to the second string in each pair. There 570*7c478bd9Sstevel@tonic-gate * is no requirement that the two (concatenated) strings be 571*7c478bd9Sstevel@tonic-gate * broken at the same point, so we have a slightly baroque 572*7c478bd9Sstevel@tonic-gate * comparsion loop. 573*7c478bd9Sstevel@tonic-gate */ 574*7c478bd9Sstevel@tonic-gate while (*s1 && *s1 == *s2) { 575*7c478bd9Sstevel@tonic-gate 576*7c478bd9Sstevel@tonic-gate /* 577*7c478bd9Sstevel@tonic-gate * strings have been identical so far, so advance the 578*7c478bd9Sstevel@tonic-gate * pointers and continue the comparison. The trick 579*7c478bd9Sstevel@tonic-gate * is that when either string ends, we have to wrap 580*7c478bd9Sstevel@tonic-gate * over to its extension. 581*7c478bd9Sstevel@tonic-gate */ 582*7c478bd9Sstevel@tonic-gate s1++; s2++; 583*7c478bd9Sstevel@tonic-gate if (*s1 && *s2) 584*7c478bd9Sstevel@tonic-gate continue; 585*7c478bd9Sstevel@tonic-gate 586*7c478bd9Sstevel@tonic-gate /* 587*7c478bd9Sstevel@tonic-gate * at least one of the strings has ended. 588*7c478bd9Sstevel@tonic-gate * there is an implicit slash between the string 589*7c478bd9Sstevel@tonic-gate * and its extension, and this has to be matched 590*7c478bd9Sstevel@tonic-gate * against the other string. 591*7c478bd9Sstevel@tonic-gate */ 592*7c478bd9Sstevel@tonic-gate if (*s1 != *s2) { 593*7c478bd9Sstevel@tonic-gate if (*s1 == 0 && *s2 == '/') 594*7c478bd9Sstevel@tonic-gate s2++; 595*7c478bd9Sstevel@tonic-gate else if (*s2 == 0 && *s1 == '/') 596*7c478bd9Sstevel@tonic-gate s1++; 597*7c478bd9Sstevel@tonic-gate else 598*7c478bd9Sstevel@tonic-gate /* the disagreement doesn't come at a slash */ 599*7c478bd9Sstevel@tonic-gate break; 600*7c478bd9Sstevel@tonic-gate } 601*7c478bd9Sstevel@tonic-gate 602*7c478bd9Sstevel@tonic-gate /* 603*7c478bd9Sstevel@tonic-gate * if either string has ended, wrap to its extension 604*7c478bd9Sstevel@tonic-gate */ 605*7c478bd9Sstevel@tonic-gate if (*s1 == 0 && x1 != 0) { 606*7c478bd9Sstevel@tonic-gate s1 = x1; 607*7c478bd9Sstevel@tonic-gate x1 = 0; 608*7c478bd9Sstevel@tonic-gate } 609*7c478bd9Sstevel@tonic-gate if (*s2 == 0 && x2 != 0) { 610*7c478bd9Sstevel@tonic-gate s2 = x2; 611*7c478bd9Sstevel@tonic-gate x2 = 0; 612*7c478bd9Sstevel@tonic-gate } 613*7c478bd9Sstevel@tonic-gate } 614*7c478bd9Sstevel@tonic-gate 615*7c478bd9Sstevel@tonic-gate return (*s1 == *s2); 616*7c478bd9Sstevel@tonic-gate } 617*7c478bd9Sstevel@tonic-gate 618*7c478bd9Sstevel@tonic-gate /* 619*7c478bd9Sstevel@tonic-gate * routine: 620*7c478bd9Sstevel@tonic-gate * find_link 621*7c478bd9Sstevel@tonic-gate * 622*7c478bd9Sstevel@tonic-gate * purpose: 623*7c478bd9Sstevel@tonic-gate * to figure out if there is a file to which we should 624*7c478bd9Sstevel@tonic-gate * be creating a link (rather than making a copy) 625*7c478bd9Sstevel@tonic-gate * 626*7c478bd9Sstevel@tonic-gate * parameters: 627*7c478bd9Sstevel@tonic-gate * file node for the file to be created (that we hope is merely a link) 628*7c478bd9Sstevel@tonic-gate * which side is to be changed (src/dst) 629*7c478bd9Sstevel@tonic-gate * 630*7c478bd9Sstevel@tonic-gate * return: 631*7c478bd9Sstevel@tonic-gate * 0 no link is appropriate 632*7c478bd9Sstevel@tonic-gate * else pointer to file node for link referent 633*7c478bd9Sstevel@tonic-gate * 634*7c478bd9Sstevel@tonic-gate * notes: 635*7c478bd9Sstevel@tonic-gate * there are a few strange heuristics in this routine and I 636*7c478bd9Sstevel@tonic-gate * wouldn't bet my soul that I got all of them right. The general 637*7c478bd9Sstevel@tonic-gate * theory is that when a new file is created, we look to see if it 638*7c478bd9Sstevel@tonic-gate * is a link to another file on the changed side, and if it is, we 639*7c478bd9Sstevel@tonic-gate * find the corresponding file on the unchanged side. 640*7c478bd9Sstevel@tonic-gate * 641*7c478bd9Sstevel@tonic-gate * cases we want to be able to handle: 642*7c478bd9Sstevel@tonic-gate * 1. one or more links are created to a prexisting file 643*7c478bd9Sstevel@tonic-gate * 2. a preexisting only link is renamed 644*7c478bd9Sstevel@tonic-gate * 3. a rename of one of multiple links to a preexisting file 645*7c478bd9Sstevel@tonic-gate * 4. a single file is created with multiple links 646*7c478bd9Sstevel@tonic-gate */ 647*7c478bd9Sstevel@tonic-gate struct file * 648*7c478bd9Sstevel@tonic-gate find_link(struct file *fp, side_t srcdst) 649*7c478bd9Sstevel@tonic-gate { struct file *lp; 650*7c478bd9Sstevel@tonic-gate side_t chgside, tgtside; 651*7c478bd9Sstevel@tonic-gate struct fileinfo *chgp, *tgtp, *basp, *fcp, *ftp; 652*7c478bd9Sstevel@tonic-gate 653*7c478bd9Sstevel@tonic-gate /* chg = side on which the change was noticed */ 654*7c478bd9Sstevel@tonic-gate /* tgt = side to which the change is to be propagated */ 655*7c478bd9Sstevel@tonic-gate chgside = (srcdst == OPT_SRC) ? OPT_DST : OPT_SRC; 656*7c478bd9Sstevel@tonic-gate tgtside = (srcdst == OPT_SRC) ? OPT_SRC : OPT_DST; 657*7c478bd9Sstevel@tonic-gate fcp = &fp->f_info[chgside]; 658*7c478bd9Sstevel@tonic-gate ftp = &fp->f_info[tgtside]; 659*7c478bd9Sstevel@tonic-gate 660*7c478bd9Sstevel@tonic-gate /* 661*7c478bd9Sstevel@tonic-gate * cases 1 and 3 662*7c478bd9Sstevel@tonic-gate * 663*7c478bd9Sstevel@tonic-gate * When a new link is created, we should be able to find 664*7c478bd9Sstevel@tonic-gate * another file in the changed hierarchy that has the same 665*7c478bd9Sstevel@tonic-gate * I-node number. We expect it to be on the changed list 666*7c478bd9Sstevel@tonic-gate * because the link count will have gone up or because all 667*7c478bd9Sstevel@tonic-gate * of the copies are new. If we find one, then the new file 668*7c478bd9Sstevel@tonic-gate * on the receiving file should be a link to the corresponding 669*7c478bd9Sstevel@tonic-gate * existing file. 670*7c478bd9Sstevel@tonic-gate * 671*7c478bd9Sstevel@tonic-gate * case 4 672*7c478bd9Sstevel@tonic-gate * 673*7c478bd9Sstevel@tonic-gate * the first link will be dealt with as a copy, but all 674*7c478bd9Sstevel@tonic-gate * subsequent links should find an existing file analogous 675*7c478bd9Sstevel@tonic-gate * to one of the links on the changed side, and create 676*7c478bd9Sstevel@tonic-gate * corresponding links on the other side. 677*7c478bd9Sstevel@tonic-gate * 678*7c478bd9Sstevel@tonic-gate * in each of these cases, there should be multiple links 679*7c478bd9Sstevel@tonic-gate * on the changed side. If the linkcount on the changed 680*7c478bd9Sstevel@tonic-gate * side is one, we needn't bother searching for other links. 681*7c478bd9Sstevel@tonic-gate */ 682*7c478bd9Sstevel@tonic-gate if (fcp->f_nlink > 1) 683*7c478bd9Sstevel@tonic-gate for (lp = changes; lp; lp = lp->f_rnext) { 684*7c478bd9Sstevel@tonic-gate /* finding the same node doesn't count */ 685*7c478bd9Sstevel@tonic-gate if (fp == lp) 686*7c478bd9Sstevel@tonic-gate continue; 687*7c478bd9Sstevel@tonic-gate 688*7c478bd9Sstevel@tonic-gate tgtp = &lp->f_info[tgtside]; 689*7c478bd9Sstevel@tonic-gate chgp = &lp->f_info[chgside]; 690*7c478bd9Sstevel@tonic-gate 691*7c478bd9Sstevel@tonic-gate /* 692*7c478bd9Sstevel@tonic-gate * if the file doesn't already exist on the target side 693*7c478bd9Sstevel@tonic-gate * we cannot make a link to it 694*7c478bd9Sstevel@tonic-gate */ 695*7c478bd9Sstevel@tonic-gate if (tgtp->f_mode == 0) 696*7c478bd9Sstevel@tonic-gate continue; 697*7c478bd9Sstevel@tonic-gate 698*7c478bd9Sstevel@tonic-gate /* 699*7c478bd9Sstevel@tonic-gate * if this is indeed a link, then the prospective file on 700*7c478bd9Sstevel@tonic-gate * the changed side will have the same dev/inum as the file 701*7c478bd9Sstevel@tonic-gate * we are looking for 702*7c478bd9Sstevel@tonic-gate */ 703*7c478bd9Sstevel@tonic-gate if (fcp->f_d_maj != chgp->f_d_maj) 704*7c478bd9Sstevel@tonic-gate continue; 705*7c478bd9Sstevel@tonic-gate if (fcp->f_d_min != chgp->f_d_min) 706*7c478bd9Sstevel@tonic-gate continue; 707*7c478bd9Sstevel@tonic-gate if (fcp->f_ino != chgp->f_ino) 708*7c478bd9Sstevel@tonic-gate continue; 709*7c478bd9Sstevel@tonic-gate 710*7c478bd9Sstevel@tonic-gate /* 711*7c478bd9Sstevel@tonic-gate * if the target side is already a link to this file, 712*7c478bd9Sstevel@tonic-gate * then there is no new link to be created 713*7c478bd9Sstevel@tonic-gate * FIX: how does this interact with copies over links 714*7c478bd9Sstevel@tonic-gate */ 715*7c478bd9Sstevel@tonic-gate if ((ftp->f_d_maj == tgtp->f_d_maj) && 716*7c478bd9Sstevel@tonic-gate (ftp->f_d_min == tgtp->f_d_min) && 717*7c478bd9Sstevel@tonic-gate (ftp->f_ino == tgtp->f_ino)) 718*7c478bd9Sstevel@tonic-gate continue; 719*7c478bd9Sstevel@tonic-gate 720*7c478bd9Sstevel@tonic-gate /* 721*7c478bd9Sstevel@tonic-gate * there is a pathological situation where a single file 722*7c478bd9Sstevel@tonic-gate * might appear under multiple base directories. This is 723*7c478bd9Sstevel@tonic-gate * damned awkward to detect in any other way, so we must 724*7c478bd9Sstevel@tonic-gate * check to see if we have just found another database 725*7c478bd9Sstevel@tonic-gate * instance for the same file (on the changed side). 726*7c478bd9Sstevel@tonic-gate */ 727*7c478bd9Sstevel@tonic-gate if ((fp->f_base != lp->f_base) && same_name(fp, lp, chgside)) 728*7c478bd9Sstevel@tonic-gate continue; 729*7c478bd9Sstevel@tonic-gate 730*7c478bd9Sstevel@tonic-gate if (opt_debug & DBG_ANAL) 731*7c478bd9Sstevel@tonic-gate fprintf(stderr, "ANAL: FIND LINK %s and %s\n", 732*7c478bd9Sstevel@tonic-gate fp->f_fullname, lp->f_fullname); 733*7c478bd9Sstevel@tonic-gate 734*7c478bd9Sstevel@tonic-gate return (lp); 735*7c478bd9Sstevel@tonic-gate } 736*7c478bd9Sstevel@tonic-gate 737*7c478bd9Sstevel@tonic-gate /* 738*7c478bd9Sstevel@tonic-gate * case 2: a simple rename of the only link 739*7c478bd9Sstevel@tonic-gate * 740*7c478bd9Sstevel@tonic-gate * In this case, there may not be any other existing file on 741*7c478bd9Sstevel@tonic-gate * the changed side that has the same I-node number. There 742*7c478bd9Sstevel@tonic-gate * might, however, be a record of such a file in the baseline. 743*7c478bd9Sstevel@tonic-gate * If we can find an identical file with a different name that 744*7c478bd9Sstevel@tonic-gate * has recently disappeared, we have a likely rename. 745*7c478bd9Sstevel@tonic-gate */ 746*7c478bd9Sstevel@tonic-gate for (lp = changes; lp; lp = lp->f_rnext) { 747*7c478bd9Sstevel@tonic-gate 748*7c478bd9Sstevel@tonic-gate /* finding the same node doesn't count */ 749*7c478bd9Sstevel@tonic-gate if (fp == lp) 750*7c478bd9Sstevel@tonic-gate continue; 751*7c478bd9Sstevel@tonic-gate 752*7c478bd9Sstevel@tonic-gate tgtp = &lp->f_info[tgtside]; 753*7c478bd9Sstevel@tonic-gate chgp = &lp->f_info[chgside]; 754*7c478bd9Sstevel@tonic-gate 755*7c478bd9Sstevel@tonic-gate /* 756*7c478bd9Sstevel@tonic-gate * if the file still exists on the changed side this is 757*7c478bd9Sstevel@tonic-gate * not a simple rename, and in fact the previous pass 758*7c478bd9Sstevel@tonic-gate * would have found it. 759*7c478bd9Sstevel@tonic-gate */ 760*7c478bd9Sstevel@tonic-gate if (chgp->f_mode != 0) 761*7c478bd9Sstevel@tonic-gate continue; 762*7c478bd9Sstevel@tonic-gate 763*7c478bd9Sstevel@tonic-gate /* 764*7c478bd9Sstevel@tonic-gate * the inode number for the new link on the changed 765*7c478bd9Sstevel@tonic-gate * side must match the inode number for the old link 766*7c478bd9Sstevel@tonic-gate * from the baseline. 767*7c478bd9Sstevel@tonic-gate */ 768*7c478bd9Sstevel@tonic-gate if (fcp->f_d_maj != ((srcdst == OPT_SRC) ? lp->f_d_maj 769*7c478bd9Sstevel@tonic-gate : lp->f_s_maj)) 770*7c478bd9Sstevel@tonic-gate continue; 771*7c478bd9Sstevel@tonic-gate if (fcp->f_d_min != ((srcdst == OPT_SRC) ? lp->f_d_min 772*7c478bd9Sstevel@tonic-gate : lp->f_s_min)) 773*7c478bd9Sstevel@tonic-gate continue; 774*7c478bd9Sstevel@tonic-gate if (fcp->f_ino != ((srcdst == OPT_SRC) ? lp->f_d_inum 775*7c478bd9Sstevel@tonic-gate : lp->f_s_inum)) 776*7c478bd9Sstevel@tonic-gate continue; 777*7c478bd9Sstevel@tonic-gate 778*7c478bd9Sstevel@tonic-gate /* finding a file we are already linked to doesn't help */ 779*7c478bd9Sstevel@tonic-gate if ((ftp->f_d_maj == tgtp->f_d_maj) && 780*7c478bd9Sstevel@tonic-gate (ftp->f_d_min == tgtp->f_d_min) && 781*7c478bd9Sstevel@tonic-gate (ftp->f_ino == tgtp->f_ino)) 782*7c478bd9Sstevel@tonic-gate continue; 783*7c478bd9Sstevel@tonic-gate 784*7c478bd9Sstevel@tonic-gate /* 785*7c478bd9Sstevel@tonic-gate * there is a danger that we will confuse an 786*7c478bd9Sstevel@tonic-gate * inode reallocation with a rename. We should 787*7c478bd9Sstevel@tonic-gate * only consider this to be a rename if the 788*7c478bd9Sstevel@tonic-gate * new file is identical to the old one 789*7c478bd9Sstevel@tonic-gate */ 790*7c478bd9Sstevel@tonic-gate basp = &lp->f_info[OPT_BASE]; 791*7c478bd9Sstevel@tonic-gate if (fcp->f_type != basp->f_type) 792*7c478bd9Sstevel@tonic-gate continue; 793*7c478bd9Sstevel@tonic-gate if (fcp->f_size != basp->f_size) 794*7c478bd9Sstevel@tonic-gate continue; 795*7c478bd9Sstevel@tonic-gate if (fcp->f_mode != basp->f_mode) 796*7c478bd9Sstevel@tonic-gate continue; 797*7c478bd9Sstevel@tonic-gate if (fcp->f_uid != basp->f_uid) 798*7c478bd9Sstevel@tonic-gate continue; 799*7c478bd9Sstevel@tonic-gate if (fcp->f_gid != basp->f_gid) 800*7c478bd9Sstevel@tonic-gate continue; 801*7c478bd9Sstevel@tonic-gate 802*7c478bd9Sstevel@tonic-gate if (opt_debug & DBG_ANAL) 803*7c478bd9Sstevel@tonic-gate fprintf(stderr, "ANAL: FIND RENAME %s and %s\n", 804*7c478bd9Sstevel@tonic-gate fp->f_fullname, lp->f_fullname); 805*7c478bd9Sstevel@tonic-gate 806*7c478bd9Sstevel@tonic-gate return (lp); 807*7c478bd9Sstevel@tonic-gate } 808*7c478bd9Sstevel@tonic-gate 809*7c478bd9Sstevel@tonic-gate return (0); 810*7c478bd9Sstevel@tonic-gate } 811*7c478bd9Sstevel@tonic-gate 812*7c478bd9Sstevel@tonic-gate /* 813*7c478bd9Sstevel@tonic-gate * routine: 814*7c478bd9Sstevel@tonic-gate * has_other_links 815*7c478bd9Sstevel@tonic-gate * 816*7c478bd9Sstevel@tonic-gate * purpose: 817*7c478bd9Sstevel@tonic-gate * to determine whether or not there is more that one link to a 818*7c478bd9Sstevel@tonic-gate * particular file. We are willing to delete a link to a file 819*7c478bd9Sstevel@tonic-gate * that has changed if we will still have other links to it. 820*7c478bd9Sstevel@tonic-gate * The trick here is that we only care about links under our 821*7c478bd9Sstevel@tonic-gate * dominion. 822*7c478bd9Sstevel@tonic-gate * 823*7c478bd9Sstevel@tonic-gate * parameters: 824*7c478bd9Sstevel@tonic-gate * file pointer to node we are interested in 825*7c478bd9Sstevel@tonic-gate * which side we are looking to additional links on 826*7c478bd9Sstevel@tonic-gate * 827*7c478bd9Sstevel@tonic-gate * returns: 828*7c478bd9Sstevel@tonic-gate * TRUE if there are multiple links 829*7c478bd9Sstevel@tonic-gate * FALSE if this is the only one we know of 830*7c478bd9Sstevel@tonic-gate */ 831*7c478bd9Sstevel@tonic-gate bool_t 832*7c478bd9Sstevel@tonic-gate has_other_links(struct file *fp, side_t srcdst) 833*7c478bd9Sstevel@tonic-gate { struct file *lp; 834*7c478bd9Sstevel@tonic-gate struct fileinfo *fip, *lip; 835*7c478bd9Sstevel@tonic-gate 836*7c478bd9Sstevel@tonic-gate fip = &fp->f_info[srcdst]; 837*7c478bd9Sstevel@tonic-gate 838*7c478bd9Sstevel@tonic-gate /* if the link count is one, there couldn't be others */ 839*7c478bd9Sstevel@tonic-gate if (fip->f_nlink < 2) 840*7c478bd9Sstevel@tonic-gate return (FALSE); 841*7c478bd9Sstevel@tonic-gate 842*7c478bd9Sstevel@tonic-gate /* look for any other files for the same inode */ 843*7c478bd9Sstevel@tonic-gate for (lp = changes; lp; lp = lp->f_rnext) { 844*7c478bd9Sstevel@tonic-gate /* finding the same node doesn't count */ 845*7c478bd9Sstevel@tonic-gate if (fp == lp) 846*7c478bd9Sstevel@tonic-gate continue; 847*7c478bd9Sstevel@tonic-gate 848*7c478bd9Sstevel@tonic-gate lip = &lp->f_info[srcdst]; 849*7c478bd9Sstevel@tonic-gate 850*7c478bd9Sstevel@tonic-gate /* 851*7c478bd9Sstevel@tonic-gate * file must still exist on this side 852*7c478bd9Sstevel@tonic-gate */ 853*7c478bd9Sstevel@tonic-gate if (lip->f_mode == 0) 854*7c478bd9Sstevel@tonic-gate continue; 855*7c478bd9Sstevel@tonic-gate 856*7c478bd9Sstevel@tonic-gate /* 857*7c478bd9Sstevel@tonic-gate * if this is indeed a link, then the prospective file on 858*7c478bd9Sstevel@tonic-gate * the changed side will have the same dev/inum as the file 859*7c478bd9Sstevel@tonic-gate * we are looking for 860*7c478bd9Sstevel@tonic-gate */ 861*7c478bd9Sstevel@tonic-gate if (lip->f_d_maj != fip->f_d_maj) 862*7c478bd9Sstevel@tonic-gate continue; 863*7c478bd9Sstevel@tonic-gate if (lip->f_d_min != fip->f_d_min) 864*7c478bd9Sstevel@tonic-gate continue; 865*7c478bd9Sstevel@tonic-gate if (lip->f_ino != fip->f_ino) 866*7c478bd9Sstevel@tonic-gate continue; 867*7c478bd9Sstevel@tonic-gate 868*7c478bd9Sstevel@tonic-gate /* 869*7c478bd9Sstevel@tonic-gate * we have found at least one other link 870*7c478bd9Sstevel@tonic-gate */ 871*7c478bd9Sstevel@tonic-gate return (TRUE); 872*7c478bd9Sstevel@tonic-gate } 873*7c478bd9Sstevel@tonic-gate 874*7c478bd9Sstevel@tonic-gate return (FALSE); 875*7c478bd9Sstevel@tonic-gate } 876*7c478bd9Sstevel@tonic-gate 877*7c478bd9Sstevel@tonic-gate /* 878*7c478bd9Sstevel@tonic-gate * routine: 879*7c478bd9Sstevel@tonic-gate * link_update 880*7c478bd9Sstevel@tonic-gate * 881*7c478bd9Sstevel@tonic-gate * purpose: 882*7c478bd9Sstevel@tonic-gate * to propoagate a stat change to all other file nodes that 883*7c478bd9Sstevel@tonic-gate * correspond to the same I-node on the changed side 884*7c478bd9Sstevel@tonic-gate * 885*7c478bd9Sstevel@tonic-gate * parameters: 886*7c478bd9Sstevel@tonic-gate * file pointer for the updated file 887*7c478bd9Sstevel@tonic-gate * which side was changed 888*7c478bd9Sstevel@tonic-gate * 889*7c478bd9Sstevel@tonic-gate * returns: 890*7c478bd9Sstevel@tonic-gate * void 891*7c478bd9Sstevel@tonic-gate * 892*7c478bd9Sstevel@tonic-gate * notes: 893*7c478bd9Sstevel@tonic-gate * if we have copied onto a file, we have copied onto all 894*7c478bd9Sstevel@tonic-gate * of its links, but since we do all stats before we do any 895*7c478bd9Sstevel@tonic-gate * copies, the stat information recently collected for links 896*7c478bd9Sstevel@tonic-gate * is no longer up-to-date, and this would result in incorrect 897*7c478bd9Sstevel@tonic-gate * reconciliation (redundant copies). 898*7c478bd9Sstevel@tonic-gate * 899*7c478bd9Sstevel@tonic-gate * There is an assumption here that all links to a changed 900*7c478bd9Sstevel@tonic-gate * file will be in the change list. This is true for almost 901*7c478bd9Sstevel@tonic-gate * all cases not involving restriction. If we do fail to 902*7c478bd9Sstevel@tonic-gate * update the baseline for a file that was off the change list, 903*7c478bd9Sstevel@tonic-gate * the worst that is likely to happen is that we will think 904*7c478bd9Sstevel@tonic-gate * it changed later (but will almost surely find that both 905*7c478bd9Sstevel@tonic-gate * copies agree). 906*7c478bd9Sstevel@tonic-gate */ 907*7c478bd9Sstevel@tonic-gate void 908*7c478bd9Sstevel@tonic-gate link_update(struct file *fp, side_t which) 909*7c478bd9Sstevel@tonic-gate { struct file *lp; 910*7c478bd9Sstevel@tonic-gate 911*7c478bd9Sstevel@tonic-gate for (lp = changes; lp; lp = lp->f_rnext) { 912*7c478bd9Sstevel@tonic-gate /* finding the current entry doesn't count */ 913*7c478bd9Sstevel@tonic-gate if (lp == fp) 914*7c478bd9Sstevel@tonic-gate continue; 915*7c478bd9Sstevel@tonic-gate 916*7c478bd9Sstevel@tonic-gate /* look for same i#, maj, min on changed side */ 917*7c478bd9Sstevel@tonic-gate if (lp->f_info[which].f_ino != fp->f_info[which].f_ino) 918*7c478bd9Sstevel@tonic-gate continue; 919*7c478bd9Sstevel@tonic-gate if (lp->f_info[which].f_d_maj != fp->f_info[which].f_d_maj) 920*7c478bd9Sstevel@tonic-gate continue; 921*7c478bd9Sstevel@tonic-gate if (lp->f_info[which].f_d_min != fp->f_info[which].f_d_min) 922*7c478bd9Sstevel@tonic-gate continue; 923*7c478bd9Sstevel@tonic-gate 924*7c478bd9Sstevel@tonic-gate /* 925*7c478bd9Sstevel@tonic-gate * this appears to be another link to the same file 926*7c478bd9Sstevel@tonic-gate * so the updated stat information for one must be 927*7c478bd9Sstevel@tonic-gate * correct for the other. 928*7c478bd9Sstevel@tonic-gate */ 929*7c478bd9Sstevel@tonic-gate lp->f_info[which].f_type = fp->f_info[which].f_type; 930*7c478bd9Sstevel@tonic-gate lp->f_info[which].f_size = fp->f_info[which].f_size; 931*7c478bd9Sstevel@tonic-gate lp->f_info[which].f_mode = fp->f_info[which].f_mode; 932*7c478bd9Sstevel@tonic-gate lp->f_info[which].f_uid = fp->f_info[which].f_uid; 933*7c478bd9Sstevel@tonic-gate lp->f_info[which].f_gid = fp->f_info[which].f_gid; 934*7c478bd9Sstevel@tonic-gate lp->f_info[which].f_modtime = fp->f_info[which].f_modtime; 935*7c478bd9Sstevel@tonic-gate lp->f_info[which].f_modns = fp->f_info[which].f_modns; 936*7c478bd9Sstevel@tonic-gate lp->f_info[which].f_nlink = fp->f_info[which].f_nlink; 937*7c478bd9Sstevel@tonic-gate lp->f_info[which].f_rd_maj = fp->f_info[which].f_rd_maj; 938*7c478bd9Sstevel@tonic-gate lp->f_info[which].f_rd_min = fp->f_info[which].f_rd_min; 939*7c478bd9Sstevel@tonic-gate 940*7c478bd9Sstevel@tonic-gate if (opt_debug & DBG_STAT) 941*7c478bd9Sstevel@tonic-gate fprintf(stderr, 942*7c478bd9Sstevel@tonic-gate "STAT: UPDATE LINK, file=%s, mod=%08lx.%08lx\n", 943*7c478bd9Sstevel@tonic-gate lp->f_name, lp->f_info[which].f_modtime, 944*7c478bd9Sstevel@tonic-gate lp->f_info[which].f_modns); 945*7c478bd9Sstevel@tonic-gate } 946*7c478bd9Sstevel@tonic-gate } 947*7c478bd9Sstevel@tonic-gate 948*7c478bd9Sstevel@tonic-gate /* 949*7c478bd9Sstevel@tonic-gate * routine: 950*7c478bd9Sstevel@tonic-gate * queue_file 951*7c478bd9Sstevel@tonic-gate * 952*7c478bd9Sstevel@tonic-gate * purpose: 953*7c478bd9Sstevel@tonic-gate * append a file to the list of needed reconciliations 954*7c478bd9Sstevel@tonic-gate * 955*7c478bd9Sstevel@tonic-gate * parameters: 956*7c478bd9Sstevel@tonic-gate * pointer to file 957*7c478bd9Sstevel@tonic-gate * 958*7c478bd9Sstevel@tonic-gate * notes: 959*7c478bd9Sstevel@tonic-gate * when a request is appended to the reconciliation list, 960*7c478bd9Sstevel@tonic-gate * we fill in the full name. We delayed this in hopes that 961*7c478bd9Sstevel@tonic-gate * it wouldn't be necessary (saving cycles and memory) 962*7c478bd9Sstevel@tonic-gate * 963*7c478bd9Sstevel@tonic-gate * There is some funny business with modification times. 964*7c478bd9Sstevel@tonic-gate * In general, we queue files in order of the latest modification 965*7c478bd9Sstevel@tonic-gate * time so that propagations preserve relative ordering. There 966*7c478bd9Sstevel@tonic-gate * are, however, a few important exceptions: 967*7c478bd9Sstevel@tonic-gate * 1. all directory creations happen at time zero, 968*7c478bd9Sstevel@tonic-gate * so that they are created before any files can 969*7c478bd9Sstevel@tonic-gate * be added to them. 970*7c478bd9Sstevel@tonic-gate * 2. all directory deletions happen at time infinity-depth, 971*7c478bd9Sstevel@tonic-gate * so that everything else can be removed before the 972*7c478bd9Sstevel@tonic-gate * directories themselves are removed. 973*7c478bd9Sstevel@tonic-gate * 3. all file deletions happen at time infinity-depth 974*7c478bd9Sstevel@tonic-gate * so that (in renames) the links will preceed the unlinks. 975*7c478bd9Sstevel@tonic-gate */ 976*7c478bd9Sstevel@tonic-gate static void 977*7c478bd9Sstevel@tonic-gate queue_file(struct file *fp) 978*7c478bd9Sstevel@tonic-gate { struct file **pp, *np; 979*7c478bd9Sstevel@tonic-gate 980*7c478bd9Sstevel@tonic-gate #define TIME_ZERO 0L /* the earliest possible time */ 981*7c478bd9Sstevel@tonic-gate #define TIME_LONG 0x7FFFFFFF /* the latest possible time */ 982*7c478bd9Sstevel@tonic-gate 983*7c478bd9Sstevel@tonic-gate /* 984*7c478bd9Sstevel@tonic-gate * figure out the modification time for sequencing purposes 985*7c478bd9Sstevel@tonic-gate */ 986*7c478bd9Sstevel@tonic-gate if ((fp->f_srcdiffs|fp->f_dstdiffs) & D_DELETE) { 987*7c478bd9Sstevel@tonic-gate /* 988*7c478bd9Sstevel@tonic-gate * deletions are performed last, and depth first 989*7c478bd9Sstevel@tonic-gate */ 990*7c478bd9Sstevel@tonic-gate fp->f_modtime = TIME_LONG - fp->f_depth; 991*7c478bd9Sstevel@tonic-gate } else if (fp->f_info[OPT_SRC].f_type != S_IFDIR && 992*7c478bd9Sstevel@tonic-gate fp->f_info[OPT_DST].f_type != S_IFDIR) { 993*7c478bd9Sstevel@tonic-gate /* 994*7c478bd9Sstevel@tonic-gate * for most files we use the latest mod time 995*7c478bd9Sstevel@tonic-gate */ 996*7c478bd9Sstevel@tonic-gate fp->f_modtime = fp->f_info[OPT_SRC].f_modtime; 997*7c478bd9Sstevel@tonic-gate fp->f_modns = fp->f_info[OPT_SRC].f_modns; 998*7c478bd9Sstevel@tonic-gate if (fp->f_modtime < fp->f_info[OPT_DST].f_modtime) { 999*7c478bd9Sstevel@tonic-gate fp->f_modtime = fp->f_info[OPT_DST].f_modtime; 1000*7c478bd9Sstevel@tonic-gate fp->f_modns = fp->f_info[OPT_DST].f_modns; 1001*7c478bd9Sstevel@tonic-gate } 1002*7c478bd9Sstevel@tonic-gate } else { 1003*7c478bd9Sstevel@tonic-gate /* 1004*7c478bd9Sstevel@tonic-gate * new directory creations need to happen before anything 1005*7c478bd9Sstevel@tonic-gate * else and are automatically sequenced in traversal order 1006*7c478bd9Sstevel@tonic-gate */ 1007*7c478bd9Sstevel@tonic-gate fp->f_modtime = TIME_ZERO; 1008*7c478bd9Sstevel@tonic-gate } 1009*7c478bd9Sstevel@tonic-gate 1010*7c478bd9Sstevel@tonic-gate /* 1011*7c478bd9Sstevel@tonic-gate * insertion is time ordered, and for equal times, 1012*7c478bd9Sstevel@tonic-gate * insertions is in (pre-order) traversal order 1013*7c478bd9Sstevel@tonic-gate */ 1014*7c478bd9Sstevel@tonic-gate for (pp = &changes; (np = *pp) != 0; pp = &np->f_rnext) { 1015*7c478bd9Sstevel@tonic-gate if (fp->f_modtime > np->f_modtime) 1016*7c478bd9Sstevel@tonic-gate continue; 1017*7c478bd9Sstevel@tonic-gate if (fp->f_modtime < np->f_modtime) 1018*7c478bd9Sstevel@tonic-gate break; 1019*7c478bd9Sstevel@tonic-gate if (fp->f_modns < np->f_modns) 1020*7c478bd9Sstevel@tonic-gate break; 1021*7c478bd9Sstevel@tonic-gate } 1022*7c478bd9Sstevel@tonic-gate 1023*7c478bd9Sstevel@tonic-gate fp->f_fullname = strdup(get_name(fp)); 1024*7c478bd9Sstevel@tonic-gate fp->f_rnext = np; 1025*7c478bd9Sstevel@tonic-gate *pp = fp; 1026*7c478bd9Sstevel@tonic-gate } 1027*7c478bd9Sstevel@tonic-gate 1028*7c478bd9Sstevel@tonic-gate 1029*7c478bd9Sstevel@tonic-gate /* 1030*7c478bd9Sstevel@tonic-gate * routines: 1031*7c478bd9Sstevel@tonic-gate * push_name/pop_name/get_name 1032*7c478bd9Sstevel@tonic-gate * 1033*7c478bd9Sstevel@tonic-gate * purpose: 1034*7c478bd9Sstevel@tonic-gate * maintain a name stack so we can form name of a particular file 1035*7c478bd9Sstevel@tonic-gate * as the concatenation of all of the names between it and the 1036*7c478bd9Sstevel@tonic-gate * (know to be fully qualified) base directory. 1037*7c478bd9Sstevel@tonic-gate * 1038*7c478bd9Sstevel@tonic-gate * notes: 1039*7c478bd9Sstevel@tonic-gate * we go to this trouble because most files never change and 1040*7c478bd9Sstevel@tonic-gate * so we don't need to associate full names with every one. 1041*7c478bd9Sstevel@tonic-gate * This stack is maintained during analysis, and if we decide 1042*7c478bd9Sstevel@tonic-gate * to add a file to the reconciliation list, we can use the 1043*7c478bd9Sstevel@tonic-gate * stack to generate a fully qualified name at that time. 1044*7c478bd9Sstevel@tonic-gate * 1045*7c478bd9Sstevel@tonic-gate * we compress out '/./' when we return a name. Given that the 1046*7c478bd9Sstevel@tonic-gate * stack was built by a tree walk, the only place a /./ should 1047*7c478bd9Sstevel@tonic-gate * appear is at the first level after the base ... but there 1048*7c478bd9Sstevel@tonic-gate * are legitimate ways for them to appear there. 1049*7c478bd9Sstevel@tonic-gate * 1050*7c478bd9Sstevel@tonic-gate * these names can get deep, so we dynamically size our name buffer 1051*7c478bd9Sstevel@tonic-gate */ 1052*7c478bd9Sstevel@tonic-gate static const char *namestack[ MAX_DEPTH + 1 ]; 1053*7c478bd9Sstevel@tonic-gate static int namedepth = 0; 1054*7c478bd9Sstevel@tonic-gate static int namelen = 0; 1055*7c478bd9Sstevel@tonic-gate 1056*7c478bd9Sstevel@tonic-gate void 1057*7c478bd9Sstevel@tonic-gate push_name(const char *name) 1058*7c478bd9Sstevel@tonic-gate { 1059*7c478bd9Sstevel@tonic-gate namestack[ namedepth++ ] = name; 1060*7c478bd9Sstevel@tonic-gate namelen += 2 + strlen(name); 1061*7c478bd9Sstevel@tonic-gate 1062*7c478bd9Sstevel@tonic-gate /* make sure we don't overflow our name stack */ 1063*7c478bd9Sstevel@tonic-gate if (namedepth >= MAX_DEPTH) { 1064*7c478bd9Sstevel@tonic-gate fprintf(stderr, gettext(ERR_deep), name); 1065*7c478bd9Sstevel@tonic-gate exit(ERR_OTHER); 1066*7c478bd9Sstevel@tonic-gate } 1067*7c478bd9Sstevel@tonic-gate } 1068*7c478bd9Sstevel@tonic-gate 1069*7c478bd9Sstevel@tonic-gate void 1070*7c478bd9Sstevel@tonic-gate pop_name(void) 1071*7c478bd9Sstevel@tonic-gate { 1072*7c478bd9Sstevel@tonic-gate namelen -= 2 + strlen(namestack[--namedepth]); 1073*7c478bd9Sstevel@tonic-gate namestack[ namedepth ] = 0; 1074*7c478bd9Sstevel@tonic-gate 1075*7c478bd9Sstevel@tonic-gate #ifdef DBG_ERRORS 1076*7c478bd9Sstevel@tonic-gate /* just a little sanity check here */ 1077*7c478bd9Sstevel@tonic-gate if (namedepth <= 0) { 1078*7c478bd9Sstevel@tonic-gate if (namedepth < 0) { 1079*7c478bd9Sstevel@tonic-gate fprintf(stderr, "ASSERTION FAILURE: namedepth < 0\n"); 1080*7c478bd9Sstevel@tonic-gate exit(ERR_OTHER); 1081*7c478bd9Sstevel@tonic-gate } else if (namelen != 0) { 1082*7c478bd9Sstevel@tonic-gate fprintf(stderr, "ASSERTION FAILURE: namelen != 0\n"); 1083*7c478bd9Sstevel@tonic-gate exit(ERR_OTHER); 1084*7c478bd9Sstevel@tonic-gate } 1085*7c478bd9Sstevel@tonic-gate } 1086*7c478bd9Sstevel@tonic-gate #endif 1087*7c478bd9Sstevel@tonic-gate } 1088*7c478bd9Sstevel@tonic-gate 1089*7c478bd9Sstevel@tonic-gate char 1090*7c478bd9Sstevel@tonic-gate *get_name(struct file *fp) 1091*7c478bd9Sstevel@tonic-gate { int i; 1092*7c478bd9Sstevel@tonic-gate static char *namebuf = 0; 1093*7c478bd9Sstevel@tonic-gate static int buflen = 0; 1094*7c478bd9Sstevel@tonic-gate 1095*7c478bd9Sstevel@tonic-gate /* make sure we have an adequate buffer */ 1096*7c478bd9Sstevel@tonic-gate i = namelen + 1 + strlen(fp->f_name); 1097*7c478bd9Sstevel@tonic-gate if (buflen < i) { 1098*7c478bd9Sstevel@tonic-gate for (buflen = MAX_PATH; buflen < i; buflen += MAX_NAME); 1099*7c478bd9Sstevel@tonic-gate namebuf = (char *) realloc(namebuf, buflen); 1100*7c478bd9Sstevel@tonic-gate } 1101*7c478bd9Sstevel@tonic-gate 1102*7c478bd9Sstevel@tonic-gate /* assemble the name */ 1103*7c478bd9Sstevel@tonic-gate namebuf[0] = 0; 1104*7c478bd9Sstevel@tonic-gate for (i = 0; i < namedepth; i++) { 1105*7c478bd9Sstevel@tonic-gate if (strcmp(namestack[i], ".")) { 1106*7c478bd9Sstevel@tonic-gate strcat(namebuf, namestack[i]); 1107*7c478bd9Sstevel@tonic-gate strcat(namebuf, "/"); 1108*7c478bd9Sstevel@tonic-gate } 1109*7c478bd9Sstevel@tonic-gate } 1110*7c478bd9Sstevel@tonic-gate 1111*7c478bd9Sstevel@tonic-gate strcat(namebuf, fp->f_name); 1112*7c478bd9Sstevel@tonic-gate 1113*7c478bd9Sstevel@tonic-gate return (namebuf); 1114*7c478bd9Sstevel@tonic-gate } 1115