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
find_renames(struct file * fp)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 */
find_oldname(struct file * dirp,struct file * new,side_t side)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
note_rename(struct file * dirp,struct file * new,struct file * old,side_t side)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 }
262