10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance
70Sstevel@tonic-gate * with the License.
80Sstevel@tonic-gate *
90Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate * See the License for the specific language governing permissions
120Sstevel@tonic-gate * and limitations under the License.
130Sstevel@tonic-gate *
140Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate *
200Sstevel@tonic-gate * CDDL HEADER END
210Sstevel@tonic-gate */
220Sstevel@tonic-gate /*
230Sstevel@tonic-gate * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved
240Sstevel@tonic-gate *
250Sstevel@tonic-gate * module:
260Sstevel@tonic-gate * anal.c
270Sstevel@tonic-gate *
280Sstevel@tonic-gate * purpose:
290Sstevel@tonic-gate * routines to analyze the file trees and figure out what has changed
300Sstevel@tonic-gate * and queue files for reconciliation. It also contains tree enumeration
310Sstevel@tonic-gate * routines to for other purposes (pruning and link location).
320Sstevel@tonic-gate *
330Sstevel@tonic-gate * contents:
340Sstevel@tonic-gate *
350Sstevel@tonic-gate * change analysis:
360Sstevel@tonic-gate * analyze .... (top level) analyze all files in the tree for changes
370Sstevel@tonic-gate * summary .... print out change/reconciliation statistics for each base
380Sstevel@tonic-gate * check_file . (static) look for changes and queue file for reconciliation
390Sstevel@tonic-gate * check_changes (static) figure out if a particular file has changed
400Sstevel@tonic-gate * queue_file . (static) add a file to the reconciliation list
410Sstevel@tonic-gate *
420Sstevel@tonic-gate * other tree enumeration functions:
430Sstevel@tonic-gate * prune_file . (static) recursive descent and actual pruning
440Sstevel@tonic-gate * prune ...... (top level) initiate pruning analysis for nonexistant files
450Sstevel@tonic-gate * find_link .. look for other files to which a file may be a link
460Sstevel@tonic-gate * link_update. propagate changed stat info to all other links
470Sstevel@tonic-gate * same_name .. (static) figure out if two nodes describe same file
480Sstevel@tonic-gate *
490Sstevel@tonic-gate * misc:
500Sstevel@tonic-gate * push_name .. maintain a running full pathname as we descend
510Sstevel@tonic-gate * pop_name ... maintain a running full pathname as we pop back
520Sstevel@tonic-gate * get_name ... return full pathname for the current file
530Sstevel@tonic-gate *
540Sstevel@tonic-gate * notes:
550Sstevel@tonic-gate * analysis is limited to files that were evaluated in the previous
560Sstevel@tonic-gate * pass ... since we don't have complete information about files that
570Sstevel@tonic-gate * were not evaluated in the previous pass.
580Sstevel@tonic-gate */
59*3517Smp204432 #pragma ident "%Z%%M% %I% %E% SMI"
600Sstevel@tonic-gate
610Sstevel@tonic-gate #include <stdio.h>
620Sstevel@tonic-gate #include <stdlib.h>
630Sstevel@tonic-gate #include <strings.h>
640Sstevel@tonic-gate
650Sstevel@tonic-gate #include "messages.h"
660Sstevel@tonic-gate #include "filesync.h"
670Sstevel@tonic-gate #include "database.h"
680Sstevel@tonic-gate #include "debug.h"
690Sstevel@tonic-gate
700Sstevel@tonic-gate /*
710Sstevel@tonic-gate * routines:
720Sstevel@tonic-gate */
730Sstevel@tonic-gate void push_name(const char *);
740Sstevel@tonic-gate void pop_name();
750Sstevel@tonic-gate char *get_name(struct file *);
760Sstevel@tonic-gate static errmask_t check_file(struct file *fp);
770Sstevel@tonic-gate static diffmask_t check_changes(struct file *fp, int first, int second);
780Sstevel@tonic-gate static int prune_file(struct file *fp);
790Sstevel@tonic-gate static void queue_file(struct file *fp);
800Sstevel@tonic-gate
810Sstevel@tonic-gate /*
820Sstevel@tonic-gate * globals
830Sstevel@tonic-gate */
840Sstevel@tonic-gate static struct file *changes; /* list of files to be reconciled */
850Sstevel@tonic-gate
860Sstevel@tonic-gate static long total_files; /* total number of files being considered */
870Sstevel@tonic-gate static long est_deletes; /* estimated number of files to be deleted */
880Sstevel@tonic-gate static long est_rmdirs; /* est rmdirs of non-empty directories */
890Sstevel@tonic-gate
900Sstevel@tonic-gate int inum_changes; /* LISTed directories whose I#s changed */
910Sstevel@tonic-gate
920Sstevel@tonic-gate /*
930Sstevel@tonic-gate * routine:
940Sstevel@tonic-gate * analyze
950Sstevel@tonic-gate *
960Sstevel@tonic-gate * purpose:
970Sstevel@tonic-gate * top level routine for the analysis/reconciliation process
980Sstevel@tonic-gate *
990Sstevel@tonic-gate * parameters:
1000Sstevel@tonic-gate * none
1010Sstevel@tonic-gate *
1020Sstevel@tonic-gate * returns:
1030Sstevel@tonic-gate * error mask
1040Sstevel@tonic-gate *
1050Sstevel@tonic-gate * notes:
1060Sstevel@tonic-gate * a critical side effect of this routine is the creation of
1070Sstevel@tonic-gate * the reconciliation list, an ordered list of files that
1080Sstevel@tonic-gate * needed to be processed in the subsequent reconciliation pass
1090Sstevel@tonic-gate */
1100Sstevel@tonic-gate errmask_t
analyze()1110Sstevel@tonic-gate analyze()
1120Sstevel@tonic-gate { struct base *bp;
1130Sstevel@tonic-gate struct file *fp;
1140Sstevel@tonic-gate int errs = 0;
1150Sstevel@tonic-gate int err;
1160Sstevel@tonic-gate int percentage;
1170Sstevel@tonic-gate bool_t aborted = FALSE;
1180Sstevel@tonic-gate char msgbuf[MAX_LINE];
1190Sstevel@tonic-gate
1200Sstevel@tonic-gate /*
1210Sstevel@tonic-gate * run through all bases and directories looking for files
1220Sstevel@tonic-gate * that have been renamed. This must be done before the
1230Sstevel@tonic-gate * difference analysis because a directory rename can introduce
1240Sstevel@tonic-gate * radical restructuring into a name-based tree.
1250Sstevel@tonic-gate */
1260Sstevel@tonic-gate for (bp = bases; bp; bp = bp->b_next) {
1270Sstevel@tonic-gate for (fp = bp->b_files; fp; fp = fp->f_next)
1280Sstevel@tonic-gate if (fp->f_flags & F_EVALUATE)
1290Sstevel@tonic-gate errs |= find_renames(fp);
1300Sstevel@tonic-gate }
1310Sstevel@tonic-gate
1320Sstevel@tonic-gate /*
1330Sstevel@tonic-gate * run through all bases and files looking for candidates
1340Sstevel@tonic-gate * note, however that we only descend into trees that have
1350Sstevel@tonic-gate * the evaluate flag turned on. As a result of new rules or
1360Sstevel@tonic-gate * restriction arguments, we may be deliberatly ignoring
1370Sstevel@tonic-gate * large amounts of the baseline. This means we won't do
1380Sstevel@tonic-gate * any stats to update the information in those nodes, and
1390Sstevel@tonic-gate * they will be written back just as they were.
1400Sstevel@tonic-gate *
1410Sstevel@tonic-gate * note that there is code to prune out baseline nodes for
1420Sstevel@tonic-gate * files that no longer exist, but that code is in reconcile
1430Sstevel@tonic-gate * and will never get a chance to run on nodes that aren't
1440Sstevel@tonic-gate * analyzed.
1450Sstevel@tonic-gate *
1460Sstevel@tonic-gate * we also want to run though all nodes with STAT errors
1470Sstevel@tonic-gate * so that we can put them on the reconciliation list.
1480Sstevel@tonic-gate */
1490Sstevel@tonic-gate for (bp = bases; bp; bp = bp->b_next) {
1500Sstevel@tonic-gate for (fp = bp->b_files; fp; fp = fp->f_next)
1510Sstevel@tonic-gate if (fp->f_flags & (F_EVALUATE|F_STAT_ERROR))
1520Sstevel@tonic-gate errs |= check_file(fp);
1530Sstevel@tonic-gate }
1540Sstevel@tonic-gate
1550Sstevel@tonic-gate /*
1560Sstevel@tonic-gate * my greatest fear is that someday, somehow, by messing with
1570Sstevel@tonic-gate * variables or baselines or who-knows-what, that someone will
1580Sstevel@tonic-gate * run a reconciliation against a large tree that doesn't correspond
1590Sstevel@tonic-gate * to the baseline, and I will infer that a bazillion files have
1600Sstevel@tonic-gate * been deleted and will propagate the slaughter before anyone
1610Sstevel@tonic-gate * can say somebody stop that maniac.
1620Sstevel@tonic-gate *
1630Sstevel@tonic-gate * in order to prevent such a possibility, we have a few different
1640Sstevel@tonic-gate * sanity checks. There is, of course, a tradeoff here between
1650Sstevel@tonic-gate * danger and irritation. The current set of heuristics for whether
1660Sstevel@tonic-gate * or not to generate a warning are (any of)
1670Sstevel@tonic-gate *
1680Sstevel@tonic-gate * at least CONFIRM_MIN files have been deleted AND
1690Sstevel@tonic-gate * CONFIRM_PCT of all files have been deleted
1700Sstevel@tonic-gate *
1710Sstevel@tonic-gate * the inode number on a LISTed directory has changed
1720Sstevel@tonic-gate *
1730Sstevel@tonic-gate * a non-empty directory has been deleted.
1740Sstevel@tonic-gate */
1750Sstevel@tonic-gate msgbuf[0] = 0;
1760Sstevel@tonic-gate
1770Sstevel@tonic-gate percentage = (est_deletes * 100) / (total_files ? total_files : 1);
1780Sstevel@tonic-gate if (est_deletes >= CONFIRM_MIN && percentage >= CONFIRM_PCT)
1790Sstevel@tonic-gate sprintf(msgbuf, gettext(WARN_deletes), est_deletes);
1800Sstevel@tonic-gate else if (inum_changes > 0)
1810Sstevel@tonic-gate sprintf(msgbuf, gettext(WARN_ichange), inum_changes);
1820Sstevel@tonic-gate else if (est_rmdirs)
1830Sstevel@tonic-gate sprintf(msgbuf, gettext(WARN_rmdirs), est_rmdirs);
1840Sstevel@tonic-gate
1850Sstevel@tonic-gate if (msgbuf[0])
1860Sstevel@tonic-gate confirm(msgbuf);
1870Sstevel@tonic-gate
1880Sstevel@tonic-gate /*
1890Sstevel@tonic-gate * TRICK:
1900Sstevel@tonic-gate * the change list contains both files that have changed
1910Sstevel@tonic-gate * (and probably warrant reconciliation) and files that
1920Sstevel@tonic-gate * we couldn't get up-to-date stat information on. The
1930Sstevel@tonic-gate * latter files should just be flagged as being in conflict
1940Sstevel@tonic-gate * so they can be reported in the summary. The same is
1950Sstevel@tonic-gate * true of all subsequent files if we abort reconciliation.
1960Sstevel@tonic-gate */
1970Sstevel@tonic-gate for (fp = changes; fp; fp = fp->f_rnext)
1980Sstevel@tonic-gate if (aborted || (fp->f_flags & F_STAT_ERROR)) {
1990Sstevel@tonic-gate fp->f_flags |= F_CONFLICT;
2000Sstevel@tonic-gate /* if it isn't in the baseline yet, don't add it */
2010Sstevel@tonic-gate if ((fp->f_flags & F_IN_BASELINE) == 0)
2020Sstevel@tonic-gate fp->f_flags |= F_REMOVE;
2030Sstevel@tonic-gate fp->f_problem = aborted ? PROB_aborted : PROB_restat;
2040Sstevel@tonic-gate (fp->f_base)->b_unresolved++;
2050Sstevel@tonic-gate errs |= ERR_UNRESOLVED;
2060Sstevel@tonic-gate if (opt_verbose)
2070Sstevel@tonic-gate fprintf(stdout,
2080Sstevel@tonic-gate gettext(aborted ? V_suppressed
2090Sstevel@tonic-gate : V_nostat),
2100Sstevel@tonic-gate fp->f_fullname);
2110Sstevel@tonic-gate } else {
2120Sstevel@tonic-gate err = reconcile(fp);
2130Sstevel@tonic-gate errs |= err;
2140Sstevel@tonic-gate if (opt_halt && (err & ERR_ABORT)) {
2150Sstevel@tonic-gate fprintf(stderr, gettext(ERR_abort_h));
2160Sstevel@tonic-gate aborted = TRUE;
2170Sstevel@tonic-gate }
2180Sstevel@tonic-gate }
2190Sstevel@tonic-gate
2200Sstevel@tonic-gate return (errs);
2210Sstevel@tonic-gate }
2220Sstevel@tonic-gate
2230Sstevel@tonic-gate /*
2240Sstevel@tonic-gate * routine:
2250Sstevel@tonic-gate * prune_file
2260Sstevel@tonic-gate *
2270Sstevel@tonic-gate * purpose:
2280Sstevel@tonic-gate * to look for file entries that should be pruned from baseline
2290Sstevel@tonic-gate * prune the current file if it needs pruning, and recursively
2300Sstevel@tonic-gate * descend if it is a directory.
2310Sstevel@tonic-gate *
2320Sstevel@tonic-gate * parameters:
2330Sstevel@tonic-gate * pointer to file node
2340Sstevel@tonic-gate */
2350Sstevel@tonic-gate static int
prune_file(struct file * fp)2360Sstevel@tonic-gate prune_file(struct file *fp)
2370Sstevel@tonic-gate { struct file *cp;
2380Sstevel@tonic-gate int prunes = 0;
2390Sstevel@tonic-gate
2400Sstevel@tonic-gate /* if node hasn't been evaluated, mark it for removal */
2410Sstevel@tonic-gate if ((fp->f_flags & (F_EVALUATE|F_STAT_ERROR)) == 0) {
2420Sstevel@tonic-gate fp->f_flags |= F_REMOVE;
2430Sstevel@tonic-gate prunes++;
2440Sstevel@tonic-gate if (opt_debug & DBG_ANAL)
2450Sstevel@tonic-gate fprintf(stderr, "ANAL: PRUNE %s\n", fp->f_name);
2460Sstevel@tonic-gate }
2470Sstevel@tonic-gate
2480Sstevel@tonic-gate /* now check our children */
2490Sstevel@tonic-gate for (cp = fp->f_files; cp; cp = cp->f_next)
2500Sstevel@tonic-gate prunes += prune_file(cp);
2510Sstevel@tonic-gate
2520Sstevel@tonic-gate return (prunes);
2530Sstevel@tonic-gate }
2540Sstevel@tonic-gate
2550Sstevel@tonic-gate /*
2560Sstevel@tonic-gate * routine:
2570Sstevel@tonic-gate * prune
2580Sstevel@tonic-gate *
2590Sstevel@tonic-gate * purpose:
2600Sstevel@tonic-gate * to prune the baseline of entries that no longer correspond to
2610Sstevel@tonic-gate * existing rules.
2620Sstevel@tonic-gate *
2630Sstevel@tonic-gate * notes:
2640Sstevel@tonic-gate * This routine just calls prune_file on the top of each base tree.
2650Sstevel@tonic-gate */
2660Sstevel@tonic-gate int
prune()2670Sstevel@tonic-gate prune()
2680Sstevel@tonic-gate { struct base *bp;
2690Sstevel@tonic-gate struct file *fp;
2700Sstevel@tonic-gate int prunes = 0;
2710Sstevel@tonic-gate
2720Sstevel@tonic-gate for (bp = bases; bp; bp = bp->b_next) {
2730Sstevel@tonic-gate for (fp = bp->b_files; fp; fp = fp->f_next)
2740Sstevel@tonic-gate prunes += prune_file(fp);
2750Sstevel@tonic-gate
2760Sstevel@tonic-gate if ((bp->b_flags & F_EVALUATE) == 0)
2770Sstevel@tonic-gate bp->b_flags |= F_REMOVE;
2780Sstevel@tonic-gate }
2790Sstevel@tonic-gate
2800Sstevel@tonic-gate return (prunes);
2810Sstevel@tonic-gate }
2820Sstevel@tonic-gate
2830Sstevel@tonic-gate /*
2840Sstevel@tonic-gate * routine:
2850Sstevel@tonic-gate * summary
2860Sstevel@tonic-gate *
2870Sstevel@tonic-gate * purpose:
2880Sstevel@tonic-gate * to print out statics and conflict lists
2890Sstevel@tonic-gate */
2900Sstevel@tonic-gate void
summary()2910Sstevel@tonic-gate summary()
2920Sstevel@tonic-gate { struct base *bp;
2930Sstevel@tonic-gate struct file *fp;
2940Sstevel@tonic-gate extern bool_t need_super;
2950Sstevel@tonic-gate
2960Sstevel@tonic-gate (void) fflush(stdout);
2970Sstevel@tonic-gate
2980Sstevel@tonic-gate for (bp = bases; bp; bp = bp->b_next) {
2990Sstevel@tonic-gate
300*3517Smp204432 /* see if this base was irrelevant */
3010Sstevel@tonic-gate if ((bp->b_flags & F_EVALUATE) == 0)
3020Sstevel@tonic-gate continue;
3030Sstevel@tonic-gate
3040Sstevel@tonic-gate /* print out a summary for this base */
3050Sstevel@tonic-gate fprintf(stderr, gettext(SUM_hd),
3060Sstevel@tonic-gate bp->b_src_spec, bp->b_dst_spec, bp->b_totfiles);
3070Sstevel@tonic-gate fprintf(stderr, gettext(SUM_dst),
3080Sstevel@tonic-gate bp->b_dst_copies, bp->b_dst_deletes, bp->b_dst_misc);
3090Sstevel@tonic-gate fprintf(stderr, gettext(SUM_src),
3100Sstevel@tonic-gate bp->b_src_copies, bp->b_src_deletes, bp->b_src_misc);
3110Sstevel@tonic-gate if (bp->b_unresolved)
3120Sstevel@tonic-gate fprintf(stderr, gettext(SUM_unresolved),
3130Sstevel@tonic-gate bp->b_unresolved);
3140Sstevel@tonic-gate
3150Sstevel@tonic-gate
3160Sstevel@tonic-gate /* print out a list of unreconciled files for this base */
3170Sstevel@tonic-gate for (fp = changes; fp; fp = fp->f_rnext) {
3180Sstevel@tonic-gate if (fp->f_base != bp)
3190Sstevel@tonic-gate continue;
3200Sstevel@tonic-gate if ((fp->f_flags & F_CONFLICT) == 0)
3210Sstevel@tonic-gate continue;
3220Sstevel@tonic-gate fprintf(stderr, "\t\t%s (%s)\n", fp->f_fullname,
3230Sstevel@tonic-gate fp->f_problem ? fp->f_problem : "???");
3240Sstevel@tonic-gate }
3250Sstevel@tonic-gate
3260Sstevel@tonic-gate fprintf(stderr, "\n");
3270Sstevel@tonic-gate }
3280Sstevel@tonic-gate
3290Sstevel@tonic-gate if (need_super)
3300Sstevel@tonic-gate fprintf(stderr, gettext(WARN_super));
3310Sstevel@tonic-gate }
3320Sstevel@tonic-gate
3330Sstevel@tonic-gate /*
3340Sstevel@tonic-gate * routine:
3350Sstevel@tonic-gate * check_file
3360Sstevel@tonic-gate *
3370Sstevel@tonic-gate * purpose:
3380Sstevel@tonic-gate * figure out if a file requires reconciliation and recursively
3390Sstevel@tonic-gate * descend into all sub-files and directories
3400Sstevel@tonic-gate *
3410Sstevel@tonic-gate * parameters:
3420Sstevel@tonic-gate * base pointer
3430Sstevel@tonic-gate * file pointer
3440Sstevel@tonic-gate *
3450Sstevel@tonic-gate * returns:
3460Sstevel@tonic-gate * error mask
3470Sstevel@tonic-gate * built up changes needed list
3480Sstevel@tonic-gate * updated statistics
3490Sstevel@tonic-gate *
3500Sstevel@tonic-gate * notes:
3510Sstevel@tonic-gate * this routine builds up a path name as it descends through
3520Sstevel@tonic-gate * the tree (see push_name, pop_name, get_name).
3530Sstevel@tonic-gate */
3540Sstevel@tonic-gate static errmask_t
check_file(struct file * fp)3550Sstevel@tonic-gate check_file(struct file *fp)
3560Sstevel@tonic-gate { struct file *cp;
3570Sstevel@tonic-gate int errs = 0;
3580Sstevel@tonic-gate
3590Sstevel@tonic-gate if ((fp->f_flags & F_STAT_ERROR) == 0) {
3600Sstevel@tonic-gate /* see if the source has changed */
3610Sstevel@tonic-gate fp->f_info[OPT_BASE].f_modtime = fp->f_s_modtime;
3620Sstevel@tonic-gate fp->f_info[OPT_BASE].f_ino = fp->f_s_inum;
3630Sstevel@tonic-gate fp->f_info[OPT_BASE].f_d_maj = fp->f_s_maj;
3640Sstevel@tonic-gate fp->f_info[OPT_BASE].f_d_min = fp->f_s_min;
3650Sstevel@tonic-gate fp->f_info[OPT_BASE].f_nlink = fp->f_s_nlink;
3660Sstevel@tonic-gate fp->f_srcdiffs |= check_changes(fp, OPT_BASE, OPT_SRC);
3670Sstevel@tonic-gate
3680Sstevel@tonic-gate /* see if the destination has changed */
3690Sstevel@tonic-gate fp->f_info[OPT_BASE].f_modtime = fp->f_d_modtime;
3700Sstevel@tonic-gate fp->f_info[OPT_BASE].f_ino = fp->f_d_inum;
3710Sstevel@tonic-gate fp->f_info[OPT_BASE].f_d_maj = fp->f_d_maj;
3720Sstevel@tonic-gate fp->f_info[OPT_BASE].f_d_min = fp->f_d_min;
3730Sstevel@tonic-gate fp->f_info[OPT_BASE].f_nlink = fp->f_d_nlink;
3740Sstevel@tonic-gate fp->f_dstdiffs |= check_changes(fp, OPT_BASE, OPT_DST);
3750Sstevel@tonic-gate
3760Sstevel@tonic-gate /* if nobody thinks the file exists, baseline needs pruning */
3770Sstevel@tonic-gate if ((fp->f_flags & (F_IN_SOURCE|F_IN_DEST)) == 0) {
3780Sstevel@tonic-gate fp->f_srcdiffs |= D_DELETE;
3790Sstevel@tonic-gate fp->f_dstdiffs |= D_DELETE;
3800Sstevel@tonic-gate }
3810Sstevel@tonic-gate
3820Sstevel@tonic-gate /* keep track of possible deletions to look for trouble */
3830Sstevel@tonic-gate if ((fp->f_dstdiffs | fp->f_srcdiffs) & D_DELETE) {
3840Sstevel@tonic-gate est_deletes++;
3850Sstevel@tonic-gate
3860Sstevel@tonic-gate /* see if file is (or has been) a non-empty directory */
3870Sstevel@tonic-gate if (fp->f_files)
3880Sstevel@tonic-gate est_rmdirs++;
3890Sstevel@tonic-gate }
3900Sstevel@tonic-gate }
3910Sstevel@tonic-gate
3920Sstevel@tonic-gate /* if we found differences, queue the file for reconciliation */
3930Sstevel@tonic-gate if (fp->f_srcdiffs || fp->f_dstdiffs || fp->f_flags & F_STAT_ERROR) {
3940Sstevel@tonic-gate queue_file(fp);
3950Sstevel@tonic-gate
3960Sstevel@tonic-gate if (opt_debug & DBG_ANAL) {
3970Sstevel@tonic-gate fprintf(stderr, "ANAL: src=%s",
3980Sstevel@tonic-gate showflags(diffmap, fp->f_srcdiffs));
3990Sstevel@tonic-gate fprintf(stderr, " dst=%s",
4000Sstevel@tonic-gate showflags(diffmap, fp->f_dstdiffs));
4010Sstevel@tonic-gate fprintf(stderr, " flgs=%s",
4020Sstevel@tonic-gate showflags(fileflags, fp->f_flags));
4030Sstevel@tonic-gate fprintf(stderr, " name=%s\n", fp->f_fullname);
4040Sstevel@tonic-gate }
4050Sstevel@tonic-gate }
4060Sstevel@tonic-gate
4070Sstevel@tonic-gate /* bump the total file count */
4080Sstevel@tonic-gate fp->f_base->b_totfiles++;
4090Sstevel@tonic-gate total_files++;
4100Sstevel@tonic-gate
4110Sstevel@tonic-gate /* if this is not a directory, we're done */
4120Sstevel@tonic-gate if (fp->f_files == 0)
4130Sstevel@tonic-gate return (errs);
4140Sstevel@tonic-gate
4150Sstevel@tonic-gate /*
4160Sstevel@tonic-gate * If this is a directory, we need to recursively analyze
4170Sstevel@tonic-gate * our children, but only children who have been evaluated.
4180Sstevel@tonic-gate * If a node has not been evaluated, then we don't have
4190Sstevel@tonic-gate * updated stat information and there is nothing to analyze.
4200Sstevel@tonic-gate *
4210Sstevel@tonic-gate * we also want to run though all nodes with STAT errors
4220Sstevel@tonic-gate * so that we can put them on the reconciliation list.
4230Sstevel@tonic-gate * If a directory is unreadable on one side, all files
4240Sstevel@tonic-gate * under that directory (ON BOTH SIDES) must be marked as
4250Sstevel@tonic-gate * blocked by stat errors.
4260Sstevel@tonic-gate */
4270Sstevel@tonic-gate push_name(fp->f_name);
4280Sstevel@tonic-gate
4290Sstevel@tonic-gate for (cp = fp->f_files; cp; cp = cp->f_next) {
4300Sstevel@tonic-gate if (fp->f_flags & F_STAT_ERROR)
4310Sstevel@tonic-gate cp->f_flags |= F_STAT_ERROR;
4320Sstevel@tonic-gate if (cp->f_flags & (F_EVALUATE|F_STAT_ERROR))
4330Sstevel@tonic-gate errs |= check_file(cp);
4340Sstevel@tonic-gate }
4350Sstevel@tonic-gate
4360Sstevel@tonic-gate pop_name();
4370Sstevel@tonic-gate
4380Sstevel@tonic-gate return (errs);
4390Sstevel@tonic-gate }
4400Sstevel@tonic-gate
4410Sstevel@tonic-gate /*
4420Sstevel@tonic-gate * routine:
4430Sstevel@tonic-gate * check_changes
4440Sstevel@tonic-gate *
4450Sstevel@tonic-gate * purpose:
4460Sstevel@tonic-gate * to figure out what has changed for a specific file
4470Sstevel@tonic-gate *
4480Sstevel@tonic-gate * parameters:
4490Sstevel@tonic-gate * file pointer
4500Sstevel@tonic-gate * the reference info
4510Sstevel@tonic-gate * the info to be checked for changes
4520Sstevel@tonic-gate *
4530Sstevel@tonic-gate * returns:
4540Sstevel@tonic-gate * diff mask
4550Sstevel@tonic-gate *
4560Sstevel@tonic-gate * notes:
4570Sstevel@tonic-gate * this routine doesn't pretend to understand what happened.
4580Sstevel@tonic-gate * it merely enumerates the ways in which the files differ.
4590Sstevel@tonic-gate */
4600Sstevel@tonic-gate static diffmask_t
check_changes(struct file * fp,int ref,int new)4610Sstevel@tonic-gate check_changes(struct file *fp, int ref, int new)
4620Sstevel@tonic-gate { struct fileinfo *rp, *np;
4630Sstevel@tonic-gate int mask = 0;
4640Sstevel@tonic-gate int type;
4650Sstevel@tonic-gate
4660Sstevel@tonic-gate rp = &fp->f_info[ref];
4670Sstevel@tonic-gate np = &fp->f_info[new];
4680Sstevel@tonic-gate
4690Sstevel@tonic-gate if (np->f_uid != rp->f_uid)
4700Sstevel@tonic-gate mask |= D_UID;
4710Sstevel@tonic-gate
4720Sstevel@tonic-gate if (np->f_gid != rp->f_gid)
4730Sstevel@tonic-gate mask |= D_GID;
4740Sstevel@tonic-gate
4750Sstevel@tonic-gate if (np->f_mode != rp->f_mode)
4760Sstevel@tonic-gate mask |= D_PROT;
4770Sstevel@tonic-gate
4780Sstevel@tonic-gate type = np->f_type;
4790Sstevel@tonic-gate if (type != rp->f_type) {
4800Sstevel@tonic-gate if (type == 0)
4810Sstevel@tonic-gate mask |= D_DELETE;
4820Sstevel@tonic-gate else if (rp->f_type == 0)
4830Sstevel@tonic-gate mask |= D_CREATE;
4840Sstevel@tonic-gate else
4850Sstevel@tonic-gate mask |= D_TYPE;
4860Sstevel@tonic-gate } else if (type == S_IFBLK || type == S_IFCHR) {
4870Sstevel@tonic-gate /*
4880Sstevel@tonic-gate * for special files, we only look at the maj/min
4890Sstevel@tonic-gate */
4900Sstevel@tonic-gate if (np->f_rd_maj != rp->f_rd_maj)
4910Sstevel@tonic-gate mask |= D_SIZE;
4920Sstevel@tonic-gate if (np->f_rd_min != rp->f_rd_min)
4930Sstevel@tonic-gate mask |= D_SIZE;
4940Sstevel@tonic-gate } else if (type != S_IFDIR) {
4950Sstevel@tonic-gate /*
4960Sstevel@tonic-gate * for directories, we don't look directly at
4970Sstevel@tonic-gate * the contents, so these fields don't mean
4980Sstevel@tonic-gate * anything. If the directories have changed
4990Sstevel@tonic-gate * in any interesting way, we'll find it by
5000Sstevel@tonic-gate * walking the tree.
5010Sstevel@tonic-gate */
5020Sstevel@tonic-gate if (np->f_modtime > rp->f_modtime)
5030Sstevel@tonic-gate mask |= D_MTIME;
5040Sstevel@tonic-gate
5050Sstevel@tonic-gate if (np->f_size != rp->f_size)
5060Sstevel@tonic-gate mask |= D_SIZE;
5070Sstevel@tonic-gate
5080Sstevel@tonic-gate if (np->f_nlink != rp->f_nlink)
5090Sstevel@tonic-gate mask |= D_LINKS;
5100Sstevel@tonic-gate }
5110Sstevel@tonic-gate
5120Sstevel@tonic-gate if (cmp_acls(rp, np) == 0)
5130Sstevel@tonic-gate mask |= D_FACLS;
5140Sstevel@tonic-gate
5150Sstevel@tonic-gate return (mask);
5160Sstevel@tonic-gate }
5170Sstevel@tonic-gate
5180Sstevel@tonic-gate /*
5190Sstevel@tonic-gate * routine:
5200Sstevel@tonic-gate * same_name
5210Sstevel@tonic-gate *
5220Sstevel@tonic-gate * purpose:
5230Sstevel@tonic-gate * to figure out whether or not two databsae nodes actually refer to
5240Sstevel@tonic-gate * the same file.
5250Sstevel@tonic-gate *
5260Sstevel@tonic-gate * parameters:
5270Sstevel@tonic-gate * pointers to two file description nodes
5280Sstevel@tonic-gate * which side we should check
5290Sstevel@tonic-gate *
5300Sstevel@tonic-gate * returns:
5310Sstevel@tonic-gate * TRUE/FALSE
5320Sstevel@tonic-gate *
5330Sstevel@tonic-gate * notes:
5340Sstevel@tonic-gate * if a single directory is specified in multiple base pairs, it
5350Sstevel@tonic-gate * is possible to have multiple nodes in the database describing
5360Sstevel@tonic-gate * the same file. This routine is supposed to detect those cases.
5370Sstevel@tonic-gate *
5380Sstevel@tonic-gate * what should be a trivial string comparison is complicated by
5390Sstevel@tonic-gate * the possibility that the two nodes might describe the same file
5400Sstevel@tonic-gate * from base directories at different depths. Thus, rather than
5410Sstevel@tonic-gate * comparing two strings, we really want to compare the concatenation
5420Sstevel@tonic-gate * of two pairs of strings. Unfortunately calling full_name would
5430Sstevel@tonic-gate * be awkward right now, so instead we have our own comparison
5440Sstevel@tonic-gate * routine that automatically skips from the first string to
5450Sstevel@tonic-gate * the second.
5460Sstevel@tonic-gate */
5470Sstevel@tonic-gate static bool_t
same_name(struct file * f1,struct file * f2,side_t srcdst)5480Sstevel@tonic-gate same_name(struct file *f1, struct file *f2, side_t srcdst)
5490Sstevel@tonic-gate {
5500Sstevel@tonic-gate char *s1, *s2, *x1, *x2;
5510Sstevel@tonic-gate
5520Sstevel@tonic-gate if (srcdst == OPT_SRC) {
5530Sstevel@tonic-gate s1 = (f1->f_base)->b_src_name;
5540Sstevel@tonic-gate s2 = (f2->f_base)->b_src_name;
5550Sstevel@tonic-gate } else {
5560Sstevel@tonic-gate s1 = (f1->f_base)->b_dst_name;
5570Sstevel@tonic-gate s2 = (f2->f_base)->b_dst_name;
5580Sstevel@tonic-gate }
5590Sstevel@tonic-gate x1 = f1->f_fullname;
5600Sstevel@tonic-gate x2 = f2->f_fullname;
5610Sstevel@tonic-gate
5620Sstevel@tonic-gate /*
5630Sstevel@tonic-gate * Compare the two names, and if they differ before they end
5640Sstevel@tonic-gate * this is a non-match. If they both end at the same time,
5650Sstevel@tonic-gate * this is a match.
5660Sstevel@tonic-gate *
5670Sstevel@tonic-gate * The trick here is that each string is actually the logical
5680Sstevel@tonic-gate * concatenation of two strings, and we need to automatically
5690Sstevel@tonic-gate * wrap from the first to the second string in each pair. There
5700Sstevel@tonic-gate * is no requirement that the two (concatenated) strings be
5710Sstevel@tonic-gate * broken at the same point, so we have a slightly baroque
5720Sstevel@tonic-gate * comparsion loop.
5730Sstevel@tonic-gate */
5740Sstevel@tonic-gate while (*s1 && *s1 == *s2) {
5750Sstevel@tonic-gate
5760Sstevel@tonic-gate /*
5770Sstevel@tonic-gate * strings have been identical so far, so advance the
5780Sstevel@tonic-gate * pointers and continue the comparison. The trick
5790Sstevel@tonic-gate * is that when either string ends, we have to wrap
5800Sstevel@tonic-gate * over to its extension.
5810Sstevel@tonic-gate */
5820Sstevel@tonic-gate s1++; s2++;
5830Sstevel@tonic-gate if (*s1 && *s2)
5840Sstevel@tonic-gate continue;
5850Sstevel@tonic-gate
5860Sstevel@tonic-gate /*
5870Sstevel@tonic-gate * at least one of the strings has ended.
5880Sstevel@tonic-gate * there is an implicit slash between the string
5890Sstevel@tonic-gate * and its extension, and this has to be matched
5900Sstevel@tonic-gate * against the other string.
5910Sstevel@tonic-gate */
5920Sstevel@tonic-gate if (*s1 != *s2) {
5930Sstevel@tonic-gate if (*s1 == 0 && *s2 == '/')
5940Sstevel@tonic-gate s2++;
5950Sstevel@tonic-gate else if (*s2 == 0 && *s1 == '/')
5960Sstevel@tonic-gate s1++;
5970Sstevel@tonic-gate else
5980Sstevel@tonic-gate /* the disagreement doesn't come at a slash */
5990Sstevel@tonic-gate break;
6000Sstevel@tonic-gate }
6010Sstevel@tonic-gate
6020Sstevel@tonic-gate /*
6030Sstevel@tonic-gate * if either string has ended, wrap to its extension
6040Sstevel@tonic-gate */
6050Sstevel@tonic-gate if (*s1 == 0 && x1 != 0) {
6060Sstevel@tonic-gate s1 = x1;
6070Sstevel@tonic-gate x1 = 0;
6080Sstevel@tonic-gate }
6090Sstevel@tonic-gate if (*s2 == 0 && x2 != 0) {
6100Sstevel@tonic-gate s2 = x2;
6110Sstevel@tonic-gate x2 = 0;
6120Sstevel@tonic-gate }
6130Sstevel@tonic-gate }
6140Sstevel@tonic-gate
6150Sstevel@tonic-gate return (*s1 == *s2);
6160Sstevel@tonic-gate }
6170Sstevel@tonic-gate
6180Sstevel@tonic-gate /*
6190Sstevel@tonic-gate * routine:
6200Sstevel@tonic-gate * find_link
6210Sstevel@tonic-gate *
6220Sstevel@tonic-gate * purpose:
6230Sstevel@tonic-gate * to figure out if there is a file to which we should
6240Sstevel@tonic-gate * be creating a link (rather than making a copy)
6250Sstevel@tonic-gate *
6260Sstevel@tonic-gate * parameters:
6270Sstevel@tonic-gate * file node for the file to be created (that we hope is merely a link)
6280Sstevel@tonic-gate * which side is to be changed (src/dst)
6290Sstevel@tonic-gate *
6300Sstevel@tonic-gate * return:
6310Sstevel@tonic-gate * 0 no link is appropriate
6320Sstevel@tonic-gate * else pointer to file node for link referent
6330Sstevel@tonic-gate *
6340Sstevel@tonic-gate * notes:
6350Sstevel@tonic-gate * there are a few strange heuristics in this routine and I
6360Sstevel@tonic-gate * wouldn't bet my soul that I got all of them right. The general
6370Sstevel@tonic-gate * theory is that when a new file is created, we look to see if it
6380Sstevel@tonic-gate * is a link to another file on the changed side, and if it is, we
6390Sstevel@tonic-gate * find the corresponding file on the unchanged side.
6400Sstevel@tonic-gate *
6410Sstevel@tonic-gate * cases we want to be able to handle:
6420Sstevel@tonic-gate * 1. one or more links are created to a prexisting file
6430Sstevel@tonic-gate * 2. a preexisting only link is renamed
6440Sstevel@tonic-gate * 3. a rename of one of multiple links to a preexisting file
6450Sstevel@tonic-gate * 4. a single file is created with multiple links
6460Sstevel@tonic-gate */
6470Sstevel@tonic-gate struct file *
find_link(struct file * fp,side_t srcdst)6480Sstevel@tonic-gate find_link(struct file *fp, side_t srcdst)
6490Sstevel@tonic-gate { struct file *lp;
6500Sstevel@tonic-gate side_t chgside, tgtside;
6510Sstevel@tonic-gate struct fileinfo *chgp, *tgtp, *basp, *fcp, *ftp;
6520Sstevel@tonic-gate
6530Sstevel@tonic-gate /* chg = side on which the change was noticed */
6540Sstevel@tonic-gate /* tgt = side to which the change is to be propagated */
6550Sstevel@tonic-gate chgside = (srcdst == OPT_SRC) ? OPT_DST : OPT_SRC;
6560Sstevel@tonic-gate tgtside = (srcdst == OPT_SRC) ? OPT_SRC : OPT_DST;
6570Sstevel@tonic-gate fcp = &fp->f_info[chgside];
6580Sstevel@tonic-gate ftp = &fp->f_info[tgtside];
6590Sstevel@tonic-gate
6600Sstevel@tonic-gate /*
6610Sstevel@tonic-gate * cases 1 and 3
6620Sstevel@tonic-gate *
6630Sstevel@tonic-gate * When a new link is created, we should be able to find
6640Sstevel@tonic-gate * another file in the changed hierarchy that has the same
6650Sstevel@tonic-gate * I-node number. We expect it to be on the changed list
6660Sstevel@tonic-gate * because the link count will have gone up or because all
6670Sstevel@tonic-gate * of the copies are new. If we find one, then the new file
6680Sstevel@tonic-gate * on the receiving file should be a link to the corresponding
6690Sstevel@tonic-gate * existing file.
6700Sstevel@tonic-gate *
6710Sstevel@tonic-gate * case 4
6720Sstevel@tonic-gate *
6730Sstevel@tonic-gate * the first link will be dealt with as a copy, but all
6740Sstevel@tonic-gate * subsequent links should find an existing file analogous
6750Sstevel@tonic-gate * to one of the links on the changed side, and create
6760Sstevel@tonic-gate * corresponding links on the other side.
6770Sstevel@tonic-gate *
6780Sstevel@tonic-gate * in each of these cases, there should be multiple links
6790Sstevel@tonic-gate * on the changed side. If the linkcount on the changed
6800Sstevel@tonic-gate * side is one, we needn't bother searching for other links.
6810Sstevel@tonic-gate */
6820Sstevel@tonic-gate if (fcp->f_nlink > 1)
6830Sstevel@tonic-gate for (lp = changes; lp; lp = lp->f_rnext) {
6840Sstevel@tonic-gate /* finding the same node doesn't count */
6850Sstevel@tonic-gate if (fp == lp)
6860Sstevel@tonic-gate continue;
6870Sstevel@tonic-gate
6880Sstevel@tonic-gate tgtp = &lp->f_info[tgtside];
6890Sstevel@tonic-gate chgp = &lp->f_info[chgside];
6900Sstevel@tonic-gate
6910Sstevel@tonic-gate /*
6920Sstevel@tonic-gate * if the file doesn't already exist on the target side
6930Sstevel@tonic-gate * we cannot make a link to it
6940Sstevel@tonic-gate */
6950Sstevel@tonic-gate if (tgtp->f_mode == 0)
6960Sstevel@tonic-gate continue;
6970Sstevel@tonic-gate
6980Sstevel@tonic-gate /*
6990Sstevel@tonic-gate * if this is indeed a link, then the prospective file on
7000Sstevel@tonic-gate * the changed side will have the same dev/inum as the file
7010Sstevel@tonic-gate * we are looking for
7020Sstevel@tonic-gate */
7030Sstevel@tonic-gate if (fcp->f_d_maj != chgp->f_d_maj)
7040Sstevel@tonic-gate continue;
7050Sstevel@tonic-gate if (fcp->f_d_min != chgp->f_d_min)
7060Sstevel@tonic-gate continue;
7070Sstevel@tonic-gate if (fcp->f_ino != chgp->f_ino)
7080Sstevel@tonic-gate continue;
7090Sstevel@tonic-gate
7100Sstevel@tonic-gate /*
7110Sstevel@tonic-gate * if the target side is already a link to this file,
7120Sstevel@tonic-gate * then there is no new link to be created
7130Sstevel@tonic-gate * FIX: how does this interact with copies over links
7140Sstevel@tonic-gate */
7150Sstevel@tonic-gate if ((ftp->f_d_maj == tgtp->f_d_maj) &&
7160Sstevel@tonic-gate (ftp->f_d_min == tgtp->f_d_min) &&
7170Sstevel@tonic-gate (ftp->f_ino == tgtp->f_ino))
7180Sstevel@tonic-gate continue;
7190Sstevel@tonic-gate
7200Sstevel@tonic-gate /*
7210Sstevel@tonic-gate * there is a pathological situation where a single file
7220Sstevel@tonic-gate * might appear under multiple base directories. This is
7230Sstevel@tonic-gate * damned awkward to detect in any other way, so we must
7240Sstevel@tonic-gate * check to see if we have just found another database
7250Sstevel@tonic-gate * instance for the same file (on the changed side).
7260Sstevel@tonic-gate */
7270Sstevel@tonic-gate if ((fp->f_base != lp->f_base) && same_name(fp, lp, chgside))
7280Sstevel@tonic-gate continue;
7290Sstevel@tonic-gate
7300Sstevel@tonic-gate if (opt_debug & DBG_ANAL)
7310Sstevel@tonic-gate fprintf(stderr, "ANAL: FIND LINK %s and %s\n",
7320Sstevel@tonic-gate fp->f_fullname, lp->f_fullname);
7330Sstevel@tonic-gate
7340Sstevel@tonic-gate return (lp);
7350Sstevel@tonic-gate }
7360Sstevel@tonic-gate
7370Sstevel@tonic-gate /*
7380Sstevel@tonic-gate * case 2: a simple rename of the only link
7390Sstevel@tonic-gate *
7400Sstevel@tonic-gate * In this case, there may not be any other existing file on
7410Sstevel@tonic-gate * the changed side that has the same I-node number. There
7420Sstevel@tonic-gate * might, however, be a record of such a file in the baseline.
7430Sstevel@tonic-gate * If we can find an identical file with a different name that
7440Sstevel@tonic-gate * has recently disappeared, we have a likely rename.
7450Sstevel@tonic-gate */
7460Sstevel@tonic-gate for (lp = changes; lp; lp = lp->f_rnext) {
7470Sstevel@tonic-gate
7480Sstevel@tonic-gate /* finding the same node doesn't count */
7490Sstevel@tonic-gate if (fp == lp)
7500Sstevel@tonic-gate continue;
7510Sstevel@tonic-gate
7520Sstevel@tonic-gate tgtp = &lp->f_info[tgtside];
7530Sstevel@tonic-gate chgp = &lp->f_info[chgside];
7540Sstevel@tonic-gate
7550Sstevel@tonic-gate /*
7560Sstevel@tonic-gate * if the file still exists on the changed side this is
7570Sstevel@tonic-gate * not a simple rename, and in fact the previous pass
7580Sstevel@tonic-gate * would have found it.
7590Sstevel@tonic-gate */
7600Sstevel@tonic-gate if (chgp->f_mode != 0)
7610Sstevel@tonic-gate continue;
7620Sstevel@tonic-gate
7630Sstevel@tonic-gate /*
7640Sstevel@tonic-gate * the inode number for the new link on the changed
7650Sstevel@tonic-gate * side must match the inode number for the old link
7660Sstevel@tonic-gate * from the baseline.
7670Sstevel@tonic-gate */
7680Sstevel@tonic-gate if (fcp->f_d_maj != ((srcdst == OPT_SRC) ? lp->f_d_maj
7690Sstevel@tonic-gate : lp->f_s_maj))
7700Sstevel@tonic-gate continue;
7710Sstevel@tonic-gate if (fcp->f_d_min != ((srcdst == OPT_SRC) ? lp->f_d_min
7720Sstevel@tonic-gate : lp->f_s_min))
7730Sstevel@tonic-gate continue;
7740Sstevel@tonic-gate if (fcp->f_ino != ((srcdst == OPT_SRC) ? lp->f_d_inum
7750Sstevel@tonic-gate : lp->f_s_inum))
7760Sstevel@tonic-gate continue;
7770Sstevel@tonic-gate
7780Sstevel@tonic-gate /* finding a file we are already linked to doesn't help */
7790Sstevel@tonic-gate if ((ftp->f_d_maj == tgtp->f_d_maj) &&
7800Sstevel@tonic-gate (ftp->f_d_min == tgtp->f_d_min) &&
7810Sstevel@tonic-gate (ftp->f_ino == tgtp->f_ino))
7820Sstevel@tonic-gate continue;
7830Sstevel@tonic-gate
7840Sstevel@tonic-gate /*
7850Sstevel@tonic-gate * there is a danger that we will confuse an
7860Sstevel@tonic-gate * inode reallocation with a rename. We should
7870Sstevel@tonic-gate * only consider this to be a rename if the
7880Sstevel@tonic-gate * new file is identical to the old one
7890Sstevel@tonic-gate */
7900Sstevel@tonic-gate basp = &lp->f_info[OPT_BASE];
7910Sstevel@tonic-gate if (fcp->f_type != basp->f_type)
7920Sstevel@tonic-gate continue;
7930Sstevel@tonic-gate if (fcp->f_size != basp->f_size)
7940Sstevel@tonic-gate continue;
7950Sstevel@tonic-gate if (fcp->f_mode != basp->f_mode)
7960Sstevel@tonic-gate continue;
7970Sstevel@tonic-gate if (fcp->f_uid != basp->f_uid)
7980Sstevel@tonic-gate continue;
7990Sstevel@tonic-gate if (fcp->f_gid != basp->f_gid)
8000Sstevel@tonic-gate continue;
8010Sstevel@tonic-gate
8020Sstevel@tonic-gate if (opt_debug & DBG_ANAL)
8030Sstevel@tonic-gate fprintf(stderr, "ANAL: FIND RENAME %s and %s\n",
8040Sstevel@tonic-gate fp->f_fullname, lp->f_fullname);
8050Sstevel@tonic-gate
8060Sstevel@tonic-gate return (lp);
8070Sstevel@tonic-gate }
8080Sstevel@tonic-gate
8090Sstevel@tonic-gate return (0);
8100Sstevel@tonic-gate }
8110Sstevel@tonic-gate
8120Sstevel@tonic-gate /*
8130Sstevel@tonic-gate * routine:
8140Sstevel@tonic-gate * has_other_links
8150Sstevel@tonic-gate *
8160Sstevel@tonic-gate * purpose:
8170Sstevel@tonic-gate * to determine whether or not there is more that one link to a
8180Sstevel@tonic-gate * particular file. We are willing to delete a link to a file
8190Sstevel@tonic-gate * that has changed if we will still have other links to it.
8200Sstevel@tonic-gate * The trick here is that we only care about links under our
8210Sstevel@tonic-gate * dominion.
8220Sstevel@tonic-gate *
8230Sstevel@tonic-gate * parameters:
8240Sstevel@tonic-gate * file pointer to node we are interested in
8250Sstevel@tonic-gate * which side we are looking to additional links on
8260Sstevel@tonic-gate *
8270Sstevel@tonic-gate * returns:
8280Sstevel@tonic-gate * TRUE if there are multiple links
8290Sstevel@tonic-gate * FALSE if this is the only one we know of
8300Sstevel@tonic-gate */
8310Sstevel@tonic-gate bool_t
has_other_links(struct file * fp,side_t srcdst)8320Sstevel@tonic-gate has_other_links(struct file *fp, side_t srcdst)
8330Sstevel@tonic-gate { struct file *lp;
8340Sstevel@tonic-gate struct fileinfo *fip, *lip;
8350Sstevel@tonic-gate
8360Sstevel@tonic-gate fip = &fp->f_info[srcdst];
8370Sstevel@tonic-gate
8380Sstevel@tonic-gate /* if the link count is one, there couldn't be others */
8390Sstevel@tonic-gate if (fip->f_nlink < 2)
8400Sstevel@tonic-gate return (FALSE);
8410Sstevel@tonic-gate
8420Sstevel@tonic-gate /* look for any other files for the same inode */
8430Sstevel@tonic-gate for (lp = changes; lp; lp = lp->f_rnext) {
8440Sstevel@tonic-gate /* finding the same node doesn't count */
8450Sstevel@tonic-gate if (fp == lp)
8460Sstevel@tonic-gate continue;
8470Sstevel@tonic-gate
8480Sstevel@tonic-gate lip = &lp->f_info[srcdst];
8490Sstevel@tonic-gate
8500Sstevel@tonic-gate /*
8510Sstevel@tonic-gate * file must still exist on this side
8520Sstevel@tonic-gate */
8530Sstevel@tonic-gate if (lip->f_mode == 0)
8540Sstevel@tonic-gate continue;
8550Sstevel@tonic-gate
8560Sstevel@tonic-gate /*
8570Sstevel@tonic-gate * if this is indeed a link, then the prospective file on
8580Sstevel@tonic-gate * the changed side will have the same dev/inum as the file
8590Sstevel@tonic-gate * we are looking for
8600Sstevel@tonic-gate */
8610Sstevel@tonic-gate if (lip->f_d_maj != fip->f_d_maj)
8620Sstevel@tonic-gate continue;
8630Sstevel@tonic-gate if (lip->f_d_min != fip->f_d_min)
8640Sstevel@tonic-gate continue;
8650Sstevel@tonic-gate if (lip->f_ino != fip->f_ino)
8660Sstevel@tonic-gate continue;
8670Sstevel@tonic-gate
8680Sstevel@tonic-gate /*
8690Sstevel@tonic-gate * we have found at least one other link
8700Sstevel@tonic-gate */
8710Sstevel@tonic-gate return (TRUE);
8720Sstevel@tonic-gate }
8730Sstevel@tonic-gate
8740Sstevel@tonic-gate return (FALSE);
8750Sstevel@tonic-gate }
8760Sstevel@tonic-gate
8770Sstevel@tonic-gate /*
8780Sstevel@tonic-gate * routine:
8790Sstevel@tonic-gate * link_update
8800Sstevel@tonic-gate *
8810Sstevel@tonic-gate * purpose:
8820Sstevel@tonic-gate * to propoagate a stat change to all other file nodes that
8830Sstevel@tonic-gate * correspond to the same I-node on the changed side
8840Sstevel@tonic-gate *
8850Sstevel@tonic-gate * parameters:
8860Sstevel@tonic-gate * file pointer for the updated file
8870Sstevel@tonic-gate * which side was changed
8880Sstevel@tonic-gate *
8890Sstevel@tonic-gate * returns:
8900Sstevel@tonic-gate * void
8910Sstevel@tonic-gate *
8920Sstevel@tonic-gate * notes:
8930Sstevel@tonic-gate * if we have copied onto a file, we have copied onto all
8940Sstevel@tonic-gate * of its links, but since we do all stats before we do any
8950Sstevel@tonic-gate * copies, the stat information recently collected for links
8960Sstevel@tonic-gate * is no longer up-to-date, and this would result in incorrect
8970Sstevel@tonic-gate * reconciliation (redundant copies).
8980Sstevel@tonic-gate *
8990Sstevel@tonic-gate * There is an assumption here that all links to a changed
9000Sstevel@tonic-gate * file will be in the change list. This is true for almost
9010Sstevel@tonic-gate * all cases not involving restriction. If we do fail to
9020Sstevel@tonic-gate * update the baseline for a file that was off the change list,
9030Sstevel@tonic-gate * the worst that is likely to happen is that we will think
9040Sstevel@tonic-gate * it changed later (but will almost surely find that both
9050Sstevel@tonic-gate * copies agree).
9060Sstevel@tonic-gate */
9070Sstevel@tonic-gate void
link_update(struct file * fp,side_t which)9080Sstevel@tonic-gate link_update(struct file *fp, side_t which)
9090Sstevel@tonic-gate { struct file *lp;
9100Sstevel@tonic-gate
9110Sstevel@tonic-gate for (lp = changes; lp; lp = lp->f_rnext) {
9120Sstevel@tonic-gate /* finding the current entry doesn't count */
9130Sstevel@tonic-gate if (lp == fp)
9140Sstevel@tonic-gate continue;
9150Sstevel@tonic-gate
9160Sstevel@tonic-gate /* look for same i#, maj, min on changed side */
9170Sstevel@tonic-gate if (lp->f_info[which].f_ino != fp->f_info[which].f_ino)
9180Sstevel@tonic-gate continue;
9190Sstevel@tonic-gate if (lp->f_info[which].f_d_maj != fp->f_info[which].f_d_maj)
9200Sstevel@tonic-gate continue;
9210Sstevel@tonic-gate if (lp->f_info[which].f_d_min != fp->f_info[which].f_d_min)
9220Sstevel@tonic-gate continue;
9230Sstevel@tonic-gate
9240Sstevel@tonic-gate /*
9250Sstevel@tonic-gate * this appears to be another link to the same file
9260Sstevel@tonic-gate * so the updated stat information for one must be
9270Sstevel@tonic-gate * correct for the other.
9280Sstevel@tonic-gate */
9290Sstevel@tonic-gate lp->f_info[which].f_type = fp->f_info[which].f_type;
9300Sstevel@tonic-gate lp->f_info[which].f_size = fp->f_info[which].f_size;
9310Sstevel@tonic-gate lp->f_info[which].f_mode = fp->f_info[which].f_mode;
9320Sstevel@tonic-gate lp->f_info[which].f_uid = fp->f_info[which].f_uid;
9330Sstevel@tonic-gate lp->f_info[which].f_gid = fp->f_info[which].f_gid;
9340Sstevel@tonic-gate lp->f_info[which].f_modtime = fp->f_info[which].f_modtime;
9350Sstevel@tonic-gate lp->f_info[which].f_modns = fp->f_info[which].f_modns;
9360Sstevel@tonic-gate lp->f_info[which].f_nlink = fp->f_info[which].f_nlink;
9370Sstevel@tonic-gate lp->f_info[which].f_rd_maj = fp->f_info[which].f_rd_maj;
9380Sstevel@tonic-gate lp->f_info[which].f_rd_min = fp->f_info[which].f_rd_min;
9390Sstevel@tonic-gate
9400Sstevel@tonic-gate if (opt_debug & DBG_STAT)
9410Sstevel@tonic-gate fprintf(stderr,
9420Sstevel@tonic-gate "STAT: UPDATE LINK, file=%s, mod=%08lx.%08lx\n",
9430Sstevel@tonic-gate lp->f_name, lp->f_info[which].f_modtime,
9440Sstevel@tonic-gate lp->f_info[which].f_modns);
9450Sstevel@tonic-gate }
9460Sstevel@tonic-gate }
9470Sstevel@tonic-gate
9480Sstevel@tonic-gate /*
9490Sstevel@tonic-gate * routine:
9500Sstevel@tonic-gate * queue_file
9510Sstevel@tonic-gate *
9520Sstevel@tonic-gate * purpose:
9530Sstevel@tonic-gate * append a file to the list of needed reconciliations
9540Sstevel@tonic-gate *
9550Sstevel@tonic-gate * parameters:
9560Sstevel@tonic-gate * pointer to file
9570Sstevel@tonic-gate *
9580Sstevel@tonic-gate * notes:
9590Sstevel@tonic-gate * when a request is appended to the reconciliation list,
9600Sstevel@tonic-gate * we fill in the full name. We delayed this in hopes that
9610Sstevel@tonic-gate * it wouldn't be necessary (saving cycles and memory)
9620Sstevel@tonic-gate *
9630Sstevel@tonic-gate * There is some funny business with modification times.
9640Sstevel@tonic-gate * In general, we queue files in order of the latest modification
9650Sstevel@tonic-gate * time so that propagations preserve relative ordering. There
9660Sstevel@tonic-gate * are, however, a few important exceptions:
9670Sstevel@tonic-gate * 1. all directory creations happen at time zero,
9680Sstevel@tonic-gate * so that they are created before any files can
9690Sstevel@tonic-gate * be added to them.
9700Sstevel@tonic-gate * 2. all directory deletions happen at time infinity-depth,
9710Sstevel@tonic-gate * so that everything else can be removed before the
9720Sstevel@tonic-gate * directories themselves are removed.
9730Sstevel@tonic-gate * 3. all file deletions happen at time infinity-depth
9740Sstevel@tonic-gate * so that (in renames) the links will preceed the unlinks.
9750Sstevel@tonic-gate */
9760Sstevel@tonic-gate static void
queue_file(struct file * fp)9770Sstevel@tonic-gate queue_file(struct file *fp)
9780Sstevel@tonic-gate { struct file **pp, *np;
9790Sstevel@tonic-gate
9800Sstevel@tonic-gate #define TIME_ZERO 0L /* the earliest possible time */
9810Sstevel@tonic-gate #define TIME_LONG 0x7FFFFFFF /* the latest possible time */
9820Sstevel@tonic-gate
9830Sstevel@tonic-gate /*
9840Sstevel@tonic-gate * figure out the modification time for sequencing purposes
9850Sstevel@tonic-gate */
9860Sstevel@tonic-gate if ((fp->f_srcdiffs|fp->f_dstdiffs) & D_DELETE) {
9870Sstevel@tonic-gate /*
9880Sstevel@tonic-gate * deletions are performed last, and depth first
9890Sstevel@tonic-gate */
9900Sstevel@tonic-gate fp->f_modtime = TIME_LONG - fp->f_depth;
9910Sstevel@tonic-gate } else if (fp->f_info[OPT_SRC].f_type != S_IFDIR &&
9920Sstevel@tonic-gate fp->f_info[OPT_DST].f_type != S_IFDIR) {
9930Sstevel@tonic-gate /*
9940Sstevel@tonic-gate * for most files we use the latest mod time
9950Sstevel@tonic-gate */
9960Sstevel@tonic-gate fp->f_modtime = fp->f_info[OPT_SRC].f_modtime;
9970Sstevel@tonic-gate fp->f_modns = fp->f_info[OPT_SRC].f_modns;
9980Sstevel@tonic-gate if (fp->f_modtime < fp->f_info[OPT_DST].f_modtime) {
9990Sstevel@tonic-gate fp->f_modtime = fp->f_info[OPT_DST].f_modtime;
10000Sstevel@tonic-gate fp->f_modns = fp->f_info[OPT_DST].f_modns;
10010Sstevel@tonic-gate }
10020Sstevel@tonic-gate } else {
10030Sstevel@tonic-gate /*
10040Sstevel@tonic-gate * new directory creations need to happen before anything
10050Sstevel@tonic-gate * else and are automatically sequenced in traversal order
10060Sstevel@tonic-gate */
10070Sstevel@tonic-gate fp->f_modtime = TIME_ZERO;
10080Sstevel@tonic-gate }
10090Sstevel@tonic-gate
10100Sstevel@tonic-gate /*
10110Sstevel@tonic-gate * insertion is time ordered, and for equal times,
10120Sstevel@tonic-gate * insertions is in (pre-order) traversal order
10130Sstevel@tonic-gate */
10140Sstevel@tonic-gate for (pp = &changes; (np = *pp) != 0; pp = &np->f_rnext) {
10150Sstevel@tonic-gate if (fp->f_modtime > np->f_modtime)
10160Sstevel@tonic-gate continue;
10170Sstevel@tonic-gate if (fp->f_modtime < np->f_modtime)
10180Sstevel@tonic-gate break;
10190Sstevel@tonic-gate if (fp->f_modns < np->f_modns)
10200Sstevel@tonic-gate break;
10210Sstevel@tonic-gate }
10220Sstevel@tonic-gate
10230Sstevel@tonic-gate fp->f_fullname = strdup(get_name(fp));
10240Sstevel@tonic-gate fp->f_rnext = np;
10250Sstevel@tonic-gate *pp = fp;
10260Sstevel@tonic-gate }
10270Sstevel@tonic-gate
10280Sstevel@tonic-gate
10290Sstevel@tonic-gate /*
10300Sstevel@tonic-gate * routines:
10310Sstevel@tonic-gate * push_name/pop_name/get_name
10320Sstevel@tonic-gate *
10330Sstevel@tonic-gate * purpose:
10340Sstevel@tonic-gate * maintain a name stack so we can form name of a particular file
10350Sstevel@tonic-gate * as the concatenation of all of the names between it and the
10360Sstevel@tonic-gate * (know to be fully qualified) base directory.
10370Sstevel@tonic-gate *
10380Sstevel@tonic-gate * notes:
10390Sstevel@tonic-gate * we go to this trouble because most files never change and
10400Sstevel@tonic-gate * so we don't need to associate full names with every one.
10410Sstevel@tonic-gate * This stack is maintained during analysis, and if we decide
10420Sstevel@tonic-gate * to add a file to the reconciliation list, we can use the
10430Sstevel@tonic-gate * stack to generate a fully qualified name at that time.
10440Sstevel@tonic-gate *
10450Sstevel@tonic-gate * we compress out '/./' when we return a name. Given that the
10460Sstevel@tonic-gate * stack was built by a tree walk, the only place a /./ should
10470Sstevel@tonic-gate * appear is at the first level after the base ... but there
10480Sstevel@tonic-gate * are legitimate ways for them to appear there.
10490Sstevel@tonic-gate *
10500Sstevel@tonic-gate * these names can get deep, so we dynamically size our name buffer
10510Sstevel@tonic-gate */
10520Sstevel@tonic-gate static const char *namestack[ MAX_DEPTH + 1 ];
10530Sstevel@tonic-gate static int namedepth = 0;
10540Sstevel@tonic-gate static int namelen = 0;
10550Sstevel@tonic-gate
10560Sstevel@tonic-gate void
push_name(const char * name)10570Sstevel@tonic-gate push_name(const char *name)
10580Sstevel@tonic-gate {
10590Sstevel@tonic-gate namestack[ namedepth++ ] = name;
10600Sstevel@tonic-gate namelen += 2 + strlen(name);
10610Sstevel@tonic-gate
10620Sstevel@tonic-gate /* make sure we don't overflow our name stack */
10630Sstevel@tonic-gate if (namedepth >= MAX_DEPTH) {
10640Sstevel@tonic-gate fprintf(stderr, gettext(ERR_deep), name);
10650Sstevel@tonic-gate exit(ERR_OTHER);
10660Sstevel@tonic-gate }
10670Sstevel@tonic-gate }
10680Sstevel@tonic-gate
10690Sstevel@tonic-gate void
pop_name(void)10700Sstevel@tonic-gate pop_name(void)
10710Sstevel@tonic-gate {
10720Sstevel@tonic-gate namelen -= 2 + strlen(namestack[--namedepth]);
10730Sstevel@tonic-gate namestack[ namedepth ] = 0;
10740Sstevel@tonic-gate
10750Sstevel@tonic-gate #ifdef DBG_ERRORS
10760Sstevel@tonic-gate /* just a little sanity check here */
10770Sstevel@tonic-gate if (namedepth <= 0) {
10780Sstevel@tonic-gate if (namedepth < 0) {
10790Sstevel@tonic-gate fprintf(stderr, "ASSERTION FAILURE: namedepth < 0\n");
10800Sstevel@tonic-gate exit(ERR_OTHER);
10810Sstevel@tonic-gate } else if (namelen != 0) {
10820Sstevel@tonic-gate fprintf(stderr, "ASSERTION FAILURE: namelen != 0\n");
10830Sstevel@tonic-gate exit(ERR_OTHER);
10840Sstevel@tonic-gate }
10850Sstevel@tonic-gate }
10860Sstevel@tonic-gate #endif
10870Sstevel@tonic-gate }
10880Sstevel@tonic-gate
10890Sstevel@tonic-gate char
get_name(struct file * fp)10900Sstevel@tonic-gate *get_name(struct file *fp)
10910Sstevel@tonic-gate { int i;
10920Sstevel@tonic-gate static char *namebuf = 0;
10930Sstevel@tonic-gate static int buflen = 0;
10940Sstevel@tonic-gate
10950Sstevel@tonic-gate /* make sure we have an adequate buffer */
10960Sstevel@tonic-gate i = namelen + 1 + strlen(fp->f_name);
10970Sstevel@tonic-gate if (buflen < i) {
10980Sstevel@tonic-gate for (buflen = MAX_PATH; buflen < i; buflen += MAX_NAME);
10990Sstevel@tonic-gate namebuf = (char *) realloc(namebuf, buflen);
11000Sstevel@tonic-gate }
11010Sstevel@tonic-gate
11020Sstevel@tonic-gate /* assemble the name */
11030Sstevel@tonic-gate namebuf[0] = 0;
11040Sstevel@tonic-gate for (i = 0; i < namedepth; i++) {
11050Sstevel@tonic-gate if (strcmp(namestack[i], ".")) {
11060Sstevel@tonic-gate strcat(namebuf, namestack[i]);
11070Sstevel@tonic-gate strcat(namebuf, "/");
11080Sstevel@tonic-gate }
11090Sstevel@tonic-gate }
11100Sstevel@tonic-gate
11110Sstevel@tonic-gate strcat(namebuf, fp->f_name);
11120Sstevel@tonic-gate
11130Sstevel@tonic-gate return (namebuf);
11140Sstevel@tonic-gate }
1115