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