xref: /illumos-gate/usr/src/cmd/filesync/action.c (revision 48bbca816818409505a6e214d0911fda44e622e3)
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
24*48bbca81SDaniel Hoffman  * Copyright (c) 2016 by Delphix. All rights reserved.
257c478bd9Sstevel@tonic-gate  *
267c478bd9Sstevel@tonic-gate  * module:
277c478bd9Sstevel@tonic-gate  *	action.c
287c478bd9Sstevel@tonic-gate  *
297c478bd9Sstevel@tonic-gate  * purpose:
307c478bd9Sstevel@tonic-gate  *	routines to carryout reconciliation actions and make the
317c478bd9Sstevel@tonic-gate  *	appropriate updates to the database file structure.
327c478bd9Sstevel@tonic-gate  *
337c478bd9Sstevel@tonic-gate  * contents:
347c478bd9Sstevel@tonic-gate  *	do_like ... change ownership and protection
357c478bd9Sstevel@tonic-gate  *	do_copy ... copy a file from one side to the other
367c478bd9Sstevel@tonic-gate  *	do_remove . remove a file from one side
377c478bd9Sstevel@tonic-gate  *	do_rename . rename a file on one side
387c478bd9Sstevel@tonic-gate  *	copy ...... (static) do the actual copy
397c478bd9Sstevel@tonic-gate  *	checksparse (static) figure out if a file is sparse
407c478bd9Sstevel@tonic-gate  *
417c478bd9Sstevel@tonic-gate  * ASSERTIONS:
427c478bd9Sstevel@tonic-gate  *	any of these action routines is responsible for all baseline
437c478bd9Sstevel@tonic-gate  *	and statistics updates associated with the reconciliation
447c478bd9Sstevel@tonic-gate  *	actions.  If notouch is specified, they should fake the
457c478bd9Sstevel@tonic-gate  *	updates well enough so that link tests will still work.
467c478bd9Sstevel@tonic-gate  *
477c478bd9Sstevel@tonic-gate  *	success:
487c478bd9Sstevel@tonic-gate  *		bump bp->b_{src,dst}_{copies,deletes,misc}
497c478bd9Sstevel@tonic-gate  *		update fp->f_info[srcdst]
507c478bd9Sstevel@tonic-gate  *		update fp->f_info[OPT_BASE] from fp->f_info[srcdst]
517c478bd9Sstevel@tonic-gate  *		if there might be multiple links, call link_update
527c478bd9Sstevel@tonic-gate  *		return ERR_RESOLVABLE
537c478bd9Sstevel@tonic-gate  *
547c478bd9Sstevel@tonic-gate  *	failure:
557c478bd9Sstevel@tonic-gate  *		set fp->f_flags |= F_CONFLICT
567c478bd9Sstevel@tonic-gate  *		set fp->f_problem
577c478bd9Sstevel@tonic-gate  *		bump bp->b_unresolved
587c478bd9Sstevel@tonic-gate  *		return ERR_UNRESOLVED
597c478bd9Sstevel@tonic-gate  *
607c478bd9Sstevel@tonic-gate  *	pretend this never happened:
617c478bd9Sstevel@tonic-gate  *		return 0, and baseline will be unchanged
627c478bd9Sstevel@tonic-gate  *
637c478bd9Sstevel@tonic-gate  * notes:
647c478bd9Sstevel@tonic-gate  *	Action routines can be called in virtually any order
657c478bd9Sstevel@tonic-gate  *	or combination, and it is certainly possible for an
667c478bd9Sstevel@tonic-gate  *	earlier action to succeed while a later action fails.
677c478bd9Sstevel@tonic-gate  *	If each successful action results in a completed baseline
687c478bd9Sstevel@tonic-gate  *	update, a subsequent failure will force the baseline to
697c478bd9Sstevel@tonic-gate  *	roll back to the last success ... which is appropriate.
707c478bd9Sstevel@tonic-gate  */
717c478bd9Sstevel@tonic-gate #ident	"%W%	%E% SMI"
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate #include <stdio.h>
747c478bd9Sstevel@tonic-gate #include <stdlib.h>
757c478bd9Sstevel@tonic-gate #include <unistd.h>
767c478bd9Sstevel@tonic-gate #include <fcntl.h>
777c478bd9Sstevel@tonic-gate #include <utime.h>
787c478bd9Sstevel@tonic-gate #include <errno.h>
797c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
807c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate #include "filesync.h"
837c478bd9Sstevel@tonic-gate #include "database.h"
847c478bd9Sstevel@tonic-gate #include "messages.h"
857c478bd9Sstevel@tonic-gate #include "debug.h"
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate /*
887c478bd9Sstevel@tonic-gate  * globals and importeds
897c478bd9Sstevel@tonic-gate  */
907c478bd9Sstevel@tonic-gate bool_t need_super;	/* warn user that we can't fix ownership	*/
917c478bd9Sstevel@tonic-gate extern char *srcname;	/* file we are emulating			*/
927c478bd9Sstevel@tonic-gate extern char *dstname;	/* file we are updating				*/
937c478bd9Sstevel@tonic-gate 
947c478bd9Sstevel@tonic-gate /*
957c478bd9Sstevel@tonic-gate  * locals
967c478bd9Sstevel@tonic-gate  */
977c478bd9Sstevel@tonic-gate static errmask_t copy(char *, char *, int);
987c478bd9Sstevel@tonic-gate static int checksparse(int);
997c478bd9Sstevel@tonic-gate static char *copy_err_str;		/* what went wrong w/copy	*/
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate /*
1027c478bd9Sstevel@tonic-gate  * routine:
1037c478bd9Sstevel@tonic-gate  *	do_like
1047c478bd9Sstevel@tonic-gate  *
1057c478bd9Sstevel@tonic-gate  * purpose:
1067c478bd9Sstevel@tonic-gate  *	to propagate ownership and protection changes between
1077c478bd9Sstevel@tonic-gate  *	one existing file and another.
1087c478bd9Sstevel@tonic-gate  *
1097c478bd9Sstevel@tonic-gate  * parameters:
1107c478bd9Sstevel@tonic-gate  *	file pointer
1117c478bd9Sstevel@tonic-gate  *	src/dst indication for who needs to change
1127c478bd9Sstevel@tonic-gate  *	whether or not to update statistics (there may be a copy and a like)
1137c478bd9Sstevel@tonic-gate  *
1147c478bd9Sstevel@tonic-gate  * returns:
1157c478bd9Sstevel@tonic-gate  *	error mask
1167c478bd9Sstevel@tonic-gate  *
1177c478bd9Sstevel@tonic-gate  * notes:
1187c478bd9Sstevel@tonic-gate  *	if we are called from reconcile, we should update
1197c478bd9Sstevel@tonic-gate  *	the statistics, but if we were called from do_copy
1207c478bd9Sstevel@tonic-gate  *	that routine will do the honors.
1217c478bd9Sstevel@tonic-gate  */
1227c478bd9Sstevel@tonic-gate errmask_t
do_like(struct file * fp,side_t srcdst,bool_t do_stats)1237c478bd9Sstevel@tonic-gate do_like(struct file *fp, side_t srcdst, bool_t do_stats)
1247c478bd9Sstevel@tonic-gate {	char *dst;
1257c478bd9Sstevel@tonic-gate 	int rc = 0;
1267c478bd9Sstevel@tonic-gate 	int do_chown, do_chmod, do_chgrp, do_acls;
1277c478bd9Sstevel@tonic-gate 	errmask_t errs = 0;
1287c478bd9Sstevel@tonic-gate 	char *errstr = 0;
1297c478bd9Sstevel@tonic-gate 	struct base *bp;
1307c478bd9Sstevel@tonic-gate 	struct fileinfo *sp;
1317c478bd9Sstevel@tonic-gate 	struct fileinfo *dp;
1327c478bd9Sstevel@tonic-gate 	struct fileinfo *ip;
1337c478bd9Sstevel@tonic-gate 	extern int errno;
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 	bp = fp->f_base;
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 	/* see if this is a forbidden propagation */
1387c478bd9Sstevel@tonic-gate 	if (srcdst == opt_oneway) {
1397c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_CONFLICT;
1407c478bd9Sstevel@tonic-gate 		fp->f_problem = gettext(PROB_prohibited);
1417c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
1427c478bd9Sstevel@tonic-gate 		return (ERR_UNRESOLVED);
1437c478bd9Sstevel@tonic-gate 	}
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate 	/* get info about source and target files		*/
1477c478bd9Sstevel@tonic-gate 	if (srcdst == OPT_SRC) {
1487c478bd9Sstevel@tonic-gate 		sp = &fp->f_info[ OPT_DST ];
1497c478bd9Sstevel@tonic-gate 		dp = &fp->f_info[ OPT_SRC ];
1507c478bd9Sstevel@tonic-gate 		dst = srcname;
1517c478bd9Sstevel@tonic-gate 	} else {
1527c478bd9Sstevel@tonic-gate 		sp = &fp->f_info[ OPT_SRC ];
1537c478bd9Sstevel@tonic-gate 		dp = &fp->f_info[ OPT_DST ];
1547c478bd9Sstevel@tonic-gate 		dst = dstname;
1557c478bd9Sstevel@tonic-gate 	}
1567c478bd9Sstevel@tonic-gate 	ip = &fp->f_info[ OPT_BASE ];
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 	/* figure out what needs fixing				*/
1597c478bd9Sstevel@tonic-gate 	do_chmod = (sp->f_mode != dp->f_mode);
1607c478bd9Sstevel@tonic-gate 	do_chown = (sp->f_uid != dp->f_uid);
1617c478bd9Sstevel@tonic-gate 	do_chgrp = (sp->f_gid != dp->f_gid);
1627c478bd9Sstevel@tonic-gate 	do_acls  = ((fp->f_srcdiffs|fp->f_dstdiffs) & D_FACLS);
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate 	/*
1657c478bd9Sstevel@tonic-gate 	 * try to anticipate things that we might not be able to
1667c478bd9Sstevel@tonic-gate 	 * do, and return appropriate errorst if the calling user
1677c478bd9Sstevel@tonic-gate 	 * cannot safely perform the requiested updates.
1687c478bd9Sstevel@tonic-gate 	 */
1697c478bd9Sstevel@tonic-gate 	if (my_uid != 0) {
1707c478bd9Sstevel@tonic-gate 		if (do_chown)
1717c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_chown);
1727c478bd9Sstevel@tonic-gate 		else if (my_uid != dp->f_uid) {
1737c478bd9Sstevel@tonic-gate 			if (do_chmod)
1747c478bd9Sstevel@tonic-gate 				errstr = gettext(PROB_chmod);
1757c478bd9Sstevel@tonic-gate 			else if (do_acls)
1767c478bd9Sstevel@tonic-gate 				errstr = gettext(PROB_chacl);
1777c478bd9Sstevel@tonic-gate 			else if (do_chgrp)
1787c478bd9Sstevel@tonic-gate 				errstr = gettext(PROB_chgrp);
1797c478bd9Sstevel@tonic-gate 		}
1807c478bd9Sstevel@tonic-gate #ifdef	ACL_UID_BUG
1817c478bd9Sstevel@tonic-gate 		else if (do_acls && my_gid != dp->f_gid)
1827c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_botch);
1837c478bd9Sstevel@tonic-gate #endif
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate 		if (errstr) {
1867c478bd9Sstevel@tonic-gate 			need_super = TRUE;
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 			/* if the user doesn't care, shine it on	*/
1897c478bd9Sstevel@tonic-gate 			if (opt_everything == 0)
1907c478bd9Sstevel@tonic-gate 				return (0);
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 			/* if the user does care, return the error	*/
1937c478bd9Sstevel@tonic-gate 			rc = -1;
1947c478bd9Sstevel@tonic-gate 			goto nogood;
1957c478bd9Sstevel@tonic-gate 		}
1967c478bd9Sstevel@tonic-gate 	}
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate 	if (opt_debug & DBG_RECON) {
1997c478bd9Sstevel@tonic-gate 		fprintf(stderr, "RECO: do_like %s (", dst);
2007c478bd9Sstevel@tonic-gate 		if (do_chmod)
2017c478bd9Sstevel@tonic-gate 			fprintf(stderr, "chmod ");
2027c478bd9Sstevel@tonic-gate 		if (do_acls)
2037c478bd9Sstevel@tonic-gate 			fprintf(stderr, "acls ");
2047c478bd9Sstevel@tonic-gate 		if (do_chown)
2057c478bd9Sstevel@tonic-gate 			fprintf(stderr, "chown ");
2067c478bd9Sstevel@tonic-gate 		if (do_chgrp)
2077c478bd9Sstevel@tonic-gate 			fprintf(stderr, "chgrp ");
2087c478bd9Sstevel@tonic-gate 		fprintf(stderr, ")\n");
2097c478bd9Sstevel@tonic-gate 	}
2107c478bd9Sstevel@tonic-gate 
2117c478bd9Sstevel@tonic-gate 	if (do_chmod) {
2127c478bd9Sstevel@tonic-gate 		if (!opt_quiet)
2137c478bd9Sstevel@tonic-gate 			fprintf(stdout, "chmod %o %s\n", sp->f_mode,
2147c478bd9Sstevel@tonic-gate 						noblanks(dst));
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
2177c478bd9Sstevel@tonic-gate 		/* should we simulate a chmod failure	*/
2187c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(dst, 'p'))
2197c478bd9Sstevel@tonic-gate 			rc = -1;
2207c478bd9Sstevel@tonic-gate 		else
2217c478bd9Sstevel@tonic-gate #endif
2227c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0 : chmod(dst, sp->f_mode);
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate 		if (opt_debug & DBG_RECON)
2257c478bd9Sstevel@tonic-gate 			fprintf(stderr, "RECO: do_chmod %o -> %d(%d)\n",
2267c478bd9Sstevel@tonic-gate 				sp->f_mode, rc, errno);
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 		/* update dest and baseline to reflect the change */
2297c478bd9Sstevel@tonic-gate 		if (rc == 0) {
2307c478bd9Sstevel@tonic-gate 			dp->f_mode = sp->f_mode;
2317c478bd9Sstevel@tonic-gate 			ip->f_mode = sp->f_mode;
2327c478bd9Sstevel@tonic-gate 		} else
2337c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_chmod);
2347c478bd9Sstevel@tonic-gate 	}
2357c478bd9Sstevel@tonic-gate 
2367c478bd9Sstevel@tonic-gate 	/*
2377c478bd9Sstevel@tonic-gate 	 * see if we need to fix the acls
2387c478bd9Sstevel@tonic-gate 	 */
2397c478bd9Sstevel@tonic-gate 	if (rc == 0 && do_acls) {
2407c478bd9Sstevel@tonic-gate 		if (!opt_quiet)
2417c478bd9Sstevel@tonic-gate 			fprintf(stdout, "setfacl %s %s\n",
2427c478bd9Sstevel@tonic-gate 				show_acls(sp->f_numacls, sp->f_acls),
2437c478bd9Sstevel@tonic-gate 				noblanks(dst));
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
2467c478bd9Sstevel@tonic-gate 		/* should we simulate a set acl failure	*/
2477c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(dst, 'a'))
2487c478bd9Sstevel@tonic-gate 			rc = -1;
2497c478bd9Sstevel@tonic-gate 		else
2507c478bd9Sstevel@tonic-gate #endif
2517c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0 : set_acls(dst, sp);
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate 		if (opt_debug & DBG_RECON)
2547c478bd9Sstevel@tonic-gate 			fprintf(stderr, "RECO: do_acls %d -> %d(%d)\n",
2557c478bd9Sstevel@tonic-gate 				sp->f_numacls, rc, errno);
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 		/* update dest and baseline to reflect the change */
2587c478bd9Sstevel@tonic-gate 		if (rc == 0) {
2597c478bd9Sstevel@tonic-gate 			dp->f_numacls = sp->f_numacls;
2607c478bd9Sstevel@tonic-gate 			dp->f_acls = sp->f_acls;
2617c478bd9Sstevel@tonic-gate 			ip->f_numacls = sp->f_numacls;
2627c478bd9Sstevel@tonic-gate 			ip->f_acls = sp->f_acls;
2637c478bd9Sstevel@tonic-gate #ifdef	ACL_UID_BUG
2647c478bd9Sstevel@tonic-gate 			/* SETFACL changes a file's UID/GID	*/
2657c478bd9Sstevel@tonic-gate 			if (my_uid != dp->f_uid) {
2667c478bd9Sstevel@tonic-gate 				do_chown = 1;
2677c478bd9Sstevel@tonic-gate 				dp->f_uid = my_uid;
2687c478bd9Sstevel@tonic-gate 			}
2697c478bd9Sstevel@tonic-gate 			if (my_gid != dp->f_gid) {
2707c478bd9Sstevel@tonic-gate 				do_chgrp = 1;
2717c478bd9Sstevel@tonic-gate 				dp->f_gid = my_gid;
2727c478bd9Sstevel@tonic-gate 			}
2737c478bd9Sstevel@tonic-gate #endif
2747c478bd9Sstevel@tonic-gate 		} else if (errno == ENOSYS) {
2757c478bd9Sstevel@tonic-gate 			/*
2767c478bd9Sstevel@tonic-gate 			 * if the file system doesn't support ACLs
2777c478bd9Sstevel@tonic-gate 			 * we should just pretend we never saw them
2787c478bd9Sstevel@tonic-gate 			 */
2797c478bd9Sstevel@tonic-gate 			fprintf(stderr, gettext(WARN_noacls), dst);
2807c478bd9Sstevel@tonic-gate 			ip->f_numacls = 0;
2817c478bd9Sstevel@tonic-gate 			sp->f_numacls = 0;
2827c478bd9Sstevel@tonic-gate 			dp->f_numacls = 0;
2837c478bd9Sstevel@tonic-gate 			rc = 0;
2847c478bd9Sstevel@tonic-gate 		} else
2857c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_chacl);
2867c478bd9Sstevel@tonic-gate 	}
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate 	/*
2897c478bd9Sstevel@tonic-gate 	 * see if we need to fix the ownership
2907c478bd9Sstevel@tonic-gate 	 */
2917c478bd9Sstevel@tonic-gate 	if (rc == 0 && (do_chown || do_chgrp)) {
2927c478bd9Sstevel@tonic-gate 		if (do_chown)
2937c478bd9Sstevel@tonic-gate 			fprintf(stdout, "chown %ld %s; ",
2947c478bd9Sstevel@tonic-gate 				sp->f_uid, noblanks(dst));
2957c478bd9Sstevel@tonic-gate 		if (do_chgrp)
2967c478bd9Sstevel@tonic-gate 			fprintf(stdout, "chgrp %ld %s",
2977c478bd9Sstevel@tonic-gate 				sp->f_gid, noblanks(dst));
2987c478bd9Sstevel@tonic-gate 
2997c478bd9Sstevel@tonic-gate 		fprintf(stdout, "\n");
3007c478bd9Sstevel@tonic-gate 
3017c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
3027c478bd9Sstevel@tonic-gate 		/* should we simulate a chown failure	*/
3037c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(dst, 'O'))
3047c478bd9Sstevel@tonic-gate 			rc = -1;
3057c478bd9Sstevel@tonic-gate 		else
3067c478bd9Sstevel@tonic-gate #endif
3077c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0 : lchown(dst, sp->f_uid, sp->f_gid);
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate 		if (opt_debug & DBG_RECON)
3107c478bd9Sstevel@tonic-gate 			fprintf(stderr, "RECO: do_chown %ld %ld -> %d(%d)\n",
3117c478bd9Sstevel@tonic-gate 					sp->f_uid, sp->f_gid, rc, errno);
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate 		/* update the destination to reflect changes */
3147c478bd9Sstevel@tonic-gate 		if (rc == 0) {
3157c478bd9Sstevel@tonic-gate 			dp->f_uid = sp->f_uid;
3167c478bd9Sstevel@tonic-gate 			dp->f_gid = sp->f_gid;
3177c478bd9Sstevel@tonic-gate 			ip->f_uid = sp->f_uid;
3187c478bd9Sstevel@tonic-gate 			ip->f_gid = sp->f_gid;
3197c478bd9Sstevel@tonic-gate 		} else {
3207c478bd9Sstevel@tonic-gate 			if (errno == EPERM) {
3217c478bd9Sstevel@tonic-gate 				need_super = TRUE;
3227c478bd9Sstevel@tonic-gate 				if (opt_everything == 0)
3237c478bd9Sstevel@tonic-gate 					return (0);
3247c478bd9Sstevel@tonic-gate 			}
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 			if (rc != 0)
3277c478bd9Sstevel@tonic-gate 				errstr = gettext(do_chown ?
3287c478bd9Sstevel@tonic-gate 						PROB_chown : PROB_chgrp);
3297c478bd9Sstevel@tonic-gate 		}
3307c478bd9Sstevel@tonic-gate 	}
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	/*
3337c478bd9Sstevel@tonic-gate 	 * if we were successful, we should make sure the other links
3347c478bd9Sstevel@tonic-gate 	 * see the changes.  If we were called from do_copy, we don't
3357c478bd9Sstevel@tonic-gate 	 * want to do the link_updates either because do_copy will
3367c478bd9Sstevel@tonic-gate 	 * handle them too.
3377c478bd9Sstevel@tonic-gate 	 */
3387c478bd9Sstevel@tonic-gate 	if (rc == 0 && do_stats)
3397c478bd9Sstevel@tonic-gate 		link_update(fp, srcdst);
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate nogood:
3427c478bd9Sstevel@tonic-gate 	if (!do_stats)
3437c478bd9Sstevel@tonic-gate 		return (errs);
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	if (rc != 0) {
3467c478bd9Sstevel@tonic-gate 		fprintf(stderr, gettext(ERR_cannot), errstr, dst);
3477c478bd9Sstevel@tonic-gate 		fp->f_problem = errstr;
3487c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_CONFLICT;
3497c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
3507c478bd9Sstevel@tonic-gate 		errs |= ERR_PERM | ERR_UNRESOLVED;
3517c478bd9Sstevel@tonic-gate 	} else {
3527c478bd9Sstevel@tonic-gate 		/*
3537c478bd9Sstevel@tonic-gate 		 * it worked, so update the baseline and statistics
3547c478bd9Sstevel@tonic-gate 		 */
3557c478bd9Sstevel@tonic-gate 		if (srcdst == OPT_SRC)
3567c478bd9Sstevel@tonic-gate 			bp->b_src_misc++;
3577c478bd9Sstevel@tonic-gate 		else
3587c478bd9Sstevel@tonic-gate 			bp->b_dst_misc++;
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 		fp->f_problem = 0;
3617c478bd9Sstevel@tonic-gate 		errs |= ERR_RESOLVABLE;
3627c478bd9Sstevel@tonic-gate 	}
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	return (errs);
3657c478bd9Sstevel@tonic-gate }
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate /*
3687c478bd9Sstevel@tonic-gate  * routine:
3697c478bd9Sstevel@tonic-gate  *	do_copy
3707c478bd9Sstevel@tonic-gate  *
3717c478bd9Sstevel@tonic-gate  * purpose:
3727c478bd9Sstevel@tonic-gate  *	to propagate a creation or change
3737c478bd9Sstevel@tonic-gate  *
3747c478bd9Sstevel@tonic-gate  * parameters:
3757c478bd9Sstevel@tonic-gate  *	file pointer
3767c478bd9Sstevel@tonic-gate  *	src/dst indication for who gets the copy
3777c478bd9Sstevel@tonic-gate  *
3787c478bd9Sstevel@tonic-gate  * returns:
3797c478bd9Sstevel@tonic-gate  *	error mask
3807c478bd9Sstevel@tonic-gate  *
3817c478bd9Sstevel@tonic-gate  * note:
3827c478bd9Sstevel@tonic-gate  *	after any successful operation we update the stat/info
3837c478bd9Sstevel@tonic-gate  *	structure for the updated file.  This is somewhat redundant
3847c478bd9Sstevel@tonic-gate  *	because we will restat at the end of the routine, but these
3857c478bd9Sstevel@tonic-gate  *	anticipatory updates help to ensure that the link finding
3867c478bd9Sstevel@tonic-gate  *	code will still behave properly in notouch mode (when restats
3877c478bd9Sstevel@tonic-gate  *	cannot be done).
3887c478bd9Sstevel@tonic-gate  */
3897c478bd9Sstevel@tonic-gate errmask_t
do_copy(struct file * fp,side_t srcdst)3907c478bd9Sstevel@tonic-gate do_copy(struct file *fp, side_t srcdst)
3917c478bd9Sstevel@tonic-gate {	char *src, *dst;
3927c478bd9Sstevel@tonic-gate 	char cmdbuf[ MAX_PATH + MAX_NAME ];
3937c478bd9Sstevel@tonic-gate 	int mode, maj, min, type;
3947c478bd9Sstevel@tonic-gate 	uid_t uid;
3957c478bd9Sstevel@tonic-gate 	gid_t gid;
3967c478bd9Sstevel@tonic-gate 	int rc;
3977c478bd9Sstevel@tonic-gate 	long mtime;
3987c478bd9Sstevel@tonic-gate 	int do_chmod = 0;
3997c478bd9Sstevel@tonic-gate 	int do_chown = 0;
4007c478bd9Sstevel@tonic-gate 	int do_chgrp = 0;
4017c478bd9Sstevel@tonic-gate 	int do_unlink = 0;
4027c478bd9Sstevel@tonic-gate 	int do_acls = 0;
4037c478bd9Sstevel@tonic-gate 	int do_create = 0;
4047c478bd9Sstevel@tonic-gate 	char *errstr = "???";
4057c478bd9Sstevel@tonic-gate 	errmask_t errs = 0;
4067c478bd9Sstevel@tonic-gate 	struct base *bp;
4077c478bd9Sstevel@tonic-gate 	struct file *lp;
4087c478bd9Sstevel@tonic-gate 	struct fileinfo *sp, *dp;
4097c478bd9Sstevel@tonic-gate 	struct utimbuf newtimes;
4107c478bd9Sstevel@tonic-gate 	struct stat statb;
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 	bp = fp->f_base;
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 	/* see if this is a forbidden propagation */
4157c478bd9Sstevel@tonic-gate 	if (srcdst == opt_oneway) {
4167c478bd9Sstevel@tonic-gate 		fp->f_problem = gettext(PROB_prohibited);
4177c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_CONFLICT;
4187c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
4197c478bd9Sstevel@tonic-gate 		return (ERR_UNRESOLVED);
4207c478bd9Sstevel@tonic-gate 	}
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate 	/* figure out who is the source and who is the destination	*/
4237c478bd9Sstevel@tonic-gate 	if (srcdst == OPT_SRC) {
4247c478bd9Sstevel@tonic-gate 		sp = &fp->f_info[ OPT_DST ];
4257c478bd9Sstevel@tonic-gate 		dp = &fp->f_info[ OPT_SRC ];
4267c478bd9Sstevel@tonic-gate 		src = dstname;
4277c478bd9Sstevel@tonic-gate 		dst = srcname;
4287c478bd9Sstevel@tonic-gate 	} else {
4297c478bd9Sstevel@tonic-gate 		sp = &fp->f_info[ OPT_SRC ];
4307c478bd9Sstevel@tonic-gate 		dp = &fp->f_info[ OPT_DST ];
4317c478bd9Sstevel@tonic-gate 		src = srcname;
4327c478bd9Sstevel@tonic-gate 		dst = dstname;
4337c478bd9Sstevel@tonic-gate 	}
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 	/* note information about the file to be created		*/
4367c478bd9Sstevel@tonic-gate 	type  = sp->f_type;		/* type of the new file		*/
4377c478bd9Sstevel@tonic-gate 	uid   = sp->f_uid;		/* owner of the new file	*/
4387c478bd9Sstevel@tonic-gate 	gid   = sp->f_gid;		/* group of the new file	*/
4397c478bd9Sstevel@tonic-gate 	mode  = sp->f_mode;		/* modes for the new file	*/
4407c478bd9Sstevel@tonic-gate 	mtime = sp->f_modtime;		/* modtime (if preserving)	*/
4417c478bd9Sstevel@tonic-gate 	maj   = sp->f_rd_maj;		/* major (if it is a device)	*/
4427c478bd9Sstevel@tonic-gate 	min   = sp->f_rd_min;		/* minor (if it is a device)	*/
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 	/*
4457c478bd9Sstevel@tonic-gate 	 * creating a file does not guarantee it will get the desired
4467c478bd9Sstevel@tonic-gate 	 * modes, uid and gid.  If the file already exists, it will
4477c478bd9Sstevel@tonic-gate 	 * retain its old ownership and protection.  If my UID/GID
4487c478bd9Sstevel@tonic-gate 	 * are not the desired ones, the new file will also require
4497c478bd9Sstevel@tonic-gate 	 * manual correction.  If the file has the wrong type, we will
4507c478bd9Sstevel@tonic-gate 	 * need to delete it and recreate it.  If the file is not writable,
4517c478bd9Sstevel@tonic-gate 	 * it is easier to delete it than to chmod it to permit overwrite
4527c478bd9Sstevel@tonic-gate 	 */
4537c478bd9Sstevel@tonic-gate 	if ((dp->f_type == S_IFREG && sp->f_type == S_IFREG) &&
4547c478bd9Sstevel@tonic-gate 	    (dp->f_mode & 0200)) {
4557c478bd9Sstevel@tonic-gate 		/* if the file already exists		*/
4567c478bd9Sstevel@tonic-gate 		if (dp->f_uid != uid)
4577c478bd9Sstevel@tonic-gate 			do_chown = 1;
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 		if (dp->f_gid != gid)
4607c478bd9Sstevel@tonic-gate 			do_chgrp = 1;
4617c478bd9Sstevel@tonic-gate 
4627c478bd9Sstevel@tonic-gate 		if (dp->f_mode != mode)
4637c478bd9Sstevel@tonic-gate 			do_chmod = 1;
4647c478bd9Sstevel@tonic-gate 	} else {
4657c478bd9Sstevel@tonic-gate 		/* if we will be creating a new file	*/
4667c478bd9Sstevel@tonic-gate 		do_create = 1;
4677c478bd9Sstevel@tonic-gate 		if (dp->f_type)
4687c478bd9Sstevel@tonic-gate 			do_unlink = 1;
4697c478bd9Sstevel@tonic-gate 		if (uid != my_uid)
4707c478bd9Sstevel@tonic-gate 			do_chown = 1;
4717c478bd9Sstevel@tonic-gate 		if (gid != my_gid)
4727c478bd9Sstevel@tonic-gate 			do_chgrp = 1;
4737c478bd9Sstevel@tonic-gate 	}
4747c478bd9Sstevel@tonic-gate 
4757c478bd9Sstevel@tonic-gate 	/*
4767c478bd9Sstevel@tonic-gate 	 * if the source has acls, we will surely have to set them for dest
4777c478bd9Sstevel@tonic-gate 	 */
4787c478bd9Sstevel@tonic-gate 	if (sp->f_numacls)
4797c478bd9Sstevel@tonic-gate 		do_acls = 1;
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate 	/*
4827c478bd9Sstevel@tonic-gate 	 * for any case other than replacing a normal file with a normal
4837c478bd9Sstevel@tonic-gate 	 * file, we need to delete the existing file before creating
4847c478bd9Sstevel@tonic-gate 	 * the new one.
4857c478bd9Sstevel@tonic-gate 	 */
4867c478bd9Sstevel@tonic-gate 	if (do_unlink) {
4877c478bd9Sstevel@tonic-gate 		if (dp->f_type == S_IFDIR) {
4887c478bd9Sstevel@tonic-gate 			if (!opt_quiet)
4897c478bd9Sstevel@tonic-gate 				fprintf(stdout, "rmdir %s\n", noblanks(dst));
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_rmdir);
4927c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
4937c478bd9Sstevel@tonic-gate 			/* should we simulate a rmdir failure	*/
4947c478bd9Sstevel@tonic-gate 			if (errno = dbg_chk_error(dst, 'D'))
4957c478bd9Sstevel@tonic-gate 				rc = -1;
4967c478bd9Sstevel@tonic-gate 			else
4977c478bd9Sstevel@tonic-gate #endif
4987c478bd9Sstevel@tonic-gate 			rc = opt_notouch ? 0 : rmdir(dst);
4997c478bd9Sstevel@tonic-gate 		} else {
5007c478bd9Sstevel@tonic-gate 			if (!opt_quiet)
5017c478bd9Sstevel@tonic-gate 				fprintf(stdout, "rm %s\n", noblanks(dst));
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_unlink);
5047c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
5057c478bd9Sstevel@tonic-gate 			/* should we simulate a unlink failure	*/
5067c478bd9Sstevel@tonic-gate 			if (errno = dbg_chk_error(dst, 'u'))
5077c478bd9Sstevel@tonic-gate 				rc = -1;
5087c478bd9Sstevel@tonic-gate 			else
5097c478bd9Sstevel@tonic-gate #endif
5107c478bd9Sstevel@tonic-gate 			rc = opt_notouch ? 0 : unlink(dst);
5117c478bd9Sstevel@tonic-gate 		}
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 		if (rc != 0)
5147c478bd9Sstevel@tonic-gate 			goto cant;
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 		/* note that this file no longer exists		*/
5177c478bd9Sstevel@tonic-gate 		dp->f_type = 0;
5187c478bd9Sstevel@tonic-gate 		dp->f_mode = 0;
5197c478bd9Sstevel@tonic-gate 	}
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate 	if (opt_debug & DBG_RECON) {
5227c478bd9Sstevel@tonic-gate 		fprintf(stderr, "RECO: do_copy %s %s (", src, dst);
5237c478bd9Sstevel@tonic-gate 		if (do_unlink)
5247c478bd9Sstevel@tonic-gate 			fprintf(stderr, "unlink ");
5257c478bd9Sstevel@tonic-gate 		if (do_chmod)
5267c478bd9Sstevel@tonic-gate 			fprintf(stderr, "chmod ");
5277c478bd9Sstevel@tonic-gate 		if (do_acls)
5287c478bd9Sstevel@tonic-gate 			fprintf(stderr, "acls ");
5297c478bd9Sstevel@tonic-gate 		if (do_chown)
5307c478bd9Sstevel@tonic-gate 			fprintf(stderr, "chown ");
5317c478bd9Sstevel@tonic-gate 		if (do_chgrp)
5327c478bd9Sstevel@tonic-gate 			fprintf(stderr, "chgrp ");
5337c478bd9Sstevel@tonic-gate 		fprintf(stderr, ")\n");
5347c478bd9Sstevel@tonic-gate 	}
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate 	/*
5377c478bd9Sstevel@tonic-gate 	 * how we go about copying a file depends on what type of file
5387c478bd9Sstevel@tonic-gate 	 * it is that we are supposed to copy
5397c478bd9Sstevel@tonic-gate 	 */
5407c478bd9Sstevel@tonic-gate 	switch (type) {
5417c478bd9Sstevel@tonic-gate 	    case S_IFDIR:
5427c478bd9Sstevel@tonic-gate 		if (!opt_quiet) {
5437c478bd9Sstevel@tonic-gate 			fprintf(stdout, "mkdir %s;", noblanks(dst));
5447c478bd9Sstevel@tonic-gate 			fprintf(stdout, " chmod %o %s;\n", mode, noblanks(dst));
5457c478bd9Sstevel@tonic-gate 		}
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate 		errstr = gettext(PROB_mkdir);
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
5507c478bd9Sstevel@tonic-gate 		/* should we simulate a mkdir failure	*/
5517c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(dst, 'd'))
5527c478bd9Sstevel@tonic-gate 			rc = -1;
5537c478bd9Sstevel@tonic-gate 		else
5547c478bd9Sstevel@tonic-gate #endif
5557c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0 : mkdir(dst, mode);
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 		/* update stat with what we have just created	*/
5587c478bd9Sstevel@tonic-gate 		if (rc == 0) {
5597c478bd9Sstevel@tonic-gate 			dp->f_type = S_IFDIR;
5607c478bd9Sstevel@tonic-gate 			dp->f_uid = my_uid;
5617c478bd9Sstevel@tonic-gate 			dp->f_gid = my_gid;
5627c478bd9Sstevel@tonic-gate 			dp->f_mode = mode;
5637c478bd9Sstevel@tonic-gate 		}
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 		break;
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate 	    case S_IFLNK:
5687c478bd9Sstevel@tonic-gate 		errstr = gettext(PROB_readlink);
5697c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
5707c478bd9Sstevel@tonic-gate 		/* should we simulate a symlink read failure	*/
5717c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(dst, 'r'))
5727c478bd9Sstevel@tonic-gate 			rc = -1;
5737c478bd9Sstevel@tonic-gate 		else
5747c478bd9Sstevel@tonic-gate #endif
5757c478bd9Sstevel@tonic-gate 		rc = readlink(src, cmdbuf, sizeof (cmdbuf));
5767c478bd9Sstevel@tonic-gate 		if (rc > 0) {
5777c478bd9Sstevel@tonic-gate 			cmdbuf[rc] = 0;
5787c478bd9Sstevel@tonic-gate 			if (!opt_quiet) {
5797c478bd9Sstevel@tonic-gate 				fprintf(stdout, "ln -s %s", noblanks(cmdbuf));
5807c478bd9Sstevel@tonic-gate 				fprintf(stdout, " %s;\n", noblanks(dst));
5817c478bd9Sstevel@tonic-gate 			}
5827c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_symlink);
5837c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
5847c478bd9Sstevel@tonic-gate 			/* should we simulate a symlink failure	*/
5857c478bd9Sstevel@tonic-gate 			if (errno = dbg_chk_error(dst, 'l'))
5867c478bd9Sstevel@tonic-gate 				rc = -1;
5877c478bd9Sstevel@tonic-gate 			else
5887c478bd9Sstevel@tonic-gate #endif
5897c478bd9Sstevel@tonic-gate 			rc = opt_notouch ? 0 : symlink(cmdbuf, dst);
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 			if (rc == 0)
5927c478bd9Sstevel@tonic-gate 				dp->f_type = S_IFLNK;
5937c478bd9Sstevel@tonic-gate 		}
5947c478bd9Sstevel@tonic-gate 		break;
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate 	    case S_IFBLK:
5977c478bd9Sstevel@tonic-gate 	    case S_IFCHR:
5987c478bd9Sstevel@tonic-gate 		if (!opt_quiet)
5997c478bd9Sstevel@tonic-gate 			fprintf(stdout, "mknod %s %s %d %d\n", noblanks(dst),
6007c478bd9Sstevel@tonic-gate 				(type == S_IFBLK) ? "b" : "c", maj, min);
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 		errstr = gettext(PROB_mknod);
6037c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
6047c478bd9Sstevel@tonic-gate 		/* should we simulate a mknod failure	*/
6057c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(dst, 'd'))
6067c478bd9Sstevel@tonic-gate 			rc = -1;
6077c478bd9Sstevel@tonic-gate 		else
6087c478bd9Sstevel@tonic-gate #endif
6097c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0
6107c478bd9Sstevel@tonic-gate 				: mknod(dst, mode|type, makedev(maj, min));
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate 		/* update stat with what we have just created	*/
6137c478bd9Sstevel@tonic-gate 		if (rc == 0) {
6147c478bd9Sstevel@tonic-gate 			dp->f_type = type;
6157c478bd9Sstevel@tonic-gate 			dp->f_uid = my_uid;
6167c478bd9Sstevel@tonic-gate 			dp->f_gid = my_gid;
6177c478bd9Sstevel@tonic-gate 			dp->f_mode = 0666;
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 			if (dp->f_mode != mode)
6207c478bd9Sstevel@tonic-gate 				do_chmod = 1;
6217c478bd9Sstevel@tonic-gate 		}
6227c478bd9Sstevel@tonic-gate 		break;
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 	    case S_IFREG:
6257c478bd9Sstevel@tonic-gate 		/*
6267c478bd9Sstevel@tonic-gate 		 * The first thing to do is ascertain whether or not
6277c478bd9Sstevel@tonic-gate 		 * the alleged new copy might in fact be a new link.
6287c478bd9Sstevel@tonic-gate 		 * We trust find_link to weigh all the various factors,
629*48bbca81SDaniel Hoffman 		 * so if it says make a link, we'll do it.
6307c478bd9Sstevel@tonic-gate 		 */
6317c478bd9Sstevel@tonic-gate 		lp = find_link(fp, srcdst);
6327c478bd9Sstevel@tonic-gate 		if (lp) {
6337c478bd9Sstevel@tonic-gate 			/* figure out name of existing file	*/
6347c478bd9Sstevel@tonic-gate 			src = full_name(lp, srcdst, OPT_BASE);
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 			/*
6377c478bd9Sstevel@tonic-gate 			 * if file already exists, it must be deleted
6387c478bd9Sstevel@tonic-gate 			 */
6397c478bd9Sstevel@tonic-gate 			if (dp->f_type) {
6407c478bd9Sstevel@tonic-gate 				if (!opt_quiet)
6417c478bd9Sstevel@tonic-gate 					fprintf(stdout, "rm %s\n",
6427c478bd9Sstevel@tonic-gate 						noblanks(dst));
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate 				errstr = gettext(PROB_unlink);
6457c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
6467c478bd9Sstevel@tonic-gate 				/* should we simulate a unlink failure	*/
6477c478bd9Sstevel@tonic-gate 				if (errno = dbg_chk_error(dst, 'u'))
6487c478bd9Sstevel@tonic-gate 					rc = -1;
6497c478bd9Sstevel@tonic-gate 				else
6507c478bd9Sstevel@tonic-gate #endif
6517c478bd9Sstevel@tonic-gate 				rc = opt_notouch ? 0 : unlink(dst);
6527c478bd9Sstevel@tonic-gate 
6537c478bd9Sstevel@tonic-gate 				/*
6547c478bd9Sstevel@tonic-gate 				 * if we couldn't do the unlink, we must
6557c478bd9Sstevel@tonic-gate 				 * mark the linkee in conflict as well
656*48bbca81SDaniel Hoffman 				 * so its reference count remains the same
657*48bbca81SDaniel Hoffman 				 * in the baseline and it continues to show
6587c478bd9Sstevel@tonic-gate 				 * up on the change list.
6597c478bd9Sstevel@tonic-gate 				 */
6607c478bd9Sstevel@tonic-gate 				if (rc != 0) {
6617c478bd9Sstevel@tonic-gate 					lp->f_flags |= F_CONFLICT;
6627c478bd9Sstevel@tonic-gate 					lp->f_problem = gettext(PROB_link);
6637c478bd9Sstevel@tonic-gate 					goto cant;
6647c478bd9Sstevel@tonic-gate 				}
6657c478bd9Sstevel@tonic-gate 			}
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 			if (!opt_quiet) {
6687c478bd9Sstevel@tonic-gate 				fprintf(stdout, "ln %s", noblanks(src));
6697c478bd9Sstevel@tonic-gate 				fprintf(stdout, " %s\n", noblanks(dst));
6707c478bd9Sstevel@tonic-gate 			}
6717c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_link);
6727c478bd9Sstevel@tonic-gate 
6737c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
6747c478bd9Sstevel@tonic-gate 			/* should we simulate a link failure	*/
6757c478bd9Sstevel@tonic-gate 			if (errno = dbg_chk_error(dst, 'l'))
6767c478bd9Sstevel@tonic-gate 				rc = -1;
6777c478bd9Sstevel@tonic-gate 			else
6787c478bd9Sstevel@tonic-gate #endif
6797c478bd9Sstevel@tonic-gate 			rc = opt_notouch ? 0 : link(src, dst);
6807c478bd9Sstevel@tonic-gate 
6817c478bd9Sstevel@tonic-gate 			/*
6827c478bd9Sstevel@tonic-gate 			 * if this is a link, there is no reason to worry
6837c478bd9Sstevel@tonic-gate 			 * about ownership and modes, they are automatic
6847c478bd9Sstevel@tonic-gate 			 */
6857c478bd9Sstevel@tonic-gate 			do_chown = 0; do_chgrp = 0; do_chmod = 0; do_acls = 0;
6867c478bd9Sstevel@tonic-gate 			if (rc == 0) {
6877c478bd9Sstevel@tonic-gate 				dp->f_type = type;
6887c478bd9Sstevel@tonic-gate 				dp->f_uid = uid;
6897c478bd9Sstevel@tonic-gate 				dp->f_gid = gid;
6907c478bd9Sstevel@tonic-gate 				dp->f_mode = mode;
6917c478bd9Sstevel@tonic-gate 				break;
6927c478bd9Sstevel@tonic-gate 			} else {
6937c478bd9Sstevel@tonic-gate 				/*
6947c478bd9Sstevel@tonic-gate 				 * if we failed to make a link, we want to
6957c478bd9Sstevel@tonic-gate 				 * mark the linkee in conflict too, so that
696*48bbca81SDaniel Hoffman 				 * its reference count remains the same in
697*48bbca81SDaniel Hoffman 				 * the baseline, and it shows up on the change
6987c478bd9Sstevel@tonic-gate 				 * list again next time.
6997c478bd9Sstevel@tonic-gate 				 */
7007c478bd9Sstevel@tonic-gate 				lp->f_flags |= F_CONFLICT;
7017c478bd9Sstevel@tonic-gate 				lp->f_problem = errstr;
7027c478bd9Sstevel@tonic-gate 				break;
7037c478bd9Sstevel@tonic-gate 			}
7047c478bd9Sstevel@tonic-gate 
7057c478bd9Sstevel@tonic-gate 			/*
7067c478bd9Sstevel@tonic-gate 			 * in some situation we haven't figured out yet
7077c478bd9Sstevel@tonic-gate 			 * we might want to fall through and try a copy
7087c478bd9Sstevel@tonic-gate 			 * if the link failed.
7097c478bd9Sstevel@tonic-gate 			 */
7107c478bd9Sstevel@tonic-gate 		}
7117c478bd9Sstevel@tonic-gate 
7127c478bd9Sstevel@tonic-gate 		/* we are going to resolve this by making a copy	*/
7137c478bd9Sstevel@tonic-gate 		if (!opt_quiet) {
7147c478bd9Sstevel@tonic-gate 			fprintf(stdout, "cp %s", noblanks(src));
7157c478bd9Sstevel@tonic-gate 			fprintf(stdout, " %s\n", noblanks(dst));
7167c478bd9Sstevel@tonic-gate 		}
7177c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0 : copy(src, dst, mode);
7187c478bd9Sstevel@tonic-gate 		if (rc != 0) {
7197c478bd9Sstevel@tonic-gate 			errs |= rc;
7207c478bd9Sstevel@tonic-gate 			if (copy_err_str)
7217c478bd9Sstevel@tonic-gate 				errstr = copy_err_str;
7227c478bd9Sstevel@tonic-gate 			else
7237c478bd9Sstevel@tonic-gate 				errstr = gettext(PROB_copy);
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate 			/*
7267c478bd9Sstevel@tonic-gate 			 * The new copy (if it exists at all) is a botch.
7277c478bd9Sstevel@tonic-gate 			 * If this was a new create or a remove and copy
7287c478bd9Sstevel@tonic-gate 			 * we should get rid of the botched copy so that
7297c478bd9Sstevel@tonic-gate 			 * it doesn't show up as two versions next time.
7307c478bd9Sstevel@tonic-gate 			 */
7317c478bd9Sstevel@tonic-gate 			if (do_create)
7327c478bd9Sstevel@tonic-gate 				unlink(dst);
7337c478bd9Sstevel@tonic-gate 		} else if (dp->f_mode == 0) {
7347c478bd9Sstevel@tonic-gate 			dp->f_type = S_IFREG;
7357c478bd9Sstevel@tonic-gate 			dp->f_uid = my_uid;
7367c478bd9Sstevel@tonic-gate 			dp->f_gid = my_gid;
7377c478bd9Sstevel@tonic-gate 			dp->f_mode = mode;
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate 			/* FIX: inode number is still wrong	*/
7407c478bd9Sstevel@tonic-gate 		}
7417c478bd9Sstevel@tonic-gate 
7427c478bd9Sstevel@tonic-gate 		/* for normal files we have an option to preserve mod time  */
7437c478bd9Sstevel@tonic-gate 		if (rc == 0 && opt_notouch == 0 && opt_mtime) {
7447c478bd9Sstevel@tonic-gate 			newtimes.actime = mtime;
7457c478bd9Sstevel@tonic-gate 			newtimes.modtime = mtime;
7467c478bd9Sstevel@tonic-gate 
7477c478bd9Sstevel@tonic-gate 			/* ignore the error return on this one	*/
7487c478bd9Sstevel@tonic-gate 			(void) utime(dst, &newtimes);
7497c478bd9Sstevel@tonic-gate 		}
7507c478bd9Sstevel@tonic-gate 		break;
7517c478bd9Sstevel@tonic-gate 
7527c478bd9Sstevel@tonic-gate 	    default:
7537c478bd9Sstevel@tonic-gate 		errstr = gettext(PROB_deal);
7547c478bd9Sstevel@tonic-gate 		rc = -1;
7557c478bd9Sstevel@tonic-gate 	}
7567c478bd9Sstevel@tonic-gate 
7577c478bd9Sstevel@tonic-gate 	/*
7587c478bd9Sstevel@tonic-gate 	 * if any of the file's attributes need attention, I should let
7597c478bd9Sstevel@tonic-gate 	 * do_like take care of them, since it knows all rules for who
7607c478bd9Sstevel@tonic-gate 	 * can and cannot make what types of changes.
7617c478bd9Sstevel@tonic-gate 	 */
7627c478bd9Sstevel@tonic-gate 	if (rc == 0 && (do_chmod || do_chown || do_chgrp || do_acls)) {
7637c478bd9Sstevel@tonic-gate 		rc = do_like(fp, srcdst, FALSE);
7647c478bd9Sstevel@tonic-gate 		errstr = fp->f_problem;
7657c478bd9Sstevel@tonic-gate 		errs |= rc;
7667c478bd9Sstevel@tonic-gate 	}
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 	/*
7697c478bd9Sstevel@tonic-gate 	 * finish off by re-stating the destination and using that to
7707c478bd9Sstevel@tonic-gate 	 * update the baseline.  If we were completely successful in
7717c478bd9Sstevel@tonic-gate 	 * our chowns/chmods, stating the destination will confirm it.
7727c478bd9Sstevel@tonic-gate 	 * If we were unable to make all the necessary changes, stating
7737c478bd9Sstevel@tonic-gate 	 * the destination will make the source appear to have changed,
7747c478bd9Sstevel@tonic-gate 	 * so that the differences will continue to reappear as new
7757c478bd9Sstevel@tonic-gate 	 * changes (inconsistancies).
7767c478bd9Sstevel@tonic-gate 	 */
7777c478bd9Sstevel@tonic-gate 	if (rc == 0)
7787c478bd9Sstevel@tonic-gate 		if (!opt_notouch) {
7797c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_restat);
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
7827c478bd9Sstevel@tonic-gate 			/* should we simulate a restat failure	*/
7837c478bd9Sstevel@tonic-gate 			if (errno = dbg_chk_error(dst, 'R'))
7847c478bd9Sstevel@tonic-gate 				rc = -1;
7857c478bd9Sstevel@tonic-gate 			else
7867c478bd9Sstevel@tonic-gate #endif
7877c478bd9Sstevel@tonic-gate 			rc = lstat(dst, &statb);
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate 			if (rc == 0) {
7907c478bd9Sstevel@tonic-gate 				note_info(fp, &statb, srcdst);
7917c478bd9Sstevel@tonic-gate 				link_update(fp, srcdst);
7927c478bd9Sstevel@tonic-gate 				if (do_acls)
7937c478bd9Sstevel@tonic-gate 					(void) get_acls(dst, dp);
7947c478bd9Sstevel@tonic-gate 				update_info(fp, srcdst);
7957c478bd9Sstevel@tonic-gate 			}
7967c478bd9Sstevel@tonic-gate 		} else {
7977c478bd9Sstevel@tonic-gate 			/*
7987c478bd9Sstevel@tonic-gate 			 * BOGOSITY ALERT
7997c478bd9Sstevel@tonic-gate 			 *	we are in notouch mode and haven't really
8007c478bd9Sstevel@tonic-gate 			 *	done anything, but if we want link detection
8017c478bd9Sstevel@tonic-gate 			 *	to work and be properly reflected in the
8027c478bd9Sstevel@tonic-gate 			 *	what-I-would-do output for a case where
8037c478bd9Sstevel@tonic-gate 			 *	multiple links are created to a new file,
8047c478bd9Sstevel@tonic-gate 			 *	we have to make the new file appear to
8057c478bd9Sstevel@tonic-gate 			 *	have been created.  Since we didn't create
8067c478bd9Sstevel@tonic-gate 			 *	the new file we can't stat it, but if
8077c478bd9Sstevel@tonic-gate 			 *	no file exists, we can't make a link to
8087c478bd9Sstevel@tonic-gate 			 *	it, so we will pretend we created a file.
8097c478bd9Sstevel@tonic-gate 			 */
8107c478bd9Sstevel@tonic-gate 			if (dp->f_ino == 0 || dp->f_nlink == 0) {
8117c478bd9Sstevel@tonic-gate 				dp->f_ino = sp->f_ino;
8127c478bd9Sstevel@tonic-gate 				dp->f_nlink = 1;
8137c478bd9Sstevel@tonic-gate 			}
8147c478bd9Sstevel@tonic-gate 		}
8157c478bd9Sstevel@tonic-gate 
8167c478bd9Sstevel@tonic-gate cant:	if (rc != 0) {
8177c478bd9Sstevel@tonic-gate 		fprintf(stderr, gettext(ERR_cannot), errstr, dst);
8187c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
8197c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_CONFLICT;
8207c478bd9Sstevel@tonic-gate 		fp->f_problem = errstr;
8217c478bd9Sstevel@tonic-gate 		if (errs == 0)
8227c478bd9Sstevel@tonic-gate 			errs = ERR_PERM;
8237c478bd9Sstevel@tonic-gate 		errs |= ERR_UNRESOLVED;
8247c478bd9Sstevel@tonic-gate 	} else {
8257c478bd9Sstevel@tonic-gate 		/* update the statistics			*/
8267c478bd9Sstevel@tonic-gate 		if (srcdst == OPT_SRC)
8277c478bd9Sstevel@tonic-gate 			bp->b_src_copies++;
8287c478bd9Sstevel@tonic-gate 		else
8297c478bd9Sstevel@tonic-gate 			bp->b_dst_copies++;
8307c478bd9Sstevel@tonic-gate 		errs |= ERR_RESOLVABLE;
8317c478bd9Sstevel@tonic-gate 	}
8327c478bd9Sstevel@tonic-gate 
8337c478bd9Sstevel@tonic-gate 	return (errs);
8347c478bd9Sstevel@tonic-gate }
8357c478bd9Sstevel@tonic-gate 
8367c478bd9Sstevel@tonic-gate /*
8377c478bd9Sstevel@tonic-gate  * routine:
8387c478bd9Sstevel@tonic-gate  *	do_remove
8397c478bd9Sstevel@tonic-gate  *
8407c478bd9Sstevel@tonic-gate  * purpose:
8417c478bd9Sstevel@tonic-gate  *	to propagate a deletion
8427c478bd9Sstevel@tonic-gate  *
8437c478bd9Sstevel@tonic-gate  * parameters:
8447c478bd9Sstevel@tonic-gate  *	file pointer
8457c478bd9Sstevel@tonic-gate  *	src/dst indication for which side gets changed
8467c478bd9Sstevel@tonic-gate  *
8477c478bd9Sstevel@tonic-gate  * returns:
8487c478bd9Sstevel@tonic-gate  *	error mask
8497c478bd9Sstevel@tonic-gate  */
8507c478bd9Sstevel@tonic-gate errmask_t
do_remove(struct file * fp,side_t srcdst)8517c478bd9Sstevel@tonic-gate do_remove(struct file *fp, side_t srcdst)
8527c478bd9Sstevel@tonic-gate {	char *name;
8537c478bd9Sstevel@tonic-gate 	int rc;
8547c478bd9Sstevel@tonic-gate 	struct base *bp = fp->f_base;
8557c478bd9Sstevel@tonic-gate 	errmask_t errs = 0;
8567c478bd9Sstevel@tonic-gate 	char *errstr = "???";
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 	/* see if this is a forbidden propagation */
8597c478bd9Sstevel@tonic-gate 	if (srcdst == opt_oneway) {
8607c478bd9Sstevel@tonic-gate 		fp->f_problem = gettext(PROB_prohibited);
8617c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_CONFLICT;
8627c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
8637c478bd9Sstevel@tonic-gate 		return (ERR_UNRESOLVED);
8647c478bd9Sstevel@tonic-gate 	}
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate 	name = (srcdst == OPT_SRC) ? srcname : dstname;
8677c478bd9Sstevel@tonic-gate 
8687c478bd9Sstevel@tonic-gate 	if (fp->f_info[0].f_type == S_IFDIR) {
8697c478bd9Sstevel@tonic-gate 		if (!opt_quiet)
8707c478bd9Sstevel@tonic-gate 			fprintf(stdout, "rmdir %s\n", noblanks(name));
8717c478bd9Sstevel@tonic-gate 
8727c478bd9Sstevel@tonic-gate 		errstr = gettext(PROB_rmdir);
8737c478bd9Sstevel@tonic-gate 
8747c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
8757c478bd9Sstevel@tonic-gate 		/* should we simulate a rmdir failure	*/
8767c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(name, 'D'))
8777c478bd9Sstevel@tonic-gate 			rc = -1;
8787c478bd9Sstevel@tonic-gate 		else
8797c478bd9Sstevel@tonic-gate #endif
8807c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0 : rmdir(name);
8817c478bd9Sstevel@tonic-gate 	} else {
8827c478bd9Sstevel@tonic-gate 		if (!opt_quiet)
8837c478bd9Sstevel@tonic-gate 			fprintf(stdout, "rm %s\n", noblanks(name));
8847c478bd9Sstevel@tonic-gate 
8857c478bd9Sstevel@tonic-gate 		errstr = gettext(PROB_unlink);
8867c478bd9Sstevel@tonic-gate 
8877c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
8887c478bd9Sstevel@tonic-gate 		/* should we simulate an unlink failure	*/
8897c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(name, 'u'))
8907c478bd9Sstevel@tonic-gate 			rc = -1;
8917c478bd9Sstevel@tonic-gate 		else
8927c478bd9Sstevel@tonic-gate #endif
8937c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0 : unlink(name);
8947c478bd9Sstevel@tonic-gate 	}
8957c478bd9Sstevel@tonic-gate 
8967c478bd9Sstevel@tonic-gate 	if (opt_debug & DBG_RECON)
8977c478bd9Sstevel@tonic-gate 		fprintf(stderr, "RECO: do_remove %s -> %d(%d)\n",
8987c478bd9Sstevel@tonic-gate 			name, rc, errno);
8997c478bd9Sstevel@tonic-gate 
9007c478bd9Sstevel@tonic-gate 	if (rc == 0) {
9017c478bd9Sstevel@tonic-gate 		/* tell any other hard links that one has gone away	*/
9027c478bd9Sstevel@tonic-gate 		fp->f_info[srcdst].f_nlink--;
9037c478bd9Sstevel@tonic-gate 		link_update(fp, srcdst);
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_REMOVE;
9067c478bd9Sstevel@tonic-gate 		if (srcdst == OPT_SRC)
9077c478bd9Sstevel@tonic-gate 			fp->f_base->b_src_deletes++;
9087c478bd9Sstevel@tonic-gate 		else
9097c478bd9Sstevel@tonic-gate 			fp->f_base->b_dst_deletes++;
9107c478bd9Sstevel@tonic-gate 		errs |= ERR_RESOLVABLE;
9117c478bd9Sstevel@tonic-gate 	} else {
9127c478bd9Sstevel@tonic-gate 		fprintf(stderr, gettext(ERR_cannot), errstr, name);
9137c478bd9Sstevel@tonic-gate 		fp->f_problem = errstr;
9147c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_CONFLICT;
9157c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
9167c478bd9Sstevel@tonic-gate 		errs |= ERR_PERM | ERR_UNRESOLVED;
9177c478bd9Sstevel@tonic-gate 	}
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 	return (errs);
9207c478bd9Sstevel@tonic-gate }
9217c478bd9Sstevel@tonic-gate 
9227c478bd9Sstevel@tonic-gate /*
9237c478bd9Sstevel@tonic-gate  * routine:
9247c478bd9Sstevel@tonic-gate  *	do_rename
9257c478bd9Sstevel@tonic-gate  *
9267c478bd9Sstevel@tonic-gate  * purpose:
9277c478bd9Sstevel@tonic-gate  *	to propagate a rename
9287c478bd9Sstevel@tonic-gate  *
9297c478bd9Sstevel@tonic-gate  * parameters:
9307c478bd9Sstevel@tonic-gate  *	file pointer for the new name
9317c478bd9Sstevel@tonic-gate  *	src/dst indication for which side gets changed
9327c478bd9Sstevel@tonic-gate  *
9337c478bd9Sstevel@tonic-gate  * returns:
9347c478bd9Sstevel@tonic-gate  *	error mask
9357c478bd9Sstevel@tonic-gate  */
9367c478bd9Sstevel@tonic-gate errmask_t
do_rename(struct file * fp,side_t srcdst)9377c478bd9Sstevel@tonic-gate do_rename(struct file *fp, side_t srcdst)
9387c478bd9Sstevel@tonic-gate {	int rc;
9397c478bd9Sstevel@tonic-gate 	struct file *pp = fp->f_previous;
9407c478bd9Sstevel@tonic-gate 	struct base *bp = fp->f_base;
9417c478bd9Sstevel@tonic-gate 	errmask_t errs = 0;
9427c478bd9Sstevel@tonic-gate 	char *errstr = "???";
9437c478bd9Sstevel@tonic-gate 	char *newname;
9447c478bd9Sstevel@tonic-gate 	char *oldname;
9457c478bd9Sstevel@tonic-gate 	struct stat statb;
9467c478bd9Sstevel@tonic-gate 
9477c478bd9Sstevel@tonic-gate 	/* see if this is a forbidden propagation */
9487c478bd9Sstevel@tonic-gate 	if (srcdst == opt_oneway) {
9497c478bd9Sstevel@tonic-gate 		fp->f_problem = gettext(PROB_prohibited);
9507c478bd9Sstevel@tonic-gate 
9517c478bd9Sstevel@tonic-gate 		/* if we can't resolve the TO, the FROM is also unresolved */
9527c478bd9Sstevel@tonic-gate 		pp->f_problem = gettext(PROB_prohibited);
9537c478bd9Sstevel@tonic-gate 		pp->f_flags |= F_CONFLICT;
9547c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
9557c478bd9Sstevel@tonic-gate 		return (ERR_UNRESOLVED);
9567c478bd9Sstevel@tonic-gate 	}
9577c478bd9Sstevel@tonic-gate 
9587c478bd9Sstevel@tonic-gate 	newname = (srcdst == OPT_SRC) ? srcname : dstname;
9597c478bd9Sstevel@tonic-gate 	oldname = full_name(pp, srcdst, OPT_BASE);
9607c478bd9Sstevel@tonic-gate 
9617c478bd9Sstevel@tonic-gate 	if (!opt_quiet)
9627c478bd9Sstevel@tonic-gate 		fprintf(stdout, "%s %s %s\n",
9637c478bd9Sstevel@tonic-gate 			(fp->f_info[0].f_type == S_IFDIR) ? "mvdir" : "mv",
9647c478bd9Sstevel@tonic-gate 			noblanks(oldname), noblanks(newname));
9657c478bd9Sstevel@tonic-gate 
9667c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
9677c478bd9Sstevel@tonic-gate 	/* should we simulate a rename failure	*/
9687c478bd9Sstevel@tonic-gate 	if (errno = dbg_chk_error(oldname, 'm'))
9697c478bd9Sstevel@tonic-gate 		rc = -1;
9707c478bd9Sstevel@tonic-gate 	else
9717c478bd9Sstevel@tonic-gate #endif
9727c478bd9Sstevel@tonic-gate 	rc = opt_notouch ? 0 : rename(oldname, newname);
9737c478bd9Sstevel@tonic-gate 
9747c478bd9Sstevel@tonic-gate 	if (opt_debug & DBG_RECON)
9757c478bd9Sstevel@tonic-gate 		fprintf(stderr, "RECO: do_rename %s %s -> %d(%d)\n",
9767c478bd9Sstevel@tonic-gate 			oldname, newname, rc, errno);
9777c478bd9Sstevel@tonic-gate 
9787c478bd9Sstevel@tonic-gate 	/* if we succeed, update the baseline			*/
9797c478bd9Sstevel@tonic-gate 	if (rc == 0)
9807c478bd9Sstevel@tonic-gate 		if (!opt_notouch) {
9817c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_restat);
9827c478bd9Sstevel@tonic-gate 
9837c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
9847c478bd9Sstevel@tonic-gate 			/* should we simulate a restat failure	*/
9857c478bd9Sstevel@tonic-gate 			if (errno = dbg_chk_error(newname, 'S'))
9867c478bd9Sstevel@tonic-gate 				rc = -1;
9877c478bd9Sstevel@tonic-gate 			else
9887c478bd9Sstevel@tonic-gate #endif
9897c478bd9Sstevel@tonic-gate 			rc = lstat(newname, &statb);
9907c478bd9Sstevel@tonic-gate 
9917c478bd9Sstevel@tonic-gate 			if (rc == 0) {
9927c478bd9Sstevel@tonic-gate 				note_info(fp, &statb, srcdst);
9937c478bd9Sstevel@tonic-gate 				link_update(fp, srcdst);
9947c478bd9Sstevel@tonic-gate 				update_info(fp, srcdst);
9957c478bd9Sstevel@tonic-gate 			}
9967c478bd9Sstevel@tonic-gate 		} else {
9977c478bd9Sstevel@tonic-gate 			/*
9987c478bd9Sstevel@tonic-gate 			 * BOGOSITY ALERT
9997c478bd9Sstevel@tonic-gate 			 * in order for link tests to work in notouch
10007c478bd9Sstevel@tonic-gate 			 * mode we have to dummy up some updated status
10017c478bd9Sstevel@tonic-gate 			 */
10027c478bd9Sstevel@tonic-gate 			fp->f_info[srcdst].f_ino = pp->f_info[srcdst].f_ino;
10037c478bd9Sstevel@tonic-gate 			fp->f_info[srcdst].f_nlink = pp->f_info[srcdst].f_nlink;
10047c478bd9Sstevel@tonic-gate 			fp->f_info[srcdst].f_type = pp->f_info[srcdst].f_type;
10057c478bd9Sstevel@tonic-gate 			fp->f_info[srcdst].f_size = pp->f_info[srcdst].f_size;
10067c478bd9Sstevel@tonic-gate 			fp->f_info[srcdst].f_mode = pp->f_info[srcdst].f_mode;
10077c478bd9Sstevel@tonic-gate 			fp->f_info[srcdst].f_uid = pp->f_info[srcdst].f_uid;
10087c478bd9Sstevel@tonic-gate 			fp->f_info[srcdst].f_gid = pp->f_info[srcdst].f_gid;
10097c478bd9Sstevel@tonic-gate 			update_info(fp, srcdst);
10107c478bd9Sstevel@tonic-gate 		}
10117c478bd9Sstevel@tonic-gate 	else
10127c478bd9Sstevel@tonic-gate 		errstr = gettext(PROB_rename2);
10137c478bd9Sstevel@tonic-gate 
10147c478bd9Sstevel@tonic-gate 	if (rc == 0) {
10157c478bd9Sstevel@tonic-gate 		pp->f_flags |= F_REMOVE;
10167c478bd9Sstevel@tonic-gate 
10177c478bd9Sstevel@tonic-gate 		if (srcdst == OPT_SRC) {
10187c478bd9Sstevel@tonic-gate 			bp->b_src_copies++;
10197c478bd9Sstevel@tonic-gate 			bp->b_src_deletes++;
10207c478bd9Sstevel@tonic-gate 		} else {
10217c478bd9Sstevel@tonic-gate 			bp->b_dst_copies++;
10227c478bd9Sstevel@tonic-gate 			bp->b_dst_deletes++;
10237c478bd9Sstevel@tonic-gate 		}
10247c478bd9Sstevel@tonic-gate 		errs |= ERR_RESOLVABLE;
10257c478bd9Sstevel@tonic-gate 	} else {
10267c478bd9Sstevel@tonic-gate 		fprintf(stderr, gettext(ERR_cannot), errstr, oldname);
10277c478bd9Sstevel@tonic-gate 
10287c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
10297c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_CONFLICT;
10307c478bd9Sstevel@tonic-gate 		pp->f_flags |= F_CONFLICT;
10317c478bd9Sstevel@tonic-gate 
10327c478bd9Sstevel@tonic-gate 		fp->f_problem = errstr;
10337c478bd9Sstevel@tonic-gate 		pp->f_problem = gettext(PROB_rename);
10347c478bd9Sstevel@tonic-gate 
10357c478bd9Sstevel@tonic-gate 		errs |= ERR_PERM | ERR_UNRESOLVED;
10367c478bd9Sstevel@tonic-gate 	}
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 	return (errs);
10397c478bd9Sstevel@tonic-gate }
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate /*
10427c478bd9Sstevel@tonic-gate  * routine:
10437c478bd9Sstevel@tonic-gate  *	copy
10447c478bd9Sstevel@tonic-gate  *
10457c478bd9Sstevel@tonic-gate  * purpose:
10467c478bd9Sstevel@tonic-gate  *	to copy one file to another
10477c478bd9Sstevel@tonic-gate  *
10487c478bd9Sstevel@tonic-gate  * parameters:
10497c478bd9Sstevel@tonic-gate  *	source file name
10507c478bd9Sstevel@tonic-gate  *	destination file name
10517c478bd9Sstevel@tonic-gate  *	desired modes
10527c478bd9Sstevel@tonic-gate  *
10537c478bd9Sstevel@tonic-gate  * returns:
10547c478bd9Sstevel@tonic-gate  *	0	OK
10557c478bd9Sstevel@tonic-gate  *	else	error mask, and a setting of copy_err_str
10567c478bd9Sstevel@tonic-gate  *
10577c478bd9Sstevel@tonic-gate  * notes:
10587c478bd9Sstevel@tonic-gate  *	We try to preserve the holes in sparse files, by skipping over
10597c478bd9Sstevel@tonic-gate  *	any holes that are at least MIN_HOLE bytes long.  There are
10607c478bd9Sstevel@tonic-gate  *	pathological cases where the hole detection test could become
10617c478bd9Sstevel@tonic-gate  *	expensive, but for most blocks of most files we will fall out
10627c478bd9Sstevel@tonic-gate  *	of the zero confirming loop in the first couple of bytes.
10637c478bd9Sstevel@tonic-gate  */
10647c478bd9Sstevel@tonic-gate static errmask_t
copy(char * src,char * dst,int mode)10657c478bd9Sstevel@tonic-gate copy(char *src, char *dst, int mode)
10667c478bd9Sstevel@tonic-gate {	int ifd, ofd, count, ret;
10677c478bd9Sstevel@tonic-gate 	long *p, *e;
10687c478bd9Sstevel@tonic-gate 	long long length;		/* total size of file	*/
10697c478bd9Sstevel@tonic-gate 	errmask_t errs = 0;
10707c478bd9Sstevel@tonic-gate 	int bsize;			/* block-size for file	*/
10717c478bd9Sstevel@tonic-gate 	bool_t sparse;			/* file may be sparse	*/
10727c478bd9Sstevel@tonic-gate 	bool_t was_hole = FALSE;		/* file ends with hole	*/
10737c478bd9Sstevel@tonic-gate 	long inbuf[ COPY_BSIZE/4 ];	/* long to speed checks	*/
10747c478bd9Sstevel@tonic-gate 	struct stat statbuf;		/* info on source file	*/
10757c478bd9Sstevel@tonic-gate 	struct statvfs statvsbuf;	/* info on target fs	*/
10767c478bd9Sstevel@tonic-gate 
10777c478bd9Sstevel@tonic-gate 	copy_err_str = 0;
10787c478bd9Sstevel@tonic-gate 
10797c478bd9Sstevel@tonic-gate 	/* open the input file			*/
10807c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
10817c478bd9Sstevel@tonic-gate 	if (opt_errors && dbg_chk_error(src, 'o'))
10827c478bd9Sstevel@tonic-gate 		ifd = -1;
10837c478bd9Sstevel@tonic-gate 	else
10847c478bd9Sstevel@tonic-gate #endif
10857c478bd9Sstevel@tonic-gate 	ifd = open(src, O_RDONLY);
10867c478bd9Sstevel@tonic-gate 
10877c478bd9Sstevel@tonic-gate 	if (ifd < 0) {
10887c478bd9Sstevel@tonic-gate 		copy_err_str = gettext(PROB_copyin);
10897c478bd9Sstevel@tonic-gate 		return (ERR_PERM);
10907c478bd9Sstevel@tonic-gate 	}
10917c478bd9Sstevel@tonic-gate 
10927c478bd9Sstevel@tonic-gate 	/*
10937c478bd9Sstevel@tonic-gate 	 * if we suspect a file may be sparse, we must process it
10947c478bd9Sstevel@tonic-gate 	 * a little more carefully, looking for holes and skipping
10957c478bd9Sstevel@tonic-gate 	 * over them in the output.  If a file is not sparse, we
10967c478bd9Sstevel@tonic-gate 	 * can move through it at greater speed.
10977c478bd9Sstevel@tonic-gate 	 */
10987c478bd9Sstevel@tonic-gate 	bsize = checksparse(ifd);
10997c478bd9Sstevel@tonic-gate 	if (bsize > 0 && bsize <= COPY_BSIZE)
11007c478bd9Sstevel@tonic-gate 		sparse = TRUE;
11017c478bd9Sstevel@tonic-gate 	else {
11027c478bd9Sstevel@tonic-gate 		sparse = FALSE;
11037c478bd9Sstevel@tonic-gate 		bsize = COPY_BSIZE;
11047c478bd9Sstevel@tonic-gate 	}
11057c478bd9Sstevel@tonic-gate 
11067c478bd9Sstevel@tonic-gate 	/*
11077c478bd9Sstevel@tonic-gate 	 * if the target file already exists and we overwrite it without
11087c478bd9Sstevel@tonic-gate 	 * first ascertaining that there is enough room, we could wind
11097c478bd9Sstevel@tonic-gate 	 * up actually losing data.  Try to determine how much space is
11107c478bd9Sstevel@tonic-gate 	 * available on the target file system, and if that is not enough
11117c478bd9Sstevel@tonic-gate 	 * for the source file, fail without even trying.  If, however,
11127c478bd9Sstevel@tonic-gate 	 * the target file does not already exist, we have nothing to
11137c478bd9Sstevel@tonic-gate 	 * lose by just doing the copy without checking the space.
11147c478bd9Sstevel@tonic-gate 	 */
11157c478bd9Sstevel@tonic-gate 	ret = statvfs(dst, &statvsbuf);
11167c478bd9Sstevel@tonic-gate 	if (ret == 0 && statvsbuf.f_frsize != 0) {
11177c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
11187c478bd9Sstevel@tonic-gate 		/* should we simulate an out-of-space situation	*/
11197c478bd9Sstevel@tonic-gate 		if ((length = dbg_chk_error(dst, 'Z')) == 0)
11207c478bd9Sstevel@tonic-gate #endif
11217c478bd9Sstevel@tonic-gate 		length = statvsbuf.f_bavail * statvsbuf.f_frsize;
11227c478bd9Sstevel@tonic-gate 
11237c478bd9Sstevel@tonic-gate 		ret = fstat(ifd, &statbuf);
11247c478bd9Sstevel@tonic-gate 		if (ret == 0) {
11257c478bd9Sstevel@tonic-gate 			length /= 512;		/* st_blocks in 512s	*/
11267c478bd9Sstevel@tonic-gate 			if (length < statbuf.st_blocks) {
11277c478bd9Sstevel@tonic-gate 				copy_err_str = gettext(PROB_space);
11287c478bd9Sstevel@tonic-gate 				close(ifd);
11297c478bd9Sstevel@tonic-gate 				return (ERR_FILES);
11307c478bd9Sstevel@tonic-gate 			}
11317c478bd9Sstevel@tonic-gate 		} else {
11327c478bd9Sstevel@tonic-gate 			copy_err_str = gettext(PROB_restat);
11337c478bd9Sstevel@tonic-gate 			close(ifd);
11347c478bd9Sstevel@tonic-gate 			return (ERR_FILES);
11357c478bd9Sstevel@tonic-gate 		}
11367c478bd9Sstevel@tonic-gate 	}
11377c478bd9Sstevel@tonic-gate 
11387c478bd9Sstevel@tonic-gate 	/* create the output file		*/
11397c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
11407c478bd9Sstevel@tonic-gate 	if (opt_errors && dbg_chk_error(dst, 'c'))
11417c478bd9Sstevel@tonic-gate 		ofd = -1;
11427c478bd9Sstevel@tonic-gate 	else
11437c478bd9Sstevel@tonic-gate #endif
11447c478bd9Sstevel@tonic-gate 	ofd = creat(dst, mode);
11457c478bd9Sstevel@tonic-gate 
11467c478bd9Sstevel@tonic-gate 	if (ofd < 0) {
11477c478bd9Sstevel@tonic-gate 		close(ifd);
11487c478bd9Sstevel@tonic-gate 		copy_err_str = gettext(PROB_copyout);
11497c478bd9Sstevel@tonic-gate 		return (ERR_PERM);
11507c478bd9Sstevel@tonic-gate 	}
11517c478bd9Sstevel@tonic-gate 
11527c478bd9Sstevel@tonic-gate 	/* copy the data from the input file to the output file	*/
11537c478bd9Sstevel@tonic-gate 	for (;;) {
11547c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
11557c478bd9Sstevel@tonic-gate 		if (opt_errors && dbg_chk_error(dst, 'r'))
11567c478bd9Sstevel@tonic-gate 			count = -1;
11577c478bd9Sstevel@tonic-gate 		else
11587c478bd9Sstevel@tonic-gate #endif
11597c478bd9Sstevel@tonic-gate 		count = read(ifd, (char *) inbuf, bsize);
11607c478bd9Sstevel@tonic-gate 		if (count <= 0)
11617c478bd9Sstevel@tonic-gate 			break;
11627c478bd9Sstevel@tonic-gate 
11637c478bd9Sstevel@tonic-gate 		/*
11647c478bd9Sstevel@tonic-gate 		 * if the file might be sparse and we got an entire block,
11657c478bd9Sstevel@tonic-gate 		 * we should see if the block is all zeros
11667c478bd9Sstevel@tonic-gate 		 */
11677c478bd9Sstevel@tonic-gate 		if (sparse && count == bsize) {
11687c478bd9Sstevel@tonic-gate 			p = inbuf; e = &inbuf[count/4];
11697c478bd9Sstevel@tonic-gate 			while (p < e && *p == 0)
11707c478bd9Sstevel@tonic-gate 				p++;
11717c478bd9Sstevel@tonic-gate 			if (p == e) {
11727c478bd9Sstevel@tonic-gate 				(void) lseek(ofd, (off_t) count, SEEK_CUR);
11737c478bd9Sstevel@tonic-gate 				was_hole = TRUE;
11747c478bd9Sstevel@tonic-gate 				continue;
11757c478bd9Sstevel@tonic-gate 			}
11767c478bd9Sstevel@tonic-gate 		}
11777c478bd9Sstevel@tonic-gate 		was_hole = FALSE;
11787c478bd9Sstevel@tonic-gate 
11797c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
11807c478bd9Sstevel@tonic-gate 		if (opt_errors && dbg_chk_error(dst, 'w'))
11817c478bd9Sstevel@tonic-gate 			ret = -1;
11827c478bd9Sstevel@tonic-gate 		else
11837c478bd9Sstevel@tonic-gate #endif
11847c478bd9Sstevel@tonic-gate 		ret = write(ofd, (char *) inbuf, count);
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate 		if (ret != count) {
11877c478bd9Sstevel@tonic-gate 			errs = ERR_FILES;
11887c478bd9Sstevel@tonic-gate 			copy_err_str = gettext(PROB_write);
11897c478bd9Sstevel@tonic-gate 			break;
11907c478bd9Sstevel@tonic-gate 		}
11917c478bd9Sstevel@tonic-gate 	}
11927c478bd9Sstevel@tonic-gate 
11937c478bd9Sstevel@tonic-gate 	if (count < 0) {
11947c478bd9Sstevel@tonic-gate 		copy_err_str = gettext(PROB_read);
11957c478bd9Sstevel@tonic-gate 		errs = ERR_FILES;
11967c478bd9Sstevel@tonic-gate 	} else if (was_hole) {
11977c478bd9Sstevel@tonic-gate 		/*
11987c478bd9Sstevel@tonic-gate 		 * if we skipped the last write because of a hole, we
11997c478bd9Sstevel@tonic-gate 		 * need to make sure that we write a single byte of null
12007c478bd9Sstevel@tonic-gate 		 * at the end of the file to update the file length.
12017c478bd9Sstevel@tonic-gate 		 */
12027c478bd9Sstevel@tonic-gate 		(void) lseek(ofd, (off_t)-1, SEEK_CUR);
12037c478bd9Sstevel@tonic-gate 		(void) write(ofd, "", 1);
12047c478bd9Sstevel@tonic-gate 	}
12057c478bd9Sstevel@tonic-gate 
12067c478bd9Sstevel@tonic-gate 	/*
12077c478bd9Sstevel@tonic-gate 	 * if the output file was botched, free up its space
12087c478bd9Sstevel@tonic-gate 	 */
12097c478bd9Sstevel@tonic-gate 	if (errs)
12107c478bd9Sstevel@tonic-gate 		ftruncate(ofd, (off_t) 0);
12117c478bd9Sstevel@tonic-gate 
12127c478bd9Sstevel@tonic-gate 	close(ifd);
12137c478bd9Sstevel@tonic-gate 	close(ofd);
12147c478bd9Sstevel@tonic-gate 	return (errs);
12157c478bd9Sstevel@tonic-gate }
12167c478bd9Sstevel@tonic-gate 
12177c478bd9Sstevel@tonic-gate /*
12187c478bd9Sstevel@tonic-gate  * routine:
12197c478bd9Sstevel@tonic-gate  *	checksparse
12207c478bd9Sstevel@tonic-gate  *
12217c478bd9Sstevel@tonic-gate  * purpose:
12227c478bd9Sstevel@tonic-gate  *	to determine whether or not a file might be sparse, and if
12237c478bd9Sstevel@tonic-gate  *	it is sparse, what the granularity of the holes is likely
12247c478bd9Sstevel@tonic-gate  *	to be.
12257c478bd9Sstevel@tonic-gate  *
12267c478bd9Sstevel@tonic-gate  * parameters:
12277c478bd9Sstevel@tonic-gate  *	file descriptor for file in question
12287c478bd9Sstevel@tonic-gate  *
12297c478bd9Sstevel@tonic-gate  * returns:
12307c478bd9Sstevel@tonic-gate  *	0	file does not appear to be sparse
12317c478bd9Sstevel@tonic-gate  *	else	block size for this file
12327c478bd9Sstevel@tonic-gate  */
12337c478bd9Sstevel@tonic-gate static int
checksparse(int fd)12347c478bd9Sstevel@tonic-gate checksparse(int fd)
12357c478bd9Sstevel@tonic-gate {
12367c478bd9Sstevel@tonic-gate 	struct stat statb;
12377c478bd9Sstevel@tonic-gate 
12387c478bd9Sstevel@tonic-gate 	/*
12397c478bd9Sstevel@tonic-gate 	 * unable to stat the file is very strange (since we got it
12407c478bd9Sstevel@tonic-gate 	 * open) but it probably isn't worth causing a fuss over.
12417c478bd9Sstevel@tonic-gate 	 * Return the conservative answer
12427c478bd9Sstevel@tonic-gate 	 */
12437c478bd9Sstevel@tonic-gate 	if (fstat(fd, &statb) < 0)
12447c478bd9Sstevel@tonic-gate 		return (MIN_HOLE);
12457c478bd9Sstevel@tonic-gate 
12467c478bd9Sstevel@tonic-gate 	/*
12477c478bd9Sstevel@tonic-gate 	 * if the file doesn't have enough blocks to account for
12487c478bd9Sstevel@tonic-gate 	 * all of its bytes, there is a reasonable chance that it
12497c478bd9Sstevel@tonic-gate 	 * is sparse.  This test is not perfect, in that it will
12507c478bd9Sstevel@tonic-gate 	 * fail to find holes in cases where the holes aren't
12517c478bd9Sstevel@tonic-gate 	 * numerous enough to componsent for the indirect blocks
12527c478bd9Sstevel@tonic-gate 	 * ... but losing those few holes is not going to be a
12537c478bd9Sstevel@tonic-gate 	 * big deal.
12547c478bd9Sstevel@tonic-gate 	 */
12557c478bd9Sstevel@tonic-gate 	if (statb.st_size > 512 * statb.st_blocks)
12567c478bd9Sstevel@tonic-gate 		return (statb.st_blksize);
12577c478bd9Sstevel@tonic-gate 	else
12587c478bd9Sstevel@tonic-gate 		return (0);
12597c478bd9Sstevel@tonic-gate }
1260