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 * recon.c 27*0Sstevel@tonic-gate * 28*0Sstevel@tonic-gate * purpose: 29*0Sstevel@tonic-gate * process the reconciliation list, figure out exactly what the 30*0Sstevel@tonic-gate * changes were, and what we should do about them. 31*0Sstevel@tonic-gate * 32*0Sstevel@tonic-gate * contents: 33*0Sstevel@tonic-gate * reconcile ... (top level) process the reconciliation list 34*0Sstevel@tonic-gate * samedata .... (static) do two files have the same contents 35*0Sstevel@tonic-gate * samestuff ... (static) do two files have the same ownership/protection 36*0Sstevel@tonic-gate * samecompare . (static) actually read and compare the contents 37*0Sstevel@tonic-gate * samelink .... (static) do two symlinks have the same contents 38*0Sstevel@tonic-gate * truncated ... (static) was one of the two copies truncted 39*0Sstevel@tonic-gate * older ....... (static) which copy is older 40*0Sstevel@tonic-gate * newer ....... (static) which copy is newer 41*0Sstevel@tonic-gate * full_name ... generate a full path name for a file 42*0Sstevel@tonic-gate * 43*0Sstevel@tonic-gate * notes: 44*0Sstevel@tonic-gate * If you only study one routine in this whole program, reconcile 45*0Sstevel@tonic-gate * is that routine. Everything else is just book keeping. 46*0Sstevel@tonic-gate * 47*0Sstevel@tonic-gate * things were put onto the reconciliation list because analyze 48*0Sstevel@tonic-gate * thought that they might have changed ... but up until now 49*0Sstevel@tonic-gate * nobody has figured out what the changes really were, or even 50*0Sstevel@tonic-gate * if there really were any changes. 51*0Sstevel@tonic-gate * 52*0Sstevel@tonic-gate * queue_file has ordered the reconciliation list with directory 53*0Sstevel@tonic-gate * creations first (depth ordered) and deletions last (inversely 54*0Sstevel@tonic-gate * depth ordered). all other changes have been ordered by mod time. 55*0Sstevel@tonic-gate */ 56*0Sstevel@tonic-gate #ident "%W% %E% SMI" 57*0Sstevel@tonic-gate 58*0Sstevel@tonic-gate #include <stdio.h> 59*0Sstevel@tonic-gate #include <unistd.h> 60*0Sstevel@tonic-gate #include <stdlib.h> 61*0Sstevel@tonic-gate #include <string.h> 62*0Sstevel@tonic-gate #include <fcntl.h> 63*0Sstevel@tonic-gate 64*0Sstevel@tonic-gate #include "filesync.h" 65*0Sstevel@tonic-gate #include "database.h" 66*0Sstevel@tonic-gate #include "messages.h" 67*0Sstevel@tonic-gate #include "debug.h" 68*0Sstevel@tonic-gate 69*0Sstevel@tonic-gate /* 70*0Sstevel@tonic-gate * local routines to figure out how the files really differ 71*0Sstevel@tonic-gate */ 72*0Sstevel@tonic-gate static bool_t samedata(struct file *); 73*0Sstevel@tonic-gate static bool_t samestuff(struct file *); 74*0Sstevel@tonic-gate static bool_t samecompare(struct file *); 75*0Sstevel@tonic-gate static bool_t truncated(struct file *); 76*0Sstevel@tonic-gate static bool_t samelink(); 77*0Sstevel@tonic-gate static side_t newer(struct file *); 78*0Sstevel@tonic-gate static side_t older(struct file *); 79*0Sstevel@tonic-gate 80*0Sstevel@tonic-gate /* 81*0Sstevel@tonic-gate * globals 82*0Sstevel@tonic-gate */ 83*0Sstevel@tonic-gate char *srcname; /* file we are emulating */ 84*0Sstevel@tonic-gate char *dstname; /* file we are updating */ 85*0Sstevel@tonic-gate 86*0Sstevel@tonic-gate /* 87*0Sstevel@tonic-gate * routine: 88*0Sstevel@tonic-gate * reconcile 89*0Sstevel@tonic-gate * 90*0Sstevel@tonic-gate * purpose: 91*0Sstevel@tonic-gate * to perform the reconciliation action associated with a file 92*0Sstevel@tonic-gate * 93*0Sstevel@tonic-gate * parameters: 94*0Sstevel@tonic-gate * file pointer 95*0Sstevel@tonic-gate * 96*0Sstevel@tonic-gate * returns: 97*0Sstevel@tonic-gate * built up error mask 98*0Sstevel@tonic-gate * updated statistics 99*0Sstevel@tonic-gate * 100*0Sstevel@tonic-gate * notes: 101*0Sstevel@tonic-gate * The switch statement handles the obvious stuff. 102*0Sstevel@tonic-gate * The TRUE side of the samedata test handles minor differences. 103*0Sstevel@tonic-gate * The interesting stuff is in the FALSE side of the samedata test. 104*0Sstevel@tonic-gate * 105*0Sstevel@tonic-gate * The desparation heuristics (in the diffmask&CONTENTS test) are 106*0Sstevel@tonic-gate * not rigorously correct ... but they always try do the right thing 107*0Sstevel@tonic-gate * with data, and only lose mode/ownership changes in relatively 108*0Sstevel@tonic-gate * pathological cases. But I claim that the benefits outweigh the 109*0Sstevel@tonic-gate * risks, and most users will be pleased with the resulting decisions. 110*0Sstevel@tonic-gate * 111*0Sstevel@tonic-gate * Another trick is in the deletion cases of the switch. We 112*0Sstevel@tonic-gate * normally won't allow an unlink that conflicts with data 113*0Sstevel@tonic-gate * changes. If there are multiple links to the file, however, 114*0Sstevel@tonic-gate * we can make the changes and do the deletion. 115*0Sstevel@tonic-gate * 116*0Sstevel@tonic-gate * The action routines do_{remove,rename,like,copy} handle all 117*0Sstevel@tonic-gate * of their own statistics and status updating. This routine 118*0Sstevel@tonic-gate * only has to handle its own reconciliation failures (when we 119*0Sstevel@tonic-gate * can't decide what to do). 120*0Sstevel@tonic-gate */ 121*0Sstevel@tonic-gate errmask_t 122*0Sstevel@tonic-gate reconcile(struct file *fp) 123*0Sstevel@tonic-gate { errmask_t errs = 0; 124*0Sstevel@tonic-gate diffmask_t diffmask; 125*0Sstevel@tonic-gate 126*0Sstevel@tonic-gate if (opt_debug & DBG_RECON) 127*0Sstevel@tonic-gate fprintf(stderr, "RECO: %s flgs=%s, mtime=%08lx.%08lx\n", 128*0Sstevel@tonic-gate fp->f_fullname, 129*0Sstevel@tonic-gate showflags(fileflags, fp->f_flags), 130*0Sstevel@tonic-gate fp->f_modtime, fp->f_modns); 131*0Sstevel@tonic-gate 132*0Sstevel@tonic-gate /* 133*0Sstevel@tonic-gate * form the fully qualified names for both files 134*0Sstevel@tonic-gate */ 135*0Sstevel@tonic-gate srcname = full_name(fp, OPT_SRC, OPT_SRC); 136*0Sstevel@tonic-gate dstname = full_name(fp, OPT_DST, OPT_DST); 137*0Sstevel@tonic-gate 138*0Sstevel@tonic-gate /* 139*0Sstevel@tonic-gate * because they are so expensive to read and so troublesome 140*0Sstevel@tonic-gate * to set, we try to put off reading ACLs as long as possible. 141*0Sstevel@tonic-gate * If we haven't read them yet, we must read them now (so that 142*0Sstevel@tonic-gate * samestuff can compare them). 143*0Sstevel@tonic-gate */ 144*0Sstevel@tonic-gate if (opt_acls == 0 && fp->f_info[ OPT_BASE ].f_numacls == 0) { 145*0Sstevel@tonic-gate if (get_acls(srcname, &fp->f_info[ OPT_SRC ])) 146*0Sstevel@tonic-gate fp->f_srcdiffs |= D_FACLS; 147*0Sstevel@tonic-gate if (get_acls(dstname, &fp->f_info[ OPT_DST ])) 148*0Sstevel@tonic-gate fp->f_dstdiffs |= D_FACLS; 149*0Sstevel@tonic-gate } 150*0Sstevel@tonic-gate 151*0Sstevel@tonic-gate /* 152*0Sstevel@tonic-gate * If a rename has been detected, we don't have to figure 153*0Sstevel@tonic-gate * it out, since both the rename-to and rename-from files 154*0Sstevel@tonic-gate * have already been designated. When we encounter a rename-to 155*0Sstevel@tonic-gate * we should carry it out. When we encounter a rename-from 156*0Sstevel@tonic-gate * we can ignore it, since it should be dealt with as a side 157*0Sstevel@tonic-gate * effect of processing the rename-to. 158*0Sstevel@tonic-gate */ 159*0Sstevel@tonic-gate if ((fp->f_srcdiffs|fp->f_dstdiffs) & D_RENAME_FROM) 160*0Sstevel@tonic-gate return (0); 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gate if ((fp->f_srcdiffs|fp->f_dstdiffs) & D_RENAME_TO) { 163*0Sstevel@tonic-gate 164*0Sstevel@tonic-gate if (opt_verbose) 165*0Sstevel@tonic-gate fprintf(stdout, gettext(V_renamed), 166*0Sstevel@tonic-gate fp->f_previous->f_fullname, fp->f_name); 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate if (fp->f_srcdiffs & D_RENAME_TO) { 169*0Sstevel@tonic-gate errs = do_rename(fp, OPT_DST); 170*0Sstevel@tonic-gate fp->f_srcdiffs &= D_MTIME | D_SIZE; 171*0Sstevel@tonic-gate } else if (fp->f_dstdiffs & D_RENAME_TO) { 172*0Sstevel@tonic-gate errs = do_rename(fp, OPT_SRC); 173*0Sstevel@tonic-gate fp->f_dstdiffs &= D_MTIME | D_SIZE; 174*0Sstevel@tonic-gate } 175*0Sstevel@tonic-gate 176*0Sstevel@tonic-gate if (errs != ERR_RESOLVABLE) 177*0Sstevel@tonic-gate goto done; 178*0Sstevel@tonic-gate 179*0Sstevel@tonic-gate /* 180*0Sstevel@tonic-gate * if any differences remain, then we may be dealing 181*0Sstevel@tonic-gate * with contents changes in addition to a rename 182*0Sstevel@tonic-gate */ 183*0Sstevel@tonic-gate if ((fp->f_srcdiffs | fp->f_dstdiffs) == 0) 184*0Sstevel@tonic-gate goto done; 185*0Sstevel@tonic-gate 186*0Sstevel@tonic-gate /* 187*0Sstevel@tonic-gate * fall through to reconcile the data changes 188*0Sstevel@tonic-gate */ 189*0Sstevel@tonic-gate } 190*0Sstevel@tonic-gate 191*0Sstevel@tonic-gate /* 192*0Sstevel@tonic-gate * pull of the easy cases (non-conflict creations & deletions) 193*0Sstevel@tonic-gate */ 194*0Sstevel@tonic-gate switch (fp->f_flags & (F_WHEREFOUND)) { 195*0Sstevel@tonic-gate case F_IN_BASELINE: /* only exists in baseline */ 196*0Sstevel@tonic-gate case 0: /* only exists in rules */ 197*0Sstevel@tonic-gate if (opt_verbose) 198*0Sstevel@tonic-gate fprintf(stdout, gettext(V_nomore), 199*0Sstevel@tonic-gate fp->f_fullname); 200*0Sstevel@tonic-gate fp->f_flags |= F_REMOVE; /* fix baseline */ 201*0Sstevel@tonic-gate return (0); 202*0Sstevel@tonic-gate 203*0Sstevel@tonic-gate case F_IN_BASELINE|F_IN_SOURCE: /* deleted from dest */ 204*0Sstevel@tonic-gate /* 205*0Sstevel@tonic-gate * the basic principle here is that we are willing 206*0Sstevel@tonic-gate * to do the deletion if: 207*0Sstevel@tonic-gate * no changes were made on the other side 208*0Sstevel@tonic-gate * OR 209*0Sstevel@tonic-gate * we have been told to force in this direction 210*0Sstevel@tonic-gate * 211*0Sstevel@tonic-gate * we do, however, make an exception for files that 212*0Sstevel@tonic-gate * will still have other links. In this case, the 213*0Sstevel@tonic-gate * (changed) data will still be accessable through 214*0Sstevel@tonic-gate * another link and so we are willing to do the unlink 215*0Sstevel@tonic-gate * inspite of conflicting changes (which may well 216*0Sstevel@tonic-gate * have been introduced through another link. 217*0Sstevel@tonic-gate * 218*0Sstevel@tonic-gate * The jury is still out on this one 219*0Sstevel@tonic-gate */ 220*0Sstevel@tonic-gate if (((fp->f_srcdiffs&D_IMPORTANT) == 0) || 221*0Sstevel@tonic-gate (opt_force == OPT_DST) || 222*0Sstevel@tonic-gate has_other_links(fp, OPT_SRC)) { 223*0Sstevel@tonic-gate if (opt_verbose) 224*0Sstevel@tonic-gate fprintf(stdout, gettext(V_deleted), 225*0Sstevel@tonic-gate fp->f_fullname, "dst"); 226*0Sstevel@tonic-gate errs = do_remove(fp, OPT_SRC); 227*0Sstevel@tonic-gate goto done; 228*0Sstevel@tonic-gate } 229*0Sstevel@tonic-gate 230*0Sstevel@tonic-gate /* a deletion combined with changes */ 231*0Sstevel@tonic-gate if (opt_verbose) 232*0Sstevel@tonic-gate fprintf(stdout, gettext(V_delconf), 233*0Sstevel@tonic-gate fp->f_fullname); 234*0Sstevel@tonic-gate 235*0Sstevel@tonic-gate /* if we are to resolve in favor of source */ 236*0Sstevel@tonic-gate if (opt_force == OPT_SRC) { 237*0Sstevel@tonic-gate errs = do_copy(fp, OPT_DST); 238*0Sstevel@tonic-gate goto done; 239*0Sstevel@tonic-gate } 240*0Sstevel@tonic-gate 241*0Sstevel@tonic-gate fp->f_problem = gettext(PROB_del_change); 242*0Sstevel@tonic-gate goto cant; 243*0Sstevel@tonic-gate 244*0Sstevel@tonic-gate case F_IN_BASELINE|F_IN_DEST: /* deleted from src */ 245*0Sstevel@tonic-gate /* just like previous case, w/sides reversed */ 246*0Sstevel@tonic-gate if (((fp->f_dstdiffs&D_IMPORTANT) == 0) || 247*0Sstevel@tonic-gate (opt_force == OPT_SRC) || 248*0Sstevel@tonic-gate has_other_links(fp, OPT_DST)) { 249*0Sstevel@tonic-gate if (opt_verbose) 250*0Sstevel@tonic-gate fprintf(stdout, gettext(V_deleted), 251*0Sstevel@tonic-gate fp->f_fullname, "src"); 252*0Sstevel@tonic-gate errs = do_remove(fp, OPT_DST); 253*0Sstevel@tonic-gate goto done; 254*0Sstevel@tonic-gate } 255*0Sstevel@tonic-gate 256*0Sstevel@tonic-gate /* a deletion combined with changes */ 257*0Sstevel@tonic-gate if (opt_verbose) 258*0Sstevel@tonic-gate fprintf(stdout, gettext(V_delconf), 259*0Sstevel@tonic-gate fp->f_fullname); 260*0Sstevel@tonic-gate 261*0Sstevel@tonic-gate /* if we are to resolve in favor of destination */ 262*0Sstevel@tonic-gate if (opt_force == OPT_DST) { 263*0Sstevel@tonic-gate errs = do_copy(fp, OPT_SRC); 264*0Sstevel@tonic-gate goto done; 265*0Sstevel@tonic-gate } 266*0Sstevel@tonic-gate 267*0Sstevel@tonic-gate fp->f_problem = gettext(PROB_del_change); 268*0Sstevel@tonic-gate goto cant; 269*0Sstevel@tonic-gate 270*0Sstevel@tonic-gate /* 271*0Sstevel@tonic-gate * if something new shows up, and for some reason we cannot 272*0Sstevel@tonic-gate * propagate it to the other side, we should suppress the 273*0Sstevel@tonic-gate * file from the baseline, so it will show up as a new 274*0Sstevel@tonic-gate * creation next time too. 275*0Sstevel@tonic-gate */ 276*0Sstevel@tonic-gate case F_IN_SOURCE: /* created in src */ 277*0Sstevel@tonic-gate if (opt_verbose) 278*0Sstevel@tonic-gate fprintf(stdout, gettext(V_created), 279*0Sstevel@tonic-gate fp->f_fullname, "src"); 280*0Sstevel@tonic-gate errs = do_copy(fp, OPT_DST); 281*0Sstevel@tonic-gate goto done; 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate case F_IN_DEST: /* created in dest */ 284*0Sstevel@tonic-gate if (opt_verbose) 285*0Sstevel@tonic-gate fprintf(stdout, gettext(V_created), 286*0Sstevel@tonic-gate fp->f_fullname, "dst"); 287*0Sstevel@tonic-gate errs = do_copy(fp, OPT_SRC); 288*0Sstevel@tonic-gate goto done; 289*0Sstevel@tonic-gate 290*0Sstevel@tonic-gate case F_IN_SOURCE|F_IN_DEST: /* not in baseline */ 291*0Sstevel@tonic-gate /* 292*0Sstevel@tonic-gate * since we don't have a baseline, we cannot 293*0Sstevel@tonic-gate * know which of the two copies should prevail 294*0Sstevel@tonic-gate */ 295*0Sstevel@tonic-gate break; 296*0Sstevel@tonic-gate 297*0Sstevel@tonic-gate case F_IN_BASELINE|F_IN_SOURCE|F_IN_DEST: 298*0Sstevel@tonic-gate /* 299*0Sstevel@tonic-gate * we have a baseline where the two copies agreed, 300*0Sstevel@tonic-gate * so maybe we can determine that only one of the 301*0Sstevel@tonic-gate * two copies have changed ... but before we decide 302*0Sstevel@tonic-gate * who should be the winner we should determine 303*0Sstevel@tonic-gate * that the two copies are actually different. 304*0Sstevel@tonic-gate */ 305*0Sstevel@tonic-gate break; 306*0Sstevel@tonic-gate } 307*0Sstevel@tonic-gate 308*0Sstevel@tonic-gate /* 309*0Sstevel@tonic-gate * if we have fallen out of the case statement, it is because 310*0Sstevel@tonic-gate * we have discovered a non-obvious situation where potentially 311*0Sstevel@tonic-gate * changed versions of the file exist on both sides. 312*0Sstevel@tonic-gate * 313*0Sstevel@tonic-gate * if the two copies turn out to be identical, this is simple 314*0Sstevel@tonic-gate */ 315*0Sstevel@tonic-gate if (samedata(fp)) { 316*0Sstevel@tonic-gate if (samestuff(fp)) { 317*0Sstevel@tonic-gate /* files are identical, just update baseline */ 318*0Sstevel@tonic-gate if (opt_verbose) 319*0Sstevel@tonic-gate fprintf(stdout, gettext(V_unchanged), 320*0Sstevel@tonic-gate fp->f_fullname); 321*0Sstevel@tonic-gate update_info(fp, OPT_SRC); 322*0Sstevel@tonic-gate goto done; 323*0Sstevel@tonic-gate } else { 324*0Sstevel@tonic-gate /* 325*0Sstevel@tonic-gate * contents agree but ownership/protection does 326*0Sstevel@tonic-gate * not agree, so we have to bring these into 327*0Sstevel@tonic-gate * agreement. We can pick a winner if one 328*0Sstevel@tonic-gate * side hasn't changed, or if the user has 329*0Sstevel@tonic-gate * specified a force flag. 330*0Sstevel@tonic-gate */ 331*0Sstevel@tonic-gate if (opt_verbose) 332*0Sstevel@tonic-gate fprintf(stdout, gettext(V_modes), 333*0Sstevel@tonic-gate fp->f_fullname); 334*0Sstevel@tonic-gate 335*0Sstevel@tonic-gate if (((fp->f_srcdiffs & D_ADMIN) == 0) || 336*0Sstevel@tonic-gate (opt_force == OPT_DST)) { 337*0Sstevel@tonic-gate errs = do_like(fp, OPT_SRC, TRUE); 338*0Sstevel@tonic-gate goto done; 339*0Sstevel@tonic-gate } 340*0Sstevel@tonic-gate 341*0Sstevel@tonic-gate if (((fp->f_dstdiffs & D_ADMIN) == 0) || 342*0Sstevel@tonic-gate (opt_force == OPT_SRC)) { 343*0Sstevel@tonic-gate errs = do_like(fp, OPT_DST, TRUE); 344*0Sstevel@tonic-gate goto done; 345*0Sstevel@tonic-gate } 346*0Sstevel@tonic-gate } 347*0Sstevel@tonic-gate /* falls down to cant */ 348*0Sstevel@tonic-gate } else { 349*0Sstevel@tonic-gate /* 350*0Sstevel@tonic-gate * The two files have different contents, so we have 351*0Sstevel@tonic-gate * a potential conflict here. If we know that only one 352*0Sstevel@tonic-gate * side has changed, we go with that side. 353*0Sstevel@tonic-gate */ 354*0Sstevel@tonic-gate if (fp->f_dstdiffs == 0 || fp->f_srcdiffs == 0) { 355*0Sstevel@tonic-gate if (opt_verbose) 356*0Sstevel@tonic-gate fprintf(stdout, gettext(V_changed), 357*0Sstevel@tonic-gate fp->f_fullname); 358*0Sstevel@tonic-gate errs = do_copy(fp, fp->f_srcdiffs ? OPT_DST : OPT_SRC); 359*0Sstevel@tonic-gate goto done; 360*0Sstevel@tonic-gate } 361*0Sstevel@tonic-gate 362*0Sstevel@tonic-gate /* 363*0Sstevel@tonic-gate * Both sides have changed, so we have a real conflict. 364*0Sstevel@tonic-gate */ 365*0Sstevel@tonic-gate if (opt_verbose) 366*0Sstevel@tonic-gate fprintf(stdout, 367*0Sstevel@tonic-gate gettext(truncated(fp) ? 368*0Sstevel@tonic-gate V_trunconf : V_different), 369*0Sstevel@tonic-gate fp->f_fullname); 370*0Sstevel@tonic-gate 371*0Sstevel@tonic-gate /* 372*0Sstevel@tonic-gate * See if the user has given us explicit instructions 373*0Sstevel@tonic-gate * on how to resolve conflicts. We may have been told 374*0Sstevel@tonic-gate * to favor the older, the newer, the source, or the 375*0Sstevel@tonic-gate * destination ... but the default is to leave the 376*0Sstevel@tonic-gate * conflict unresolved. 377*0Sstevel@tonic-gate */ 378*0Sstevel@tonic-gate if (opt_force == OPT_OLD) { 379*0Sstevel@tonic-gate errs = do_copy(fp, newer(fp)); 380*0Sstevel@tonic-gate goto done; 381*0Sstevel@tonic-gate } 382*0Sstevel@tonic-gate 383*0Sstevel@tonic-gate if (opt_force == OPT_NEW) { 384*0Sstevel@tonic-gate errs = do_copy(fp, older(fp)); 385*0Sstevel@tonic-gate goto done; 386*0Sstevel@tonic-gate } 387*0Sstevel@tonic-gate 388*0Sstevel@tonic-gate if (opt_force != 0) { 389*0Sstevel@tonic-gate errs = do_copy(fp, (opt_force == OPT_SRC) ? 390*0Sstevel@tonic-gate OPT_DST : OPT_SRC); 391*0Sstevel@tonic-gate goto done; 392*0Sstevel@tonic-gate } 393*0Sstevel@tonic-gate 394*0Sstevel@tonic-gate 395*0Sstevel@tonic-gate /* 396*0Sstevel@tonic-gate * This is our last chance before giving up. 397*0Sstevel@tonic-gate * 398*0Sstevel@tonic-gate * We know that the files have different contents and 399*0Sstevel@tonic-gate * that there were changes on both sides. The only way 400*0Sstevel@tonic-gate * we can safely handle this is if there were pure contents 401*0Sstevel@tonic-gate * changes on one side and pure ownership changes on the 402*0Sstevel@tonic-gate * other side. In this case we can propagate the ownership 403*0Sstevel@tonic-gate * one way and the contents the other way. 404*0Sstevel@tonic-gate * 405*0Sstevel@tonic-gate * We decide whether or not this is possible by ANDing 406*0Sstevel@tonic-gate * together the changes on the two sides, and seeing 407*0Sstevel@tonic-gate * if the changes were all orthogonal (none of the same 408*0Sstevel@tonic-gate * things changed on both sides). 409*0Sstevel@tonic-gate */ 410*0Sstevel@tonic-gate diffmask = fp->f_srcdiffs & fp->f_dstdiffs; 411*0Sstevel@tonic-gate if ((diffmask & D_CONTENTS) == 0) { 412*0Sstevel@tonic-gate /* 413*0Sstevel@tonic-gate * if ownership changes were only made on one side 414*0Sstevel@tonic-gate * (presumably the side that didn't have data changes) 415*0Sstevel@tonic-gate * we can handle them separately. In this case, 416*0Sstevel@tonic-gate * ownership changes must be fixed first, because 417*0Sstevel@tonic-gate * the subsequent do_copy will overwrite them. 418*0Sstevel@tonic-gate */ 419*0Sstevel@tonic-gate if ((diffmask & D_ADMIN) == 0) 420*0Sstevel@tonic-gate errs |= do_like(fp, (fp->f_srcdiffs&D_ADMIN) ? 421*0Sstevel@tonic-gate OPT_DST : OPT_SRC, 422*0Sstevel@tonic-gate TRUE); 423*0Sstevel@tonic-gate 424*0Sstevel@tonic-gate /* 425*0Sstevel@tonic-gate * Now we can deal with the propagation of the data 426*0Sstevel@tonic-gate * changes. Note that any ownership/protection 427*0Sstevel@tonic-gate * changes (from the other side) that have not been 428*0Sstevel@tonic-gate * propagated yet are about to be lost. The cases 429*0Sstevel@tonic-gate * in which this might happen are all pathological 430*0Sstevel@tonic-gate * and the consequences of losing the protection 431*0Sstevel@tonic-gate * changes are (IMHO) minor when compared to the 432*0Sstevel@tonic-gate * obviously correct data propagation. 433*0Sstevel@tonic-gate */ 434*0Sstevel@tonic-gate errs |= do_copy(fp, (fp->f_srcdiffs&D_CONTENTS) ? 435*0Sstevel@tonic-gate OPT_DST : OPT_SRC); 436*0Sstevel@tonic-gate goto done; 437*0Sstevel@tonic-gate } 438*0Sstevel@tonic-gate 439*0Sstevel@tonic-gate /* 440*0Sstevel@tonic-gate * there are conflicting changes, nobody has told us how to 441*0Sstevel@tonic-gate * resolve conflicts, and we cannot figure out how to merge 442*0Sstevel@tonic-gate * the differences. 443*0Sstevel@tonic-gate */ 444*0Sstevel@tonic-gate fp->f_problem = gettext(PROB_different); 445*0Sstevel@tonic-gate } 446*0Sstevel@tonic-gate 447*0Sstevel@tonic-gate cant: 448*0Sstevel@tonic-gate /* 449*0Sstevel@tonic-gate * I'm not smart enough to resolve this conflict automatically, 450*0Sstevel@tonic-gate * so I have no choice but to bounce it back to the user. 451*0Sstevel@tonic-gate */ 452*0Sstevel@tonic-gate fp->f_flags |= F_CONFLICT; 453*0Sstevel@tonic-gate fp->f_base->b_unresolved++; 454*0Sstevel@tonic-gate errs |= ERR_UNRESOLVED; 455*0Sstevel@tonic-gate 456*0Sstevel@tonic-gate done: 457*0Sstevel@tonic-gate /* 458*0Sstevel@tonic-gate * if we have a conflict and the file is not in the baseline, 459*0Sstevel@tonic-gate * then there was never any point at which the two copies were 460*0Sstevel@tonic-gate * in agreement, and we want to preserve the conflict for future 461*0Sstevel@tonic-gate * resolution. 462*0Sstevel@tonic-gate */ 463*0Sstevel@tonic-gate if ((errs&ERR_UNRESOLVED) && (fp->f_flags & F_IN_BASELINE) == 0) 464*0Sstevel@tonic-gate if (fp->f_files == 0) 465*0Sstevel@tonic-gate /* 466*0Sstevel@tonic-gate * in most cases, this is most easily done by just 467*0Sstevel@tonic-gate * excluding the file in question from the baseline 468*0Sstevel@tonic-gate */ 469*0Sstevel@tonic-gate fp->f_flags |= F_REMOVE; 470*0Sstevel@tonic-gate else 471*0Sstevel@tonic-gate /* 472*0Sstevel@tonic-gate * but ... if the file in question is a directory 473*0Sstevel@tonic-gate * with children, excluding it from the baseline 474*0Sstevel@tonic-gate * would keep all of its children (even those with 475*0Sstevel@tonic-gate * no conflicts) out of the baseline as well. In 476*0Sstevel@tonic-gate * This case, it is better to tell a lie and to 477*0Sstevel@tonic-gate * manufacture a point of imaginary agreement 478*0Sstevel@tonic-gate * in the baseline ... but one that is absurd enough 479*0Sstevel@tonic-gate * that we will still see conflicts each time we run. 480*0Sstevel@tonic-gate * 481*0Sstevel@tonic-gate * recording a type of directory, and everything 482*0Sstevel@tonic-gate * else as zero should be absurd enough. 483*0Sstevel@tonic-gate */ 484*0Sstevel@tonic-gate fp->f_info[ OPT_BASE ].f_type = S_IFDIR; 485*0Sstevel@tonic-gate 486*0Sstevel@tonic-gate if (opt_debug & DBG_MISC) 487*0Sstevel@tonic-gate fprintf(stderr, "MISC: %s ERRS=%s\n", fp->f_fullname, 488*0Sstevel@tonic-gate showflags(errmap, errs)); 489*0Sstevel@tonic-gate 490*0Sstevel@tonic-gate return (errs); 491*0Sstevel@tonic-gate } 492*0Sstevel@tonic-gate 493*0Sstevel@tonic-gate /* 494*0Sstevel@tonic-gate * routine: 495*0Sstevel@tonic-gate * newer 496*0Sstevel@tonic-gate * 497*0Sstevel@tonic-gate * purpose: 498*0Sstevel@tonic-gate * determine which of two files is newer 499*0Sstevel@tonic-gate * 500*0Sstevel@tonic-gate * parameters: 501*0Sstevel@tonic-gate * struct file 502*0Sstevel@tonic-gate * 503*0Sstevel@tonic-gate * returns: 504*0Sstevel@tonic-gate * side_t (src/dest) 505*0Sstevel@tonic-gate */ 506*0Sstevel@tonic-gate static side_t 507*0Sstevel@tonic-gate newer(struct file *fp) 508*0Sstevel@tonic-gate { 509*0Sstevel@tonic-gate struct fileinfo *sp, *dp; 510*0Sstevel@tonic-gate 511*0Sstevel@tonic-gate sp = &fp->f_info[OPT_SRC]; 512*0Sstevel@tonic-gate dp = &fp->f_info[OPT_DST]; 513*0Sstevel@tonic-gate 514*0Sstevel@tonic-gate if (sp->f_modtime > dp->f_modtime) 515*0Sstevel@tonic-gate return (OPT_SRC); 516*0Sstevel@tonic-gate 517*0Sstevel@tonic-gate if (sp->f_modtime < dp->f_modtime) 518*0Sstevel@tonic-gate return (OPT_DST); 519*0Sstevel@tonic-gate 520*0Sstevel@tonic-gate if (sp->f_modns >= dp->f_modns) 521*0Sstevel@tonic-gate return (OPT_SRC); 522*0Sstevel@tonic-gate 523*0Sstevel@tonic-gate return (OPT_DST); 524*0Sstevel@tonic-gate } 525*0Sstevel@tonic-gate 526*0Sstevel@tonic-gate /* 527*0Sstevel@tonic-gate * routine: 528*0Sstevel@tonic-gate * older 529*0Sstevel@tonic-gate * 530*0Sstevel@tonic-gate * purpose: 531*0Sstevel@tonic-gate * determine which of two files is older 532*0Sstevel@tonic-gate * 533*0Sstevel@tonic-gate * parameters: 534*0Sstevel@tonic-gate * struct file 535*0Sstevel@tonic-gate * 536*0Sstevel@tonic-gate * returns: 537*0Sstevel@tonic-gate * side_t (src/dest) 538*0Sstevel@tonic-gate */ 539*0Sstevel@tonic-gate static side_t 540*0Sstevel@tonic-gate older(struct file *fp) 541*0Sstevel@tonic-gate { 542*0Sstevel@tonic-gate struct fileinfo *sp, *dp; 543*0Sstevel@tonic-gate 544*0Sstevel@tonic-gate sp = &fp->f_info[OPT_SRC]; 545*0Sstevel@tonic-gate dp = &fp->f_info[OPT_DST]; 546*0Sstevel@tonic-gate 547*0Sstevel@tonic-gate if (sp->f_modtime < dp->f_modtime) 548*0Sstevel@tonic-gate return (OPT_SRC); 549*0Sstevel@tonic-gate 550*0Sstevel@tonic-gate if (sp->f_modtime > dp->f_modtime) 551*0Sstevel@tonic-gate return (OPT_DST); 552*0Sstevel@tonic-gate 553*0Sstevel@tonic-gate if (sp->f_modns <= dp->f_modns) 554*0Sstevel@tonic-gate return (OPT_SRC); 555*0Sstevel@tonic-gate 556*0Sstevel@tonic-gate return (OPT_DST); 557*0Sstevel@tonic-gate } 558*0Sstevel@tonic-gate 559*0Sstevel@tonic-gate /* 560*0Sstevel@tonic-gate * routine: 561*0Sstevel@tonic-gate * samedata 562*0Sstevel@tonic-gate * 563*0Sstevel@tonic-gate * purpose: 564*0Sstevel@tonic-gate * determine whether or not two files contain the same data 565*0Sstevel@tonic-gate * 566*0Sstevel@tonic-gate * parameters: 567*0Sstevel@tonic-gate * struct file 568*0Sstevel@tonic-gate * 569*0Sstevel@tonic-gate * returns: 570*0Sstevel@tonic-gate * bool_t (true/false) 571*0Sstevel@tonic-gate */ 572*0Sstevel@tonic-gate static bool_t 573*0Sstevel@tonic-gate samedata(struct file *fp) 574*0Sstevel@tonic-gate { 575*0Sstevel@tonic-gate struct fileinfo *sp, *dp; 576*0Sstevel@tonic-gate 577*0Sstevel@tonic-gate sp = &fp->f_info[OPT_SRC]; 578*0Sstevel@tonic-gate dp = &fp->f_info[OPT_DST]; 579*0Sstevel@tonic-gate 580*0Sstevel@tonic-gate /* cheap test: types are different */ 581*0Sstevel@tonic-gate if (sp->f_type != dp->f_type) 582*0Sstevel@tonic-gate return (FALSE); 583*0Sstevel@tonic-gate 584*0Sstevel@tonic-gate /* cheap test: directories have same contents */ 585*0Sstevel@tonic-gate if (sp->f_type == S_IFDIR) 586*0Sstevel@tonic-gate return (TRUE); 587*0Sstevel@tonic-gate 588*0Sstevel@tonic-gate /* special files are compared via their maj/min */ 589*0Sstevel@tonic-gate if ((sp->f_type == S_IFBLK) || (sp->f_type == S_IFCHR)) { 590*0Sstevel@tonic-gate if (sp->f_rd_maj != dp->f_rd_maj) 591*0Sstevel@tonic-gate return (FALSE); 592*0Sstevel@tonic-gate if (sp->f_rd_min != dp->f_rd_min) 593*0Sstevel@tonic-gate return (FALSE); 594*0Sstevel@tonic-gate return (TRUE); 595*0Sstevel@tonic-gate } 596*0Sstevel@tonic-gate 597*0Sstevel@tonic-gate /* symlinks are the same if their contents are the same */ 598*0Sstevel@tonic-gate if (sp->f_type == S_IFLNK) 599*0Sstevel@tonic-gate return (samelink()); 600*0Sstevel@tonic-gate 601*0Sstevel@tonic-gate /* cheap test: sizes are different */ 602*0Sstevel@tonic-gate if (fp->f_info[OPT_SRC].f_size != fp->f_info[OPT_DST].f_size) 603*0Sstevel@tonic-gate return (FALSE); 604*0Sstevel@tonic-gate 605*0Sstevel@tonic-gate /* expensive test: byte for byte comparison */ 606*0Sstevel@tonic-gate if (samecompare(fp) == 0) 607*0Sstevel@tonic-gate return (FALSE); 608*0Sstevel@tonic-gate 609*0Sstevel@tonic-gate return (TRUE); 610*0Sstevel@tonic-gate } 611*0Sstevel@tonic-gate 612*0Sstevel@tonic-gate /* 613*0Sstevel@tonic-gate * routine: 614*0Sstevel@tonic-gate * samestuff 615*0Sstevel@tonic-gate * 616*0Sstevel@tonic-gate * purpose: 617*0Sstevel@tonic-gate * determine whether or not two files have same owner/protection 618*0Sstevel@tonic-gate * 619*0Sstevel@tonic-gate * parameters: 620*0Sstevel@tonic-gate * struct file 621*0Sstevel@tonic-gate * 622*0Sstevel@tonic-gate * returns: 623*0Sstevel@tonic-gate * bool_t (true/false) 624*0Sstevel@tonic-gate */ 625*0Sstevel@tonic-gate static bool_t 626*0Sstevel@tonic-gate samestuff(struct file *fp) 627*0Sstevel@tonic-gate { int same_mode, same_uid, same_gid, same_acl; 628*0Sstevel@tonic-gate struct fileinfo *sp, *dp; 629*0Sstevel@tonic-gate 630*0Sstevel@tonic-gate sp = &fp->f_info[OPT_SRC]; 631*0Sstevel@tonic-gate dp = &fp->f_info[OPT_DST]; 632*0Sstevel@tonic-gate 633*0Sstevel@tonic-gate same_mode = (sp->f_mode == dp->f_mode); 634*0Sstevel@tonic-gate same_uid = (sp->f_uid == dp->f_uid); 635*0Sstevel@tonic-gate same_gid = (sp->f_gid == dp->f_gid); 636*0Sstevel@tonic-gate same_acl = cmp_acls(sp, dp); 637*0Sstevel@tonic-gate 638*0Sstevel@tonic-gate /* if the are all the same, it is easy to tell the truth */ 639*0Sstevel@tonic-gate if (same_uid && same_gid && same_mode && same_acl) 640*0Sstevel@tonic-gate return (TRUE); 641*0Sstevel@tonic-gate 642*0Sstevel@tonic-gate /* note the nature of the conflict */ 643*0Sstevel@tonic-gate if (!same_uid || !same_gid || !same_acl) 644*0Sstevel@tonic-gate fp->f_problem = gettext(PROB_ownership); 645*0Sstevel@tonic-gate else 646*0Sstevel@tonic-gate fp->f_problem = gettext(PROB_protection); 647*0Sstevel@tonic-gate 648*0Sstevel@tonic-gate return (FALSE); 649*0Sstevel@tonic-gate } 650*0Sstevel@tonic-gate 651*0Sstevel@tonic-gate /* 652*0Sstevel@tonic-gate * routine: 653*0Sstevel@tonic-gate * samecompare 654*0Sstevel@tonic-gate * 655*0Sstevel@tonic-gate * purpose: 656*0Sstevel@tonic-gate * do a byte-for-byte comparison of two files 657*0Sstevel@tonic-gate * 658*0Sstevel@tonic-gate * parameters: 659*0Sstevel@tonic-gate * struct file 660*0Sstevel@tonic-gate * 661*0Sstevel@tonic-gate * returns: 662*0Sstevel@tonic-gate * bool_t (true/false) 663*0Sstevel@tonic-gate */ 664*0Sstevel@tonic-gate static bool_t 665*0Sstevel@tonic-gate samecompare(struct file *fp) 666*0Sstevel@tonic-gate { int sfd, dfd; 667*0Sstevel@tonic-gate int i, count; 668*0Sstevel@tonic-gate char srcbuf[ COPY_BSIZE ], dstbuf[ COPY_BSIZE ]; 669*0Sstevel@tonic-gate bool_t same = TRUE; 670*0Sstevel@tonic-gate 671*0Sstevel@tonic-gate 672*0Sstevel@tonic-gate sfd = open(srcname, 0); 673*0Sstevel@tonic-gate if (sfd < 0) 674*0Sstevel@tonic-gate return (FALSE); 675*0Sstevel@tonic-gate 676*0Sstevel@tonic-gate dfd = open(dstname, 0); 677*0Sstevel@tonic-gate if (dfd < 0) { 678*0Sstevel@tonic-gate close(sfd); 679*0Sstevel@tonic-gate return (FALSE); 680*0Sstevel@tonic-gate } 681*0Sstevel@tonic-gate 682*0Sstevel@tonic-gate for ( 683*0Sstevel@tonic-gate count = read(sfd, srcbuf, COPY_BSIZE); 684*0Sstevel@tonic-gate count > 0; 685*0Sstevel@tonic-gate count = read(sfd, srcbuf, COPY_BSIZE)) { 686*0Sstevel@tonic-gate 687*0Sstevel@tonic-gate /* do a matching read */ 688*0Sstevel@tonic-gate if (read(dfd, dstbuf, COPY_BSIZE) != count) { 689*0Sstevel@tonic-gate same = FALSE; 690*0Sstevel@tonic-gate goto done; 691*0Sstevel@tonic-gate } 692*0Sstevel@tonic-gate 693*0Sstevel@tonic-gate /* do the comparison for this block */ 694*0Sstevel@tonic-gate for (i = 0; i < count; i++) { 695*0Sstevel@tonic-gate if (srcbuf[i] != dstbuf[i]) { 696*0Sstevel@tonic-gate same = FALSE; 697*0Sstevel@tonic-gate goto done; 698*0Sstevel@tonic-gate } 699*0Sstevel@tonic-gate } 700*0Sstevel@tonic-gate } 701*0Sstevel@tonic-gate 702*0Sstevel@tonic-gate done: 703*0Sstevel@tonic-gate if (opt_debug & DBG_ANAL) 704*0Sstevel@tonic-gate fprintf(stderr, "ANAL: SAME=%d %s\n", same, fp->f_fullname); 705*0Sstevel@tonic-gate 706*0Sstevel@tonic-gate close(sfd); 707*0Sstevel@tonic-gate close(dfd); 708*0Sstevel@tonic-gate return (same); 709*0Sstevel@tonic-gate } 710*0Sstevel@tonic-gate 711*0Sstevel@tonic-gate /* 712*0Sstevel@tonic-gate * routine: 713*0Sstevel@tonic-gate * truncated 714*0Sstevel@tonic-gate * 715*0Sstevel@tonic-gate * purpose: 716*0Sstevel@tonic-gate * to determine whether or not a file has been truncated 717*0Sstevel@tonic-gate * 718*0Sstevel@tonic-gate * parameters: 719*0Sstevel@tonic-gate * pointer to file structure 720*0Sstevel@tonic-gate * 721*0Sstevel@tonic-gate * returns: 722*0Sstevel@tonic-gate * true/false 723*0Sstevel@tonic-gate */ 724*0Sstevel@tonic-gate static bool_t 725*0Sstevel@tonic-gate truncated(struct file *fp) 726*0Sstevel@tonic-gate { 727*0Sstevel@tonic-gate /* either source or destination must now be zero length */ 728*0Sstevel@tonic-gate if (fp->f_info[OPT_SRC].f_size && fp->f_info[OPT_DST].f_size) 729*0Sstevel@tonic-gate return (FALSE); 730*0Sstevel@tonic-gate 731*0Sstevel@tonic-gate /* file must have originally had a non-zero length */ 732*0Sstevel@tonic-gate if (fp->f_info[OPT_BASE].f_size == 0) 733*0Sstevel@tonic-gate return (FALSE); 734*0Sstevel@tonic-gate 735*0Sstevel@tonic-gate /* file type must "normal" all around */ 736*0Sstevel@tonic-gate if (fp->f_info[OPT_BASE].f_type != S_IFREG) 737*0Sstevel@tonic-gate return (FALSE); 738*0Sstevel@tonic-gate if (fp->f_info[OPT_SRC].f_type != S_IFREG) 739*0Sstevel@tonic-gate return (FALSE); 740*0Sstevel@tonic-gate if (fp->f_info[OPT_DST].f_type != S_IFREG) 741*0Sstevel@tonic-gate return (FALSE); 742*0Sstevel@tonic-gate 743*0Sstevel@tonic-gate 744*0Sstevel@tonic-gate return (TRUE); 745*0Sstevel@tonic-gate } 746*0Sstevel@tonic-gate 747*0Sstevel@tonic-gate /* 748*0Sstevel@tonic-gate * routine: 749*0Sstevel@tonic-gate * samelink 750*0Sstevel@tonic-gate * 751*0Sstevel@tonic-gate * purpose: 752*0Sstevel@tonic-gate * to determine whether or not two symbolic links agree 753*0Sstevel@tonic-gate * 754*0Sstevel@tonic-gate * parameters: 755*0Sstevel@tonic-gate * pointer to file structure 756*0Sstevel@tonic-gate * 757*0Sstevel@tonic-gate * returns: 758*0Sstevel@tonic-gate * true/false 759*0Sstevel@tonic-gate */ 760*0Sstevel@tonic-gate static bool_t 761*0Sstevel@tonic-gate samelink() 762*0Sstevel@tonic-gate { int i, srclen, dstlen; 763*0Sstevel@tonic-gate char srcbuf[ MAX_PATH ], dstbuf[ MAX_PATH ]; 764*0Sstevel@tonic-gate 765*0Sstevel@tonic-gate 766*0Sstevel@tonic-gate /* read both copies of the link */ 767*0Sstevel@tonic-gate srclen = readlink(srcname, srcbuf, sizeof (srcbuf)); 768*0Sstevel@tonic-gate dstlen = readlink(dstname, dstbuf, sizeof (dstbuf)); 769*0Sstevel@tonic-gate 770*0Sstevel@tonic-gate /* if they aren't the same length, they disagree */ 771*0Sstevel@tonic-gate if (srclen < 0 || dstlen < 0 || srclen != dstlen) 772*0Sstevel@tonic-gate return (FALSE); 773*0Sstevel@tonic-gate 774*0Sstevel@tonic-gate /* look for differences in contents */ 775*0Sstevel@tonic-gate for (i = 0; i < srclen; i++) 776*0Sstevel@tonic-gate if (srcbuf[i] != dstbuf[i]) 777*0Sstevel@tonic-gate return (FALSE); 778*0Sstevel@tonic-gate 779*0Sstevel@tonic-gate return (TRUE); 780*0Sstevel@tonic-gate } 781*0Sstevel@tonic-gate 782*0Sstevel@tonic-gate /* 783*0Sstevel@tonic-gate * routine: 784*0Sstevel@tonic-gate * full_name 785*0Sstevel@tonic-gate * 786*0Sstevel@tonic-gate * purpose: 787*0Sstevel@tonic-gate * to figure out the fully qualified path name to a file on the 788*0Sstevel@tonic-gate * reconciliation list. 789*0Sstevel@tonic-gate * 790*0Sstevel@tonic-gate * parameters: 791*0Sstevel@tonic-gate * pointer to the file structure 792*0Sstevel@tonic-gate * side indication for which base to use 793*0Sstevel@tonic-gate * side indication for which buffer to use 794*0Sstevel@tonic-gate * 795*0Sstevel@tonic-gate * returns: 796*0Sstevel@tonic-gate * pointer to a clobberable buffer 797*0Sstevel@tonic-gate * 798*0Sstevel@tonic-gate * notes: 799*0Sstevel@tonic-gate * the zero'th buffer is used for renames and links, where 800*0Sstevel@tonic-gate * we need the name of another file on the same side. 801*0Sstevel@tonic-gate */ 802*0Sstevel@tonic-gate char * 803*0Sstevel@tonic-gate full_name(struct file *fp, side_t srcdst, side_t whichbuf) 804*0Sstevel@tonic-gate { static char *buffers[3]; 805*0Sstevel@tonic-gate static int buflen = 0; 806*0Sstevel@tonic-gate char *p, *b; 807*0Sstevel@tonic-gate int l; 808*0Sstevel@tonic-gate 809*0Sstevel@tonic-gate /* see if the existing buffer is long enough */ 810*0Sstevel@tonic-gate b = (srcdst == OPT_SRC) ? fp->f_base->b_src_name 811*0Sstevel@tonic-gate : fp->f_base->b_dst_name; 812*0Sstevel@tonic-gate 813*0Sstevel@tonic-gate /* see if the allocated buffer is long enough */ 814*0Sstevel@tonic-gate l = strlen(b) + strlen(fp->f_fullname) + 2; 815*0Sstevel@tonic-gate if (l > buflen) { 816*0Sstevel@tonic-gate /* figure out the next "nice" size to use */ 817*0Sstevel@tonic-gate for (buflen = MAX_PATH; buflen < l; buflen += MAX_NAME); 818*0Sstevel@tonic-gate 819*0Sstevel@tonic-gate /* reallocate all buffers to this size */ 820*0Sstevel@tonic-gate for (l = 0; l < 3; l++) { 821*0Sstevel@tonic-gate buffers[l] = (char *) realloc(buffers[l], buflen); 822*0Sstevel@tonic-gate if (buffers[l] == 0) 823*0Sstevel@tonic-gate nomem("full name"); 824*0Sstevel@tonic-gate } 825*0Sstevel@tonic-gate } 826*0Sstevel@tonic-gate 827*0Sstevel@tonic-gate /* assemble the name in the buffer and reurn it */ 828*0Sstevel@tonic-gate p = buffers[whichbuf]; 829*0Sstevel@tonic-gate strcpy(p, b); 830*0Sstevel@tonic-gate strcat(p, "/"); 831*0Sstevel@tonic-gate strcat(p, fp->f_fullname); 832*0Sstevel@tonic-gate return (p); 833*0Sstevel@tonic-gate } 834