17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 57c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 67c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 77c478bd9Sstevel@tonic-gate * with the License. 87c478bd9Sstevel@tonic-gate * 97c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 107c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 117c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 127c478bd9Sstevel@tonic-gate * and limitations under the License. 137c478bd9Sstevel@tonic-gate * 147c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 157c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 167c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 177c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 187c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 197c478bd9Sstevel@tonic-gate * 207c478bd9Sstevel@tonic-gate * CDDL HEADER END 217c478bd9Sstevel@tonic-gate */ 227c478bd9Sstevel@tonic-gate /* 237c478bd9Sstevel@tonic-gate * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved 247c478bd9Sstevel@tonic-gate * 257c478bd9Sstevel@tonic-gate * module: 267c478bd9Sstevel@tonic-gate * anal.c 277c478bd9Sstevel@tonic-gate * 287c478bd9Sstevel@tonic-gate * purpose: 297c478bd9Sstevel@tonic-gate * routines to analyze the file trees and figure out what has changed 307c478bd9Sstevel@tonic-gate * and queue files for reconciliation. It also contains tree enumeration 317c478bd9Sstevel@tonic-gate * routines to for other purposes (pruning and link location). 327c478bd9Sstevel@tonic-gate * 337c478bd9Sstevel@tonic-gate * contents: 347c478bd9Sstevel@tonic-gate * 357c478bd9Sstevel@tonic-gate * change analysis: 367c478bd9Sstevel@tonic-gate * analyze .... (top level) analyze all files in the tree for changes 377c478bd9Sstevel@tonic-gate * summary .... print out change/reconciliation statistics for each base 387c478bd9Sstevel@tonic-gate * check_file . (static) look for changes and queue file for reconciliation 397c478bd9Sstevel@tonic-gate * check_changes (static) figure out if a particular file has changed 407c478bd9Sstevel@tonic-gate * queue_file . (static) add a file to the reconciliation list 417c478bd9Sstevel@tonic-gate * 427c478bd9Sstevel@tonic-gate * other tree enumeration functions: 437c478bd9Sstevel@tonic-gate * prune_file . (static) recursive descent and actual pruning 447c478bd9Sstevel@tonic-gate * prune ...... (top level) initiate pruning analysis for nonexistant files 457c478bd9Sstevel@tonic-gate * find_link .. look for other files to which a file may be a link 467c478bd9Sstevel@tonic-gate * link_update. propagate changed stat info to all other links 477c478bd9Sstevel@tonic-gate * same_name .. (static) figure out if two nodes describe same file 487c478bd9Sstevel@tonic-gate * 497c478bd9Sstevel@tonic-gate * misc: 507c478bd9Sstevel@tonic-gate * push_name .. maintain a running full pathname as we descend 517c478bd9Sstevel@tonic-gate * pop_name ... maintain a running full pathname as we pop back 527c478bd9Sstevel@tonic-gate * get_name ... return full pathname for the current file 537c478bd9Sstevel@tonic-gate * 547c478bd9Sstevel@tonic-gate * notes: 557c478bd9Sstevel@tonic-gate * analysis is limited to files that were evaluated in the previous 567c478bd9Sstevel@tonic-gate * pass ... since we don't have complete information about files that 577c478bd9Sstevel@tonic-gate * were not evaluated in the previous pass. 587c478bd9Sstevel@tonic-gate */ 59*63360950Smp204432 #pragma ident "%Z%%M% %I% %E% SMI" 607c478bd9Sstevel@tonic-gate 617c478bd9Sstevel@tonic-gate #include <stdio.h> 627c478bd9Sstevel@tonic-gate #include <stdlib.h> 637c478bd9Sstevel@tonic-gate #include <strings.h> 647c478bd9Sstevel@tonic-gate 657c478bd9Sstevel@tonic-gate #include "messages.h" 667c478bd9Sstevel@tonic-gate #include "filesync.h" 677c478bd9Sstevel@tonic-gate #include "database.h" 687c478bd9Sstevel@tonic-gate #include "debug.h" 697c478bd9Sstevel@tonic-gate 707c478bd9Sstevel@tonic-gate /* 717c478bd9Sstevel@tonic-gate * routines: 727c478bd9Sstevel@tonic-gate */ 737c478bd9Sstevel@tonic-gate void push_name(const char *); 747c478bd9Sstevel@tonic-gate void pop_name(); 757c478bd9Sstevel@tonic-gate char *get_name(struct file *); 767c478bd9Sstevel@tonic-gate static errmask_t check_file(struct file *fp); 777c478bd9Sstevel@tonic-gate static diffmask_t check_changes(struct file *fp, int first, int second); 787c478bd9Sstevel@tonic-gate static int prune_file(struct file *fp); 797c478bd9Sstevel@tonic-gate static void queue_file(struct file *fp); 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate /* 827c478bd9Sstevel@tonic-gate * globals 837c478bd9Sstevel@tonic-gate */ 847c478bd9Sstevel@tonic-gate static struct file *changes; /* list of files to be reconciled */ 857c478bd9Sstevel@tonic-gate 867c478bd9Sstevel@tonic-gate static long total_files; /* total number of files being considered */ 877c478bd9Sstevel@tonic-gate static long est_deletes; /* estimated number of files to be deleted */ 887c478bd9Sstevel@tonic-gate static long est_rmdirs; /* est rmdirs of non-empty directories */ 897c478bd9Sstevel@tonic-gate 907c478bd9Sstevel@tonic-gate int inum_changes; /* LISTed directories whose I#s changed */ 917c478bd9Sstevel@tonic-gate 927c478bd9Sstevel@tonic-gate /* 937c478bd9Sstevel@tonic-gate * routine: 947c478bd9Sstevel@tonic-gate * analyze 957c478bd9Sstevel@tonic-gate * 967c478bd9Sstevel@tonic-gate * purpose: 977c478bd9Sstevel@tonic-gate * top level routine for the analysis/reconciliation process 987c478bd9Sstevel@tonic-gate * 997c478bd9Sstevel@tonic-gate * parameters: 1007c478bd9Sstevel@tonic-gate * none 1017c478bd9Sstevel@tonic-gate * 1027c478bd9Sstevel@tonic-gate * returns: 1037c478bd9Sstevel@tonic-gate * error mask 1047c478bd9Sstevel@tonic-gate * 1057c478bd9Sstevel@tonic-gate * notes: 1067c478bd9Sstevel@tonic-gate * a critical side effect of this routine is the creation of 1077c478bd9Sstevel@tonic-gate * the reconciliation list, an ordered list of files that 1087c478bd9Sstevel@tonic-gate * needed to be processed in the subsequent reconciliation pass 1097c478bd9Sstevel@tonic-gate */ 1107c478bd9Sstevel@tonic-gate errmask_t 1117c478bd9Sstevel@tonic-gate analyze() 1127c478bd9Sstevel@tonic-gate { struct base *bp; 1137c478bd9Sstevel@tonic-gate struct file *fp; 1147c478bd9Sstevel@tonic-gate int errs = 0; 1157c478bd9Sstevel@tonic-gate int err; 1167c478bd9Sstevel@tonic-gate int percentage; 1177c478bd9Sstevel@tonic-gate bool_t aborted = FALSE; 1187c478bd9Sstevel@tonic-gate char msgbuf[MAX_LINE]; 1197c478bd9Sstevel@tonic-gate 1207c478bd9Sstevel@tonic-gate /* 1217c478bd9Sstevel@tonic-gate * run through all bases and directories looking for files 1227c478bd9Sstevel@tonic-gate * that have been renamed. This must be done before the 1237c478bd9Sstevel@tonic-gate * difference analysis because a directory rename can introduce 1247c478bd9Sstevel@tonic-gate * radical restructuring into a name-based tree. 1257c478bd9Sstevel@tonic-gate */ 1267c478bd9Sstevel@tonic-gate for (bp = bases; bp; bp = bp->b_next) { 1277c478bd9Sstevel@tonic-gate for (fp = bp->b_files; fp; fp = fp->f_next) 1287c478bd9Sstevel@tonic-gate if (fp->f_flags & F_EVALUATE) 1297c478bd9Sstevel@tonic-gate errs |= find_renames(fp); 1307c478bd9Sstevel@tonic-gate } 1317c478bd9Sstevel@tonic-gate 1327c478bd9Sstevel@tonic-gate /* 1337c478bd9Sstevel@tonic-gate * run through all bases and files looking for candidates 1347c478bd9Sstevel@tonic-gate * note, however that we only descend into trees that have 1357c478bd9Sstevel@tonic-gate * the evaluate flag turned on. As a result of new rules or 1367c478bd9Sstevel@tonic-gate * restriction arguments, we may be deliberatly ignoring 1377c478bd9Sstevel@tonic-gate * large amounts of the baseline. This means we won't do 1387c478bd9Sstevel@tonic-gate * any stats to update the information in those nodes, and 1397c478bd9Sstevel@tonic-gate * they will be written back just as they were. 1407c478bd9Sstevel@tonic-gate * 1417c478bd9Sstevel@tonic-gate * note that there is code to prune out baseline nodes for 1427c478bd9Sstevel@tonic-gate * files that no longer exist, but that code is in reconcile 1437c478bd9Sstevel@tonic-gate * and will never get a chance to run on nodes that aren't 1447c478bd9Sstevel@tonic-gate * analyzed. 1457c478bd9Sstevel@tonic-gate * 1467c478bd9Sstevel@tonic-gate * we also want to run though all nodes with STAT errors 1477c478bd9Sstevel@tonic-gate * so that we can put them on the reconciliation list. 1487c478bd9Sstevel@tonic-gate */ 1497c478bd9Sstevel@tonic-gate for (bp = bases; bp; bp = bp->b_next) { 1507c478bd9Sstevel@tonic-gate for (fp = bp->b_files; fp; fp = fp->f_next) 1517c478bd9Sstevel@tonic-gate if (fp->f_flags & (F_EVALUATE|F_STAT_ERROR)) 1527c478bd9Sstevel@tonic-gate errs |= check_file(fp); 1537c478bd9Sstevel@tonic-gate } 1547c478bd9Sstevel@tonic-gate 1557c478bd9Sstevel@tonic-gate /* 1567c478bd9Sstevel@tonic-gate * my greatest fear is that someday, somehow, by messing with 1577c478bd9Sstevel@tonic-gate * variables or baselines or who-knows-what, that someone will 1587c478bd9Sstevel@tonic-gate * run a reconciliation against a large tree that doesn't correspond 1597c478bd9Sstevel@tonic-gate * to the baseline, and I will infer that a bazillion files have 1607c478bd9Sstevel@tonic-gate * been deleted and will propagate the slaughter before anyone 1617c478bd9Sstevel@tonic-gate * can say somebody stop that maniac. 1627c478bd9Sstevel@tonic-gate * 1637c478bd9Sstevel@tonic-gate * in order to prevent such a possibility, we have a few different 1647c478bd9Sstevel@tonic-gate * sanity checks. There is, of course, a tradeoff here between 1657c478bd9Sstevel@tonic-gate * danger and irritation. The current set of heuristics for whether 1667c478bd9Sstevel@tonic-gate * or not to generate a warning are (any of) 1677c478bd9Sstevel@tonic-gate * 1687c478bd9Sstevel@tonic-gate * at least CONFIRM_MIN files have been deleted AND 1697c478bd9Sstevel@tonic-gate * CONFIRM_PCT of all files have been deleted 1707c478bd9Sstevel@tonic-gate * 1717c478bd9Sstevel@tonic-gate * the inode number on a LISTed directory has changed 1727c478bd9Sstevel@tonic-gate * 1737c478bd9Sstevel@tonic-gate * a non-empty directory has been deleted. 1747c478bd9Sstevel@tonic-gate */ 1757c478bd9Sstevel@tonic-gate msgbuf[0] = 0; 1767c478bd9Sstevel@tonic-gate 1777c478bd9Sstevel@tonic-gate percentage = (est_deletes * 100) / (total_files ? total_files : 1); 1787c478bd9Sstevel@tonic-gate if (est_deletes >= CONFIRM_MIN && percentage >= CONFIRM_PCT) 1797c478bd9Sstevel@tonic-gate sprintf(msgbuf, gettext(WARN_deletes), est_deletes); 1807c478bd9Sstevel@tonic-gate else if (inum_changes > 0) 1817c478bd9Sstevel@tonic-gate sprintf(msgbuf, gettext(WARN_ichange), inum_changes); 1827c478bd9Sstevel@tonic-gate else if (est_rmdirs) 1837c478bd9Sstevel@tonic-gate sprintf(msgbuf, gettext(WARN_rmdirs), est_rmdirs); 1847c478bd9Sstevel@tonic-gate 1857c478bd9Sstevel@tonic-gate if (msgbuf[0]) 1867c478bd9Sstevel@tonic-gate confirm(msgbuf); 1877c478bd9Sstevel@tonic-gate 1887c478bd9Sstevel@tonic-gate /* 1897c478bd9Sstevel@tonic-gate * TRICK: 1907c478bd9Sstevel@tonic-gate * the change list contains both files that have changed 1917c478bd9Sstevel@tonic-gate * (and probably warrant reconciliation) and files that 1927c478bd9Sstevel@tonic-gate * we couldn't get up-to-date stat information on. The 1937c478bd9Sstevel@tonic-gate * latter files should just be flagged as being in conflict 1947c478bd9Sstevel@tonic-gate * so they can be reported in the summary. The same is 1957c478bd9Sstevel@tonic-gate * true of all subsequent files if we abort reconciliation. 1967c478bd9Sstevel@tonic-gate */ 1977c478bd9Sstevel@tonic-gate for (fp = changes; fp; fp = fp->f_rnext) 1987c478bd9Sstevel@tonic-gate if (aborted || (fp->f_flags & F_STAT_ERROR)) { 1997c478bd9Sstevel@tonic-gate fp->f_flags |= F_CONFLICT; 2007c478bd9Sstevel@tonic-gate /* if it isn't in the baseline yet, don't add it */ 2017c478bd9Sstevel@tonic-gate if ((fp->f_flags & F_IN_BASELINE) == 0) 2027c478bd9Sstevel@tonic-gate fp->f_flags |= F_REMOVE; 2037c478bd9Sstevel@tonic-gate fp->f_problem = aborted ? PROB_aborted : PROB_restat; 2047c478bd9Sstevel@tonic-gate (fp->f_base)->b_unresolved++; 2057c478bd9Sstevel@tonic-gate errs |= ERR_UNRESOLVED; 2067c478bd9Sstevel@tonic-gate if (opt_verbose) 2077c478bd9Sstevel@tonic-gate fprintf(stdout, 2087c478bd9Sstevel@tonic-gate gettext(aborted ? V_suppressed 2097c478bd9Sstevel@tonic-gate : V_nostat), 2107c478bd9Sstevel@tonic-gate fp->f_fullname); 2117c478bd9Sstevel@tonic-gate } else { 2127c478bd9Sstevel@tonic-gate err = reconcile(fp); 2137c478bd9Sstevel@tonic-gate errs |= err; 2147c478bd9Sstevel@tonic-gate if (opt_halt && (err & ERR_ABORT)) { 2157c478bd9Sstevel@tonic-gate fprintf(stderr, gettext(ERR_abort_h)); 2167c478bd9Sstevel@tonic-gate aborted = TRUE; 2177c478bd9Sstevel@tonic-gate } 2187c478bd9Sstevel@tonic-gate } 2197c478bd9Sstevel@tonic-gate 2207c478bd9Sstevel@tonic-gate return (errs); 2217c478bd9Sstevel@tonic-gate } 2227c478bd9Sstevel@tonic-gate 2237c478bd9Sstevel@tonic-gate /* 2247c478bd9Sstevel@tonic-gate * routine: 2257c478bd9Sstevel@tonic-gate * prune_file 2267c478bd9Sstevel@tonic-gate * 2277c478bd9Sstevel@tonic-gate * purpose: 2287c478bd9Sstevel@tonic-gate * to look for file entries that should be pruned from baseline 2297c478bd9Sstevel@tonic-gate * prune the current file if it needs pruning, and recursively 2307c478bd9Sstevel@tonic-gate * descend if it is a directory. 2317c478bd9Sstevel@tonic-gate * 2327c478bd9Sstevel@tonic-gate * parameters: 2337c478bd9Sstevel@tonic-gate * pointer to file node 2347c478bd9Sstevel@tonic-gate */ 2357c478bd9Sstevel@tonic-gate static int 2367c478bd9Sstevel@tonic-gate prune_file(struct file *fp) 2377c478bd9Sstevel@tonic-gate { struct file *cp; 2387c478bd9Sstevel@tonic-gate int prunes = 0; 2397c478bd9Sstevel@tonic-gate 2407c478bd9Sstevel@tonic-gate /* if node hasn't been evaluated, mark it for removal */ 2417c478bd9Sstevel@tonic-gate if ((fp->f_flags & (F_EVALUATE|F_STAT_ERROR)) == 0) { 2427c478bd9Sstevel@tonic-gate fp->f_flags |= F_REMOVE; 2437c478bd9Sstevel@tonic-gate prunes++; 2447c478bd9Sstevel@tonic-gate if (opt_debug & DBG_ANAL) 2457c478bd9Sstevel@tonic-gate fprintf(stderr, "ANAL: PRUNE %s\n", fp->f_name); 2467c478bd9Sstevel@tonic-gate } 2477c478bd9Sstevel@tonic-gate 2487c478bd9Sstevel@tonic-gate /* now check our children */ 2497c478bd9Sstevel@tonic-gate for (cp = fp->f_files; cp; cp = cp->f_next) 2507c478bd9Sstevel@tonic-gate prunes += prune_file(cp); 2517c478bd9Sstevel@tonic-gate 2527c478bd9Sstevel@tonic-gate return (prunes); 2537c478bd9Sstevel@tonic-gate } 2547c478bd9Sstevel@tonic-gate 2557c478bd9Sstevel@tonic-gate /* 2567c478bd9Sstevel@tonic-gate * routine: 2577c478bd9Sstevel@tonic-gate * prune 2587c478bd9Sstevel@tonic-gate * 2597c478bd9Sstevel@tonic-gate * purpose: 2607c478bd9Sstevel@tonic-gate * to prune the baseline of entries that no longer correspond to 2617c478bd9Sstevel@tonic-gate * existing rules. 2627c478bd9Sstevel@tonic-gate * 2637c478bd9Sstevel@tonic-gate * notes: 2647c478bd9Sstevel@tonic-gate * This routine just calls prune_file on the top of each base tree. 2657c478bd9Sstevel@tonic-gate */ 2667c478bd9Sstevel@tonic-gate int 2677c478bd9Sstevel@tonic-gate prune() 2687c478bd9Sstevel@tonic-gate { struct base *bp; 2697c478bd9Sstevel@tonic-gate struct file *fp; 2707c478bd9Sstevel@tonic-gate int prunes = 0; 2717c478bd9Sstevel@tonic-gate 2727c478bd9Sstevel@tonic-gate for (bp = bases; bp; bp = bp->b_next) { 2737c478bd9Sstevel@tonic-gate for (fp = bp->b_files; fp; fp = fp->f_next) 2747c478bd9Sstevel@tonic-gate prunes += prune_file(fp); 2757c478bd9Sstevel@tonic-gate 2767c478bd9Sstevel@tonic-gate if ((bp->b_flags & F_EVALUATE) == 0) 2777c478bd9Sstevel@tonic-gate bp->b_flags |= F_REMOVE; 2787c478bd9Sstevel@tonic-gate } 2797c478bd9Sstevel@tonic-gate 2807c478bd9Sstevel@tonic-gate return (prunes); 2817c478bd9Sstevel@tonic-gate } 2827c478bd9Sstevel@tonic-gate 2837c478bd9Sstevel@tonic-gate /* 2847c478bd9Sstevel@tonic-gate * routine: 2857c478bd9Sstevel@tonic-gate * summary 2867c478bd9Sstevel@tonic-gate * 2877c478bd9Sstevel@tonic-gate * purpose: 2887c478bd9Sstevel@tonic-gate * to print out statics and conflict lists 2897c478bd9Sstevel@tonic-gate */ 2907c478bd9Sstevel@tonic-gate void 2917c478bd9Sstevel@tonic-gate summary() 2927c478bd9Sstevel@tonic-gate { struct base *bp; 2937c478bd9Sstevel@tonic-gate struct file *fp; 2947c478bd9Sstevel@tonic-gate extern bool_t need_super; 2957c478bd9Sstevel@tonic-gate 2967c478bd9Sstevel@tonic-gate (void) fflush(stdout); 2977c478bd9Sstevel@tonic-gate 2987c478bd9Sstevel@tonic-gate for (bp = bases; bp; bp = bp->b_next) { 2997c478bd9Sstevel@tonic-gate 300*63360950Smp204432 /* see if this base was irrelevant */ 3017c478bd9Sstevel@tonic-gate if ((bp->b_flags & F_EVALUATE) == 0) 3027c478bd9Sstevel@tonic-gate continue; 3037c478bd9Sstevel@tonic-gate 3047c478bd9Sstevel@tonic-gate /* print out a summary for this base */ 3057c478bd9Sstevel@tonic-gate fprintf(stderr, gettext(SUM_hd), 3067c478bd9Sstevel@tonic-gate bp->b_src_spec, bp->b_dst_spec, bp->b_totfiles); 3077c478bd9Sstevel@tonic-gate fprintf(stderr, gettext(SUM_dst), 3087c478bd9Sstevel@tonic-gate bp->b_dst_copies, bp->b_dst_deletes, bp->b_dst_misc); 3097c478bd9Sstevel@tonic-gate fprintf(stderr, gettext(SUM_src), 3107c478bd9Sstevel@tonic-gate bp->b_src_copies, bp->b_src_deletes, bp->b_src_misc); 3117c478bd9Sstevel@tonic-gate if (bp->b_unresolved) 3127c478bd9Sstevel@tonic-gate fprintf(stderr, gettext(SUM_unresolved), 3137c478bd9Sstevel@tonic-gate bp->b_unresolved); 3147c478bd9Sstevel@tonic-gate 3157c478bd9Sstevel@tonic-gate 3167c478bd9Sstevel@tonic-gate /* print out a list of unreconciled files for this base */ 3177c478bd9Sstevel@tonic-gate for (fp = changes; fp; fp = fp->f_rnext) { 3187c478bd9Sstevel@tonic-gate if (fp->f_base != bp) 3197c478bd9Sstevel@tonic-gate continue; 3207c478bd9Sstevel@tonic-gate if ((fp->f_flags & F_CONFLICT) == 0) 3217c478bd9Sstevel@tonic-gate continue; 3227c478bd9Sstevel@tonic-gate fprintf(stderr, "\t\t%s (%s)\n", fp->f_fullname, 3237c478bd9Sstevel@tonic-gate fp->f_problem ? fp->f_problem : "???"); 3247c478bd9Sstevel@tonic-gate } 3257c478bd9Sstevel@tonic-gate 3267c478bd9Sstevel@tonic-gate fprintf(stderr, "\n"); 3277c478bd9Sstevel@tonic-gate } 3287c478bd9Sstevel@tonic-gate 3297c478bd9Sstevel@tonic-gate if (need_super) 3307c478bd9Sstevel@tonic-gate fprintf(stderr, gettext(WARN_super)); 3317c478bd9Sstevel@tonic-gate } 3327c478bd9Sstevel@tonic-gate 3337c478bd9Sstevel@tonic-gate /* 3347c478bd9Sstevel@tonic-gate * routine: 3357c478bd9Sstevel@tonic-gate * check_file 3367c478bd9Sstevel@tonic-gate * 3377c478bd9Sstevel@tonic-gate * purpose: 3387c478bd9Sstevel@tonic-gate * figure out if a file requires reconciliation and recursively 3397c478bd9Sstevel@tonic-gate * descend into all sub-files and directories 3407c478bd9Sstevel@tonic-gate * 3417c478bd9Sstevel@tonic-gate * parameters: 3427c478bd9Sstevel@tonic-gate * base pointer 3437c478bd9Sstevel@tonic-gate * file pointer 3447c478bd9Sstevel@tonic-gate * 3457c478bd9Sstevel@tonic-gate * returns: 3467c478bd9Sstevel@tonic-gate * error mask 3477c478bd9Sstevel@tonic-gate * built up changes needed list 3487c478bd9Sstevel@tonic-gate * updated statistics 3497c478bd9Sstevel@tonic-gate * 3507c478bd9Sstevel@tonic-gate * notes: 3517c478bd9Sstevel@tonic-gate * this routine builds up a path name as it descends through 3527c478bd9Sstevel@tonic-gate * the tree (see push_name, pop_name, get_name). 3537c478bd9Sstevel@tonic-gate */ 3547c478bd9Sstevel@tonic-gate static errmask_t 3557c478bd9Sstevel@tonic-gate check_file(struct file *fp) 3567c478bd9Sstevel@tonic-gate { struct file *cp; 3577c478bd9Sstevel@tonic-gate int errs = 0; 3587c478bd9Sstevel@tonic-gate 3597c478bd9Sstevel@tonic-gate if ((fp->f_flags & F_STAT_ERROR) == 0) { 3607c478bd9Sstevel@tonic-gate /* see if the source has changed */ 3617c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_modtime = fp->f_s_modtime; 3627c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_ino = fp->f_s_inum; 3637c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_d_maj = fp->f_s_maj; 3647c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_d_min = fp->f_s_min; 3657c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_nlink = fp->f_s_nlink; 3667c478bd9Sstevel@tonic-gate fp->f_srcdiffs |= check_changes(fp, OPT_BASE, OPT_SRC); 3677c478bd9Sstevel@tonic-gate 3687c478bd9Sstevel@tonic-gate /* see if the destination has changed */ 3697c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_modtime = fp->f_d_modtime; 3707c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_ino = fp->f_d_inum; 3717c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_d_maj = fp->f_d_maj; 3727c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_d_min = fp->f_d_min; 3737c478bd9Sstevel@tonic-gate fp->f_info[OPT_BASE].f_nlink = fp->f_d_nlink; 3747c478bd9Sstevel@tonic-gate fp->f_dstdiffs |= check_changes(fp, OPT_BASE, OPT_DST); 3757c478bd9Sstevel@tonic-gate 3767c478bd9Sstevel@tonic-gate /* if nobody thinks the file exists, baseline needs pruning */ 3777c478bd9Sstevel@tonic-gate if ((fp->f_flags & (F_IN_SOURCE|F_IN_DEST)) == 0) { 3787c478bd9Sstevel@tonic-gate fp->f_srcdiffs |= D_DELETE; 3797c478bd9Sstevel@tonic-gate fp->f_dstdiffs |= D_DELETE; 3807c478bd9Sstevel@tonic-gate } 3817c478bd9Sstevel@tonic-gate 3827c478bd9Sstevel@tonic-gate /* keep track of possible deletions to look for trouble */ 3837c478bd9Sstevel@tonic-gate if ((fp->f_dstdiffs | fp->f_srcdiffs) & D_DELETE) { 3847c478bd9Sstevel@tonic-gate est_deletes++; 3857c478bd9Sstevel@tonic-gate 3867c478bd9Sstevel@tonic-gate /* see if file is (or has been) a non-empty directory */ 3877c478bd9Sstevel@tonic-gate if (fp->f_files) 3887c478bd9Sstevel@tonic-gate est_rmdirs++; 3897c478bd9Sstevel@tonic-gate } 3907c478bd9Sstevel@tonic-gate } 3917c478bd9Sstevel@tonic-gate 3927c478bd9Sstevel@tonic-gate /* if we found differences, queue the file for reconciliation */ 3937c478bd9Sstevel@tonic-gate if (fp->f_srcdiffs || fp->f_dstdiffs || fp->f_flags & F_STAT_ERROR) { 3947c478bd9Sstevel@tonic-gate queue_file(fp); 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate if (opt_debug & DBG_ANAL) { 3977c478bd9Sstevel@tonic-gate fprintf(stderr, "ANAL: src=%s", 3987c478bd9Sstevel@tonic-gate showflags(diffmap, fp->f_srcdiffs)); 3997c478bd9Sstevel@tonic-gate fprintf(stderr, " dst=%s", 4007c478bd9Sstevel@tonic-gate showflags(diffmap, fp->f_dstdiffs)); 4017c478bd9Sstevel@tonic-gate fprintf(stderr, " flgs=%s", 4027c478bd9Sstevel@tonic-gate showflags(fileflags, fp->f_flags)); 4037c478bd9Sstevel@tonic-gate fprintf(stderr, " name=%s\n", fp->f_fullname); 4047c478bd9Sstevel@tonic-gate } 4057c478bd9Sstevel@tonic-gate } 4067c478bd9Sstevel@tonic-gate 4077c478bd9Sstevel@tonic-gate /* bump the total file count */ 4087c478bd9Sstevel@tonic-gate fp->f_base->b_totfiles++; 4097c478bd9Sstevel@tonic-gate total_files++; 4107c478bd9Sstevel@tonic-gate 4117c478bd9Sstevel@tonic-gate /* if this is not a directory, we're done */ 4127c478bd9Sstevel@tonic-gate if (fp->f_files == 0) 4137c478bd9Sstevel@tonic-gate return (errs); 4147c478bd9Sstevel@tonic-gate 4157c478bd9Sstevel@tonic-gate /* 4167c478bd9Sstevel@tonic-gate * If this is a directory, we need to recursively analyze 4177c478bd9Sstevel@tonic-gate * our children, but only children who have been evaluated. 4187c478bd9Sstevel@tonic-gate * If a node has not been evaluated, then we don't have 4197c478bd9Sstevel@tonic-gate * updated stat information and there is nothing to analyze. 4207c478bd9Sstevel@tonic-gate * 4217c478bd9Sstevel@tonic-gate * we also want to run though all nodes with STAT errors 4227c478bd9Sstevel@tonic-gate * so that we can put them on the reconciliation list. 4237c478bd9Sstevel@tonic-gate * If a directory is unreadable on one side, all files 4247c478bd9Sstevel@tonic-gate * under that directory (ON BOTH SIDES) must be marked as 4257c478bd9Sstevel@tonic-gate * blocked by stat errors. 4267c478bd9Sstevel@tonic-gate */ 4277c478bd9Sstevel@tonic-gate push_name(fp->f_name); 4287c478bd9Sstevel@tonic-gate 4297c478bd9Sstevel@tonic-gate for (cp = fp->f_files; cp; cp = cp->f_next) { 4307c478bd9Sstevel@tonic-gate if (fp->f_flags & F_STAT_ERROR) 4317c478bd9Sstevel@tonic-gate cp->f_flags |= F_STAT_ERROR; 4327c478bd9Sstevel@tonic-gate if (cp->f_flags & (F_EVALUATE|F_STAT_ERROR)) 4337c478bd9Sstevel@tonic-gate errs |= check_file(cp); 4347c478bd9Sstevel@tonic-gate } 4357c478bd9Sstevel@tonic-gate 4367c478bd9Sstevel@tonic-gate pop_name(); 4377c478bd9Sstevel@tonic-gate 4387c478bd9Sstevel@tonic-gate return (errs); 4397c478bd9Sstevel@tonic-gate } 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate /* 4427c478bd9Sstevel@tonic-gate * routine: 4437c478bd9Sstevel@tonic-gate * check_changes 4447c478bd9Sstevel@tonic-gate * 4457c478bd9Sstevel@tonic-gate * purpose: 4467c478bd9Sstevel@tonic-gate * to figure out what has changed for a specific file 4477c478bd9Sstevel@tonic-gate * 4487c478bd9Sstevel@tonic-gate * parameters: 4497c478bd9Sstevel@tonic-gate * file pointer 4507c478bd9Sstevel@tonic-gate * the reference info 4517c478bd9Sstevel@tonic-gate * the info to be checked for changes 4527c478bd9Sstevel@tonic-gate * 4537c478bd9Sstevel@tonic-gate * returns: 4547c478bd9Sstevel@tonic-gate * diff mask 4557c478bd9Sstevel@tonic-gate * 4567c478bd9Sstevel@tonic-gate * notes: 4577c478bd9Sstevel@tonic-gate * this routine doesn't pretend to understand what happened. 4587c478bd9Sstevel@tonic-gate * it merely enumerates the ways in which the files differ. 4597c478bd9Sstevel@tonic-gate */ 4607c478bd9Sstevel@tonic-gate static diffmask_t 4617c478bd9Sstevel@tonic-gate check_changes(struct file *fp, int ref, int new) 4627c478bd9Sstevel@tonic-gate { struct fileinfo *rp, *np; 4637c478bd9Sstevel@tonic-gate int mask = 0; 4647c478bd9Sstevel@tonic-gate int type; 4657c478bd9Sstevel@tonic-gate 4667c478bd9Sstevel@tonic-gate rp = &fp->f_info[ref]; 4677c478bd9Sstevel@tonic-gate np = &fp->f_info[new]; 4687c478bd9Sstevel@tonic-gate 4697c478bd9Sstevel@tonic-gate if (np->f_uid != rp->f_uid) 4707c478bd9Sstevel@tonic-gate mask |= D_UID; 4717c478bd9Sstevel@tonic-gate 4727c478bd9Sstevel@tonic-gate if (np->f_gid != rp->f_gid) 4737c478bd9Sstevel@tonic-gate mask |= D_GID; 4747c478bd9Sstevel@tonic-gate 4757c478bd9Sstevel@tonic-gate if (np->f_mode != rp->f_mode) 4767c478bd9Sstevel@tonic-gate mask |= D_PROT; 4777c478bd9Sstevel@tonic-gate 4787c478bd9Sstevel@tonic-gate type = np->f_type; 4797c478bd9Sstevel@tonic-gate if (type != rp->f_type) { 4807c478bd9Sstevel@tonic-gate if (type == 0) 4817c478bd9Sstevel@tonic-gate mask |= D_DELETE; 4827c478bd9Sstevel@tonic-gate else if (rp->f_type == 0) 4837c478bd9Sstevel@tonic-gate mask |= D_CREATE; 4847c478bd9Sstevel@tonic-gate else 4857c478bd9Sstevel@tonic-gate mask |= D_TYPE; 4867c478bd9Sstevel@tonic-gate } else if (type == S_IFBLK || type == S_IFCHR) { 4877c478bd9Sstevel@tonic-gate /* 4887c478bd9Sstevel@tonic-gate * for special files, we only look at the maj/min 4897c478bd9Sstevel@tonic-gate */ 4907c478bd9Sstevel@tonic-gate if (np->f_rd_maj != rp->f_rd_maj) 4917c478bd9Sstevel@tonic-gate mask |= D_SIZE; 4927c478bd9Sstevel@tonic-gate if (np->f_rd_min != rp->f_rd_min) 4937c478bd9Sstevel@tonic-gate mask |= D_SIZE; 4947c478bd9Sstevel@tonic-gate } else if (type != S_IFDIR) { 4957c478bd9Sstevel@tonic-gate /* 4967c478bd9Sstevel@tonic-gate * for directories, we don't look directly at 4977c478bd9Sstevel@tonic-gate * the contents, so these fields don't mean 4987c478bd9Sstevel@tonic-gate * anything. If the directories have changed 4997c478bd9Sstevel@tonic-gate * in any interesting way, we'll find it by 5007c478bd9Sstevel@tonic-gate * walking the tree. 5017c478bd9Sstevel@tonic-gate */ 5027c478bd9Sstevel@tonic-gate if (np->f_modtime > rp->f_modtime) 5037c478bd9Sstevel@tonic-gate mask |= D_MTIME; 5047c478bd9Sstevel@tonic-gate 5057c478bd9Sstevel@tonic-gate if (np->f_size != rp->f_size) 5067c478bd9Sstevel@tonic-gate mask |= D_SIZE; 5077c478bd9Sstevel@tonic-gate 5087c478bd9Sstevel@tonic-gate if (np->f_nlink != rp->f_nlink) 5097c478bd9Sstevel@tonic-gate mask |= D_LINKS; 5107c478bd9Sstevel@tonic-gate } 5117c478bd9Sstevel@tonic-gate 5127c478bd9Sstevel@tonic-gate if (cmp_acls(rp, np) == 0) 5137c478bd9Sstevel@tonic-gate mask |= D_FACLS; 5147c478bd9Sstevel@tonic-gate 5157c478bd9Sstevel@tonic-gate return (mask); 5167c478bd9Sstevel@tonic-gate } 5177c478bd9Sstevel@tonic-gate 5187c478bd9Sstevel@tonic-gate /* 5197c478bd9Sstevel@tonic-gate * routine: 5207c478bd9Sstevel@tonic-gate * same_name 5217c478bd9Sstevel@tonic-gate * 5227c478bd9Sstevel@tonic-gate * purpose: 5237c478bd9Sstevel@tonic-gate * to figure out whether or not two databsae nodes actually refer to 5247c478bd9Sstevel@tonic-gate * the same file. 5257c478bd9Sstevel@tonic-gate * 5267c478bd9Sstevel@tonic-gate * parameters: 5277c478bd9Sstevel@tonic-gate * pointers to two file description nodes 5287c478bd9Sstevel@tonic-gate * which side we should check 5297c478bd9Sstevel@tonic-gate * 5307c478bd9Sstevel@tonic-gate * returns: 5317c478bd9Sstevel@tonic-gate * TRUE/FALSE 5327c478bd9Sstevel@tonic-gate * 5337c478bd9Sstevel@tonic-gate * notes: 5347c478bd9Sstevel@tonic-gate * if a single directory is specified in multiple base pairs, it 5357c478bd9Sstevel@tonic-gate * is possible to have multiple nodes in the database describing 5367c478bd9Sstevel@tonic-gate * the same file. This routine is supposed to detect those cases. 5377c478bd9Sstevel@tonic-gate * 5387c478bd9Sstevel@tonic-gate * what should be a trivial string comparison is complicated by 5397c478bd9Sstevel@tonic-gate * the possibility that the two nodes might describe the same file 5407c478bd9Sstevel@tonic-gate * from base directories at different depths. Thus, rather than 5417c478bd9Sstevel@tonic-gate * comparing two strings, we really want to compare the concatenation 5427c478bd9Sstevel@tonic-gate * of two pairs of strings. Unfortunately calling full_name would 5437c478bd9Sstevel@tonic-gate * be awkward right now, so instead we have our own comparison 5447c478bd9Sstevel@tonic-gate * routine that automatically skips from the first string to 5457c478bd9Sstevel@tonic-gate * the second. 5467c478bd9Sstevel@tonic-gate */ 5477c478bd9Sstevel@tonic-gate static bool_t 5487c478bd9Sstevel@tonic-gate same_name(struct file *f1, struct file *f2, side_t srcdst) 5497c478bd9Sstevel@tonic-gate { 5507c478bd9Sstevel@tonic-gate char *s1, *s2, *x1, *x2; 5517c478bd9Sstevel@tonic-gate 5527c478bd9Sstevel@tonic-gate if (srcdst == OPT_SRC) { 5537c478bd9Sstevel@tonic-gate s1 = (f1->f_base)->b_src_name; 5547c478bd9Sstevel@tonic-gate s2 = (f2->f_base)->b_src_name; 5557c478bd9Sstevel@tonic-gate } else { 5567c478bd9Sstevel@tonic-gate s1 = (f1->f_base)->b_dst_name; 5577c478bd9Sstevel@tonic-gate s2 = (f2->f_base)->b_dst_name; 5587c478bd9Sstevel@tonic-gate } 5597c478bd9Sstevel@tonic-gate x1 = f1->f_fullname; 5607c478bd9Sstevel@tonic-gate x2 = f2->f_fullname; 5617c478bd9Sstevel@tonic-gate 5627c478bd9Sstevel@tonic-gate /* 5637c478bd9Sstevel@tonic-gate * Compare the two names, and if they differ before they end 5647c478bd9Sstevel@tonic-gate * this is a non-match. If they both end at the same time, 5657c478bd9Sstevel@tonic-gate * this is a match. 5667c478bd9Sstevel@tonic-gate * 5677c478bd9Sstevel@tonic-gate * The trick here is that each string is actually the logical 5687c478bd9Sstevel@tonic-gate * concatenation of two strings, and we need to automatically 5697c478bd9Sstevel@tonic-gate * wrap from the first to the second string in each pair. There 5707c478bd9Sstevel@tonic-gate * is no requirement that the two (concatenated) strings be 5717c478bd9Sstevel@tonic-gate * broken at the same point, so we have a slightly baroque 5727c478bd9Sstevel@tonic-gate * comparsion loop. 5737c478bd9Sstevel@tonic-gate */ 5747c478bd9Sstevel@tonic-gate while (*s1 && *s1 == *s2) { 5757c478bd9Sstevel@tonic-gate 5767c478bd9Sstevel@tonic-gate /* 5777c478bd9Sstevel@tonic-gate * strings have been identical so far, so advance the 5787c478bd9Sstevel@tonic-gate * pointers and continue the comparison. The trick 5797c478bd9Sstevel@tonic-gate * is that when either string ends, we have to wrap 5807c478bd9Sstevel@tonic-gate * over to its extension. 5817c478bd9Sstevel@tonic-gate */ 5827c478bd9Sstevel@tonic-gate s1++; s2++; 5837c478bd9Sstevel@tonic-gate if (*s1 && *s2) 5847c478bd9Sstevel@tonic-gate continue; 5857c478bd9Sstevel@tonic-gate 5867c478bd9Sstevel@tonic-gate /* 5877c478bd9Sstevel@tonic-gate * at least one of the strings has ended. 5887c478bd9Sstevel@tonic-gate * there is an implicit slash between the string 5897c478bd9Sstevel@tonic-gate * and its extension, and this has to be matched 5907c478bd9Sstevel@tonic-gate * against the other string. 5917c478bd9Sstevel@tonic-gate */ 5927c478bd9Sstevel@tonic-gate if (*s1 != *s2) { 5937c478bd9Sstevel@tonic-gate if (*s1 == 0 && *s2 == '/') 5947c478bd9Sstevel@tonic-gate s2++; 5957c478bd9Sstevel@tonic-gate else if (*s2 == 0 && *s1 == '/') 5967c478bd9Sstevel@tonic-gate s1++; 5977c478bd9Sstevel@tonic-gate else 5987c478bd9Sstevel@tonic-gate /* the disagreement doesn't come at a slash */ 5997c478bd9Sstevel@tonic-gate break; 6007c478bd9Sstevel@tonic-gate } 6017c478bd9Sstevel@tonic-gate 6027c478bd9Sstevel@tonic-gate /* 6037c478bd9Sstevel@tonic-gate * if either string has ended, wrap to its extension 6047c478bd9Sstevel@tonic-gate */ 6057c478bd9Sstevel@tonic-gate if (*s1 == 0 && x1 != 0) { 6067c478bd9Sstevel@tonic-gate s1 = x1; 6077c478bd9Sstevel@tonic-gate x1 = 0; 6087c478bd9Sstevel@tonic-gate } 6097c478bd9Sstevel@tonic-gate if (*s2 == 0 && x2 != 0) { 6107c478bd9Sstevel@tonic-gate s2 = x2; 6117c478bd9Sstevel@tonic-gate x2 = 0; 6127c478bd9Sstevel@tonic-gate } 6137c478bd9Sstevel@tonic-gate } 6147c478bd9Sstevel@tonic-gate 6157c478bd9Sstevel@tonic-gate return (*s1 == *s2); 6167c478bd9Sstevel@tonic-gate } 6177c478bd9Sstevel@tonic-gate 6187c478bd9Sstevel@tonic-gate /* 6197c478bd9Sstevel@tonic-gate * routine: 6207c478bd9Sstevel@tonic-gate * find_link 6217c478bd9Sstevel@tonic-gate * 6227c478bd9Sstevel@tonic-gate * purpose: 6237c478bd9Sstevel@tonic-gate * to figure out if there is a file to which we should 6247c478bd9Sstevel@tonic-gate * be creating a link (rather than making a copy) 6257c478bd9Sstevel@tonic-gate * 6267c478bd9Sstevel@tonic-gate * parameters: 6277c478bd9Sstevel@tonic-gate * file node for the file to be created (that we hope is merely a link) 6287c478bd9Sstevel@tonic-gate * which side is to be changed (src/dst) 6297c478bd9Sstevel@tonic-gate * 6307c478bd9Sstevel@tonic-gate * return: 6317c478bd9Sstevel@tonic-gate * 0 no link is appropriate 6327c478bd9Sstevel@tonic-gate * else pointer to file node for link referent 6337c478bd9Sstevel@tonic-gate * 6347c478bd9Sstevel@tonic-gate * notes: 6357c478bd9Sstevel@tonic-gate * there are a few strange heuristics in this routine and I 6367c478bd9Sstevel@tonic-gate * wouldn't bet my soul that I got all of them right. The general 6377c478bd9Sstevel@tonic-gate * theory is that when a new file is created, we look to see if it 6387c478bd9Sstevel@tonic-gate * is a link to another file on the changed side, and if it is, we 6397c478bd9Sstevel@tonic-gate * find the corresponding file on the unchanged side. 6407c478bd9Sstevel@tonic-gate * 6417c478bd9Sstevel@tonic-gate * cases we want to be able to handle: 6427c478bd9Sstevel@tonic-gate * 1. one or more links are created to a prexisting file 6437c478bd9Sstevel@tonic-gate * 2. a preexisting only link is renamed 6447c478bd9Sstevel@tonic-gate * 3. a rename of one of multiple links to a preexisting file 6457c478bd9Sstevel@tonic-gate * 4. a single file is created with multiple links 6467c478bd9Sstevel@tonic-gate */ 6477c478bd9Sstevel@tonic-gate struct file * 6487c478bd9Sstevel@tonic-gate find_link(struct file *fp, side_t srcdst) 6497c478bd9Sstevel@tonic-gate { struct file *lp; 6507c478bd9Sstevel@tonic-gate side_t chgside, tgtside; 6517c478bd9Sstevel@tonic-gate struct fileinfo *chgp, *tgtp, *basp, *fcp, *ftp; 6527c478bd9Sstevel@tonic-gate 6537c478bd9Sstevel@tonic-gate /* chg = side on which the change was noticed */ 6547c478bd9Sstevel@tonic-gate /* tgt = side to which the change is to be propagated */ 6557c478bd9Sstevel@tonic-gate chgside = (srcdst == OPT_SRC) ? OPT_DST : OPT_SRC; 6567c478bd9Sstevel@tonic-gate tgtside = (srcdst == OPT_SRC) ? OPT_SRC : OPT_DST; 6577c478bd9Sstevel@tonic-gate fcp = &fp->f_info[chgside]; 6587c478bd9Sstevel@tonic-gate ftp = &fp->f_info[tgtside]; 6597c478bd9Sstevel@tonic-gate 6607c478bd9Sstevel@tonic-gate /* 6617c478bd9Sstevel@tonic-gate * cases 1 and 3 6627c478bd9Sstevel@tonic-gate * 6637c478bd9Sstevel@tonic-gate * When a new link is created, we should be able to find 6647c478bd9Sstevel@tonic-gate * another file in the changed hierarchy that has the same 6657c478bd9Sstevel@tonic-gate * I-node number. We expect it to be on the changed list 6667c478bd9Sstevel@tonic-gate * because the link count will have gone up or because all 6677c478bd9Sstevel@tonic-gate * of the copies are new. If we find one, then the new file 6687c478bd9Sstevel@tonic-gate * on the receiving file should be a link to the corresponding 6697c478bd9Sstevel@tonic-gate * existing file. 6707c478bd9Sstevel@tonic-gate * 6717c478bd9Sstevel@tonic-gate * case 4 6727c478bd9Sstevel@tonic-gate * 6737c478bd9Sstevel@tonic-gate * the first link will be dealt with as a copy, but all 6747c478bd9Sstevel@tonic-gate * subsequent links should find an existing file analogous 6757c478bd9Sstevel@tonic-gate * to one of the links on the changed side, and create 6767c478bd9Sstevel@tonic-gate * corresponding links on the other side. 6777c478bd9Sstevel@tonic-gate * 6787c478bd9Sstevel@tonic-gate * in each of these cases, there should be multiple links 6797c478bd9Sstevel@tonic-gate * on the changed side. If the linkcount on the changed 6807c478bd9Sstevel@tonic-gate * side is one, we needn't bother searching for other links. 6817c478bd9Sstevel@tonic-gate */ 6827c478bd9Sstevel@tonic-gate if (fcp->f_nlink > 1) 6837c478bd9Sstevel@tonic-gate for (lp = changes; lp; lp = lp->f_rnext) { 6847c478bd9Sstevel@tonic-gate /* finding the same node doesn't count */ 6857c478bd9Sstevel@tonic-gate if (fp == lp) 6867c478bd9Sstevel@tonic-gate continue; 6877c478bd9Sstevel@tonic-gate 6887c478bd9Sstevel@tonic-gate tgtp = &lp->f_info[tgtside]; 6897c478bd9Sstevel@tonic-gate chgp = &lp->f_info[chgside]; 6907c478bd9Sstevel@tonic-gate 6917c478bd9Sstevel@tonic-gate /* 6927c478bd9Sstevel@tonic-gate * if the file doesn't already exist on the target side 6937c478bd9Sstevel@tonic-gate * we cannot make a link to it 6947c478bd9Sstevel@tonic-gate */ 6957c478bd9Sstevel@tonic-gate if (tgtp->f_mode == 0) 6967c478bd9Sstevel@tonic-gate continue; 6977c478bd9Sstevel@tonic-gate 6987c478bd9Sstevel@tonic-gate /* 6997c478bd9Sstevel@tonic-gate * if this is indeed a link, then the prospective file on 7007c478bd9Sstevel@tonic-gate * the changed side will have the same dev/inum as the file 7017c478bd9Sstevel@tonic-gate * we are looking for 7027c478bd9Sstevel@tonic-gate */ 7037c478bd9Sstevel@tonic-gate if (fcp->f_d_maj != chgp->f_d_maj) 7047c478bd9Sstevel@tonic-gate continue; 7057c478bd9Sstevel@tonic-gate if (fcp->f_d_min != chgp->f_d_min) 7067c478bd9Sstevel@tonic-gate continue; 7077c478bd9Sstevel@tonic-gate if (fcp->f_ino != chgp->f_ino) 7087c478bd9Sstevel@tonic-gate continue; 7097c478bd9Sstevel@tonic-gate 7107c478bd9Sstevel@tonic-gate /* 7117c478bd9Sstevel@tonic-gate * if the target side is already a link to this file, 7127c478bd9Sstevel@tonic-gate * then there is no new link to be created 7137c478bd9Sstevel@tonic-gate * FIX: how does this interact with copies over links 7147c478bd9Sstevel@tonic-gate */ 7157c478bd9Sstevel@tonic-gate if ((ftp->f_d_maj == tgtp->f_d_maj) && 7167c478bd9Sstevel@tonic-gate (ftp->f_d_min == tgtp->f_d_min) && 7177c478bd9Sstevel@tonic-gate (ftp->f_ino == tgtp->f_ino)) 7187c478bd9Sstevel@tonic-gate continue; 7197c478bd9Sstevel@tonic-gate 7207c478bd9Sstevel@tonic-gate /* 7217c478bd9Sstevel@tonic-gate * there is a pathological situation where a single file 7227c478bd9Sstevel@tonic-gate * might appear under multiple base directories. This is 7237c478bd9Sstevel@tonic-gate * damned awkward to detect in any other way, so we must 7247c478bd9Sstevel@tonic-gate * check to see if we have just found another database 7257c478bd9Sstevel@tonic-gate * instance for the same file (on the changed side). 7267c478bd9Sstevel@tonic-gate */ 7277c478bd9Sstevel@tonic-gate if ((fp->f_base != lp->f_base) && same_name(fp, lp, chgside)) 7287c478bd9Sstevel@tonic-gate continue; 7297c478bd9Sstevel@tonic-gate 7307c478bd9Sstevel@tonic-gate if (opt_debug & DBG_ANAL) 7317c478bd9Sstevel@tonic-gate fprintf(stderr, "ANAL: FIND LINK %s and %s\n", 7327c478bd9Sstevel@tonic-gate fp->f_fullname, lp->f_fullname); 7337c478bd9Sstevel@tonic-gate 7347c478bd9Sstevel@tonic-gate return (lp); 7357c478bd9Sstevel@tonic-gate } 7367c478bd9Sstevel@tonic-gate 7377c478bd9Sstevel@tonic-gate /* 7387c478bd9Sstevel@tonic-gate * case 2: a simple rename of the only link 7397c478bd9Sstevel@tonic-gate * 7407c478bd9Sstevel@tonic-gate * In this case, there may not be any other existing file on 7417c478bd9Sstevel@tonic-gate * the changed side that has the same I-node number. There 7427c478bd9Sstevel@tonic-gate * might, however, be a record of such a file in the baseline. 7437c478bd9Sstevel@tonic-gate * If we can find an identical file with a different name that 7447c478bd9Sstevel@tonic-gate * has recently disappeared, we have a likely rename. 7457c478bd9Sstevel@tonic-gate */ 7467c478bd9Sstevel@tonic-gate for (lp = changes; lp; lp = lp->f_rnext) { 7477c478bd9Sstevel@tonic-gate 7487c478bd9Sstevel@tonic-gate /* finding the same node doesn't count */ 7497c478bd9Sstevel@tonic-gate if (fp == lp) 7507c478bd9Sstevel@tonic-gate continue; 7517c478bd9Sstevel@tonic-gate 7527c478bd9Sstevel@tonic-gate tgtp = &lp->f_info[tgtside]; 7537c478bd9Sstevel@tonic-gate chgp = &lp->f_info[chgside]; 7547c478bd9Sstevel@tonic-gate 7557c478bd9Sstevel@tonic-gate /* 7567c478bd9Sstevel@tonic-gate * if the file still exists on the changed side this is 7577c478bd9Sstevel@tonic-gate * not a simple rename, and in fact the previous pass 7587c478bd9Sstevel@tonic-gate * would have found it. 7597c478bd9Sstevel@tonic-gate */ 7607c478bd9Sstevel@tonic-gate if (chgp->f_mode != 0) 7617c478bd9Sstevel@tonic-gate continue; 7627c478bd9Sstevel@tonic-gate 7637c478bd9Sstevel@tonic-gate /* 7647c478bd9Sstevel@tonic-gate * the inode number for the new link on the changed 7657c478bd9Sstevel@tonic-gate * side must match the inode number for the old link 7667c478bd9Sstevel@tonic-gate * from the baseline. 7677c478bd9Sstevel@tonic-gate */ 7687c478bd9Sstevel@tonic-gate if (fcp->f_d_maj != ((srcdst == OPT_SRC) ? lp->f_d_maj 7697c478bd9Sstevel@tonic-gate : lp->f_s_maj)) 7707c478bd9Sstevel@tonic-gate continue; 7717c478bd9Sstevel@tonic-gate if (fcp->f_d_min != ((srcdst == OPT_SRC) ? lp->f_d_min 7727c478bd9Sstevel@tonic-gate : lp->f_s_min)) 7737c478bd9Sstevel@tonic-gate continue; 7747c478bd9Sstevel@tonic-gate if (fcp->f_ino != ((srcdst == OPT_SRC) ? lp->f_d_inum 7757c478bd9Sstevel@tonic-gate : lp->f_s_inum)) 7767c478bd9Sstevel@tonic-gate continue; 7777c478bd9Sstevel@tonic-gate 7787c478bd9Sstevel@tonic-gate /* finding a file we are already linked to doesn't help */ 7797c478bd9Sstevel@tonic-gate if ((ftp->f_d_maj == tgtp->f_d_maj) && 7807c478bd9Sstevel@tonic-gate (ftp->f_d_min == tgtp->f_d_min) && 7817c478bd9Sstevel@tonic-gate (ftp->f_ino == tgtp->f_ino)) 7827c478bd9Sstevel@tonic-gate continue; 7837c478bd9Sstevel@tonic-gate 7847c478bd9Sstevel@tonic-gate /* 7857c478bd9Sstevel@tonic-gate * there is a danger that we will confuse an 7867c478bd9Sstevel@tonic-gate * inode reallocation with a rename. We should 7877c478bd9Sstevel@tonic-gate * only consider this to be a rename if the 7887c478bd9Sstevel@tonic-gate * new file is identical to the old one 7897c478bd9Sstevel@tonic-gate */ 7907c478bd9Sstevel@tonic-gate basp = &lp->f_info[OPT_BASE]; 7917c478bd9Sstevel@tonic-gate if (fcp->f_type != basp->f_type) 7927c478bd9Sstevel@tonic-gate continue; 7937c478bd9Sstevel@tonic-gate if (fcp->f_size != basp->f_size) 7947c478bd9Sstevel@tonic-gate continue; 7957c478bd9Sstevel@tonic-gate if (fcp->f_mode != basp->f_mode) 7967c478bd9Sstevel@tonic-gate continue; 7977c478bd9Sstevel@tonic-gate if (fcp->f_uid != basp->f_uid) 7987c478bd9Sstevel@tonic-gate continue; 7997c478bd9Sstevel@tonic-gate if (fcp->f_gid != basp->f_gid) 8007c478bd9Sstevel@tonic-gate continue; 8017c478bd9Sstevel@tonic-gate 8027c478bd9Sstevel@tonic-gate if (opt_debug & DBG_ANAL) 8037c478bd9Sstevel@tonic-gate fprintf(stderr, "ANAL: FIND RENAME %s and %s\n", 8047c478bd9Sstevel@tonic-gate fp->f_fullname, lp->f_fullname); 8057c478bd9Sstevel@tonic-gate 8067c478bd9Sstevel@tonic-gate return (lp); 8077c478bd9Sstevel@tonic-gate } 8087c478bd9Sstevel@tonic-gate 8097c478bd9Sstevel@tonic-gate return (0); 8107c478bd9Sstevel@tonic-gate } 8117c478bd9Sstevel@tonic-gate 8127c478bd9Sstevel@tonic-gate /* 8137c478bd9Sstevel@tonic-gate * routine: 8147c478bd9Sstevel@tonic-gate * has_other_links 8157c478bd9Sstevel@tonic-gate * 8167c478bd9Sstevel@tonic-gate * purpose: 8177c478bd9Sstevel@tonic-gate * to determine whether or not there is more that one link to a 8187c478bd9Sstevel@tonic-gate * particular file. We are willing to delete a link to a file 8197c478bd9Sstevel@tonic-gate * that has changed if we will still have other links to it. 8207c478bd9Sstevel@tonic-gate * The trick here is that we only care about links under our 8217c478bd9Sstevel@tonic-gate * dominion. 8227c478bd9Sstevel@tonic-gate * 8237c478bd9Sstevel@tonic-gate * parameters: 8247c478bd9Sstevel@tonic-gate * file pointer to node we are interested in 8257c478bd9Sstevel@tonic-gate * which side we are looking to additional links on 8267c478bd9Sstevel@tonic-gate * 8277c478bd9Sstevel@tonic-gate * returns: 8287c478bd9Sstevel@tonic-gate * TRUE if there are multiple links 8297c478bd9Sstevel@tonic-gate * FALSE if this is the only one we know of 8307c478bd9Sstevel@tonic-gate */ 8317c478bd9Sstevel@tonic-gate bool_t 8327c478bd9Sstevel@tonic-gate has_other_links(struct file *fp, side_t srcdst) 8337c478bd9Sstevel@tonic-gate { struct file *lp; 8347c478bd9Sstevel@tonic-gate struct fileinfo *fip, *lip; 8357c478bd9Sstevel@tonic-gate 8367c478bd9Sstevel@tonic-gate fip = &fp->f_info[srcdst]; 8377c478bd9Sstevel@tonic-gate 8387c478bd9Sstevel@tonic-gate /* if the link count is one, there couldn't be others */ 8397c478bd9Sstevel@tonic-gate if (fip->f_nlink < 2) 8407c478bd9Sstevel@tonic-gate return (FALSE); 8417c478bd9Sstevel@tonic-gate 8427c478bd9Sstevel@tonic-gate /* look for any other files for the same inode */ 8437c478bd9Sstevel@tonic-gate for (lp = changes; lp; lp = lp->f_rnext) { 8447c478bd9Sstevel@tonic-gate /* finding the same node doesn't count */ 8457c478bd9Sstevel@tonic-gate if (fp == lp) 8467c478bd9Sstevel@tonic-gate continue; 8477c478bd9Sstevel@tonic-gate 8487c478bd9Sstevel@tonic-gate lip = &lp->f_info[srcdst]; 8497c478bd9Sstevel@tonic-gate 8507c478bd9Sstevel@tonic-gate /* 8517c478bd9Sstevel@tonic-gate * file must still exist on this side 8527c478bd9Sstevel@tonic-gate */ 8537c478bd9Sstevel@tonic-gate if (lip->f_mode == 0) 8547c478bd9Sstevel@tonic-gate continue; 8557c478bd9Sstevel@tonic-gate 8567c478bd9Sstevel@tonic-gate /* 8577c478bd9Sstevel@tonic-gate * if this is indeed a link, then the prospective file on 8587c478bd9Sstevel@tonic-gate * the changed side will have the same dev/inum as the file 8597c478bd9Sstevel@tonic-gate * we are looking for 8607c478bd9Sstevel@tonic-gate */ 8617c478bd9Sstevel@tonic-gate if (lip->f_d_maj != fip->f_d_maj) 8627c478bd9Sstevel@tonic-gate continue; 8637c478bd9Sstevel@tonic-gate if (lip->f_d_min != fip->f_d_min) 8647c478bd9Sstevel@tonic-gate continue; 8657c478bd9Sstevel@tonic-gate if (lip->f_ino != fip->f_ino) 8667c478bd9Sstevel@tonic-gate continue; 8677c478bd9Sstevel@tonic-gate 8687c478bd9Sstevel@tonic-gate /* 8697c478bd9Sstevel@tonic-gate * we have found at least one other link 8707c478bd9Sstevel@tonic-gate */ 8717c478bd9Sstevel@tonic-gate return (TRUE); 8727c478bd9Sstevel@tonic-gate } 8737c478bd9Sstevel@tonic-gate 8747c478bd9Sstevel@tonic-gate return (FALSE); 8757c478bd9Sstevel@tonic-gate } 8767c478bd9Sstevel@tonic-gate 8777c478bd9Sstevel@tonic-gate /* 8787c478bd9Sstevel@tonic-gate * routine: 8797c478bd9Sstevel@tonic-gate * link_update 8807c478bd9Sstevel@tonic-gate * 8817c478bd9Sstevel@tonic-gate * purpose: 8827c478bd9Sstevel@tonic-gate * to propoagate a stat change to all other file nodes that 8837c478bd9Sstevel@tonic-gate * correspond to the same I-node on the changed side 8847c478bd9Sstevel@tonic-gate * 8857c478bd9Sstevel@tonic-gate * parameters: 8867c478bd9Sstevel@tonic-gate * file pointer for the updated file 8877c478bd9Sstevel@tonic-gate * which side was changed 8887c478bd9Sstevel@tonic-gate * 8897c478bd9Sstevel@tonic-gate * returns: 8907c478bd9Sstevel@tonic-gate * void 8917c478bd9Sstevel@tonic-gate * 8927c478bd9Sstevel@tonic-gate * notes: 8937c478bd9Sstevel@tonic-gate * if we have copied onto a file, we have copied onto all 8947c478bd9Sstevel@tonic-gate * of its links, but since we do all stats before we do any 8957c478bd9Sstevel@tonic-gate * copies, the stat information recently collected for links 8967c478bd9Sstevel@tonic-gate * is no longer up-to-date, and this would result in incorrect 8977c478bd9Sstevel@tonic-gate * reconciliation (redundant copies). 8987c478bd9Sstevel@tonic-gate * 8997c478bd9Sstevel@tonic-gate * There is an assumption here that all links to a changed 9007c478bd9Sstevel@tonic-gate * file will be in the change list. This is true for almost 9017c478bd9Sstevel@tonic-gate * all cases not involving restriction. If we do fail to 9027c478bd9Sstevel@tonic-gate * update the baseline for a file that was off the change list, 9037c478bd9Sstevel@tonic-gate * the worst that is likely to happen is that we will think 9047c478bd9Sstevel@tonic-gate * it changed later (but will almost surely find that both 9057c478bd9Sstevel@tonic-gate * copies agree). 9067c478bd9Sstevel@tonic-gate */ 9077c478bd9Sstevel@tonic-gate void 9087c478bd9Sstevel@tonic-gate link_update(struct file *fp, side_t which) 9097c478bd9Sstevel@tonic-gate { struct file *lp; 9107c478bd9Sstevel@tonic-gate 9117c478bd9Sstevel@tonic-gate for (lp = changes; lp; lp = lp->f_rnext) { 9127c478bd9Sstevel@tonic-gate /* finding the current entry doesn't count */ 9137c478bd9Sstevel@tonic-gate if (lp == fp) 9147c478bd9Sstevel@tonic-gate continue; 9157c478bd9Sstevel@tonic-gate 9167c478bd9Sstevel@tonic-gate /* look for same i#, maj, min on changed side */ 9177c478bd9Sstevel@tonic-gate if (lp->f_info[which].f_ino != fp->f_info[which].f_ino) 9187c478bd9Sstevel@tonic-gate continue; 9197c478bd9Sstevel@tonic-gate if (lp->f_info[which].f_d_maj != fp->f_info[which].f_d_maj) 9207c478bd9Sstevel@tonic-gate continue; 9217c478bd9Sstevel@tonic-gate if (lp->f_info[which].f_d_min != fp->f_info[which].f_d_min) 9227c478bd9Sstevel@tonic-gate continue; 9237c478bd9Sstevel@tonic-gate 9247c478bd9Sstevel@tonic-gate /* 9257c478bd9Sstevel@tonic-gate * this appears to be another link to the same file 9267c478bd9Sstevel@tonic-gate * so the updated stat information for one must be 9277c478bd9Sstevel@tonic-gate * correct for the other. 9287c478bd9Sstevel@tonic-gate */ 9297c478bd9Sstevel@tonic-gate lp->f_info[which].f_type = fp->f_info[which].f_type; 9307c478bd9Sstevel@tonic-gate lp->f_info[which].f_size = fp->f_info[which].f_size; 9317c478bd9Sstevel@tonic-gate lp->f_info[which].f_mode = fp->f_info[which].f_mode; 9327c478bd9Sstevel@tonic-gate lp->f_info[which].f_uid = fp->f_info[which].f_uid; 9337c478bd9Sstevel@tonic-gate lp->f_info[which].f_gid = fp->f_info[which].f_gid; 9347c478bd9Sstevel@tonic-gate lp->f_info[which].f_modtime = fp->f_info[which].f_modtime; 9357c478bd9Sstevel@tonic-gate lp->f_info[which].f_modns = fp->f_info[which].f_modns; 9367c478bd9Sstevel@tonic-gate lp->f_info[which].f_nlink = fp->f_info[which].f_nlink; 9377c478bd9Sstevel@tonic-gate lp->f_info[which].f_rd_maj = fp->f_info[which].f_rd_maj; 9387c478bd9Sstevel@tonic-gate lp->f_info[which].f_rd_min = fp->f_info[which].f_rd_min; 9397c478bd9Sstevel@tonic-gate 9407c478bd9Sstevel@tonic-gate if (opt_debug & DBG_STAT) 9417c478bd9Sstevel@tonic-gate fprintf(stderr, 9427c478bd9Sstevel@tonic-gate "STAT: UPDATE LINK, file=%s, mod=%08lx.%08lx\n", 9437c478bd9Sstevel@tonic-gate lp->f_name, lp->f_info[which].f_modtime, 9447c478bd9Sstevel@tonic-gate lp->f_info[which].f_modns); 9457c478bd9Sstevel@tonic-gate } 9467c478bd9Sstevel@tonic-gate } 9477c478bd9Sstevel@tonic-gate 9487c478bd9Sstevel@tonic-gate /* 9497c478bd9Sstevel@tonic-gate * routine: 9507c478bd9Sstevel@tonic-gate * queue_file 9517c478bd9Sstevel@tonic-gate * 9527c478bd9Sstevel@tonic-gate * purpose: 9537c478bd9Sstevel@tonic-gate * append a file to the list of needed reconciliations 9547c478bd9Sstevel@tonic-gate * 9557c478bd9Sstevel@tonic-gate * parameters: 9567c478bd9Sstevel@tonic-gate * pointer to file 9577c478bd9Sstevel@tonic-gate * 9587c478bd9Sstevel@tonic-gate * notes: 9597c478bd9Sstevel@tonic-gate * when a request is appended to the reconciliation list, 9607c478bd9Sstevel@tonic-gate * we fill in the full name. We delayed this in hopes that 9617c478bd9Sstevel@tonic-gate * it wouldn't be necessary (saving cycles and memory) 9627c478bd9Sstevel@tonic-gate * 9637c478bd9Sstevel@tonic-gate * There is some funny business with modification times. 9647c478bd9Sstevel@tonic-gate * In general, we queue files in order of the latest modification 9657c478bd9Sstevel@tonic-gate * time so that propagations preserve relative ordering. There 9667c478bd9Sstevel@tonic-gate * are, however, a few important exceptions: 9677c478bd9Sstevel@tonic-gate * 1. all directory creations happen at time zero, 9687c478bd9Sstevel@tonic-gate * so that they are created before any files can 9697c478bd9Sstevel@tonic-gate * be added to them. 9707c478bd9Sstevel@tonic-gate * 2. all directory deletions happen at time infinity-depth, 9717c478bd9Sstevel@tonic-gate * so that everything else can be removed before the 9727c478bd9Sstevel@tonic-gate * directories themselves are removed. 9737c478bd9Sstevel@tonic-gate * 3. all file deletions happen at time infinity-depth 9747c478bd9Sstevel@tonic-gate * so that (in renames) the links will preceed the unlinks. 9757c478bd9Sstevel@tonic-gate */ 9767c478bd9Sstevel@tonic-gate static void 9777c478bd9Sstevel@tonic-gate queue_file(struct file *fp) 9787c478bd9Sstevel@tonic-gate { struct file **pp, *np; 9797c478bd9Sstevel@tonic-gate 9807c478bd9Sstevel@tonic-gate #define TIME_ZERO 0L /* the earliest possible time */ 9817c478bd9Sstevel@tonic-gate #define TIME_LONG 0x7FFFFFFF /* the latest possible time */ 9827c478bd9Sstevel@tonic-gate 9837c478bd9Sstevel@tonic-gate /* 9847c478bd9Sstevel@tonic-gate * figure out the modification time for sequencing purposes 9857c478bd9Sstevel@tonic-gate */ 9867c478bd9Sstevel@tonic-gate if ((fp->f_srcdiffs|fp->f_dstdiffs) & D_DELETE) { 9877c478bd9Sstevel@tonic-gate /* 9887c478bd9Sstevel@tonic-gate * deletions are performed last, and depth first 9897c478bd9Sstevel@tonic-gate */ 9907c478bd9Sstevel@tonic-gate fp->f_modtime = TIME_LONG - fp->f_depth; 9917c478bd9Sstevel@tonic-gate } else if (fp->f_info[OPT_SRC].f_type != S_IFDIR && 9927c478bd9Sstevel@tonic-gate fp->f_info[OPT_DST].f_type != S_IFDIR) { 9937c478bd9Sstevel@tonic-gate /* 9947c478bd9Sstevel@tonic-gate * for most files we use the latest mod time 9957c478bd9Sstevel@tonic-gate */ 9967c478bd9Sstevel@tonic-gate fp->f_modtime = fp->f_info[OPT_SRC].f_modtime; 9977c478bd9Sstevel@tonic-gate fp->f_modns = fp->f_info[OPT_SRC].f_modns; 9987c478bd9Sstevel@tonic-gate if (fp->f_modtime < fp->f_info[OPT_DST].f_modtime) { 9997c478bd9Sstevel@tonic-gate fp->f_modtime = fp->f_info[OPT_DST].f_modtime; 10007c478bd9Sstevel@tonic-gate fp->f_modns = fp->f_info[OPT_DST].f_modns; 10017c478bd9Sstevel@tonic-gate } 10027c478bd9Sstevel@tonic-gate } else { 10037c478bd9Sstevel@tonic-gate /* 10047c478bd9Sstevel@tonic-gate * new directory creations need to happen before anything 10057c478bd9Sstevel@tonic-gate * else and are automatically sequenced in traversal order 10067c478bd9Sstevel@tonic-gate */ 10077c478bd9Sstevel@tonic-gate fp->f_modtime = TIME_ZERO; 10087c478bd9Sstevel@tonic-gate } 10097c478bd9Sstevel@tonic-gate 10107c478bd9Sstevel@tonic-gate /* 10117c478bd9Sstevel@tonic-gate * insertion is time ordered, and for equal times, 10127c478bd9Sstevel@tonic-gate * insertions is in (pre-order) traversal order 10137c478bd9Sstevel@tonic-gate */ 10147c478bd9Sstevel@tonic-gate for (pp = &changes; (np = *pp) != 0; pp = &np->f_rnext) { 10157c478bd9Sstevel@tonic-gate if (fp->f_modtime > np->f_modtime) 10167c478bd9Sstevel@tonic-gate continue; 10177c478bd9Sstevel@tonic-gate if (fp->f_modtime < np->f_modtime) 10187c478bd9Sstevel@tonic-gate break; 10197c478bd9Sstevel@tonic-gate if (fp->f_modns < np->f_modns) 10207c478bd9Sstevel@tonic-gate break; 10217c478bd9Sstevel@tonic-gate } 10227c478bd9Sstevel@tonic-gate 10237c478bd9Sstevel@tonic-gate fp->f_fullname = strdup(get_name(fp)); 10247c478bd9Sstevel@tonic-gate fp->f_rnext = np; 10257c478bd9Sstevel@tonic-gate *pp = fp; 10267c478bd9Sstevel@tonic-gate } 10277c478bd9Sstevel@tonic-gate 10287c478bd9Sstevel@tonic-gate 10297c478bd9Sstevel@tonic-gate /* 10307c478bd9Sstevel@tonic-gate * routines: 10317c478bd9Sstevel@tonic-gate * push_name/pop_name/get_name 10327c478bd9Sstevel@tonic-gate * 10337c478bd9Sstevel@tonic-gate * purpose: 10347c478bd9Sstevel@tonic-gate * maintain a name stack so we can form name of a particular file 10357c478bd9Sstevel@tonic-gate * as the concatenation of all of the names between it and the 10367c478bd9Sstevel@tonic-gate * (know to be fully qualified) base directory. 10377c478bd9Sstevel@tonic-gate * 10387c478bd9Sstevel@tonic-gate * notes: 10397c478bd9Sstevel@tonic-gate * we go to this trouble because most files never change and 10407c478bd9Sstevel@tonic-gate * so we don't need to associate full names with every one. 10417c478bd9Sstevel@tonic-gate * This stack is maintained during analysis, and if we decide 10427c478bd9Sstevel@tonic-gate * to add a file to the reconciliation list, we can use the 10437c478bd9Sstevel@tonic-gate * stack to generate a fully qualified name at that time. 10447c478bd9Sstevel@tonic-gate * 10457c478bd9Sstevel@tonic-gate * we compress out '/./' when we return a name. Given that the 10467c478bd9Sstevel@tonic-gate * stack was built by a tree walk, the only place a /./ should 10477c478bd9Sstevel@tonic-gate * appear is at the first level after the base ... but there 10487c478bd9Sstevel@tonic-gate * are legitimate ways for them to appear there. 10497c478bd9Sstevel@tonic-gate * 10507c478bd9Sstevel@tonic-gate * these names can get deep, so we dynamically size our name buffer 10517c478bd9Sstevel@tonic-gate */ 10527c478bd9Sstevel@tonic-gate static const char *namestack[ MAX_DEPTH + 1 ]; 10537c478bd9Sstevel@tonic-gate static int namedepth = 0; 10547c478bd9Sstevel@tonic-gate static int namelen = 0; 10557c478bd9Sstevel@tonic-gate 10567c478bd9Sstevel@tonic-gate void 10577c478bd9Sstevel@tonic-gate push_name(const char *name) 10587c478bd9Sstevel@tonic-gate { 10597c478bd9Sstevel@tonic-gate namestack[ namedepth++ ] = name; 10607c478bd9Sstevel@tonic-gate namelen += 2 + strlen(name); 10617c478bd9Sstevel@tonic-gate 10627c478bd9Sstevel@tonic-gate /* make sure we don't overflow our name stack */ 10637c478bd9Sstevel@tonic-gate if (namedepth >= MAX_DEPTH) { 10647c478bd9Sstevel@tonic-gate fprintf(stderr, gettext(ERR_deep), name); 10657c478bd9Sstevel@tonic-gate exit(ERR_OTHER); 10667c478bd9Sstevel@tonic-gate } 10677c478bd9Sstevel@tonic-gate } 10687c478bd9Sstevel@tonic-gate 10697c478bd9Sstevel@tonic-gate void 10707c478bd9Sstevel@tonic-gate pop_name(void) 10717c478bd9Sstevel@tonic-gate { 10727c478bd9Sstevel@tonic-gate namelen -= 2 + strlen(namestack[--namedepth]); 10737c478bd9Sstevel@tonic-gate namestack[ namedepth ] = 0; 10747c478bd9Sstevel@tonic-gate 10757c478bd9Sstevel@tonic-gate #ifdef DBG_ERRORS 10767c478bd9Sstevel@tonic-gate /* just a little sanity check here */ 10777c478bd9Sstevel@tonic-gate if (namedepth <= 0) { 10787c478bd9Sstevel@tonic-gate if (namedepth < 0) { 10797c478bd9Sstevel@tonic-gate fprintf(stderr, "ASSERTION FAILURE: namedepth < 0\n"); 10807c478bd9Sstevel@tonic-gate exit(ERR_OTHER); 10817c478bd9Sstevel@tonic-gate } else if (namelen != 0) { 10827c478bd9Sstevel@tonic-gate fprintf(stderr, "ASSERTION FAILURE: namelen != 0\n"); 10837c478bd9Sstevel@tonic-gate exit(ERR_OTHER); 10847c478bd9Sstevel@tonic-gate } 10857c478bd9Sstevel@tonic-gate } 10867c478bd9Sstevel@tonic-gate #endif 10877c478bd9Sstevel@tonic-gate } 10887c478bd9Sstevel@tonic-gate 10897c478bd9Sstevel@tonic-gate char 10907c478bd9Sstevel@tonic-gate *get_name(struct file *fp) 10917c478bd9Sstevel@tonic-gate { int i; 10927c478bd9Sstevel@tonic-gate static char *namebuf = 0; 10937c478bd9Sstevel@tonic-gate static int buflen = 0; 10947c478bd9Sstevel@tonic-gate 10957c478bd9Sstevel@tonic-gate /* make sure we have an adequate buffer */ 10967c478bd9Sstevel@tonic-gate i = namelen + 1 + strlen(fp->f_name); 10977c478bd9Sstevel@tonic-gate if (buflen < i) { 10987c478bd9Sstevel@tonic-gate for (buflen = MAX_PATH; buflen < i; buflen += MAX_NAME); 10997c478bd9Sstevel@tonic-gate namebuf = (char *) realloc(namebuf, buflen); 11007c478bd9Sstevel@tonic-gate } 11017c478bd9Sstevel@tonic-gate 11027c478bd9Sstevel@tonic-gate /* assemble the name */ 11037c478bd9Sstevel@tonic-gate namebuf[0] = 0; 11047c478bd9Sstevel@tonic-gate for (i = 0; i < namedepth; i++) { 11057c478bd9Sstevel@tonic-gate if (strcmp(namestack[i], ".")) { 11067c478bd9Sstevel@tonic-gate strcat(namebuf, namestack[i]); 11077c478bd9Sstevel@tonic-gate strcat(namebuf, "/"); 11087c478bd9Sstevel@tonic-gate } 11097c478bd9Sstevel@tonic-gate } 11107c478bd9Sstevel@tonic-gate 11117c478bd9Sstevel@tonic-gate strcat(namebuf, fp->f_name); 11127c478bd9Sstevel@tonic-gate 11137c478bd9Sstevel@tonic-gate return (namebuf); 11147c478bd9Sstevel@tonic-gate } 1115