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