xref: /titanic_52/usr/src/cmd/filesync/action.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1995 Sun Microsystems, Inc.  All Rights Reserved
24*7c478bd9Sstevel@tonic-gate  *
25*7c478bd9Sstevel@tonic-gate  * module:
26*7c478bd9Sstevel@tonic-gate  *	action.c
27*7c478bd9Sstevel@tonic-gate  *
28*7c478bd9Sstevel@tonic-gate  * purpose:
29*7c478bd9Sstevel@tonic-gate  *	routines to carryout reconciliation actions and make the
30*7c478bd9Sstevel@tonic-gate  *	appropriate updates to the database file structure.
31*7c478bd9Sstevel@tonic-gate  *
32*7c478bd9Sstevel@tonic-gate  * contents:
33*7c478bd9Sstevel@tonic-gate  *	do_like ... change ownership and protection
34*7c478bd9Sstevel@tonic-gate  *	do_copy ... copy a file from one side to the other
35*7c478bd9Sstevel@tonic-gate  *	do_remove . remove a file from one side
36*7c478bd9Sstevel@tonic-gate  *	do_rename . rename a file on one side
37*7c478bd9Sstevel@tonic-gate  *	copy ...... (static) do the actual copy
38*7c478bd9Sstevel@tonic-gate  *	checksparse (static) figure out if a file is sparse
39*7c478bd9Sstevel@tonic-gate  *
40*7c478bd9Sstevel@tonic-gate  * ASSERTIONS:
41*7c478bd9Sstevel@tonic-gate  *	any of these action routines is responsible for all baseline
42*7c478bd9Sstevel@tonic-gate  *	and statistics updates associated with the reconciliation
43*7c478bd9Sstevel@tonic-gate  *	actions.  If notouch is specified, they should fake the
44*7c478bd9Sstevel@tonic-gate  *	updates well enough so that link tests will still work.
45*7c478bd9Sstevel@tonic-gate  *
46*7c478bd9Sstevel@tonic-gate  *	success:
47*7c478bd9Sstevel@tonic-gate  *		bump bp->b_{src,dst}_{copies,deletes,misc}
48*7c478bd9Sstevel@tonic-gate  *		update fp->f_info[srcdst]
49*7c478bd9Sstevel@tonic-gate  *		update fp->f_info[OPT_BASE] from fp->f_info[srcdst]
50*7c478bd9Sstevel@tonic-gate  *		if there might be multiple links, call link_update
51*7c478bd9Sstevel@tonic-gate  *		return ERR_RESOLVABLE
52*7c478bd9Sstevel@tonic-gate  *
53*7c478bd9Sstevel@tonic-gate  *	failure:
54*7c478bd9Sstevel@tonic-gate  *		set fp->f_flags |= F_CONFLICT
55*7c478bd9Sstevel@tonic-gate  *		set fp->f_problem
56*7c478bd9Sstevel@tonic-gate  *		bump bp->b_unresolved
57*7c478bd9Sstevel@tonic-gate  *		return ERR_UNRESOLVED
58*7c478bd9Sstevel@tonic-gate  *
59*7c478bd9Sstevel@tonic-gate  *	pretend this never happened:
60*7c478bd9Sstevel@tonic-gate  *		return 0, and baseline will be unchanged
61*7c478bd9Sstevel@tonic-gate  *
62*7c478bd9Sstevel@tonic-gate  * notes:
63*7c478bd9Sstevel@tonic-gate  *	Action routines can be called in virtually any order
64*7c478bd9Sstevel@tonic-gate  *	or combination, and it is certainly possible for an
65*7c478bd9Sstevel@tonic-gate  *	earlier action to succeed while a later action fails.
66*7c478bd9Sstevel@tonic-gate  *	If each successful action results in a completed baseline
67*7c478bd9Sstevel@tonic-gate  *	update, a subsequent failure will force the baseline to
68*7c478bd9Sstevel@tonic-gate  *	roll back to the last success ... which is appropriate.
69*7c478bd9Sstevel@tonic-gate  */
70*7c478bd9Sstevel@tonic-gate #ident	"%W%	%E% SMI"
71*7c478bd9Sstevel@tonic-gate 
72*7c478bd9Sstevel@tonic-gate #include <stdio.h>
73*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
74*7c478bd9Sstevel@tonic-gate #include <unistd.h>
75*7c478bd9Sstevel@tonic-gate #include <fcntl.h>
76*7c478bd9Sstevel@tonic-gate #include <utime.h>
77*7c478bd9Sstevel@tonic-gate #include <errno.h>
78*7c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
79*7c478bd9Sstevel@tonic-gate #include <sys/statvfs.h>
80*7c478bd9Sstevel@tonic-gate 
81*7c478bd9Sstevel@tonic-gate #include "filesync.h"
82*7c478bd9Sstevel@tonic-gate #include "database.h"
83*7c478bd9Sstevel@tonic-gate #include "messages.h"
84*7c478bd9Sstevel@tonic-gate #include "debug.h"
85*7c478bd9Sstevel@tonic-gate 
86*7c478bd9Sstevel@tonic-gate /*
87*7c478bd9Sstevel@tonic-gate  * globals and importeds
88*7c478bd9Sstevel@tonic-gate  */
89*7c478bd9Sstevel@tonic-gate bool_t need_super;	/* warn user that we can't fix ownership	*/
90*7c478bd9Sstevel@tonic-gate extern char *srcname;	/* file we are emulating			*/
91*7c478bd9Sstevel@tonic-gate extern char *dstname;	/* file we are updating				*/
92*7c478bd9Sstevel@tonic-gate 
93*7c478bd9Sstevel@tonic-gate /*
94*7c478bd9Sstevel@tonic-gate  * locals
95*7c478bd9Sstevel@tonic-gate  */
96*7c478bd9Sstevel@tonic-gate static errmask_t copy(char *, char *, int);
97*7c478bd9Sstevel@tonic-gate static int checksparse(int);
98*7c478bd9Sstevel@tonic-gate static char *copy_err_str;		/* what went wrong w/copy	*/
99*7c478bd9Sstevel@tonic-gate 
100*7c478bd9Sstevel@tonic-gate /*
101*7c478bd9Sstevel@tonic-gate  * routine:
102*7c478bd9Sstevel@tonic-gate  *	do_like
103*7c478bd9Sstevel@tonic-gate  *
104*7c478bd9Sstevel@tonic-gate  * purpose:
105*7c478bd9Sstevel@tonic-gate  *	to propagate ownership and protection changes between
106*7c478bd9Sstevel@tonic-gate  *	one existing file and another.
107*7c478bd9Sstevel@tonic-gate  *
108*7c478bd9Sstevel@tonic-gate  * parameters:
109*7c478bd9Sstevel@tonic-gate  *	file pointer
110*7c478bd9Sstevel@tonic-gate  *	src/dst indication for who needs to change
111*7c478bd9Sstevel@tonic-gate  *	whether or not to update statistics (there may be a copy and a like)
112*7c478bd9Sstevel@tonic-gate  *
113*7c478bd9Sstevel@tonic-gate  * returns:
114*7c478bd9Sstevel@tonic-gate  *	error mask
115*7c478bd9Sstevel@tonic-gate  *
116*7c478bd9Sstevel@tonic-gate  * notes:
117*7c478bd9Sstevel@tonic-gate  *	if we are called from reconcile, we should update
118*7c478bd9Sstevel@tonic-gate  *	the statistics, but if we were called from do_copy
119*7c478bd9Sstevel@tonic-gate  *	that routine will do the honors.
120*7c478bd9Sstevel@tonic-gate  */
121*7c478bd9Sstevel@tonic-gate errmask_t
122*7c478bd9Sstevel@tonic-gate do_like(struct file *fp, side_t srcdst, bool_t do_stats)
123*7c478bd9Sstevel@tonic-gate {	char *dst;
124*7c478bd9Sstevel@tonic-gate 	int rc = 0;
125*7c478bd9Sstevel@tonic-gate 	int do_chown, do_chmod, do_chgrp, do_acls;
126*7c478bd9Sstevel@tonic-gate 	errmask_t errs = 0;
127*7c478bd9Sstevel@tonic-gate 	char *errstr = 0;
128*7c478bd9Sstevel@tonic-gate 	struct base *bp;
129*7c478bd9Sstevel@tonic-gate 	struct fileinfo *sp;
130*7c478bd9Sstevel@tonic-gate 	struct fileinfo *dp;
131*7c478bd9Sstevel@tonic-gate 	struct fileinfo *ip;
132*7c478bd9Sstevel@tonic-gate 	extern int errno;
133*7c478bd9Sstevel@tonic-gate 
134*7c478bd9Sstevel@tonic-gate 	bp = fp->f_base;
135*7c478bd9Sstevel@tonic-gate 
136*7c478bd9Sstevel@tonic-gate 	/* see if this is a forbidden propagation */
137*7c478bd9Sstevel@tonic-gate 	if (srcdst == opt_oneway) {
138*7c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_CONFLICT;
139*7c478bd9Sstevel@tonic-gate 		fp->f_problem = gettext(PROB_prohibited);
140*7c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
141*7c478bd9Sstevel@tonic-gate 		return (ERR_UNRESOLVED);
142*7c478bd9Sstevel@tonic-gate 	}
143*7c478bd9Sstevel@tonic-gate 
144*7c478bd9Sstevel@tonic-gate 
145*7c478bd9Sstevel@tonic-gate 	/* get info about source and target files		*/
146*7c478bd9Sstevel@tonic-gate 	if (srcdst == OPT_SRC) {
147*7c478bd9Sstevel@tonic-gate 		sp = &fp->f_info[ OPT_DST ];
148*7c478bd9Sstevel@tonic-gate 		dp = &fp->f_info[ OPT_SRC ];
149*7c478bd9Sstevel@tonic-gate 		dst = srcname;
150*7c478bd9Sstevel@tonic-gate 	} else {
151*7c478bd9Sstevel@tonic-gate 		sp = &fp->f_info[ OPT_SRC ];
152*7c478bd9Sstevel@tonic-gate 		dp = &fp->f_info[ OPT_DST ];
153*7c478bd9Sstevel@tonic-gate 		dst = dstname;
154*7c478bd9Sstevel@tonic-gate 	}
155*7c478bd9Sstevel@tonic-gate 	ip = &fp->f_info[ OPT_BASE ];
156*7c478bd9Sstevel@tonic-gate 
157*7c478bd9Sstevel@tonic-gate 	/* figure out what needs fixing				*/
158*7c478bd9Sstevel@tonic-gate 	do_chmod = (sp->f_mode != dp->f_mode);
159*7c478bd9Sstevel@tonic-gate 	do_chown = (sp->f_uid != dp->f_uid);
160*7c478bd9Sstevel@tonic-gate 	do_chgrp = (sp->f_gid != dp->f_gid);
161*7c478bd9Sstevel@tonic-gate 	do_acls  = ((fp->f_srcdiffs|fp->f_dstdiffs) & D_FACLS);
162*7c478bd9Sstevel@tonic-gate 
163*7c478bd9Sstevel@tonic-gate 	/*
164*7c478bd9Sstevel@tonic-gate 	 * try to anticipate things that we might not be able to
165*7c478bd9Sstevel@tonic-gate 	 * do, and return appropriate errorst if the calling user
166*7c478bd9Sstevel@tonic-gate 	 * cannot safely perform the requiested updates.
167*7c478bd9Sstevel@tonic-gate 	 */
168*7c478bd9Sstevel@tonic-gate 	if (my_uid != 0) {
169*7c478bd9Sstevel@tonic-gate 		if (do_chown)
170*7c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_chown);
171*7c478bd9Sstevel@tonic-gate 		else if (my_uid != dp->f_uid) {
172*7c478bd9Sstevel@tonic-gate 			if (do_chmod)
173*7c478bd9Sstevel@tonic-gate 				errstr = gettext(PROB_chmod);
174*7c478bd9Sstevel@tonic-gate 			else if (do_acls)
175*7c478bd9Sstevel@tonic-gate 				errstr = gettext(PROB_chacl);
176*7c478bd9Sstevel@tonic-gate 			else if (do_chgrp)
177*7c478bd9Sstevel@tonic-gate 				errstr = gettext(PROB_chgrp);
178*7c478bd9Sstevel@tonic-gate 		}
179*7c478bd9Sstevel@tonic-gate #ifdef	ACL_UID_BUG
180*7c478bd9Sstevel@tonic-gate 		else if (do_acls && my_gid != dp->f_gid)
181*7c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_botch);
182*7c478bd9Sstevel@tonic-gate #endif
183*7c478bd9Sstevel@tonic-gate 
184*7c478bd9Sstevel@tonic-gate 		if (errstr) {
185*7c478bd9Sstevel@tonic-gate 			need_super = TRUE;
186*7c478bd9Sstevel@tonic-gate 
187*7c478bd9Sstevel@tonic-gate 			/* if the user doesn't care, shine it on	*/
188*7c478bd9Sstevel@tonic-gate 			if (opt_everything == 0)
189*7c478bd9Sstevel@tonic-gate 				return (0);
190*7c478bd9Sstevel@tonic-gate 
191*7c478bd9Sstevel@tonic-gate 			/* if the user does care, return the error	*/
192*7c478bd9Sstevel@tonic-gate 			rc = -1;
193*7c478bd9Sstevel@tonic-gate 			goto nogood;
194*7c478bd9Sstevel@tonic-gate 		}
195*7c478bd9Sstevel@tonic-gate 	}
196*7c478bd9Sstevel@tonic-gate 
197*7c478bd9Sstevel@tonic-gate 	if (opt_debug & DBG_RECON) {
198*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, "RECO: do_like %s (", dst);
199*7c478bd9Sstevel@tonic-gate 		if (do_chmod)
200*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "chmod ");
201*7c478bd9Sstevel@tonic-gate 		if (do_acls)
202*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "acls ");
203*7c478bd9Sstevel@tonic-gate 		if (do_chown)
204*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "chown ");
205*7c478bd9Sstevel@tonic-gate 		if (do_chgrp)
206*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "chgrp ");
207*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, ")\n");
208*7c478bd9Sstevel@tonic-gate 	}
209*7c478bd9Sstevel@tonic-gate 
210*7c478bd9Sstevel@tonic-gate 	if (do_chmod) {
211*7c478bd9Sstevel@tonic-gate 		if (!opt_quiet)
212*7c478bd9Sstevel@tonic-gate 			fprintf(stdout, "chmod %o %s\n", sp->f_mode,
213*7c478bd9Sstevel@tonic-gate 						noblanks(dst));
214*7c478bd9Sstevel@tonic-gate 
215*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
216*7c478bd9Sstevel@tonic-gate 		/* should we simulate a chmod failure	*/
217*7c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(dst, 'p'))
218*7c478bd9Sstevel@tonic-gate 			rc = -1;
219*7c478bd9Sstevel@tonic-gate 		else
220*7c478bd9Sstevel@tonic-gate #endif
221*7c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0 : chmod(dst, sp->f_mode);
222*7c478bd9Sstevel@tonic-gate 
223*7c478bd9Sstevel@tonic-gate 		if (opt_debug & DBG_RECON)
224*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "RECO: do_chmod %o -> %d(%d)\n",
225*7c478bd9Sstevel@tonic-gate 				sp->f_mode, rc, errno);
226*7c478bd9Sstevel@tonic-gate 
227*7c478bd9Sstevel@tonic-gate 		/* update dest and baseline to reflect the change */
228*7c478bd9Sstevel@tonic-gate 		if (rc == 0) {
229*7c478bd9Sstevel@tonic-gate 			dp->f_mode = sp->f_mode;
230*7c478bd9Sstevel@tonic-gate 			ip->f_mode = sp->f_mode;
231*7c478bd9Sstevel@tonic-gate 		} else
232*7c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_chmod);
233*7c478bd9Sstevel@tonic-gate 	}
234*7c478bd9Sstevel@tonic-gate 
235*7c478bd9Sstevel@tonic-gate 	/*
236*7c478bd9Sstevel@tonic-gate 	 * see if we need to fix the acls
237*7c478bd9Sstevel@tonic-gate 	 */
238*7c478bd9Sstevel@tonic-gate 	if (rc == 0 && do_acls) {
239*7c478bd9Sstevel@tonic-gate 		if (!opt_quiet)
240*7c478bd9Sstevel@tonic-gate 			fprintf(stdout, "setfacl %s %s\n",
241*7c478bd9Sstevel@tonic-gate 				show_acls(sp->f_numacls, sp->f_acls),
242*7c478bd9Sstevel@tonic-gate 				noblanks(dst));
243*7c478bd9Sstevel@tonic-gate 
244*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
245*7c478bd9Sstevel@tonic-gate 		/* should we simulate a set acl failure	*/
246*7c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(dst, 'a'))
247*7c478bd9Sstevel@tonic-gate 			rc = -1;
248*7c478bd9Sstevel@tonic-gate 		else
249*7c478bd9Sstevel@tonic-gate #endif
250*7c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0 : set_acls(dst, sp);
251*7c478bd9Sstevel@tonic-gate 
252*7c478bd9Sstevel@tonic-gate 		if (opt_debug & DBG_RECON)
253*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "RECO: do_acls %d -> %d(%d)\n",
254*7c478bd9Sstevel@tonic-gate 				sp->f_numacls, rc, errno);
255*7c478bd9Sstevel@tonic-gate 
256*7c478bd9Sstevel@tonic-gate 		/* update dest and baseline to reflect the change */
257*7c478bd9Sstevel@tonic-gate 		if (rc == 0) {
258*7c478bd9Sstevel@tonic-gate 			dp->f_numacls = sp->f_numacls;
259*7c478bd9Sstevel@tonic-gate 			dp->f_acls = sp->f_acls;
260*7c478bd9Sstevel@tonic-gate 			ip->f_numacls = sp->f_numacls;
261*7c478bd9Sstevel@tonic-gate 			ip->f_acls = sp->f_acls;
262*7c478bd9Sstevel@tonic-gate #ifdef	ACL_UID_BUG
263*7c478bd9Sstevel@tonic-gate 			/* SETFACL changes a file's UID/GID	*/
264*7c478bd9Sstevel@tonic-gate 			if (my_uid != dp->f_uid) {
265*7c478bd9Sstevel@tonic-gate 				do_chown = 1;
266*7c478bd9Sstevel@tonic-gate 				dp->f_uid = my_uid;
267*7c478bd9Sstevel@tonic-gate 			}
268*7c478bd9Sstevel@tonic-gate 			if (my_gid != dp->f_gid) {
269*7c478bd9Sstevel@tonic-gate 				do_chgrp = 1;
270*7c478bd9Sstevel@tonic-gate 				dp->f_gid = my_gid;
271*7c478bd9Sstevel@tonic-gate 			}
272*7c478bd9Sstevel@tonic-gate #endif
273*7c478bd9Sstevel@tonic-gate 		} else if (errno == ENOSYS) {
274*7c478bd9Sstevel@tonic-gate 			/*
275*7c478bd9Sstevel@tonic-gate 			 * if the file system doesn't support ACLs
276*7c478bd9Sstevel@tonic-gate 			 * we should just pretend we never saw them
277*7c478bd9Sstevel@tonic-gate 			 */
278*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, gettext(WARN_noacls), dst);
279*7c478bd9Sstevel@tonic-gate 			ip->f_numacls = 0;
280*7c478bd9Sstevel@tonic-gate 			sp->f_numacls = 0;
281*7c478bd9Sstevel@tonic-gate 			dp->f_numacls = 0;
282*7c478bd9Sstevel@tonic-gate 			rc = 0;
283*7c478bd9Sstevel@tonic-gate 		} else
284*7c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_chacl);
285*7c478bd9Sstevel@tonic-gate 	}
286*7c478bd9Sstevel@tonic-gate 
287*7c478bd9Sstevel@tonic-gate 	/*
288*7c478bd9Sstevel@tonic-gate 	 * see if we need to fix the ownership
289*7c478bd9Sstevel@tonic-gate 	 */
290*7c478bd9Sstevel@tonic-gate 	if (rc == 0 && (do_chown || do_chgrp)) {
291*7c478bd9Sstevel@tonic-gate 		if (do_chown)
292*7c478bd9Sstevel@tonic-gate 			fprintf(stdout, "chown %ld %s; ",
293*7c478bd9Sstevel@tonic-gate 				sp->f_uid, noblanks(dst));
294*7c478bd9Sstevel@tonic-gate 		if (do_chgrp)
295*7c478bd9Sstevel@tonic-gate 			fprintf(stdout, "chgrp %ld %s",
296*7c478bd9Sstevel@tonic-gate 				sp->f_gid, noblanks(dst));
297*7c478bd9Sstevel@tonic-gate 
298*7c478bd9Sstevel@tonic-gate 		fprintf(stdout, "\n");
299*7c478bd9Sstevel@tonic-gate 
300*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
301*7c478bd9Sstevel@tonic-gate 		/* should we simulate a chown failure	*/
302*7c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(dst, 'O'))
303*7c478bd9Sstevel@tonic-gate 			rc = -1;
304*7c478bd9Sstevel@tonic-gate 		else
305*7c478bd9Sstevel@tonic-gate #endif
306*7c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0 : lchown(dst, sp->f_uid, sp->f_gid);
307*7c478bd9Sstevel@tonic-gate 
308*7c478bd9Sstevel@tonic-gate 		if (opt_debug & DBG_RECON)
309*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "RECO: do_chown %ld %ld -> %d(%d)\n",
310*7c478bd9Sstevel@tonic-gate 					sp->f_uid, sp->f_gid, rc, errno);
311*7c478bd9Sstevel@tonic-gate 
312*7c478bd9Sstevel@tonic-gate 		/* update the destination to reflect changes */
313*7c478bd9Sstevel@tonic-gate 		if (rc == 0) {
314*7c478bd9Sstevel@tonic-gate 			dp->f_uid = sp->f_uid;
315*7c478bd9Sstevel@tonic-gate 			dp->f_gid = sp->f_gid;
316*7c478bd9Sstevel@tonic-gate 			ip->f_uid = sp->f_uid;
317*7c478bd9Sstevel@tonic-gate 			ip->f_gid = sp->f_gid;
318*7c478bd9Sstevel@tonic-gate 		} else {
319*7c478bd9Sstevel@tonic-gate 			if (errno == EPERM) {
320*7c478bd9Sstevel@tonic-gate 				need_super = TRUE;
321*7c478bd9Sstevel@tonic-gate 				if (opt_everything == 0)
322*7c478bd9Sstevel@tonic-gate 					return (0);
323*7c478bd9Sstevel@tonic-gate 			}
324*7c478bd9Sstevel@tonic-gate 
325*7c478bd9Sstevel@tonic-gate 			if (rc != 0)
326*7c478bd9Sstevel@tonic-gate 				errstr = gettext(do_chown ?
327*7c478bd9Sstevel@tonic-gate 						PROB_chown : PROB_chgrp);
328*7c478bd9Sstevel@tonic-gate 		}
329*7c478bd9Sstevel@tonic-gate 	}
330*7c478bd9Sstevel@tonic-gate 
331*7c478bd9Sstevel@tonic-gate 	/*
332*7c478bd9Sstevel@tonic-gate 	 * if we were successful, we should make sure the other links
333*7c478bd9Sstevel@tonic-gate 	 * see the changes.  If we were called from do_copy, we don't
334*7c478bd9Sstevel@tonic-gate 	 * want to do the link_updates either because do_copy will
335*7c478bd9Sstevel@tonic-gate 	 * handle them too.
336*7c478bd9Sstevel@tonic-gate 	 */
337*7c478bd9Sstevel@tonic-gate 	if (rc == 0 && do_stats)
338*7c478bd9Sstevel@tonic-gate 		link_update(fp, srcdst);
339*7c478bd9Sstevel@tonic-gate 
340*7c478bd9Sstevel@tonic-gate nogood:
341*7c478bd9Sstevel@tonic-gate 	if (!do_stats)
342*7c478bd9Sstevel@tonic-gate 		return (errs);
343*7c478bd9Sstevel@tonic-gate 
344*7c478bd9Sstevel@tonic-gate 	if (rc != 0) {
345*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, gettext(ERR_cannot), errstr, dst);
346*7c478bd9Sstevel@tonic-gate 		fp->f_problem = errstr;
347*7c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_CONFLICT;
348*7c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
349*7c478bd9Sstevel@tonic-gate 		errs |= ERR_PERM | ERR_UNRESOLVED;
350*7c478bd9Sstevel@tonic-gate 	} else {
351*7c478bd9Sstevel@tonic-gate 		/*
352*7c478bd9Sstevel@tonic-gate 		 * it worked, so update the baseline and statistics
353*7c478bd9Sstevel@tonic-gate 		 */
354*7c478bd9Sstevel@tonic-gate 		if (srcdst == OPT_SRC)
355*7c478bd9Sstevel@tonic-gate 			bp->b_src_misc++;
356*7c478bd9Sstevel@tonic-gate 		else
357*7c478bd9Sstevel@tonic-gate 			bp->b_dst_misc++;
358*7c478bd9Sstevel@tonic-gate 
359*7c478bd9Sstevel@tonic-gate 		fp->f_problem = 0;
360*7c478bd9Sstevel@tonic-gate 		errs |= ERR_RESOLVABLE;
361*7c478bd9Sstevel@tonic-gate 	}
362*7c478bd9Sstevel@tonic-gate 
363*7c478bd9Sstevel@tonic-gate 	return (errs);
364*7c478bd9Sstevel@tonic-gate }
365*7c478bd9Sstevel@tonic-gate 
366*7c478bd9Sstevel@tonic-gate /*
367*7c478bd9Sstevel@tonic-gate  * routine:
368*7c478bd9Sstevel@tonic-gate  *	do_copy
369*7c478bd9Sstevel@tonic-gate  *
370*7c478bd9Sstevel@tonic-gate  * purpose:
371*7c478bd9Sstevel@tonic-gate  *	to propagate a creation or change
372*7c478bd9Sstevel@tonic-gate  *
373*7c478bd9Sstevel@tonic-gate  * parameters:
374*7c478bd9Sstevel@tonic-gate  *	file pointer
375*7c478bd9Sstevel@tonic-gate  *	src/dst indication for who gets the copy
376*7c478bd9Sstevel@tonic-gate  *
377*7c478bd9Sstevel@tonic-gate  * returns:
378*7c478bd9Sstevel@tonic-gate  *	error mask
379*7c478bd9Sstevel@tonic-gate  *
380*7c478bd9Sstevel@tonic-gate  * note:
381*7c478bd9Sstevel@tonic-gate  *	after any successful operation we update the stat/info
382*7c478bd9Sstevel@tonic-gate  *	structure for the updated file.  This is somewhat redundant
383*7c478bd9Sstevel@tonic-gate  *	because we will restat at the end of the routine, but these
384*7c478bd9Sstevel@tonic-gate  *	anticipatory updates help to ensure that the link finding
385*7c478bd9Sstevel@tonic-gate  *	code will still behave properly in notouch mode (when restats
386*7c478bd9Sstevel@tonic-gate  *	cannot be done).
387*7c478bd9Sstevel@tonic-gate  */
388*7c478bd9Sstevel@tonic-gate errmask_t
389*7c478bd9Sstevel@tonic-gate do_copy(struct file *fp, side_t srcdst)
390*7c478bd9Sstevel@tonic-gate {	char *src, *dst;
391*7c478bd9Sstevel@tonic-gate 	char cmdbuf[ MAX_PATH + MAX_NAME ];
392*7c478bd9Sstevel@tonic-gate 	int mode, maj, min, type;
393*7c478bd9Sstevel@tonic-gate 	uid_t uid;
394*7c478bd9Sstevel@tonic-gate 	gid_t gid;
395*7c478bd9Sstevel@tonic-gate 	int rc;
396*7c478bd9Sstevel@tonic-gate 	long mtime;
397*7c478bd9Sstevel@tonic-gate 	int do_chmod = 0;
398*7c478bd9Sstevel@tonic-gate 	int do_chown = 0;
399*7c478bd9Sstevel@tonic-gate 	int do_chgrp = 0;
400*7c478bd9Sstevel@tonic-gate 	int do_unlink = 0;
401*7c478bd9Sstevel@tonic-gate 	int do_acls = 0;
402*7c478bd9Sstevel@tonic-gate 	int do_create = 0;
403*7c478bd9Sstevel@tonic-gate 	char *errstr = "???";
404*7c478bd9Sstevel@tonic-gate 	errmask_t errs = 0;
405*7c478bd9Sstevel@tonic-gate 	struct base *bp;
406*7c478bd9Sstevel@tonic-gate 	struct file *lp;
407*7c478bd9Sstevel@tonic-gate 	struct fileinfo *sp, *dp;
408*7c478bd9Sstevel@tonic-gate 	struct utimbuf newtimes;
409*7c478bd9Sstevel@tonic-gate 	struct stat statb;
410*7c478bd9Sstevel@tonic-gate 
411*7c478bd9Sstevel@tonic-gate 	bp = fp->f_base;
412*7c478bd9Sstevel@tonic-gate 
413*7c478bd9Sstevel@tonic-gate 	/* see if this is a forbidden propagation */
414*7c478bd9Sstevel@tonic-gate 	if (srcdst == opt_oneway) {
415*7c478bd9Sstevel@tonic-gate 		fp->f_problem = gettext(PROB_prohibited);
416*7c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_CONFLICT;
417*7c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
418*7c478bd9Sstevel@tonic-gate 		return (ERR_UNRESOLVED);
419*7c478bd9Sstevel@tonic-gate 	}
420*7c478bd9Sstevel@tonic-gate 
421*7c478bd9Sstevel@tonic-gate 	/* figure out who is the source and who is the destination	*/
422*7c478bd9Sstevel@tonic-gate 	if (srcdst == OPT_SRC) {
423*7c478bd9Sstevel@tonic-gate 		sp = &fp->f_info[ OPT_DST ];
424*7c478bd9Sstevel@tonic-gate 		dp = &fp->f_info[ OPT_SRC ];
425*7c478bd9Sstevel@tonic-gate 		src = dstname;
426*7c478bd9Sstevel@tonic-gate 		dst = srcname;
427*7c478bd9Sstevel@tonic-gate 	} else {
428*7c478bd9Sstevel@tonic-gate 		sp = &fp->f_info[ OPT_SRC ];
429*7c478bd9Sstevel@tonic-gate 		dp = &fp->f_info[ OPT_DST ];
430*7c478bd9Sstevel@tonic-gate 		src = srcname;
431*7c478bd9Sstevel@tonic-gate 		dst = dstname;
432*7c478bd9Sstevel@tonic-gate 	}
433*7c478bd9Sstevel@tonic-gate 
434*7c478bd9Sstevel@tonic-gate 	/* note information about the file to be created		*/
435*7c478bd9Sstevel@tonic-gate 	type  = sp->f_type;		/* type of the new file		*/
436*7c478bd9Sstevel@tonic-gate 	uid   = sp->f_uid;		/* owner of the new file	*/
437*7c478bd9Sstevel@tonic-gate 	gid   = sp->f_gid;		/* group of the new file	*/
438*7c478bd9Sstevel@tonic-gate 	mode  = sp->f_mode;		/* modes for the new file	*/
439*7c478bd9Sstevel@tonic-gate 	mtime = sp->f_modtime;		/* modtime (if preserving)	*/
440*7c478bd9Sstevel@tonic-gate 	maj   = sp->f_rd_maj;		/* major (if it is a device)	*/
441*7c478bd9Sstevel@tonic-gate 	min   = sp->f_rd_min;		/* minor (if it is a device)	*/
442*7c478bd9Sstevel@tonic-gate 
443*7c478bd9Sstevel@tonic-gate 	/*
444*7c478bd9Sstevel@tonic-gate 	 * creating a file does not guarantee it will get the desired
445*7c478bd9Sstevel@tonic-gate 	 * modes, uid and gid.  If the file already exists, it will
446*7c478bd9Sstevel@tonic-gate 	 * retain its old ownership and protection.  If my UID/GID
447*7c478bd9Sstevel@tonic-gate 	 * are not the desired ones, the new file will also require
448*7c478bd9Sstevel@tonic-gate 	 * manual correction.  If the file has the wrong type, we will
449*7c478bd9Sstevel@tonic-gate 	 * need to delete it and recreate it.  If the file is not writable,
450*7c478bd9Sstevel@tonic-gate 	 * it is easier to delete it than to chmod it to permit overwrite
451*7c478bd9Sstevel@tonic-gate 	 */
452*7c478bd9Sstevel@tonic-gate 	if ((dp->f_type == S_IFREG && sp->f_type == S_IFREG) &&
453*7c478bd9Sstevel@tonic-gate 	    (dp->f_mode & 0200)) {
454*7c478bd9Sstevel@tonic-gate 		/* if the file already exists		*/
455*7c478bd9Sstevel@tonic-gate 		if (dp->f_uid != uid)
456*7c478bd9Sstevel@tonic-gate 			do_chown = 1;
457*7c478bd9Sstevel@tonic-gate 
458*7c478bd9Sstevel@tonic-gate 		if (dp->f_gid != gid)
459*7c478bd9Sstevel@tonic-gate 			do_chgrp = 1;
460*7c478bd9Sstevel@tonic-gate 
461*7c478bd9Sstevel@tonic-gate 		if (dp->f_mode != mode)
462*7c478bd9Sstevel@tonic-gate 			do_chmod = 1;
463*7c478bd9Sstevel@tonic-gate 	} else {
464*7c478bd9Sstevel@tonic-gate 		/* if we will be creating a new file	*/
465*7c478bd9Sstevel@tonic-gate 		do_create = 1;
466*7c478bd9Sstevel@tonic-gate 		if (dp->f_type)
467*7c478bd9Sstevel@tonic-gate 			do_unlink = 1;
468*7c478bd9Sstevel@tonic-gate 		if (uid != my_uid)
469*7c478bd9Sstevel@tonic-gate 			do_chown = 1;
470*7c478bd9Sstevel@tonic-gate 		if (gid != my_gid)
471*7c478bd9Sstevel@tonic-gate 			do_chgrp = 1;
472*7c478bd9Sstevel@tonic-gate 	}
473*7c478bd9Sstevel@tonic-gate 
474*7c478bd9Sstevel@tonic-gate 	/*
475*7c478bd9Sstevel@tonic-gate 	 * if the source has acls, we will surely have to set them for dest
476*7c478bd9Sstevel@tonic-gate 	 */
477*7c478bd9Sstevel@tonic-gate 	if (sp->f_numacls)
478*7c478bd9Sstevel@tonic-gate 		do_acls = 1;
479*7c478bd9Sstevel@tonic-gate 
480*7c478bd9Sstevel@tonic-gate 	/*
481*7c478bd9Sstevel@tonic-gate 	 * for any case other than replacing a normal file with a normal
482*7c478bd9Sstevel@tonic-gate 	 * file, we need to delete the existing file before creating
483*7c478bd9Sstevel@tonic-gate 	 * the new one.
484*7c478bd9Sstevel@tonic-gate 	 */
485*7c478bd9Sstevel@tonic-gate 	if (do_unlink) {
486*7c478bd9Sstevel@tonic-gate 		if (dp->f_type == S_IFDIR) {
487*7c478bd9Sstevel@tonic-gate 			if (!opt_quiet)
488*7c478bd9Sstevel@tonic-gate 				fprintf(stdout, "rmdir %s\n", noblanks(dst));
489*7c478bd9Sstevel@tonic-gate 
490*7c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_rmdir);
491*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
492*7c478bd9Sstevel@tonic-gate 			/* should we simulate a rmdir failure	*/
493*7c478bd9Sstevel@tonic-gate 			if (errno = dbg_chk_error(dst, 'D'))
494*7c478bd9Sstevel@tonic-gate 				rc = -1;
495*7c478bd9Sstevel@tonic-gate 			else
496*7c478bd9Sstevel@tonic-gate #endif
497*7c478bd9Sstevel@tonic-gate 			rc = opt_notouch ? 0 : rmdir(dst);
498*7c478bd9Sstevel@tonic-gate 		} else {
499*7c478bd9Sstevel@tonic-gate 			if (!opt_quiet)
500*7c478bd9Sstevel@tonic-gate 				fprintf(stdout, "rm %s\n", noblanks(dst));
501*7c478bd9Sstevel@tonic-gate 
502*7c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_unlink);
503*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
504*7c478bd9Sstevel@tonic-gate 			/* should we simulate a unlink failure	*/
505*7c478bd9Sstevel@tonic-gate 			if (errno = dbg_chk_error(dst, 'u'))
506*7c478bd9Sstevel@tonic-gate 				rc = -1;
507*7c478bd9Sstevel@tonic-gate 			else
508*7c478bd9Sstevel@tonic-gate #endif
509*7c478bd9Sstevel@tonic-gate 			rc = opt_notouch ? 0 : unlink(dst);
510*7c478bd9Sstevel@tonic-gate 		}
511*7c478bd9Sstevel@tonic-gate 
512*7c478bd9Sstevel@tonic-gate 		if (rc != 0)
513*7c478bd9Sstevel@tonic-gate 			goto cant;
514*7c478bd9Sstevel@tonic-gate 
515*7c478bd9Sstevel@tonic-gate 		/* note that this file no longer exists		*/
516*7c478bd9Sstevel@tonic-gate 		dp->f_type = 0;
517*7c478bd9Sstevel@tonic-gate 		dp->f_mode = 0;
518*7c478bd9Sstevel@tonic-gate 	}
519*7c478bd9Sstevel@tonic-gate 
520*7c478bd9Sstevel@tonic-gate 	if (opt_debug & DBG_RECON) {
521*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, "RECO: do_copy %s %s (", src, dst);
522*7c478bd9Sstevel@tonic-gate 		if (do_unlink)
523*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "unlink ");
524*7c478bd9Sstevel@tonic-gate 		if (do_chmod)
525*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "chmod ");
526*7c478bd9Sstevel@tonic-gate 		if (do_acls)
527*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "acls ");
528*7c478bd9Sstevel@tonic-gate 		if (do_chown)
529*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "chown ");
530*7c478bd9Sstevel@tonic-gate 		if (do_chgrp)
531*7c478bd9Sstevel@tonic-gate 			fprintf(stderr, "chgrp ");
532*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, ")\n");
533*7c478bd9Sstevel@tonic-gate 	}
534*7c478bd9Sstevel@tonic-gate 
535*7c478bd9Sstevel@tonic-gate 	/*
536*7c478bd9Sstevel@tonic-gate 	 * how we go about copying a file depends on what type of file
537*7c478bd9Sstevel@tonic-gate 	 * it is that we are supposed to copy
538*7c478bd9Sstevel@tonic-gate 	 */
539*7c478bd9Sstevel@tonic-gate 	switch (type) {
540*7c478bd9Sstevel@tonic-gate 	    case S_IFDIR:
541*7c478bd9Sstevel@tonic-gate 		if (!opt_quiet) {
542*7c478bd9Sstevel@tonic-gate 			fprintf(stdout, "mkdir %s;", noblanks(dst));
543*7c478bd9Sstevel@tonic-gate 			fprintf(stdout, " chmod %o %s;\n", mode, noblanks(dst));
544*7c478bd9Sstevel@tonic-gate 		}
545*7c478bd9Sstevel@tonic-gate 
546*7c478bd9Sstevel@tonic-gate 		errstr = gettext(PROB_mkdir);
547*7c478bd9Sstevel@tonic-gate 
548*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
549*7c478bd9Sstevel@tonic-gate 		/* should we simulate a mkdir failure	*/
550*7c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(dst, 'd'))
551*7c478bd9Sstevel@tonic-gate 			rc = -1;
552*7c478bd9Sstevel@tonic-gate 		else
553*7c478bd9Sstevel@tonic-gate #endif
554*7c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0 : mkdir(dst, mode);
555*7c478bd9Sstevel@tonic-gate 
556*7c478bd9Sstevel@tonic-gate 		/* update stat with what we have just created	*/
557*7c478bd9Sstevel@tonic-gate 		if (rc == 0) {
558*7c478bd9Sstevel@tonic-gate 			dp->f_type = S_IFDIR;
559*7c478bd9Sstevel@tonic-gate 			dp->f_uid = my_uid;
560*7c478bd9Sstevel@tonic-gate 			dp->f_gid = my_gid;
561*7c478bd9Sstevel@tonic-gate 			dp->f_mode = mode;
562*7c478bd9Sstevel@tonic-gate 		}
563*7c478bd9Sstevel@tonic-gate 
564*7c478bd9Sstevel@tonic-gate 		break;
565*7c478bd9Sstevel@tonic-gate 
566*7c478bd9Sstevel@tonic-gate 	    case S_IFLNK:
567*7c478bd9Sstevel@tonic-gate 		errstr = gettext(PROB_readlink);
568*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
569*7c478bd9Sstevel@tonic-gate 		/* should we simulate a symlink read failure	*/
570*7c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(dst, 'r'))
571*7c478bd9Sstevel@tonic-gate 			rc = -1;
572*7c478bd9Sstevel@tonic-gate 		else
573*7c478bd9Sstevel@tonic-gate #endif
574*7c478bd9Sstevel@tonic-gate 		rc = readlink(src, cmdbuf, sizeof (cmdbuf));
575*7c478bd9Sstevel@tonic-gate 		if (rc > 0) {
576*7c478bd9Sstevel@tonic-gate 			cmdbuf[rc] = 0;
577*7c478bd9Sstevel@tonic-gate 			if (!opt_quiet) {
578*7c478bd9Sstevel@tonic-gate 				fprintf(stdout, "ln -s %s", noblanks(cmdbuf));
579*7c478bd9Sstevel@tonic-gate 				fprintf(stdout, " %s;\n", noblanks(dst));
580*7c478bd9Sstevel@tonic-gate 			}
581*7c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_symlink);
582*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
583*7c478bd9Sstevel@tonic-gate 			/* should we simulate a symlink failure	*/
584*7c478bd9Sstevel@tonic-gate 			if (errno = dbg_chk_error(dst, 'l'))
585*7c478bd9Sstevel@tonic-gate 				rc = -1;
586*7c478bd9Sstevel@tonic-gate 			else
587*7c478bd9Sstevel@tonic-gate #endif
588*7c478bd9Sstevel@tonic-gate 			rc = opt_notouch ? 0 : symlink(cmdbuf, dst);
589*7c478bd9Sstevel@tonic-gate 
590*7c478bd9Sstevel@tonic-gate 			if (rc == 0)
591*7c478bd9Sstevel@tonic-gate 				dp->f_type = S_IFLNK;
592*7c478bd9Sstevel@tonic-gate 		}
593*7c478bd9Sstevel@tonic-gate 		break;
594*7c478bd9Sstevel@tonic-gate 
595*7c478bd9Sstevel@tonic-gate 	    case S_IFBLK:
596*7c478bd9Sstevel@tonic-gate 	    case S_IFCHR:
597*7c478bd9Sstevel@tonic-gate 		if (!opt_quiet)
598*7c478bd9Sstevel@tonic-gate 			fprintf(stdout, "mknod %s %s %d %d\n", noblanks(dst),
599*7c478bd9Sstevel@tonic-gate 				(type == S_IFBLK) ? "b" : "c", maj, min);
600*7c478bd9Sstevel@tonic-gate 
601*7c478bd9Sstevel@tonic-gate 		errstr = gettext(PROB_mknod);
602*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
603*7c478bd9Sstevel@tonic-gate 		/* should we simulate a mknod failure	*/
604*7c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(dst, 'd'))
605*7c478bd9Sstevel@tonic-gate 			rc = -1;
606*7c478bd9Sstevel@tonic-gate 		else
607*7c478bd9Sstevel@tonic-gate #endif
608*7c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0
609*7c478bd9Sstevel@tonic-gate 				: mknod(dst, mode|type, makedev(maj, min));
610*7c478bd9Sstevel@tonic-gate 
611*7c478bd9Sstevel@tonic-gate 		/* update stat with what we have just created	*/
612*7c478bd9Sstevel@tonic-gate 		if (rc == 0) {
613*7c478bd9Sstevel@tonic-gate 			dp->f_type = type;
614*7c478bd9Sstevel@tonic-gate 			dp->f_uid = my_uid;
615*7c478bd9Sstevel@tonic-gate 			dp->f_gid = my_gid;
616*7c478bd9Sstevel@tonic-gate 			dp->f_mode = 0666;
617*7c478bd9Sstevel@tonic-gate 
618*7c478bd9Sstevel@tonic-gate 			if (dp->f_mode != mode)
619*7c478bd9Sstevel@tonic-gate 				do_chmod = 1;
620*7c478bd9Sstevel@tonic-gate 		}
621*7c478bd9Sstevel@tonic-gate 		break;
622*7c478bd9Sstevel@tonic-gate 
623*7c478bd9Sstevel@tonic-gate 	    case S_IFREG:
624*7c478bd9Sstevel@tonic-gate 		/*
625*7c478bd9Sstevel@tonic-gate 		 * The first thing to do is ascertain whether or not
626*7c478bd9Sstevel@tonic-gate 		 * the alleged new copy might in fact be a new link.
627*7c478bd9Sstevel@tonic-gate 		 * We trust find_link to weigh all the various factors,
628*7c478bd9Sstevel@tonic-gate 		 * so if he says make a link, we'll do it.
629*7c478bd9Sstevel@tonic-gate 		 */
630*7c478bd9Sstevel@tonic-gate 		lp = find_link(fp, srcdst);
631*7c478bd9Sstevel@tonic-gate 		if (lp) {
632*7c478bd9Sstevel@tonic-gate 			/* figure out name of existing file	*/
633*7c478bd9Sstevel@tonic-gate 			src = full_name(lp, srcdst, OPT_BASE);
634*7c478bd9Sstevel@tonic-gate 
635*7c478bd9Sstevel@tonic-gate 			/*
636*7c478bd9Sstevel@tonic-gate 			 * if file already exists, it must be deleted
637*7c478bd9Sstevel@tonic-gate 			 */
638*7c478bd9Sstevel@tonic-gate 			if (dp->f_type) {
639*7c478bd9Sstevel@tonic-gate 				if (!opt_quiet)
640*7c478bd9Sstevel@tonic-gate 					fprintf(stdout, "rm %s\n",
641*7c478bd9Sstevel@tonic-gate 						noblanks(dst));
642*7c478bd9Sstevel@tonic-gate 
643*7c478bd9Sstevel@tonic-gate 				errstr = gettext(PROB_unlink);
644*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
645*7c478bd9Sstevel@tonic-gate 				/* should we simulate a unlink failure	*/
646*7c478bd9Sstevel@tonic-gate 				if (errno = dbg_chk_error(dst, 'u'))
647*7c478bd9Sstevel@tonic-gate 					rc = -1;
648*7c478bd9Sstevel@tonic-gate 				else
649*7c478bd9Sstevel@tonic-gate #endif
650*7c478bd9Sstevel@tonic-gate 				rc = opt_notouch ? 0 : unlink(dst);
651*7c478bd9Sstevel@tonic-gate 
652*7c478bd9Sstevel@tonic-gate 				/*
653*7c478bd9Sstevel@tonic-gate 				 * if we couldn't do the unlink, we must
654*7c478bd9Sstevel@tonic-gate 				 * mark the linkee in conflict as well
655*7c478bd9Sstevel@tonic-gate 				 * so his reference count remains the same
656*7c478bd9Sstevel@tonic-gate 				 * in the baseline and he continues to show
657*7c478bd9Sstevel@tonic-gate 				 * up on the change list.
658*7c478bd9Sstevel@tonic-gate 				 */
659*7c478bd9Sstevel@tonic-gate 				if (rc != 0) {
660*7c478bd9Sstevel@tonic-gate 					lp->f_flags |= F_CONFLICT;
661*7c478bd9Sstevel@tonic-gate 					lp->f_problem = gettext(PROB_link);
662*7c478bd9Sstevel@tonic-gate 					goto cant;
663*7c478bd9Sstevel@tonic-gate 				}
664*7c478bd9Sstevel@tonic-gate 			}
665*7c478bd9Sstevel@tonic-gate 
666*7c478bd9Sstevel@tonic-gate 			if (!opt_quiet) {
667*7c478bd9Sstevel@tonic-gate 				fprintf(stdout, "ln %s", noblanks(src));
668*7c478bd9Sstevel@tonic-gate 				fprintf(stdout, " %s\n", noblanks(dst));
669*7c478bd9Sstevel@tonic-gate 			}
670*7c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_link);
671*7c478bd9Sstevel@tonic-gate 
672*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
673*7c478bd9Sstevel@tonic-gate 			/* should we simulate a link failure	*/
674*7c478bd9Sstevel@tonic-gate 			if (errno = dbg_chk_error(dst, 'l'))
675*7c478bd9Sstevel@tonic-gate 				rc = -1;
676*7c478bd9Sstevel@tonic-gate 			else
677*7c478bd9Sstevel@tonic-gate #endif
678*7c478bd9Sstevel@tonic-gate 			rc = opt_notouch ? 0 : link(src, dst);
679*7c478bd9Sstevel@tonic-gate 
680*7c478bd9Sstevel@tonic-gate 			/*
681*7c478bd9Sstevel@tonic-gate 			 * if this is a link, there is no reason to worry
682*7c478bd9Sstevel@tonic-gate 			 * about ownership and modes, they are automatic
683*7c478bd9Sstevel@tonic-gate 			 */
684*7c478bd9Sstevel@tonic-gate 			do_chown = 0; do_chgrp = 0; do_chmod = 0; do_acls = 0;
685*7c478bd9Sstevel@tonic-gate 			if (rc == 0) {
686*7c478bd9Sstevel@tonic-gate 				dp->f_type = type;
687*7c478bd9Sstevel@tonic-gate 				dp->f_uid = uid;
688*7c478bd9Sstevel@tonic-gate 				dp->f_gid = gid;
689*7c478bd9Sstevel@tonic-gate 				dp->f_mode = mode;
690*7c478bd9Sstevel@tonic-gate 				break;
691*7c478bd9Sstevel@tonic-gate 			} else {
692*7c478bd9Sstevel@tonic-gate 				/*
693*7c478bd9Sstevel@tonic-gate 				 * if we failed to make a link, we want to
694*7c478bd9Sstevel@tonic-gate 				 * mark the linkee in conflict too, so that
695*7c478bd9Sstevel@tonic-gate 				 * his reference count remains the same in
696*7c478bd9Sstevel@tonic-gate 				 * the baseline, and he shows up on the change
697*7c478bd9Sstevel@tonic-gate 				 * list again next time.
698*7c478bd9Sstevel@tonic-gate 				 */
699*7c478bd9Sstevel@tonic-gate 				lp->f_flags |= F_CONFLICT;
700*7c478bd9Sstevel@tonic-gate 				lp->f_problem = errstr;
701*7c478bd9Sstevel@tonic-gate 				break;
702*7c478bd9Sstevel@tonic-gate 			}
703*7c478bd9Sstevel@tonic-gate 
704*7c478bd9Sstevel@tonic-gate 			/*
705*7c478bd9Sstevel@tonic-gate 			 * in some situation we haven't figured out yet
706*7c478bd9Sstevel@tonic-gate 			 * we might want to fall through and try a copy
707*7c478bd9Sstevel@tonic-gate 			 * if the link failed.
708*7c478bd9Sstevel@tonic-gate 			 */
709*7c478bd9Sstevel@tonic-gate 		}
710*7c478bd9Sstevel@tonic-gate 
711*7c478bd9Sstevel@tonic-gate 		/* we are going to resolve this by making a copy	*/
712*7c478bd9Sstevel@tonic-gate 		if (!opt_quiet) {
713*7c478bd9Sstevel@tonic-gate 			fprintf(stdout, "cp %s", noblanks(src));
714*7c478bd9Sstevel@tonic-gate 			fprintf(stdout, " %s\n", noblanks(dst));
715*7c478bd9Sstevel@tonic-gate 		}
716*7c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0 : copy(src, dst, mode);
717*7c478bd9Sstevel@tonic-gate 		if (rc != 0) {
718*7c478bd9Sstevel@tonic-gate 			errs |= rc;
719*7c478bd9Sstevel@tonic-gate 			if (copy_err_str)
720*7c478bd9Sstevel@tonic-gate 				errstr = copy_err_str;
721*7c478bd9Sstevel@tonic-gate 			else
722*7c478bd9Sstevel@tonic-gate 				errstr = gettext(PROB_copy);
723*7c478bd9Sstevel@tonic-gate 
724*7c478bd9Sstevel@tonic-gate 			/*
725*7c478bd9Sstevel@tonic-gate 			 * The new copy (if it exists at all) is a botch.
726*7c478bd9Sstevel@tonic-gate 			 * If this was a new create or a remove and copy
727*7c478bd9Sstevel@tonic-gate 			 * we should get rid of the botched copy so that
728*7c478bd9Sstevel@tonic-gate 			 * it doesn't show up as two versions next time.
729*7c478bd9Sstevel@tonic-gate 			 */
730*7c478bd9Sstevel@tonic-gate 			if (do_create)
731*7c478bd9Sstevel@tonic-gate 				unlink(dst);
732*7c478bd9Sstevel@tonic-gate 		} else if (dp->f_mode == 0) {
733*7c478bd9Sstevel@tonic-gate 			dp->f_type = S_IFREG;
734*7c478bd9Sstevel@tonic-gate 			dp->f_uid = my_uid;
735*7c478bd9Sstevel@tonic-gate 			dp->f_gid = my_gid;
736*7c478bd9Sstevel@tonic-gate 			dp->f_mode = mode;
737*7c478bd9Sstevel@tonic-gate 
738*7c478bd9Sstevel@tonic-gate 			/* FIX: inode number is still wrong	*/
739*7c478bd9Sstevel@tonic-gate 		}
740*7c478bd9Sstevel@tonic-gate 
741*7c478bd9Sstevel@tonic-gate 		/* for normal files we have an option to preserve mod time  */
742*7c478bd9Sstevel@tonic-gate 		if (rc == 0 && opt_notouch == 0 && opt_mtime) {
743*7c478bd9Sstevel@tonic-gate 			newtimes.actime = mtime;
744*7c478bd9Sstevel@tonic-gate 			newtimes.modtime = mtime;
745*7c478bd9Sstevel@tonic-gate 
746*7c478bd9Sstevel@tonic-gate 			/* ignore the error return on this one	*/
747*7c478bd9Sstevel@tonic-gate 			(void) utime(dst, &newtimes);
748*7c478bd9Sstevel@tonic-gate 		}
749*7c478bd9Sstevel@tonic-gate 		break;
750*7c478bd9Sstevel@tonic-gate 
751*7c478bd9Sstevel@tonic-gate 	    default:
752*7c478bd9Sstevel@tonic-gate 		errstr = gettext(PROB_deal);
753*7c478bd9Sstevel@tonic-gate 		rc = -1;
754*7c478bd9Sstevel@tonic-gate 	}
755*7c478bd9Sstevel@tonic-gate 
756*7c478bd9Sstevel@tonic-gate 	/*
757*7c478bd9Sstevel@tonic-gate 	 * if any of the file's attributes need attention, I should let
758*7c478bd9Sstevel@tonic-gate 	 * do_like take care of them, since it knows all rules for who
759*7c478bd9Sstevel@tonic-gate 	 * can and cannot make what types of changes.
760*7c478bd9Sstevel@tonic-gate 	 */
761*7c478bd9Sstevel@tonic-gate 	if (rc == 0 && (do_chmod || do_chown || do_chgrp || do_acls)) {
762*7c478bd9Sstevel@tonic-gate 		rc = do_like(fp, srcdst, FALSE);
763*7c478bd9Sstevel@tonic-gate 		errstr = fp->f_problem;
764*7c478bd9Sstevel@tonic-gate 		errs |= rc;
765*7c478bd9Sstevel@tonic-gate 	}
766*7c478bd9Sstevel@tonic-gate 
767*7c478bd9Sstevel@tonic-gate 	/*
768*7c478bd9Sstevel@tonic-gate 	 * finish off by re-stating the destination and using that to
769*7c478bd9Sstevel@tonic-gate 	 * update the baseline.  If we were completely successful in
770*7c478bd9Sstevel@tonic-gate 	 * our chowns/chmods, stating the destination will confirm it.
771*7c478bd9Sstevel@tonic-gate 	 * If we were unable to make all the necessary changes, stating
772*7c478bd9Sstevel@tonic-gate 	 * the destination will make the source appear to have changed,
773*7c478bd9Sstevel@tonic-gate 	 * so that the differences will continue to reappear as new
774*7c478bd9Sstevel@tonic-gate 	 * changes (inconsistancies).
775*7c478bd9Sstevel@tonic-gate 	 */
776*7c478bd9Sstevel@tonic-gate 	if (rc == 0)
777*7c478bd9Sstevel@tonic-gate 		if (!opt_notouch) {
778*7c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_restat);
779*7c478bd9Sstevel@tonic-gate 
780*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
781*7c478bd9Sstevel@tonic-gate 			/* should we simulate a restat failure	*/
782*7c478bd9Sstevel@tonic-gate 			if (errno = dbg_chk_error(dst, 'R'))
783*7c478bd9Sstevel@tonic-gate 				rc = -1;
784*7c478bd9Sstevel@tonic-gate 			else
785*7c478bd9Sstevel@tonic-gate #endif
786*7c478bd9Sstevel@tonic-gate 			rc = lstat(dst, &statb);
787*7c478bd9Sstevel@tonic-gate 
788*7c478bd9Sstevel@tonic-gate 			if (rc == 0) {
789*7c478bd9Sstevel@tonic-gate 				note_info(fp, &statb, srcdst);
790*7c478bd9Sstevel@tonic-gate 				link_update(fp, srcdst);
791*7c478bd9Sstevel@tonic-gate 				if (do_acls)
792*7c478bd9Sstevel@tonic-gate 					(void) get_acls(dst, dp);
793*7c478bd9Sstevel@tonic-gate 				update_info(fp, srcdst);
794*7c478bd9Sstevel@tonic-gate 			}
795*7c478bd9Sstevel@tonic-gate 		} else {
796*7c478bd9Sstevel@tonic-gate 			/*
797*7c478bd9Sstevel@tonic-gate 			 * BOGOSITY ALERT
798*7c478bd9Sstevel@tonic-gate 			 *	we are in notouch mode and haven't really
799*7c478bd9Sstevel@tonic-gate 			 *	done anything, but if we want link detection
800*7c478bd9Sstevel@tonic-gate 			 *	to work and be properly reflected in the
801*7c478bd9Sstevel@tonic-gate 			 *	what-I-would-do output for a case where
802*7c478bd9Sstevel@tonic-gate 			 *	multiple links are created to a new file,
803*7c478bd9Sstevel@tonic-gate 			 *	we have to make the new file appear to
804*7c478bd9Sstevel@tonic-gate 			 *	have been created.  Since we didn't create
805*7c478bd9Sstevel@tonic-gate 			 *	the new file we can't stat it, but if
806*7c478bd9Sstevel@tonic-gate 			 *	no file exists, we can't make a link to
807*7c478bd9Sstevel@tonic-gate 			 *	it, so we will pretend we created a file.
808*7c478bd9Sstevel@tonic-gate 			 */
809*7c478bd9Sstevel@tonic-gate 			if (dp->f_ino == 0 || dp->f_nlink == 0) {
810*7c478bd9Sstevel@tonic-gate 				dp->f_ino = sp->f_ino;
811*7c478bd9Sstevel@tonic-gate 				dp->f_nlink = 1;
812*7c478bd9Sstevel@tonic-gate 			}
813*7c478bd9Sstevel@tonic-gate 		}
814*7c478bd9Sstevel@tonic-gate 
815*7c478bd9Sstevel@tonic-gate cant:	if (rc != 0) {
816*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, gettext(ERR_cannot), errstr, dst);
817*7c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
818*7c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_CONFLICT;
819*7c478bd9Sstevel@tonic-gate 		fp->f_problem = errstr;
820*7c478bd9Sstevel@tonic-gate 		if (errs == 0)
821*7c478bd9Sstevel@tonic-gate 			errs = ERR_PERM;
822*7c478bd9Sstevel@tonic-gate 		errs |= ERR_UNRESOLVED;
823*7c478bd9Sstevel@tonic-gate 	} else {
824*7c478bd9Sstevel@tonic-gate 		/* update the statistics			*/
825*7c478bd9Sstevel@tonic-gate 		if (srcdst == OPT_SRC)
826*7c478bd9Sstevel@tonic-gate 			bp->b_src_copies++;
827*7c478bd9Sstevel@tonic-gate 		else
828*7c478bd9Sstevel@tonic-gate 			bp->b_dst_copies++;
829*7c478bd9Sstevel@tonic-gate 		errs |= ERR_RESOLVABLE;
830*7c478bd9Sstevel@tonic-gate 	}
831*7c478bd9Sstevel@tonic-gate 
832*7c478bd9Sstevel@tonic-gate 	return (errs);
833*7c478bd9Sstevel@tonic-gate }
834*7c478bd9Sstevel@tonic-gate 
835*7c478bd9Sstevel@tonic-gate /*
836*7c478bd9Sstevel@tonic-gate  * routine:
837*7c478bd9Sstevel@tonic-gate  *	do_remove
838*7c478bd9Sstevel@tonic-gate  *
839*7c478bd9Sstevel@tonic-gate  * purpose:
840*7c478bd9Sstevel@tonic-gate  *	to propagate a deletion
841*7c478bd9Sstevel@tonic-gate  *
842*7c478bd9Sstevel@tonic-gate  * parameters:
843*7c478bd9Sstevel@tonic-gate  *	file pointer
844*7c478bd9Sstevel@tonic-gate  *	src/dst indication for which side gets changed
845*7c478bd9Sstevel@tonic-gate  *
846*7c478bd9Sstevel@tonic-gate  * returns:
847*7c478bd9Sstevel@tonic-gate  *	error mask
848*7c478bd9Sstevel@tonic-gate  */
849*7c478bd9Sstevel@tonic-gate errmask_t
850*7c478bd9Sstevel@tonic-gate do_remove(struct file *fp, side_t srcdst)
851*7c478bd9Sstevel@tonic-gate {	char *name;
852*7c478bd9Sstevel@tonic-gate 	int rc;
853*7c478bd9Sstevel@tonic-gate 	struct base *bp = fp->f_base;
854*7c478bd9Sstevel@tonic-gate 	errmask_t errs = 0;
855*7c478bd9Sstevel@tonic-gate 	char *errstr = "???";
856*7c478bd9Sstevel@tonic-gate 
857*7c478bd9Sstevel@tonic-gate 	/* see if this is a forbidden propagation */
858*7c478bd9Sstevel@tonic-gate 	if (srcdst == opt_oneway) {
859*7c478bd9Sstevel@tonic-gate 		fp->f_problem = gettext(PROB_prohibited);
860*7c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_CONFLICT;
861*7c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
862*7c478bd9Sstevel@tonic-gate 		return (ERR_UNRESOLVED);
863*7c478bd9Sstevel@tonic-gate 	}
864*7c478bd9Sstevel@tonic-gate 
865*7c478bd9Sstevel@tonic-gate 	name = (srcdst == OPT_SRC) ? srcname : dstname;
866*7c478bd9Sstevel@tonic-gate 
867*7c478bd9Sstevel@tonic-gate 	if (fp->f_info[0].f_type == S_IFDIR) {
868*7c478bd9Sstevel@tonic-gate 		if (!opt_quiet)
869*7c478bd9Sstevel@tonic-gate 			fprintf(stdout, "rmdir %s\n", noblanks(name));
870*7c478bd9Sstevel@tonic-gate 
871*7c478bd9Sstevel@tonic-gate 		errstr = gettext(PROB_rmdir);
872*7c478bd9Sstevel@tonic-gate 
873*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
874*7c478bd9Sstevel@tonic-gate 		/* should we simulate a rmdir failure	*/
875*7c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(name, 'D'))
876*7c478bd9Sstevel@tonic-gate 			rc = -1;
877*7c478bd9Sstevel@tonic-gate 		else
878*7c478bd9Sstevel@tonic-gate #endif
879*7c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0 : rmdir(name);
880*7c478bd9Sstevel@tonic-gate 	} else {
881*7c478bd9Sstevel@tonic-gate 		if (!opt_quiet)
882*7c478bd9Sstevel@tonic-gate 			fprintf(stdout, "rm %s\n", noblanks(name));
883*7c478bd9Sstevel@tonic-gate 
884*7c478bd9Sstevel@tonic-gate 		errstr = gettext(PROB_unlink);
885*7c478bd9Sstevel@tonic-gate 
886*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
887*7c478bd9Sstevel@tonic-gate 		/* should we simulate an unlink failure	*/
888*7c478bd9Sstevel@tonic-gate 		if (errno = dbg_chk_error(name, 'u'))
889*7c478bd9Sstevel@tonic-gate 			rc = -1;
890*7c478bd9Sstevel@tonic-gate 		else
891*7c478bd9Sstevel@tonic-gate #endif
892*7c478bd9Sstevel@tonic-gate 		rc = opt_notouch ? 0 : unlink(name);
893*7c478bd9Sstevel@tonic-gate 	}
894*7c478bd9Sstevel@tonic-gate 
895*7c478bd9Sstevel@tonic-gate 	if (opt_debug & DBG_RECON)
896*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, "RECO: do_remove %s -> %d(%d)\n",
897*7c478bd9Sstevel@tonic-gate 			name, rc, errno);
898*7c478bd9Sstevel@tonic-gate 
899*7c478bd9Sstevel@tonic-gate 	if (rc == 0) {
900*7c478bd9Sstevel@tonic-gate 		/* tell any other hard links that one has gone away	*/
901*7c478bd9Sstevel@tonic-gate 		fp->f_info[srcdst].f_nlink--;
902*7c478bd9Sstevel@tonic-gate 		link_update(fp, srcdst);
903*7c478bd9Sstevel@tonic-gate 
904*7c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_REMOVE;
905*7c478bd9Sstevel@tonic-gate 		if (srcdst == OPT_SRC)
906*7c478bd9Sstevel@tonic-gate 			fp->f_base->b_src_deletes++;
907*7c478bd9Sstevel@tonic-gate 		else
908*7c478bd9Sstevel@tonic-gate 			fp->f_base->b_dst_deletes++;
909*7c478bd9Sstevel@tonic-gate 		errs |= ERR_RESOLVABLE;
910*7c478bd9Sstevel@tonic-gate 	} else {
911*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, gettext(ERR_cannot), errstr, name);
912*7c478bd9Sstevel@tonic-gate 		fp->f_problem = errstr;
913*7c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_CONFLICT;
914*7c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
915*7c478bd9Sstevel@tonic-gate 		errs |= ERR_PERM | ERR_UNRESOLVED;
916*7c478bd9Sstevel@tonic-gate 	}
917*7c478bd9Sstevel@tonic-gate 
918*7c478bd9Sstevel@tonic-gate 	return (errs);
919*7c478bd9Sstevel@tonic-gate }
920*7c478bd9Sstevel@tonic-gate 
921*7c478bd9Sstevel@tonic-gate /*
922*7c478bd9Sstevel@tonic-gate  * routine:
923*7c478bd9Sstevel@tonic-gate  *	do_rename
924*7c478bd9Sstevel@tonic-gate  *
925*7c478bd9Sstevel@tonic-gate  * purpose:
926*7c478bd9Sstevel@tonic-gate  *	to propagate a rename
927*7c478bd9Sstevel@tonic-gate  *
928*7c478bd9Sstevel@tonic-gate  * parameters:
929*7c478bd9Sstevel@tonic-gate  *	file pointer for the new name
930*7c478bd9Sstevel@tonic-gate  *	src/dst indication for which side gets changed
931*7c478bd9Sstevel@tonic-gate  *
932*7c478bd9Sstevel@tonic-gate  * returns:
933*7c478bd9Sstevel@tonic-gate  *	error mask
934*7c478bd9Sstevel@tonic-gate  */
935*7c478bd9Sstevel@tonic-gate errmask_t
936*7c478bd9Sstevel@tonic-gate do_rename(struct file *fp, side_t srcdst)
937*7c478bd9Sstevel@tonic-gate {	int rc;
938*7c478bd9Sstevel@tonic-gate 	struct file *pp = fp->f_previous;
939*7c478bd9Sstevel@tonic-gate 	struct base *bp = fp->f_base;
940*7c478bd9Sstevel@tonic-gate 	errmask_t errs = 0;
941*7c478bd9Sstevel@tonic-gate 	char *errstr = "???";
942*7c478bd9Sstevel@tonic-gate 	char *newname;
943*7c478bd9Sstevel@tonic-gate 	char *oldname;
944*7c478bd9Sstevel@tonic-gate 	struct stat statb;
945*7c478bd9Sstevel@tonic-gate 
946*7c478bd9Sstevel@tonic-gate 	/* see if this is a forbidden propagation */
947*7c478bd9Sstevel@tonic-gate 	if (srcdst == opt_oneway) {
948*7c478bd9Sstevel@tonic-gate 		fp->f_problem = gettext(PROB_prohibited);
949*7c478bd9Sstevel@tonic-gate 
950*7c478bd9Sstevel@tonic-gate 		/* if we can't resolve the TO, the FROM is also unresolved */
951*7c478bd9Sstevel@tonic-gate 		pp->f_problem = gettext(PROB_prohibited);
952*7c478bd9Sstevel@tonic-gate 		pp->f_flags |= F_CONFLICT;
953*7c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
954*7c478bd9Sstevel@tonic-gate 		return (ERR_UNRESOLVED);
955*7c478bd9Sstevel@tonic-gate 	}
956*7c478bd9Sstevel@tonic-gate 
957*7c478bd9Sstevel@tonic-gate 	newname = (srcdst == OPT_SRC) ? srcname : dstname;
958*7c478bd9Sstevel@tonic-gate 	oldname = full_name(pp, srcdst, OPT_BASE);
959*7c478bd9Sstevel@tonic-gate 
960*7c478bd9Sstevel@tonic-gate 	if (!opt_quiet)
961*7c478bd9Sstevel@tonic-gate 		fprintf(stdout, "%s %s %s\n",
962*7c478bd9Sstevel@tonic-gate 			(fp->f_info[0].f_type == S_IFDIR) ? "mvdir" : "mv",
963*7c478bd9Sstevel@tonic-gate 			noblanks(oldname), noblanks(newname));
964*7c478bd9Sstevel@tonic-gate 
965*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
966*7c478bd9Sstevel@tonic-gate 	/* should we simulate a rename failure	*/
967*7c478bd9Sstevel@tonic-gate 	if (errno = dbg_chk_error(oldname, 'm'))
968*7c478bd9Sstevel@tonic-gate 		rc = -1;
969*7c478bd9Sstevel@tonic-gate 	else
970*7c478bd9Sstevel@tonic-gate #endif
971*7c478bd9Sstevel@tonic-gate 	rc = opt_notouch ? 0 : rename(oldname, newname);
972*7c478bd9Sstevel@tonic-gate 
973*7c478bd9Sstevel@tonic-gate 	if (opt_debug & DBG_RECON)
974*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, "RECO: do_rename %s %s -> %d(%d)\n",
975*7c478bd9Sstevel@tonic-gate 			oldname, newname, rc, errno);
976*7c478bd9Sstevel@tonic-gate 
977*7c478bd9Sstevel@tonic-gate 	/* if we succeed, update the baseline			*/
978*7c478bd9Sstevel@tonic-gate 	if (rc == 0)
979*7c478bd9Sstevel@tonic-gate 		if (!opt_notouch) {
980*7c478bd9Sstevel@tonic-gate 			errstr = gettext(PROB_restat);
981*7c478bd9Sstevel@tonic-gate 
982*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
983*7c478bd9Sstevel@tonic-gate 			/* should we simulate a restat failure	*/
984*7c478bd9Sstevel@tonic-gate 			if (errno = dbg_chk_error(newname, 'S'))
985*7c478bd9Sstevel@tonic-gate 				rc = -1;
986*7c478bd9Sstevel@tonic-gate 			else
987*7c478bd9Sstevel@tonic-gate #endif
988*7c478bd9Sstevel@tonic-gate 			rc = lstat(newname, &statb);
989*7c478bd9Sstevel@tonic-gate 
990*7c478bd9Sstevel@tonic-gate 			if (rc == 0) {
991*7c478bd9Sstevel@tonic-gate 				note_info(fp, &statb, srcdst);
992*7c478bd9Sstevel@tonic-gate 				link_update(fp, srcdst);
993*7c478bd9Sstevel@tonic-gate 				update_info(fp, srcdst);
994*7c478bd9Sstevel@tonic-gate 			}
995*7c478bd9Sstevel@tonic-gate 		} else {
996*7c478bd9Sstevel@tonic-gate 			/*
997*7c478bd9Sstevel@tonic-gate 			 * BOGOSITY ALERT
998*7c478bd9Sstevel@tonic-gate 			 * in order for link tests to work in notouch
999*7c478bd9Sstevel@tonic-gate 			 * mode we have to dummy up some updated status
1000*7c478bd9Sstevel@tonic-gate 			 */
1001*7c478bd9Sstevel@tonic-gate 			fp->f_info[srcdst].f_ino = pp->f_info[srcdst].f_ino;
1002*7c478bd9Sstevel@tonic-gate 			fp->f_info[srcdst].f_nlink = pp->f_info[srcdst].f_nlink;
1003*7c478bd9Sstevel@tonic-gate 			fp->f_info[srcdst].f_type = pp->f_info[srcdst].f_type;
1004*7c478bd9Sstevel@tonic-gate 			fp->f_info[srcdst].f_size = pp->f_info[srcdst].f_size;
1005*7c478bd9Sstevel@tonic-gate 			fp->f_info[srcdst].f_mode = pp->f_info[srcdst].f_mode;
1006*7c478bd9Sstevel@tonic-gate 			fp->f_info[srcdst].f_uid = pp->f_info[srcdst].f_uid;
1007*7c478bd9Sstevel@tonic-gate 			fp->f_info[srcdst].f_gid = pp->f_info[srcdst].f_gid;
1008*7c478bd9Sstevel@tonic-gate 			update_info(fp, srcdst);
1009*7c478bd9Sstevel@tonic-gate 		}
1010*7c478bd9Sstevel@tonic-gate 	else
1011*7c478bd9Sstevel@tonic-gate 		errstr = gettext(PROB_rename2);
1012*7c478bd9Sstevel@tonic-gate 
1013*7c478bd9Sstevel@tonic-gate 	if (rc == 0) {
1014*7c478bd9Sstevel@tonic-gate 		pp->f_flags |= F_REMOVE;
1015*7c478bd9Sstevel@tonic-gate 
1016*7c478bd9Sstevel@tonic-gate 		if (srcdst == OPT_SRC) {
1017*7c478bd9Sstevel@tonic-gate 			bp->b_src_copies++;
1018*7c478bd9Sstevel@tonic-gate 			bp->b_src_deletes++;
1019*7c478bd9Sstevel@tonic-gate 		} else {
1020*7c478bd9Sstevel@tonic-gate 			bp->b_dst_copies++;
1021*7c478bd9Sstevel@tonic-gate 			bp->b_dst_deletes++;
1022*7c478bd9Sstevel@tonic-gate 		}
1023*7c478bd9Sstevel@tonic-gate 		errs |= ERR_RESOLVABLE;
1024*7c478bd9Sstevel@tonic-gate 	} else {
1025*7c478bd9Sstevel@tonic-gate 		fprintf(stderr, gettext(ERR_cannot), errstr, oldname);
1026*7c478bd9Sstevel@tonic-gate 
1027*7c478bd9Sstevel@tonic-gate 		bp->b_unresolved++;
1028*7c478bd9Sstevel@tonic-gate 		fp->f_flags |= F_CONFLICT;
1029*7c478bd9Sstevel@tonic-gate 		pp->f_flags |= F_CONFLICT;
1030*7c478bd9Sstevel@tonic-gate 
1031*7c478bd9Sstevel@tonic-gate 		fp->f_problem = errstr;
1032*7c478bd9Sstevel@tonic-gate 		pp->f_problem = gettext(PROB_rename);
1033*7c478bd9Sstevel@tonic-gate 
1034*7c478bd9Sstevel@tonic-gate 		errs |= ERR_PERM | ERR_UNRESOLVED;
1035*7c478bd9Sstevel@tonic-gate 	}
1036*7c478bd9Sstevel@tonic-gate 
1037*7c478bd9Sstevel@tonic-gate 	return (errs);
1038*7c478bd9Sstevel@tonic-gate }
1039*7c478bd9Sstevel@tonic-gate 
1040*7c478bd9Sstevel@tonic-gate /*
1041*7c478bd9Sstevel@tonic-gate  * routine:
1042*7c478bd9Sstevel@tonic-gate  *	copy
1043*7c478bd9Sstevel@tonic-gate  *
1044*7c478bd9Sstevel@tonic-gate  * purpose:
1045*7c478bd9Sstevel@tonic-gate  *	to copy one file to another
1046*7c478bd9Sstevel@tonic-gate  *
1047*7c478bd9Sstevel@tonic-gate  * parameters:
1048*7c478bd9Sstevel@tonic-gate  *	source file name
1049*7c478bd9Sstevel@tonic-gate  *	destination file name
1050*7c478bd9Sstevel@tonic-gate  *	desired modes
1051*7c478bd9Sstevel@tonic-gate  *
1052*7c478bd9Sstevel@tonic-gate  * returns:
1053*7c478bd9Sstevel@tonic-gate  *	0	OK
1054*7c478bd9Sstevel@tonic-gate  *	else	error mask, and a setting of copy_err_str
1055*7c478bd9Sstevel@tonic-gate  *
1056*7c478bd9Sstevel@tonic-gate  * notes:
1057*7c478bd9Sstevel@tonic-gate  *	We try to preserve the holes in sparse files, by skipping over
1058*7c478bd9Sstevel@tonic-gate  *	any holes that are at least MIN_HOLE bytes long.  There are
1059*7c478bd9Sstevel@tonic-gate  *	pathological cases where the hole detection test could become
1060*7c478bd9Sstevel@tonic-gate  *	expensive, but for most blocks of most files we will fall out
1061*7c478bd9Sstevel@tonic-gate  *	of the zero confirming loop in the first couple of bytes.
1062*7c478bd9Sstevel@tonic-gate  */
1063*7c478bd9Sstevel@tonic-gate static errmask_t
1064*7c478bd9Sstevel@tonic-gate copy(char *src, char *dst, int mode)
1065*7c478bd9Sstevel@tonic-gate {	int ifd, ofd, count, ret;
1066*7c478bd9Sstevel@tonic-gate 	long *p, *e;
1067*7c478bd9Sstevel@tonic-gate 	long long length;		/* total size of file	*/
1068*7c478bd9Sstevel@tonic-gate 	errmask_t errs = 0;
1069*7c478bd9Sstevel@tonic-gate 	int bsize;			/* block-size for file	*/
1070*7c478bd9Sstevel@tonic-gate 	bool_t sparse;			/* file may be sparse	*/
1071*7c478bd9Sstevel@tonic-gate 	bool_t was_hole = FALSE;		/* file ends with hole	*/
1072*7c478bd9Sstevel@tonic-gate 	long inbuf[ COPY_BSIZE/4 ];	/* long to speed checks	*/
1073*7c478bd9Sstevel@tonic-gate 	struct stat statbuf;		/* info on source file	*/
1074*7c478bd9Sstevel@tonic-gate 	struct statvfs statvsbuf;	/* info on target fs	*/
1075*7c478bd9Sstevel@tonic-gate 
1076*7c478bd9Sstevel@tonic-gate 	copy_err_str = 0;
1077*7c478bd9Sstevel@tonic-gate 
1078*7c478bd9Sstevel@tonic-gate 	/* open the input file			*/
1079*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
1080*7c478bd9Sstevel@tonic-gate 	if (opt_errors && dbg_chk_error(src, 'o'))
1081*7c478bd9Sstevel@tonic-gate 		ifd = -1;
1082*7c478bd9Sstevel@tonic-gate 	else
1083*7c478bd9Sstevel@tonic-gate #endif
1084*7c478bd9Sstevel@tonic-gate 	ifd = open(src, O_RDONLY);
1085*7c478bd9Sstevel@tonic-gate 
1086*7c478bd9Sstevel@tonic-gate 	if (ifd < 0) {
1087*7c478bd9Sstevel@tonic-gate 		copy_err_str = gettext(PROB_copyin);
1088*7c478bd9Sstevel@tonic-gate 		return (ERR_PERM);
1089*7c478bd9Sstevel@tonic-gate 	}
1090*7c478bd9Sstevel@tonic-gate 
1091*7c478bd9Sstevel@tonic-gate 	/*
1092*7c478bd9Sstevel@tonic-gate 	 * if we suspect a file may be sparse, we must process it
1093*7c478bd9Sstevel@tonic-gate 	 * a little more carefully, looking for holes and skipping
1094*7c478bd9Sstevel@tonic-gate 	 * over them in the output.  If a file is not sparse, we
1095*7c478bd9Sstevel@tonic-gate 	 * can move through it at greater speed.
1096*7c478bd9Sstevel@tonic-gate 	 */
1097*7c478bd9Sstevel@tonic-gate 	bsize = checksparse(ifd);
1098*7c478bd9Sstevel@tonic-gate 	if (bsize > 0 && bsize <= COPY_BSIZE)
1099*7c478bd9Sstevel@tonic-gate 		sparse = TRUE;
1100*7c478bd9Sstevel@tonic-gate 	else {
1101*7c478bd9Sstevel@tonic-gate 		sparse = FALSE;
1102*7c478bd9Sstevel@tonic-gate 		bsize = COPY_BSIZE;
1103*7c478bd9Sstevel@tonic-gate 	}
1104*7c478bd9Sstevel@tonic-gate 
1105*7c478bd9Sstevel@tonic-gate 	/*
1106*7c478bd9Sstevel@tonic-gate 	 * if the target file already exists and we overwrite it without
1107*7c478bd9Sstevel@tonic-gate 	 * first ascertaining that there is enough room, we could wind
1108*7c478bd9Sstevel@tonic-gate 	 * up actually losing data.  Try to determine how much space is
1109*7c478bd9Sstevel@tonic-gate 	 * available on the target file system, and if that is not enough
1110*7c478bd9Sstevel@tonic-gate 	 * for the source file, fail without even trying.  If, however,
1111*7c478bd9Sstevel@tonic-gate 	 * the target file does not already exist, we have nothing to
1112*7c478bd9Sstevel@tonic-gate 	 * lose by just doing the copy without checking the space.
1113*7c478bd9Sstevel@tonic-gate 	 */
1114*7c478bd9Sstevel@tonic-gate 	ret = statvfs(dst, &statvsbuf);
1115*7c478bd9Sstevel@tonic-gate 	if (ret == 0 && statvsbuf.f_frsize != 0) {
1116*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
1117*7c478bd9Sstevel@tonic-gate 		/* should we simulate an out-of-space situation	*/
1118*7c478bd9Sstevel@tonic-gate 		if ((length = dbg_chk_error(dst, 'Z')) == 0)
1119*7c478bd9Sstevel@tonic-gate #endif
1120*7c478bd9Sstevel@tonic-gate 		length = statvsbuf.f_bavail * statvsbuf.f_frsize;
1121*7c478bd9Sstevel@tonic-gate 
1122*7c478bd9Sstevel@tonic-gate 		ret = fstat(ifd, &statbuf);
1123*7c478bd9Sstevel@tonic-gate 		if (ret == 0) {
1124*7c478bd9Sstevel@tonic-gate 			length /= 512;		/* st_blocks in 512s	*/
1125*7c478bd9Sstevel@tonic-gate 			if (length < statbuf.st_blocks) {
1126*7c478bd9Sstevel@tonic-gate 				copy_err_str = gettext(PROB_space);
1127*7c478bd9Sstevel@tonic-gate 				close(ifd);
1128*7c478bd9Sstevel@tonic-gate 				return (ERR_FILES);
1129*7c478bd9Sstevel@tonic-gate 			}
1130*7c478bd9Sstevel@tonic-gate 		} else {
1131*7c478bd9Sstevel@tonic-gate 			copy_err_str = gettext(PROB_restat);
1132*7c478bd9Sstevel@tonic-gate 			close(ifd);
1133*7c478bd9Sstevel@tonic-gate 			return (ERR_FILES);
1134*7c478bd9Sstevel@tonic-gate 		}
1135*7c478bd9Sstevel@tonic-gate 	}
1136*7c478bd9Sstevel@tonic-gate 
1137*7c478bd9Sstevel@tonic-gate 	/* create the output file		*/
1138*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
1139*7c478bd9Sstevel@tonic-gate 	if (opt_errors && dbg_chk_error(dst, 'c'))
1140*7c478bd9Sstevel@tonic-gate 		ofd = -1;
1141*7c478bd9Sstevel@tonic-gate 	else
1142*7c478bd9Sstevel@tonic-gate #endif
1143*7c478bd9Sstevel@tonic-gate 	ofd = creat(dst, mode);
1144*7c478bd9Sstevel@tonic-gate 
1145*7c478bd9Sstevel@tonic-gate 	if (ofd < 0) {
1146*7c478bd9Sstevel@tonic-gate 		close(ifd);
1147*7c478bd9Sstevel@tonic-gate 		copy_err_str = gettext(PROB_copyout);
1148*7c478bd9Sstevel@tonic-gate 		return (ERR_PERM);
1149*7c478bd9Sstevel@tonic-gate 	}
1150*7c478bd9Sstevel@tonic-gate 
1151*7c478bd9Sstevel@tonic-gate 	/* copy the data from the input file to the output file	*/
1152*7c478bd9Sstevel@tonic-gate 	for (;;) {
1153*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
1154*7c478bd9Sstevel@tonic-gate 		if (opt_errors && dbg_chk_error(dst, 'r'))
1155*7c478bd9Sstevel@tonic-gate 			count = -1;
1156*7c478bd9Sstevel@tonic-gate 		else
1157*7c478bd9Sstevel@tonic-gate #endif
1158*7c478bd9Sstevel@tonic-gate 		count = read(ifd, (char *) inbuf, bsize);
1159*7c478bd9Sstevel@tonic-gate 		if (count <= 0)
1160*7c478bd9Sstevel@tonic-gate 			break;
1161*7c478bd9Sstevel@tonic-gate 
1162*7c478bd9Sstevel@tonic-gate 		/*
1163*7c478bd9Sstevel@tonic-gate 		 * if the file might be sparse and we got an entire block,
1164*7c478bd9Sstevel@tonic-gate 		 * we should see if the block is all zeros
1165*7c478bd9Sstevel@tonic-gate 		 */
1166*7c478bd9Sstevel@tonic-gate 		if (sparse && count == bsize) {
1167*7c478bd9Sstevel@tonic-gate 			p = inbuf; e = &inbuf[count/4];
1168*7c478bd9Sstevel@tonic-gate 			while (p < e && *p == 0)
1169*7c478bd9Sstevel@tonic-gate 				p++;
1170*7c478bd9Sstevel@tonic-gate 			if (p == e) {
1171*7c478bd9Sstevel@tonic-gate 				(void) lseek(ofd, (off_t) count, SEEK_CUR);
1172*7c478bd9Sstevel@tonic-gate 				was_hole = TRUE;
1173*7c478bd9Sstevel@tonic-gate 				continue;
1174*7c478bd9Sstevel@tonic-gate 			}
1175*7c478bd9Sstevel@tonic-gate 		}
1176*7c478bd9Sstevel@tonic-gate 		was_hole = FALSE;
1177*7c478bd9Sstevel@tonic-gate 
1178*7c478bd9Sstevel@tonic-gate #ifdef	DBG_ERRORS
1179*7c478bd9Sstevel@tonic-gate 		if (opt_errors && dbg_chk_error(dst, 'w'))
1180*7c478bd9Sstevel@tonic-gate 			ret = -1;
1181*7c478bd9Sstevel@tonic-gate 		else
1182*7c478bd9Sstevel@tonic-gate #endif
1183*7c478bd9Sstevel@tonic-gate 		ret = write(ofd, (char *) inbuf, count);
1184*7c478bd9Sstevel@tonic-gate 
1185*7c478bd9Sstevel@tonic-gate 		if (ret != count) {
1186*7c478bd9Sstevel@tonic-gate 			errs = ERR_FILES;
1187*7c478bd9Sstevel@tonic-gate 			copy_err_str = gettext(PROB_write);
1188*7c478bd9Sstevel@tonic-gate 			break;
1189*7c478bd9Sstevel@tonic-gate 		}
1190*7c478bd9Sstevel@tonic-gate 	}
1191*7c478bd9Sstevel@tonic-gate 
1192*7c478bd9Sstevel@tonic-gate 	if (count < 0) {
1193*7c478bd9Sstevel@tonic-gate 		copy_err_str = gettext(PROB_read);
1194*7c478bd9Sstevel@tonic-gate 		errs = ERR_FILES;
1195*7c478bd9Sstevel@tonic-gate 	} else if (was_hole) {
1196*7c478bd9Sstevel@tonic-gate 		/*
1197*7c478bd9Sstevel@tonic-gate 		 * if we skipped the last write because of a hole, we
1198*7c478bd9Sstevel@tonic-gate 		 * need to make sure that we write a single byte of null
1199*7c478bd9Sstevel@tonic-gate 		 * at the end of the file to update the file length.
1200*7c478bd9Sstevel@tonic-gate 		 */
1201*7c478bd9Sstevel@tonic-gate 		(void) lseek(ofd, (off_t)-1, SEEK_CUR);
1202*7c478bd9Sstevel@tonic-gate 		(void) write(ofd, "", 1);
1203*7c478bd9Sstevel@tonic-gate 	}
1204*7c478bd9Sstevel@tonic-gate 
1205*7c478bd9Sstevel@tonic-gate 	/*
1206*7c478bd9Sstevel@tonic-gate 	 * if the output file was botched, free up its space
1207*7c478bd9Sstevel@tonic-gate 	 */
1208*7c478bd9Sstevel@tonic-gate 	if (errs)
1209*7c478bd9Sstevel@tonic-gate 		ftruncate(ofd, (off_t) 0);
1210*7c478bd9Sstevel@tonic-gate 
1211*7c478bd9Sstevel@tonic-gate 	close(ifd);
1212*7c478bd9Sstevel@tonic-gate 	close(ofd);
1213*7c478bd9Sstevel@tonic-gate 	return (errs);
1214*7c478bd9Sstevel@tonic-gate }
1215*7c478bd9Sstevel@tonic-gate 
1216*7c478bd9Sstevel@tonic-gate /*
1217*7c478bd9Sstevel@tonic-gate  * routine:
1218*7c478bd9Sstevel@tonic-gate  *	checksparse
1219*7c478bd9Sstevel@tonic-gate  *
1220*7c478bd9Sstevel@tonic-gate  * purpose:
1221*7c478bd9Sstevel@tonic-gate  *	to determine whether or not a file might be sparse, and if
1222*7c478bd9Sstevel@tonic-gate  *	it is sparse, what the granularity of the holes is likely
1223*7c478bd9Sstevel@tonic-gate  *	to be.
1224*7c478bd9Sstevel@tonic-gate  *
1225*7c478bd9Sstevel@tonic-gate  * parameters:
1226*7c478bd9Sstevel@tonic-gate  *	file descriptor for file in question
1227*7c478bd9Sstevel@tonic-gate  *
1228*7c478bd9Sstevel@tonic-gate  * returns:
1229*7c478bd9Sstevel@tonic-gate  *	0	file does not appear to be sparse
1230*7c478bd9Sstevel@tonic-gate  *	else	block size for this file
1231*7c478bd9Sstevel@tonic-gate  */
1232*7c478bd9Sstevel@tonic-gate static int
1233*7c478bd9Sstevel@tonic-gate checksparse(int fd)
1234*7c478bd9Sstevel@tonic-gate {
1235*7c478bd9Sstevel@tonic-gate 	struct stat statb;
1236*7c478bd9Sstevel@tonic-gate 
1237*7c478bd9Sstevel@tonic-gate 	/*
1238*7c478bd9Sstevel@tonic-gate 	 * unable to stat the file is very strange (since we got it
1239*7c478bd9Sstevel@tonic-gate 	 * open) but it probably isn't worth causing a fuss over.
1240*7c478bd9Sstevel@tonic-gate 	 * Return the conservative answer
1241*7c478bd9Sstevel@tonic-gate 	 */
1242*7c478bd9Sstevel@tonic-gate 	if (fstat(fd, &statb) < 0)
1243*7c478bd9Sstevel@tonic-gate 		return (MIN_HOLE);
1244*7c478bd9Sstevel@tonic-gate 
1245*7c478bd9Sstevel@tonic-gate 	/*
1246*7c478bd9Sstevel@tonic-gate 	 * if the file doesn't have enough blocks to account for
1247*7c478bd9Sstevel@tonic-gate 	 * all of its bytes, there is a reasonable chance that it
1248*7c478bd9Sstevel@tonic-gate 	 * is sparse.  This test is not perfect, in that it will
1249*7c478bd9Sstevel@tonic-gate 	 * fail to find holes in cases where the holes aren't
1250*7c478bd9Sstevel@tonic-gate 	 * numerous enough to componsent for the indirect blocks
1251*7c478bd9Sstevel@tonic-gate 	 * ... but losing those few holes is not going to be a
1252*7c478bd9Sstevel@tonic-gate 	 * big deal.
1253*7c478bd9Sstevel@tonic-gate 	 */
1254*7c478bd9Sstevel@tonic-gate 	if (statb.st_size > 512 * statb.st_blocks)
1255*7c478bd9Sstevel@tonic-gate 		return (statb.st_blksize);
1256*7c478bd9Sstevel@tonic-gate 	else
1257*7c478bd9Sstevel@tonic-gate 		return (0);
1258*7c478bd9Sstevel@tonic-gate }
1259