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