[PATCH] Unsnarl missing_below/dry_run logic.
Matt McCutchen
matt at mattmccutchen.net
Wed Mar 19 02:20:09 GMT 2008
The generator can skip a directory's contents altogether due to
--ignore-non-existing, a daemon exclude, or a mkdir failure. On a --dry-run,
the generator can also note the missingness of a directory while still scanning
its contents. These two scenarios were conflated using a single set of
missing_below/missing_dir variables in combination with transient increments in
dry_run; this caused at least three bugs.
Now recv_generator has separate variables for the two scenarios, called skip_dir
and dry_missing_dir, respectively. For simplicity, we take the F_DEPTH instead
of having separate *_below variables. We mark both kinds of missing dirs with
FLAG_MISSING_DIR. (dry_run > 1) iff the *root* of the destination does not
exist; it is no longer incremented for missing subdirs. I added tests for the
three fixed bugs in missing.test.
---
generator.c | 79 +++++++++++++++++++++++++-----------------------
testsuite/missing.test | 28 +++++++++++++++++
2 files changed, 69 insertions(+), 38 deletions(-)
create mode 100644 testsuite/missing.test
diff --git a/generator.c b/generator.c
index c06ea0d..b209812 100644
--- a/generator.c
+++ b/generator.c
@@ -1213,6 +1213,14 @@ static void list_file_entry(struct file_struct *f)
static int phase = 0;
static int dflt_perms;
+static int implied_dirs_are_missing;
+/* Helper for recv_generator's skip_dir and dry_missing_dir tests. */
+static BOOL is_below(struct file_struct *file, struct file_struct *subtree)
+{
+ return F_DEPTH(file) > F_DEPTH(subtree)
+ && (!implied_dirs_are_missing || f_name_has_prefix(file, subtree));
+}
+
/* Acts on the indicated item in cur_flist whose name is fname. If a dir,
* make sure it exists, and has the right permissions/timestamp info. For
* all other non-regular files (symlinks, etc.) we create them here. For
@@ -1227,9 +1235,12 @@ static int dflt_perms;
static void recv_generator(char *fname, struct file_struct *file, int ndx,
int itemizing, enum logcode code, int f_out)
{
- static int missing_below = -1;
static const char *parent_dirname = "";
- static struct file_struct *missing_dir = NULL;
+ /* Missing dir not created due to --dry-run; will still be scanned. */
+ static struct file_struct *dry_missing_dir = NULL;
+ /* Missing dir whose contents are skipped altogether due to
+ * --ignore-non-existing, daemon exclude, or mkdir failure. */
+ static struct file_struct *skip_dir = NULL;
static struct file_list *fuzzy_dirlist = NULL;
static int need_fuzzy_dirlist = 0;
struct file_struct *fuzzy_file = NULL;
@@ -1241,7 +1252,6 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
char *fnamecmp, *partialptr, *backupptr = NULL;
char fnamecmpbuf[MAXPATHLEN];
uchar fnamecmp_type;
- int implied_dirs_are_missing = relative_paths && !implied_dirs && protocol_version < 30;
int del_opts = delete_mode || force_delete ? DEL_RECURSE : 0;
int is_dir = !S_ISDIR(file->mode) ? 0
: inc_recurse && ndx != cur_flist->ndx_start - 1 ? -1
@@ -1258,22 +1268,16 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
return;
}
- if (missing_below >= 0) {
- if (F_DEPTH(file) <= missing_below
- || (implied_dirs_are_missing && !f_name_has_prefix(file, missing_dir))) {
- if (dry_run)
- dry_run--;
- missing_below = -1;
- } else if (!dry_run) {
- if (is_dir)
- file->flags |= FLAG_MISSING_DIR;
+ if (skip_dir && is_below(file, skip_dir)) {
+ if (is_dir)
+ file->flags |= FLAG_MISSING_DIR;
#ifdef SUPPORT_HARD_LINKS
- if (F_IS_HLINKED(file))
- handle_skipped_hlink(file, itemizing, code, f_out);
+ else if (F_IS_HLINKED(file))
+ handle_skipped_hlink(file, itemizing, code, f_out);
#endif
- return;
- }
- }
+ return;
+ } else
+ skip_dir = NULL;
if (server_filter_list.head) {
if (check_filter(&server_filter_list, fname, is_dir) < 0) {
@@ -1298,7 +1302,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
#ifdef SUPPORT_XATTRS
sx.xattr = NULL;
#endif
- if (dry_run > 1) {
+ if (dry_run > 1 || (dry_missing_dir && is_below(file, dry_missing_dir))) {
+ parent_is_dry_missing:
if (fuzzy_dirlist) {
flist_free(fuzzy_dirlist);
fuzzy_dirlist = NULL;
@@ -1307,14 +1312,18 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
statret = -1;
stat_errno = ENOENT;
} else {
+ dry_missing_dir = NULL;
const char *dn = file->dirname ? file->dirname : ".";
if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) {
if (relative_paths && !implied_dirs
- && do_stat(dn, &sx.st) < 0
- && create_directory_path(fname) < 0) {
- rsyserr(FERROR_XFER, errno,
- "recv_generator: mkdir %s failed",
- full_fname(dn));
+ && do_stat(dn, &sx.st) < 0) {
+ if (dry_run)
+ goto parent_is_dry_missing;
+ if (create_directory_path(fname) < 0) {
+ rsyserr(FERROR_XFER, errno,
+ "recv_generator: mkdir %s failed",
+ full_fname(dn));
+ }
}
if (fuzzy_dirlist) {
flist_free(fuzzy_dirlist);
@@ -1343,12 +1352,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
if (is_dir) {
if (is_dir < 0)
return;
- if (missing_below < 0) {
- if (dry_run)
- dry_run++;
- missing_below = F_DEPTH(file);
- missing_dir = file;
- }
+ skip_dir = file;
file->flags |= FLAG_MISSING_DIR;
}
#ifdef SUPPORT_HARD_LINKS
@@ -1404,10 +1408,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
goto skipping_dir_contents;
statret = -1;
}
- if (dry_run && statret != 0 && missing_below < 0) {
- missing_below = F_DEPTH(file);
- missing_dir = file;
- dry_run++;
+ if (dry_run && statret != 0) {
+ dry_missing_dir = file;
+ file->flags |= FLAG_MISSING_DIR;
}
real_ret = statret;
real_sx = sx;
@@ -1440,8 +1443,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
skipping_dir_contents:
rprintf(FERROR,
"*** Skipping any contents from this failed directory ***\n");
- missing_below = F_DEPTH(file);
- missing_dir = file;
+ skip_dir = file;
file->flags |= FLAG_MISSING_DIR;
goto cleanup;
}
@@ -1474,8 +1476,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
DEV_MINOR(devp) = minor(real_sx.st.st_dev);
}
}
- else if (delete_during && f_out != -1 && !phase && dry_run < 2
- && (file->flags & FLAG_CONTENT_DIR))
+ else if (delete_during && f_out != -1 && !phase
+ && BITS_SETnUNSET(file->flags, FLAG_CONTENT_DIR, FLAG_MISSING_DIR))
delete_in_dir(fname, file, &real_sx.st.st_dev);
goto cleanup;
}
@@ -1729,7 +1731,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
} else
partialptr = NULL;
- if (statret != 0 && fuzzy_dirlist && dry_run <= 1) {
+ if (statret != 0 && fuzzy_dirlist) {
int j = find_fuzzy(file, fuzzy_dirlist);
if (j >= 0) {
fuzzy_file = fuzzy_dirlist->files[j];
@@ -2125,6 +2127,7 @@ void generate_files(int f_out, const char *local_name)
lull_mod = allowed_lull * 5;
symlink_timeset_failed_flags = ITEM_REPORT_TIME
| (protocol_version >= 30 || !am_server ? ITEM_REPORT_TIMEFAIL : 0);
+ implied_dirs_are_missing = relative_paths && !implied_dirs && protocol_version < 30;
if (verbose > 2)
rprintf(FINFO, "generator starting pid=%ld\n", (long)getpid());
diff --git a/testsuite/missing.test b/testsuite/missing.test
new file mode 100644
index 0000000..705050b
--- /dev/null
+++ b/testsuite/missing.test
@@ -0,0 +1,28 @@
+#! /bin/sh
+
+# This program is distributable under the terms of the GNU GPL (see
+# COPYING).
+
+# Test three bugs fixed by my redoing of the missing_below logic.
+
+. $srcdir/testsuite/rsync.fns
+
+mkdir "$fromdir" "$todir"
+mkdir "$fromdir/subdir"
+echo data >"$fromdir/subdir/file"
+echo data >"$todir/other"
+
+# Test 1: Too much "not creating new..." output on a dry run
+$RSYNC -n -r --ignore-non-existing -vv "$fromdir/" "$todir/" | tee "$scratchdir/out"
+if grep 'not creating new.*subdir/file' "$scratchdir/out" >/dev/null; then
+ test_fail 'test 1 failed'
+fi
+
+# Test 2: Attempt to make a fuzzy dirlist for a dir not created on a dry run
+$RSYNC -n -r -R --no-implied-dirs -y "$fromdir/./subdir/file" "$todir/" \
+ || test_fail 'test 2 failed'
+
+# Test 3: --delete-after pass skipped when last dir is dry-missing
+$RSYNC -n -r --delete-after -i "$fromdir/" "$todir/" | tee "$scratchdir/out"
+grep '^\*deleting other' "$scratchdir/out" >/dev/null \
+ || test_fail 'test 3 failed'
--
1.5.4.3.193.g6dd0e
More information about the rsync
mailing list