xref: /illumos-gate/usr/src/cmd/filesync/rename.c (revision 7c478bd9)
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