xref: /illumos-gate/usr/src/cmd/filesync/anal.c (revision 63360950)
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*63360950Smp #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
analyze()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
prune_file(struct file * fp)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
prune()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
summary()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*63360950Smp 		/* 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
check_file(struct file * fp)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
check_changes(struct file * fp,int ref,int new)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
same_name(struct file * f1,struct file * f2,side_t srcdst)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 *
find_link(struct file * fp,side_t srcdst)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
has_other_links(struct file * fp,side_t srcdst)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
link_update(struct file * fp,side_t which)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
queue_file(struct file * fp)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
push_name(const char * name)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
pop_name(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
get_name(struct file * fp)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