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