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