diff options
Diffstat (limited to 'usr/src/cmd/filesync/rename.c')
-rw-r--r-- | usr/src/cmd/filesync/rename.c | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/usr/src/cmd/filesync/rename.c b/usr/src/cmd/filesync/rename.c new file mode 100644 index 0000000000..ed2860706f --- /dev/null +++ b/usr/src/cmd/filesync/rename.c @@ -0,0 +1,261 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved + * + * module: + * rename.c + * + * purpose: + * routines to determine whether or not any renames have taken place + * and note them (for reconciliation) if we find any + * + * contents: + * find_renames . look for files that have been renamed + * find_oldname . (static) find the file we were renamed from + * note_rename .. (static) note the rename for subsequent reconciliation + * + * notes: + * the reason renames warrant special attention is because the tree + * we have constructed is name based, and a directory rename can + * appear as zillions of changes. We attempt to find and deal with + * renames prior to doing the difference analysis. + * + * The only case we deal with here is simple renames. If new links + * have been created beneath other directories (i.e. a file has been + * moved from one directory to another), the generalized link finding + * stuff will deal with it. + * + * This is still under construction, and to completely deal with + * directory renames may require some non-trivial tree restructuring. + * There is a whole design note on this subject. In the mean time, + * we still detect file renames, so that the user will see them + * reported as "mv"s rather than as "ln"s and "rm"s. Until directory + * renames are fully implemented, they will instead be handled as + * mkdirs, massive links and unlinks, and rmdirs. + */ +#ident "%W% %E% SMI" + +#include <stdio.h> + +#include "filesync.h" +#include "database.h" + + +/* local routines */ +static struct file *find_oldname(struct file *, struct file *, side_t); +static errmask_t + note_rename(struct file *, struct file *, struct file *, side_t); + +/* + * routine: + * find_renames + * + * purpose: + * recursively perform rename analysis on a directory + * + * parameters: + * file node for the suspected directory + * + * returns: + * error mask + * + * note: + * the basic algorithm here is to search every directory + * for files that have been newly created on one side, + * and then look to see if they correspond to an identical + * file that has been newly deleted on the same side. + */ +errmask_t +find_renames(struct file *fp) +{ struct file *np, *rp; + errmask_t errs = 0; + int stype, dtype, btype, side; + + /* if this isn't a directory, there is nothing to analyze */ + if (fp->f_files == 0) + return (0); + + /* look for any files under this directory that may have been renamed */ + for (np = fp->f_files; np; np = np->f_next) { + btype = np->f_info[OPT_BASE].f_type; + stype = np->f_info[OPT_SRC].f_type; + dtype = np->f_info[OPT_DST].f_type; + + /* a rename must be a file that is new on only one side */ + if (btype == 0 && stype != dtype && (!stype || !dtype)) { + side = stype ? OPT_SRC : OPT_DST; + rp = find_oldname(fp, np, side); + if (rp) + errs |= note_rename(fp, np, rp, side); + } + } + + /* recursively examine all my children */ + for (np = fp->f_files; np; np = np->f_next) { + errs |= find_renames(np); + } + + return (errs); +} + +/* + * routine: + * find_oldname + * + * purpose: + * to search for an old name for a newly discovered file + * + * parameters: + * file node for the containing directory + * file node for the new file + * which side the rename is believed to have happened on + * + * returns: + * pointer to likely previous file + * 0 no candidate found + * + * note: + * this routine only deals with simple renames within a single + * directory. + */ +static struct file *find_oldname(struct file *dirp, struct file *new, + side_t side) +{ struct file *fp; + long maj, min; + ino_t inum; + off_t size; + side_t otherside = (side == OPT_SRC) ? OPT_DST : OPT_SRC; + + /* figure out what we're looking for */ + inum = new->f_info[side].f_ino; + maj = new->f_info[side].f_d_maj; + min = new->f_info[side].f_d_min; + size = new->f_info[side].f_size; + + /* + * search the same directory for any entry that might describe + * the previous name of the new file. + */ + for (fp = dirp->f_files; fp; fp = fp->f_next) { + /* previous name on changed side must no longer exist */ + if (fp->f_info[side].f_type != 0) + continue; + + /* previous name on the other side must still exist */ + if (fp->f_info[otherside].f_type == 0) + continue; + + /* it must describe the same inode as the new file */ + if (fp->f_info[OPT_BASE].f_type != new->f_info[side].f_type) + continue; /* must be same type */ + if (((side == OPT_SRC) ? fp->f_s_inum : fp->f_d_inum) != inum) + continue; /* must be same inode # */ + if (((side == OPT_SRC) ? fp->f_s_maj : fp->f_d_maj) != maj) + continue; /* must be same major # */ + if (((side == OPT_SRC) ? fp->f_s_min : fp->f_d_min) != min) + continue; /* must be same minor # */ + + /* + * occasionally a prompt delete and create can reuse the + * same i-node in the same directory. What we really + * want is generation, but that isn't available just + * yet, so our poor-man's approximation is the size. + * There is little point in checking ownership and + * modes, since the fact that it is in the same + * directory strongly suggests that it is the same + * user who is doing the deleting and creating. + */ + if (fp->f_info[OPT_BASE].f_size != size) + continue; + + /* looks like we found a match */ + return (fp); + } + + /* no joy */ + return (0); +} + +/* + * routine: + * note_rename + * + * purpose: + * to record a discovered rename, so that the reconciliation + * phase will deal with it as a rename rather than as link + * followed by an unlink. + * + * parameters: + * file node for the containing directory + * file node for the new file + * file node for the old file + * which side the rename is believed to have happened on + * + * returns: + * error mask + */ +static errmask_t +note_rename(struct file *dirp, struct file *new, + struct file *old, side_t side) +{ + int dir; + errmask_t errs = 0; + static char *sidenames[] = {"base", "source", "dest"}; + + dir = new->f_info[side].f_type == S_IFDIR; + + if (opt_debug & DBG_ANAL) + fprintf(stderr, "ANAL: NOTE RENAME %s %s/%s -> %s/%s on %s\n", + dir ? "directory" : "file", + dirp->f_name, old->f_name, dirp->f_name, new->f_name, + sidenames[side]); + + /* FIX: we don't deal with directory renames yet */ + if (dir) + return (0); + + /* note that a rename has taken place */ + if (side == OPT_SRC) { + new->f_srcdiffs |= D_RENAME_TO; + old->f_srcdiffs |= D_RENAME_FROM; + } else { + new->f_dstdiffs |= D_RENAME_TO; + old->f_dstdiffs |= D_RENAME_FROM; + } + + /* put a link to the old name in the new name */ + new->f_previous = old; + + /* for most files, there is nothing else we have to do */ + if (!dir) + return (errs); + + /* + * FIX ... someday we are going to have to merge the old and + * new children into a single tree, but there are + * horrendous backout problems if we are unable to + * do the mvdir, so I have postponed this feature. + */ + + return (errs); +} |