Improve handling of existing files for alt-dest opts.
authorWayne Davison <wayned@samba.org>
Fri, 18 Jan 2013 19:54:01 +0000 (11:54 -0800)
committerWayne Davison <wayned@samba.org>
Fri, 18 Jan 2013 19:57:49 +0000 (11:57 -0800)
Fixes bug #5644.

generator.c
rsync.yo

index 973e03bd0156d26744657a0bfb6e8071ea2384ea..f95e57f6b732fe553a1f4fce978f70a0edb4954c 100644 (file)
@@ -844,10 +844,14 @@ static int copy_altdest_file(const char *src, const char *dest, struct file_stru
 
 /* This is only called for regular files.  We return -2 if we've finished
  * handling the file, -1 if no dest-linking occurred, or a non-negative
- * value if we found an alternate basis file. */
+ * value if we found an alternate basis file.  If we're called with the
+ * find_exact_for_existing flag, the destination file already exists, so
+ * we only try to find an exact alt-dest match.  In this case, the returns
+ * can be -2 & -1 (both as above) as well as -3, which means that we
+ * removed the dest file but failed to create a hard link for it. */
 static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
-                        char *cmpbuf, stat_x *sxp, int itemizing,
-                        enum logcode code)
+                        char *cmpbuf, stat_x *sxp, int find_exact_for_existing,
+                        int itemizing, enum logcode code)
 {
        int best_match = -1;
        int match_level = 0;
@@ -889,10 +893,17 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
        }
 
        if (match_level == 3 && !copy_dest) {
+               if (find_exact_for_existing) {
+                       if (do_unlink(fname) < 0 && errno != ENOENT)
+                               return -1;
+               }
 #ifdef SUPPORT_HARD_LINKS
                if (link_dest) {
-                       if (!hard_link_one(file, fname, cmpbuf, 1))
+                       if (!hard_link_one(file, fname, cmpbuf, 1)) {
+                               if (find_exact_for_existing)
+                                       return -3;
                                goto try_a_copy;
+                       }
                        if (preserve_hard_links && F_IS_HLINKED(file))
                                finish_hard_link(file, fname, ndx, &sxp->st, itemizing, code, j);
                        if (!maybe_ATTRS_REPORT && (INFO_GTE(NAME, 2) || stdout_format_has_i > 1)) {
@@ -902,13 +913,18 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
                        }
                } else
 #endif
-               if (itemizing)
-                       itemize(cmpbuf, file, ndx, 0, sxp, 0, 0, NULL);
+               {
+                       if (itemizing)
+                               itemize(cmpbuf, file, ndx, 0, sxp, 0, 0, NULL);
+               }
                if (INFO_GTE(NAME, 2) && maybe_ATTRS_REPORT)
                        rprintf(FCLIENT, "%s is uptodate\n", fname);
                return -2;
        }
 
+       if (find_exact_for_existing)
+               return -1;
+
        if (match_level >= 2) {
 #ifdef SUPPORT_HARD_LINKS
          try_a_copy: /* Copy the file locally. */
@@ -1640,9 +1656,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                stat_errno = ENOENT;
        }
 
-       if (statret != 0 && basis_dir[0] != NULL) {
+       if (basis_dir[0] != NULL && (statret != 0 || !copy_dest)) {
                int j = try_dests_reg(file, fname, ndx, fnamecmpbuf, &sx,
-                                     itemizing, code);
+                                     statret == 0, itemizing, code);
                if (j == -2) {
                        if (remove_source_files == 1)
                                goto return_with_success;
@@ -1652,6 +1668,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        fnamecmp = fnamecmpbuf;
                        fnamecmp_type = j;
                        statret = 0;
+               } else if (j == -3) {
+                       statret = -1;
+                       stat_errno = ENOENT;
                }
        }
 
index a0620899a0fa5ef3a58fff35a311573915666002..655301f6f47747685b5a3fddb973a91894f3be42 100644 (file)
--- a/rsync.yo
+++ b/rsync.yo
@@ -1773,6 +1773,8 @@ directory).  If a file is found in em(DIR) that is identical to the
 sender's file, the file will NOT be transferred to the destination
 directory.  This is useful for creating a sparse backup of just files that
 have changed from an earlier backup.
+This option is typically used to copy into an empty (or newly created)
+directory.
 
 Beginning in version 2.6.4, multiple bf(--compare-dest) directories may be
 provided, which will cause rsync to search the list in the order specified
@@ -1785,6 +1787,10 @@ selected to try to speed up the transfer.
 If em(DIR) is a relative path, it is relative to the destination directory.
 See also bf(--copy-dest) and bf(--link-dest).
 
+NOTE: beginning with version 3.1.0, rsync will remove a file from a non-empty
+destination hierarchy if an exact match is found in one of the compare-dest
+hierarchies (making the end result more closely match a fresh copy).
+
 dit(bf(--copy-dest=DIR)) This option behaves like bf(--compare-dest), but
 rsync will also copy unchanged files found in em(DIR) to the destination
 directory using a local copy.
@@ -1822,10 +1828,11 @@ If a match is not found, a basis file from one of the em(DIR)s will be
 selected to try to speed up the transfer.
 
 This option works best when copying into an empty destination hierarchy, as
-rsync treats existing files as definitive (so it never looks in the link-dest
-dirs when a destination file already exists), and as malleable (so it might
-change the attributes of a destination file, which affects all the hard-linked
-versions).
+existing files may get their attributes tweaked, and that can affect alternate
+destination files via hard-links.  Also, itemizing of changes can get a bit
+muddled.  Note that prior to version 3.1.0, an alternate-directory exact match
+would never be found (nor linked into the destination) when a destination file
+already exists.
 
 Note that if you combine this option with bf(--ignore-times), rsync will not
 link any files together because it only links identical files together as a