[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