Implement --tweak, --no-tweak, --no-tweak-hlinked options.
[rsync/rsync.git] / generator.c
index ff31e06..4a3e65b 100644 (file)
@@ -58,6 +58,7 @@ extern int update_only;
 extern int ignore_existing;
 extern int ignore_non_existing;
 extern int inplace;
+extern int tweak_attrs;
 extern int append_mode;
 extern int make_backups;
 extern int csum_length;
@@ -1210,6 +1211,11 @@ static void list_file_entry(struct file_struct *f)
        }
 }
 
+static int may_tweak(STRUCT_STAT *st)
+{
+       return tweak_attrs == 2 || (tweak_attrs == 1 && st->st_nlink == 1);
+}
+
 static int phase = 0;
 static int dflt_perms;
 
@@ -1501,6 +1507,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
 
        if (preserve_links && S_ISLNK(file->mode)) {
 #ifdef SUPPORT_LINKS
+               int iflags = 0;
                const char *sl = F_SYMLINK(file);
                if (safe_symlinks && unsafe_symlink(sl, fname)) {
                        if (verbose) {
@@ -1521,7 +1528,15 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        else if ((len = readlink(fname, lnk, MAXPATHLEN-1)) > 0
                              && strncmp(lnk, sl, len) == 0 && sl[len] == '\0') {
                                /* The link is pointing to the right place. */
-                               set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
+                               if (may_tweak(&sx.st)) {
+                                       if (verbose > 2)
+                                               rprintf(FINFO, "possibly tweaking attributes of %s\n", fname);
+                                       set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
+                               } else if (!unchanged_attrs(fname, file, &sx)) {
+                                       if (verbose > 2)
+                                               rprintf(FINFO, "recreating %s due to changed attributes\n", fname);
+                                       goto recreate_symlink;
+                               }
                                if (itemizing)
                                        itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
 #if defined SUPPORT_HARD_LINKS && defined CAN_HARDLINK_SYMLINK
@@ -1531,7 +1546,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                if (remove_source_files == 1)
                                        goto return_with_success;
                                goto cleanup;
-                       }
+                       } else
+                               iflags = ITEM_REPORT_CHANGE;
+                 recreate_symlink:
                        /* Not the right symlink (or not a symlink), so
                         * delete it. */
                        if (delete_item(fname, sx.st.st_mode, del_opts | DEL_FOR_SYMLINK) != 0)
@@ -1565,18 +1582,20 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        set_file_attrs(fname, file, NULL, NULL, 0);
                        if (itemizing) {
                                itemize(fname, file, ndx, statret, &sx,
-                                       ITEM_LOCAL_CHANGE|ITEM_REPORT_CHANGE, 0, NULL);
+                                       ITEM_LOCAL_CHANGE|iflags, 0, NULL);
                        }
-                       if (code != FNONE && verbose)
+                       if ((iflags & ITEM_REPORT_CHANGE) && code != FNONE && verbose)
                                rprintf(code, "%s -> %s\n", fname, sl);
 #ifdef SUPPORT_HARD_LINKS
                        if (preserve_hard_links && F_IS_HLINKED(file))
                                finish_hard_link(file, fname, ndx, NULL, itemizing, code, -1);
 #endif
-                       /* This does not check remove_source_files == 1
-                        * because this is one of the items that the old
-                        * --remove-sent-files option would remove. */
-                       if (remove_source_files)
+                       /* When the symlink value changed, we do not check
+                        * remove_source_files == 1 because this is one of the
+                        * items that the old --remove-sent-files option would
+                        * remove. */
+                       if ((iflags & ITEM_REPORT_CHANGE) ? remove_source_files
+                             : remove_source_files == 1)
                                goto return_with_success;
                }
 #endif
@@ -1585,6 +1604,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
 
        if ((am_root && preserve_devices && IS_DEVICE(file->mode))
         || (preserve_specials && IS_SPECIAL(file->mode))) {
+               int iflags = 0;
                uint32 *devp = F_RDEV_P(file);
                dev_t rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
                if (statret == 0) {
@@ -1602,7 +1622,15 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                         && BITS_EQUAL(sx.st.st_mode, file->mode, _S_IFMT)
                         && sx.st.st_rdev == rdev) {
                                /* The device or special file is identical. */
-                               set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
+                               if (may_tweak(&sx.st)) {
+                                       if (verbose > 2)
+                                               rprintf(FINFO, "possibly tweaking attributes of %s\n", fname);
+                                       set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
+                               } else if (!unchanged_attrs(fname, file, &sx)) {
+                                       if (verbose > 2)
+                                               rprintf(FINFO, "recreating %s due to changed attributes\n", fname);
+                                       goto recreate_D;
+                               }
                                if (itemizing)
                                        itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
 #ifdef SUPPORT_HARD_LINKS
@@ -1612,7 +1640,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                if (remove_source_files == 1)
                                        goto return_with_success;
                                goto cleanup;
-                       }
+                       } else
+                               iflags = ITEM_REPORT_CHANGE;
+                 recreate_D:
                        if (delete_item(fname, sx.st.st_mode, del_opts | del_for_flag) != 0)
                                goto cleanup;
                } else if (basis_dir[0] != NULL) {
@@ -1649,9 +1679,9 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        set_file_attrs(fname, file, NULL, NULL, 0);
                        if (itemizing) {
                                itemize(fname, file, ndx, statret, &sx,
-                                       ITEM_LOCAL_CHANGE|ITEM_REPORT_CHANGE, 0, NULL);
+                                       ITEM_LOCAL_CHANGE|iflags, 0, NULL);
                        }
-                       if (code != FNONE && verbose)
+                       if ((iflags & ITEM_REPORT_CHANGE) && code != FNONE && verbose)
                                rprintf(code, "%s\n", fname);
 #ifdef SUPPORT_HARD_LINKS
                        if (preserve_hard_links && F_IS_HLINKED(file))
@@ -1769,13 +1799,31 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
        else if (fnamecmp_type == FNAMECMP_FUZZY)
                ;
        else if (unchanged_file(fnamecmp, file, &sx.st)) {
+               /* fnamecmp == fname, fnamecmp_type == FNAMECMP_FNAME */
+               int iflags = 0;
+
                if (partialptr) {
                        do_unlink(partialptr);
                        handle_partial_dir(partialptr, PDIR_DELETE);
                }
-               set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
+               if (may_tweak(&sx.st)) {
+                       /* Currently, we call set_file_attrs on all tweakable
+                        * files, though ideally it would have no effect when
+                        * unchanged_attrs returns true. */
+                       if (verbose > 2)
+                               rprintf(FINFO, "possibly tweaking attributes of %s\n", fname);
+                       set_file_attrs(fname, file, &sx, NULL, maybe_ATTRS_REPORT);
+               } else if (!unchanged_attrs(fname, file, &sx)) {
+                       /* Need to recreate the file.
+                        * copy_altdest_file sets its attributes, etc. */
+                       if (verbose > 2)
+                               rprintf(FINFO, "recreating %s due to changed attributes\n", fname);
+                       if (!dry_run && copy_altdest_file(fnamecmp, fname, file))
+                               goto cleanup;
+                       iflags |= ITEM_LOCAL_CHANGE;
+               }
                if (itemizing)
-                       itemize(fnamecmp, file, ndx, statret, &sx, 0, 0, NULL);
+                       itemize(fnamecmp, file, ndx, statret, &sx, iflags, 0, NULL);
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_IS_HLINKED(file))
                        finish_hard_link(file, fname, ndx, &sx.st, itemizing, code, -1);