[Rsync-patches] [PATCH] Make delete_in_dir use a directoryness-insensitive file-list search.

Matt McCutchen <matt at mattmccutchen.net>
Sun Nov 2 19:22:20 PST 2008


delete_in_dir uses flist_find to determine whether a destination file is
matched in the file list and thus shouldn't be deleted.  But flist_find is
directoryness-sensitive, so rsync prematurely deleted a dir that was to be
replaced by a non-dir (or vice versa) during the delete pass.  This led to
inconsistent itemization and --max-delete counting of such replacements and
necessated an ugly hack in make_file for --keep-dirlinks to work at all with
--delete.  The failure of this hack in incremental recursion mode was noticed
by eric casteleijn:

http://lists.samba.org/archive/rsync/2008-November/022019.html

This patch changes delete_in_dir to use a new flist_find_ignore_dirness
function, fixing the inconsistencies, and removes the obsolete chunk of code
from make_file.
---

This patch, which is in wip/delete-dirness of my repository, seems to
fix the problem but needs further testing.  I plan to write a test case.

Matt

 flist.c     |   41 ++++++++++++++++++++++-------------------
 generator.c |    5 ++++-
 2 files changed, 26 insertions(+), 20 deletions(-)

diff --git a/flist.c b/flist.c
index cb667aa..6042b00 100644
--- a/flist.c
+++ b/flist.c
@@ -1332,25 +1332,6 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
 	else if (!pool)
 		F_DEPTH(file) = extra_len / EXTRA_LEN;
 
-	/* This code is only used by the receiver when it is building
-	 * a list of files for a delete pass. */
-	if (keep_dirlinks && linkname_len && flist) {
-		STRUCT_STAT st2;
-		int save_mode = file->mode;
-		file->mode = S_IFDIR; /* Find a directory with our name. */
-		if (flist_find(dir_flist, file) >= 0
-		 && x_stat(thisname, &st2, NULL) == 0 && S_ISDIR(st2.st_mode)) {
-			file->modtime = st2.st_mtime;
-			file->len32 = 0;
-			file->mode = st2.st_mode;
-			if (uid_ndx)
-				F_OWNER(file) = st2.st_uid;
-			if (gid_ndx)
-				F_GROUP(file) = st2.st_gid;
-		} else
-			file->mode = save_mode;
-	}
-
 	if (basename_len == 0+1) {
 		if (!pool)
 			unmake_file(file);
@@ -2533,6 +2514,28 @@ int flist_find(struct file_list *flist, struct file_struct *f)
 	return -1;
 }
 
+/* Search for an identically-named item in the file list.  Differs from
+ * flist_find in that an item that agrees with "f" in directory-ness is
+ * preferred but one that does not is still found. */
+int flist_find_ignore_dirness(struct file_list *flist, struct file_struct *f)
+{
+	mode_t save_mode;
+	int ndx;
+
+	/* First look for an item that agrees in directory-ness. */
+	ndx = flist_find(flist, f);
+	if (ndx >= 0)
+		return ndx;
+
+	/* Temporarily flip f->mode to look for an item of opposite
+	 * directory-ness. */
+	save_mode = f->mode;
+	f->mode = S_ISDIR(f->mode) ? S_IFREG : S_IFDIR;
+	ndx = flist_find(flist, f);
+	f->mode = save_mode;
+	return ndx;
+}
+
 /*
  * Free up any resources a file_struct has allocated
  * and clear the file.
diff --git a/generator.c b/generator.c
index 6a0d527..1db0fe0 100644
--- a/generator.c
+++ b/generator.c
@@ -535,7 +535,10 @@ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t *fs_dev)
 					f_name(fp, NULL));
 			continue;
 		}
-		if (flist_find(cur_flist, fp) < 0) {
+		/* Here we want to match regardless of file type.  Replacement
+		 * of a file with one of another type is handled separately by
+		 * a delete_item call with a DEL_MAKE_ROOM flag. */
+		if (flist_find_ignore_dirness(cur_flist, fp) < 0) {
 			int flags = DEL_RECURSE;
 			if (!(fp->mode & S_IWUSR) && !am_root && (uid_t)F_OWNER(fp) == our_uid)
 				flags |= DEL_NO_UID_WRITE;
-- 
1.6.0.2.593.g91df




More information about the rsync-patches mailing list