xref: /onnv-gate/usr/src/cmd/filesync/rename.c (revision 0:68f95e015346)
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