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 * rename.c 27*7c478bd9Sstevel@tonic-gate * 28*7c478bd9Sstevel@tonic-gate * purpose: 29*7c478bd9Sstevel@tonic-gate * routines to determine whether or not any renames have taken place 30*7c478bd9Sstevel@tonic-gate * and note them (for reconciliation) if we find any 31*7c478bd9Sstevel@tonic-gate * 32*7c478bd9Sstevel@tonic-gate * contents: 33*7c478bd9Sstevel@tonic-gate * find_renames . look for files that have been renamed 34*7c478bd9Sstevel@tonic-gate * find_oldname . (static) find the file we were renamed from 35*7c478bd9Sstevel@tonic-gate * note_rename .. (static) note the rename for subsequent reconciliation 36*7c478bd9Sstevel@tonic-gate * 37*7c478bd9Sstevel@tonic-gate * notes: 38*7c478bd9Sstevel@tonic-gate * the reason renames warrant special attention is because the tree 39*7c478bd9Sstevel@tonic-gate * we have constructed is name based, and a directory rename can 40*7c478bd9Sstevel@tonic-gate * appear as zillions of changes. We attempt to find and deal with 41*7c478bd9Sstevel@tonic-gate * renames prior to doing the difference analysis. 42*7c478bd9Sstevel@tonic-gate * 43*7c478bd9Sstevel@tonic-gate * The only case we deal with here is simple renames. If new links 44*7c478bd9Sstevel@tonic-gate * have been created beneath other directories (i.e. a file has been 45*7c478bd9Sstevel@tonic-gate * moved from one directory to another), the generalized link finding 46*7c478bd9Sstevel@tonic-gate * stuff will deal with it. 47*7c478bd9Sstevel@tonic-gate * 48*7c478bd9Sstevel@tonic-gate * This is still under construction, and to completely deal with 49*7c478bd9Sstevel@tonic-gate * directory renames may require some non-trivial tree restructuring. 50*7c478bd9Sstevel@tonic-gate * There is a whole design note on this subject. In the mean time, 51*7c478bd9Sstevel@tonic-gate * we still detect file renames, so that the user will see them 52*7c478bd9Sstevel@tonic-gate * reported as "mv"s rather than as "ln"s and "rm"s. Until directory 53*7c478bd9Sstevel@tonic-gate * renames are fully implemented, they will instead be handled as 54*7c478bd9Sstevel@tonic-gate * mkdirs, massive links and unlinks, and rmdirs. 55*7c478bd9Sstevel@tonic-gate */ 56*7c478bd9Sstevel@tonic-gate #ident "%W% %E% SMI" 57*7c478bd9Sstevel@tonic-gate 58*7c478bd9Sstevel@tonic-gate #include <stdio.h> 59*7c478bd9Sstevel@tonic-gate 60*7c478bd9Sstevel@tonic-gate #include "filesync.h" 61*7c478bd9Sstevel@tonic-gate #include "database.h" 62*7c478bd9Sstevel@tonic-gate 63*7c478bd9Sstevel@tonic-gate 64*7c478bd9Sstevel@tonic-gate /* local routines */ 65*7c478bd9Sstevel@tonic-gate static struct file *find_oldname(struct file *, struct file *, side_t); 66*7c478bd9Sstevel@tonic-gate static errmask_t 67*7c478bd9Sstevel@tonic-gate note_rename(struct file *, struct file *, struct file *, side_t); 68*7c478bd9Sstevel@tonic-gate 69*7c478bd9Sstevel@tonic-gate /* 70*7c478bd9Sstevel@tonic-gate * routine: 71*7c478bd9Sstevel@tonic-gate * find_renames 72*7c478bd9Sstevel@tonic-gate * 73*7c478bd9Sstevel@tonic-gate * purpose: 74*7c478bd9Sstevel@tonic-gate * recursively perform rename analysis on a directory 75*7c478bd9Sstevel@tonic-gate * 76*7c478bd9Sstevel@tonic-gate * parameters: 77*7c478bd9Sstevel@tonic-gate * file node for the suspected directory 78*7c478bd9Sstevel@tonic-gate * 79*7c478bd9Sstevel@tonic-gate * returns: 80*7c478bd9Sstevel@tonic-gate * error mask 81*7c478bd9Sstevel@tonic-gate * 82*7c478bd9Sstevel@tonic-gate * note: 83*7c478bd9Sstevel@tonic-gate * the basic algorithm here is to search every directory 84*7c478bd9Sstevel@tonic-gate * for files that have been newly created on one side, 85*7c478bd9Sstevel@tonic-gate * and then look to see if they correspond to an identical 86*7c478bd9Sstevel@tonic-gate * file that has been newly deleted on the same side. 87*7c478bd9Sstevel@tonic-gate */ 88*7c478bd9Sstevel@tonic-gate errmask_t 89*7c478bd9Sstevel@tonic-gate find_renames(struct file *fp) 90*7c478bd9Sstevel@tonic-gate { struct file *np, *rp; 91*7c478bd9Sstevel@tonic-gate errmask_t errs = 0; 92*7c478bd9Sstevel@tonic-gate int stype, dtype, btype, side; 93*7c478bd9Sstevel@tonic-gate 94*7c478bd9Sstevel@tonic-gate /* if this isn't a directory, there is nothing to analyze */ 95*7c478bd9Sstevel@tonic-gate if (fp->f_files == 0) 96*7c478bd9Sstevel@tonic-gate return (0); 97*7c478bd9Sstevel@tonic-gate 98*7c478bd9Sstevel@tonic-gate /* look for any files under this directory that may have been renamed */ 99*7c478bd9Sstevel@tonic-gate for (np = fp->f_files; np; np = np->f_next) { 100*7c478bd9Sstevel@tonic-gate btype = np->f_info[OPT_BASE].f_type; 101*7c478bd9Sstevel@tonic-gate stype = np->f_info[OPT_SRC].f_type; 102*7c478bd9Sstevel@tonic-gate dtype = np->f_info[OPT_DST].f_type; 103*7c478bd9Sstevel@tonic-gate 104*7c478bd9Sstevel@tonic-gate /* a rename must be a file that is new on only one side */ 105*7c478bd9Sstevel@tonic-gate if (btype == 0 && stype != dtype && (!stype || !dtype)) { 106*7c478bd9Sstevel@tonic-gate side = stype ? OPT_SRC : OPT_DST; 107*7c478bd9Sstevel@tonic-gate rp = find_oldname(fp, np, side); 108*7c478bd9Sstevel@tonic-gate if (rp) 109*7c478bd9Sstevel@tonic-gate errs |= note_rename(fp, np, rp, side); 110*7c478bd9Sstevel@tonic-gate } 111*7c478bd9Sstevel@tonic-gate } 112*7c478bd9Sstevel@tonic-gate 113*7c478bd9Sstevel@tonic-gate /* recursively examine all my children */ 114*7c478bd9Sstevel@tonic-gate for (np = fp->f_files; np; np = np->f_next) { 115*7c478bd9Sstevel@tonic-gate errs |= find_renames(np); 116*7c478bd9Sstevel@tonic-gate } 117*7c478bd9Sstevel@tonic-gate 118*7c478bd9Sstevel@tonic-gate return (errs); 119*7c478bd9Sstevel@tonic-gate } 120*7c478bd9Sstevel@tonic-gate 121*7c478bd9Sstevel@tonic-gate /* 122*7c478bd9Sstevel@tonic-gate * routine: 123*7c478bd9Sstevel@tonic-gate * find_oldname 124*7c478bd9Sstevel@tonic-gate * 125*7c478bd9Sstevel@tonic-gate * purpose: 126*7c478bd9Sstevel@tonic-gate * to search for an old name for a newly discovered file 127*7c478bd9Sstevel@tonic-gate * 128*7c478bd9Sstevel@tonic-gate * parameters: 129*7c478bd9Sstevel@tonic-gate * file node for the containing directory 130*7c478bd9Sstevel@tonic-gate * file node for the new file 131*7c478bd9Sstevel@tonic-gate * which side the rename is believed to have happened on 132*7c478bd9Sstevel@tonic-gate * 133*7c478bd9Sstevel@tonic-gate * returns: 134*7c478bd9Sstevel@tonic-gate * pointer to likely previous file 135*7c478bd9Sstevel@tonic-gate * 0 no candidate found 136*7c478bd9Sstevel@tonic-gate * 137*7c478bd9Sstevel@tonic-gate * note: 138*7c478bd9Sstevel@tonic-gate * this routine only deals with simple renames within a single 139*7c478bd9Sstevel@tonic-gate * directory. 140*7c478bd9Sstevel@tonic-gate */ 141*7c478bd9Sstevel@tonic-gate static struct file *find_oldname(struct file *dirp, struct file *new, 142*7c478bd9Sstevel@tonic-gate side_t side) 143*7c478bd9Sstevel@tonic-gate { struct file *fp; 144*7c478bd9Sstevel@tonic-gate long maj, min; 145*7c478bd9Sstevel@tonic-gate ino_t inum; 146*7c478bd9Sstevel@tonic-gate off_t size; 147*7c478bd9Sstevel@tonic-gate side_t otherside = (side == OPT_SRC) ? OPT_DST : OPT_SRC; 148*7c478bd9Sstevel@tonic-gate 149*7c478bd9Sstevel@tonic-gate /* figure out what we're looking for */ 150*7c478bd9Sstevel@tonic-gate inum = new->f_info[side].f_ino; 151*7c478bd9Sstevel@tonic-gate maj = new->f_info[side].f_d_maj; 152*7c478bd9Sstevel@tonic-gate min = new->f_info[side].f_d_min; 153*7c478bd9Sstevel@tonic-gate size = new->f_info[side].f_size; 154*7c478bd9Sstevel@tonic-gate 155*7c478bd9Sstevel@tonic-gate /* 156*7c478bd9Sstevel@tonic-gate * search the same directory for any entry that might describe 157*7c478bd9Sstevel@tonic-gate * the previous name of the new file. 158*7c478bd9Sstevel@tonic-gate */ 159*7c478bd9Sstevel@tonic-gate for (fp = dirp->f_files; fp; fp = fp->f_next) { 160*7c478bd9Sstevel@tonic-gate /* previous name on changed side must no longer exist */ 161*7c478bd9Sstevel@tonic-gate if (fp->f_info[side].f_type != 0) 162*7c478bd9Sstevel@tonic-gate continue; 163*7c478bd9Sstevel@tonic-gate 164*7c478bd9Sstevel@tonic-gate /* previous name on the other side must still exist */ 165*7c478bd9Sstevel@tonic-gate if (fp->f_info[otherside].f_type == 0) 166*7c478bd9Sstevel@tonic-gate continue; 167*7c478bd9Sstevel@tonic-gate 168*7c478bd9Sstevel@tonic-gate /* it must describe the same inode as the new file */ 169*7c478bd9Sstevel@tonic-gate if (fp->f_info[OPT_BASE].f_type != new->f_info[side].f_type) 170*7c478bd9Sstevel@tonic-gate continue; /* must be same type */ 171*7c478bd9Sstevel@tonic-gate if (((side == OPT_SRC) ? fp->f_s_inum : fp->f_d_inum) != inum) 172*7c478bd9Sstevel@tonic-gate continue; /* must be same inode # */ 173*7c478bd9Sstevel@tonic-gate if (((side == OPT_SRC) ? fp->f_s_maj : fp->f_d_maj) != maj) 174*7c478bd9Sstevel@tonic-gate continue; /* must be same major # */ 175*7c478bd9Sstevel@tonic-gate if (((side == OPT_SRC) ? fp->f_s_min : fp->f_d_min) != min) 176*7c478bd9Sstevel@tonic-gate continue; /* must be same minor # */ 177*7c478bd9Sstevel@tonic-gate 178*7c478bd9Sstevel@tonic-gate /* 179*7c478bd9Sstevel@tonic-gate * occasionally a prompt delete and create can reuse the 180*7c478bd9Sstevel@tonic-gate * same i-node in the same directory. What we really 181*7c478bd9Sstevel@tonic-gate * want is generation, but that isn't available just 182*7c478bd9Sstevel@tonic-gate * yet, so our poor-man's approximation is the size. 183*7c478bd9Sstevel@tonic-gate * There is little point in checking ownership and 184*7c478bd9Sstevel@tonic-gate * modes, since the fact that it is in the same 185*7c478bd9Sstevel@tonic-gate * directory strongly suggests that it is the same 186*7c478bd9Sstevel@tonic-gate * user who is doing the deleting and creating. 187*7c478bd9Sstevel@tonic-gate */ 188*7c478bd9Sstevel@tonic-gate if (fp->f_info[OPT_BASE].f_size != size) 189*7c478bd9Sstevel@tonic-gate continue; 190*7c478bd9Sstevel@tonic-gate 191*7c478bd9Sstevel@tonic-gate /* looks like we found a match */ 192*7c478bd9Sstevel@tonic-gate return (fp); 193*7c478bd9Sstevel@tonic-gate } 194*7c478bd9Sstevel@tonic-gate 195*7c478bd9Sstevel@tonic-gate /* no joy */ 196*7c478bd9Sstevel@tonic-gate return (0); 197*7c478bd9Sstevel@tonic-gate } 198*7c478bd9Sstevel@tonic-gate 199*7c478bd9Sstevel@tonic-gate /* 200*7c478bd9Sstevel@tonic-gate * routine: 201*7c478bd9Sstevel@tonic-gate * note_rename 202*7c478bd9Sstevel@tonic-gate * 203*7c478bd9Sstevel@tonic-gate * purpose: 204*7c478bd9Sstevel@tonic-gate * to record a discovered rename, so that the reconciliation 205*7c478bd9Sstevel@tonic-gate * phase will deal with it as a rename rather than as link 206*7c478bd9Sstevel@tonic-gate * followed by an unlink. 207*7c478bd9Sstevel@tonic-gate * 208*7c478bd9Sstevel@tonic-gate * parameters: 209*7c478bd9Sstevel@tonic-gate * file node for the containing directory 210*7c478bd9Sstevel@tonic-gate * file node for the new file 211*7c478bd9Sstevel@tonic-gate * file node for the old file 212*7c478bd9Sstevel@tonic-gate * which side the rename is believed to have happened on 213*7c478bd9Sstevel@tonic-gate * 214*7c478bd9Sstevel@tonic-gate * returns: 215*7c478bd9Sstevel@tonic-gate * error mask 216*7c478bd9Sstevel@tonic-gate */ 217*7c478bd9Sstevel@tonic-gate static errmask_t 218*7c478bd9Sstevel@tonic-gate note_rename(struct file *dirp, struct file *new, 219*7c478bd9Sstevel@tonic-gate struct file *old, side_t side) 220*7c478bd9Sstevel@tonic-gate { 221*7c478bd9Sstevel@tonic-gate int dir; 222*7c478bd9Sstevel@tonic-gate errmask_t errs = 0; 223*7c478bd9Sstevel@tonic-gate static char *sidenames[] = {"base", "source", "dest"}; 224*7c478bd9Sstevel@tonic-gate 225*7c478bd9Sstevel@tonic-gate dir = new->f_info[side].f_type == S_IFDIR; 226*7c478bd9Sstevel@tonic-gate 227*7c478bd9Sstevel@tonic-gate if (opt_debug & DBG_ANAL) 228*7c478bd9Sstevel@tonic-gate fprintf(stderr, "ANAL: NOTE RENAME %s %s/%s -> %s/%s on %s\n", 229*7c478bd9Sstevel@tonic-gate dir ? "directory" : "file", 230*7c478bd9Sstevel@tonic-gate dirp->f_name, old->f_name, dirp->f_name, new->f_name, 231*7c478bd9Sstevel@tonic-gate sidenames[side]); 232*7c478bd9Sstevel@tonic-gate 233*7c478bd9Sstevel@tonic-gate /* FIX: we don't deal with directory renames yet */ 234*7c478bd9Sstevel@tonic-gate if (dir) 235*7c478bd9Sstevel@tonic-gate return (0); 236*7c478bd9Sstevel@tonic-gate 237*7c478bd9Sstevel@tonic-gate /* note that a rename has taken place */ 238*7c478bd9Sstevel@tonic-gate if (side == OPT_SRC) { 239*7c478bd9Sstevel@tonic-gate new->f_srcdiffs |= D_RENAME_TO; 240*7c478bd9Sstevel@tonic-gate old->f_srcdiffs |= D_RENAME_FROM; 241*7c478bd9Sstevel@tonic-gate } else { 242*7c478bd9Sstevel@tonic-gate new->f_dstdiffs |= D_RENAME_TO; 243*7c478bd9Sstevel@tonic-gate old->f_dstdiffs |= D_RENAME_FROM; 244*7c478bd9Sstevel@tonic-gate } 245*7c478bd9Sstevel@tonic-gate 246*7c478bd9Sstevel@tonic-gate /* put a link to the old name in the new name */ 247*7c478bd9Sstevel@tonic-gate new->f_previous = old; 248*7c478bd9Sstevel@tonic-gate 249*7c478bd9Sstevel@tonic-gate /* for most files, there is nothing else we have to do */ 250*7c478bd9Sstevel@tonic-gate if (!dir) 251*7c478bd9Sstevel@tonic-gate return (errs); 252*7c478bd9Sstevel@tonic-gate 253*7c478bd9Sstevel@tonic-gate /* 254*7c478bd9Sstevel@tonic-gate * FIX ... someday we are going to have to merge the old and 255*7c478bd9Sstevel@tonic-gate * new children into a single tree, but there are 256*7c478bd9Sstevel@tonic-gate * horrendous backout problems if we are unable to 257*7c478bd9Sstevel@tonic-gate * do the mvdir, so I have postponed this feature. 258*7c478bd9Sstevel@tonic-gate */ 259*7c478bd9Sstevel@tonic-gate 260*7c478bd9Sstevel@tonic-gate return (errs); 261*7c478bd9Sstevel@tonic-gate } 262