Add support for comparing nanoseconds on the receiver.
authorWayne Davison <wayned@samba.org>
Sun, 24 Jan 2016 19:12:38 +0000 (11:12 -0800)
committerWayne Davison <wayned@samba.org>
Sun, 24 Jan 2016 19:16:10 +0000 (11:16 -0800)
This patch adds the ability to specify --modify-window=-1 (aka -@-1) to
ask rsync to compare files with the full nanosecond timestamps.  The
default is still -@0 for the moment, which ignores nanoseconds in time
comparisons.  Changing the default to -1 would cause a copy from ext4 to
ext3 to constantly compare as different, or a copy there and back again
to do a full copy as it zeroed all the nanosecond times.  Such a change
might be too much of a functional difference for things like backup
solutions to handle without a warning period.  The current plan is to
support nanosecond comparisons for those that want them, and possibly
change the default window value from 0 to -1 at some point in the
future.

generator.c
options.c
rsync.yo
util.c

index ddf44a021a21596b043068de334de33778a0a1cb..fd7c8f863646a5a4ef2142a27938c042512d9448 100644 (file)
@@ -382,9 +382,13 @@ static void do_delete_pass(void)
                rprintf(FINFO, "                    \r");
 }
 
-static inline int time_differs(struct file_struct *file, stat_x *sxp)
+static inline int time_diff(STRUCT_STAT *stp, struct file_struct *file)
 {
-       return cmp_time(sxp->st.st_mtime, file->modtime);
+#ifdef ST_MTIME_NSEC
+       return cmp_time(stp->st_mtime, stp->ST_MTIME_NSEC, file->modtime, F_MOD_NSEC(file));
+#else
+       return cmp_time(stp->st_mtime, 0L, file->modtime, 0L);
+#endif
 }
 
 static inline int perms_differ(struct file_struct *file, stat_x *sxp)
@@ -441,7 +445,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
 {
        if (S_ISLNK(file->mode)) {
 #ifdef CAN_SET_SYMLINK_TIMES
-               if (preserve_times & PRESERVE_LINK_TIMES && time_differs(file, sxp))
+               if (preserve_times & PRESERVE_LINK_TIMES && time_diff(&sxp->st, file))
                        return 0;
 #endif
 #ifdef CAN_CHMOD_SYMLINK
@@ -461,7 +465,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
                        return 0;
 #endif
        } else {
-               if (preserve_times && time_differs(file, sxp))
+               if (preserve_times && time_diff(&sxp->st, file))
                        return 0;
                if (perms_differ(file, sxp))
                        return 0;
@@ -496,7 +500,7 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
                        if (iflags & ITEM_LOCAL_CHANGE)
                                iflags |= symlink_timeset_failed_flags;
                } else if (keep_time
-                ? cmp_time(file->modtime, sxp->st.st_mtime) != 0
+                ? time_diff(&sxp->st, file)
                 : iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !(iflags & ITEM_MATCHED)
                  && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
                        iflags |= ITEM_REPORT_TIME;
@@ -588,7 +592,7 @@ int unchanged_file(char *fn, struct file_struct *file, STRUCT_STAT *st)
        if (ignore_times)
                return 0;
 
-       return cmp_time(st->st_mtime, file->modtime) == 0;
+       return time_diff(st, file) == 0;
 }
 
 
@@ -765,7 +769,7 @@ static struct file_struct *find_fuzzy(struct file_struct *file, struct file_list
                        if (!S_ISREG(fp->mode) || !F_LENGTH(fp) || fp->flags & FLAG_FILE_SENT)
                                continue;
 
-                       if (F_LENGTH(fp) == F_LENGTH(file) && cmp_time(fp->modtime, file->modtime) == 0) {
+                       if (F_LENGTH(fp) == F_LENGTH(file) && cmp_time(fp->modtime, 0L, file->modtime, 0L) == 0) {
                                if (DEBUG_GTE(FUZZY, 2))
                                        rprintf(FINFO, "fuzzy size/modtime match for %s\n", f_name(fp, NULL));
                                *fnamecmp_type_ptr = FNAMECMP_FUZZY + i;
@@ -1674,8 +1678,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                goto cleanup;
        }
 
-       if (update_only > 0 && statret == 0
-           && cmp_time(sx.st.st_mtime, file->modtime) > 0) {
+       if (update_only > 0 && statret == 0 && time_diff(&sx.st, file) > 0) {
                if (INFO_GTE(SKIP, 1))
                        rprintf(FINFO, "%s is newer\n", fname);
 #ifdef SUPPORT_HARD_LINKS
@@ -2062,8 +2065,7 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
                        do_chmod(fname, file->mode);
                if (need_retouch_dir_times) {
                        STRUCT_STAT st;
-                       if (link_stat(fname, &st, 0) == 0
-                        && cmp_time(st.st_mtime, file->modtime) != 0)
+                       if (link_stat(fname, &st, 0) == 0 && time_diff(&st, file))
                                set_modtime(fname, file->modtime, F_MOD_NSEC(file), file->mode);
                }
                if (counter >= loopchk_limit) {
index 74239bf29cbf5058f660ea6404181dcb5cec9c59..4a5cdc8bb6e8a3c48bb74ae41020424ecf34b892 100644 (file)
--- a/options.c
+++ b/options.c
@@ -755,7 +755,7 @@ void usage(enum logcode F)
   rprintf(F," -I, --ignore-times          don't skip files that match in size and mod-time\n");
   rprintf(F," -M, --remote-option=OPTION  send OPTION to the remote side only\n");
   rprintf(F,"     --size-only             skip files that match in size\n");
-  rprintf(F,"     --modify-window=NUM     compare mod-times with reduced accuracy\n");
+  rprintf(F," -@, --modify-window=NUM     set the accuracy for mod-time comparisons\n");
   rprintf(F," -T, --temp-dir=DIR          create temporary files in directory DIR\n");
   rprintf(F," -y, --fuzzy                 find similar file for basis if no dest file\n");
   rprintf(F,"     --compare-dest=DIR      also compare destination files relative to DIR\n");
@@ -871,7 +871,7 @@ static struct poptOption long_options[] = {
   {"omit-link-times", 'J', POPT_ARG_VAL,    &omit_link_times, 1, 0, 0 },
   {"no-omit-link-times",0, POPT_ARG_VAL,    &omit_link_times, 0, 0, 0 },
   {"no-J",             0,  POPT_ARG_VAL,    &omit_link_times, 0, 0, 0 },
-  {"modify-window",    0,  POPT_ARG_INT,    &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
+  {"modify-window",   '@', POPT_ARG_INT,    &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
   {"super",            0,  POPT_ARG_VAL,    &am_root, 2, 0, 0 },
   {"no-super",         0,  POPT_ARG_VAL,    &am_root, 0, 0, 0 },
   {"fake-super",       0,  POPT_ARG_VAL,    &am_root, -1, 0, 0 },
@@ -2649,8 +2649,9 @@ void server_options(char **args, int *argc_p)
        else if (missing_args == 1 && !am_sender)
                args[ac++] = "--ignore-missing-args";
 
-       if (modify_window_set) {
-               if (asprintf(&arg, "--modify-window=%d", modify_window) < 0)
+       if (modify_window_set && am_sender) {
+               char *fmt = modify_window < 0 ? "-@%d" : "--modify-window=%d";
+               if (asprintf(&arg, fmt, modify_window) < 0)
                        goto oom;
                args[ac++] = arg;
        }
index 09ee92f6980c19f609bbc95bb7a82b39f48cd15d..897182860d4f483d46d2633ee781a0d98bb5472b 100644 (file)
--- a/rsync.yo
+++ b/rsync.yo
@@ -413,7 +413,7 @@ to the detailed description below for a complete description.  verb(
      --contimeout=SECONDS    set daemon connection timeout in seconds
  -I, --ignore-times          don't skip files that match size and time
      --size-only             skip files that match in size
-     --modify-window=NUM     compare mod-times with reduced accuracy
+ -@, --modify-window=NUM     set the accuracy for mod-time comparisons
  -T, --temp-dir=DIR          create temporary files in directory DIR
  -y, --fuzzy                 find similar file for basis if no dest file
      --compare-dest=DIR      also compare received files relative to DIR
@@ -605,13 +605,23 @@ time to just looking for files that have changed in size.  This is useful
 when starting to use rsync after using another mirroring system which may
 not preserve timestamps exactly.
 
-dit(bf(--modify-window)) When comparing two timestamps, rsync treats the
+dit(bf(-@, --modify-window)) When comparing two timestamps, rsync treats the
 timestamps as being equal if they differ by no more than the modify-window
-value.  This is normally 0 (for an exact match), but you may find it useful
-to set this to a larger value in some situations.  In particular, when
-transferring to or from an MS Windows FAT filesystem (which represents
-times with a 2-second resolution), bf(--modify-window=1) is useful
-(allowing times to differ by up to 1 second).
+value.  The default is 0, which matches just integer seconds.  If you specify a
+negative value (and the receiver is at least version 3.1.3) then nanoseconds
+will also be taken into account.  Specifying 1 is useful for copies to/from MS
+Windows FAT filesystems, because FAT represents times with a 2-second
+resolution (allowing times to differ from the original by up to 1 second).
+
+If you want all your transfers to default to comparing nanoseconds, you can
+create a ~/.popt file and put these lines in it:
+
+quote(tt(   rsync alias -a -a@-1))
+quote(tt(   rsync alias -t -t@-1))
+
+With that as the default, you'd need to specify bf(--modify-window=0) (aka
+bf(-@0)) to override it and ignore nanoseconds, e.g. if you're copying between
+ext3 and ext4, or if the receiving rsync is older than 3.1.3.
 
 dit(bf(-c, --checksum)) This changes the way rsync checks if the files have
 been changed and are in need of a transfer.  Without this option, rsync
diff --git a/util.c b/util.c
index baee467c1a6df49fded3552591c7e72989db6165..2a3e5ba98c113f3dce5010c37da989122cbeea22 100644 (file)
--- a/util.c
+++ b/util.c
@@ -1324,15 +1324,20 @@ char *timestring(time_t t)
  *
  * @retval -1 if the 2nd is later
  **/
-int cmp_time(time_t file1, time_t file2)
+int cmp_time(time_t f1_sec, unsigned long f1_nsec, time_t f2_sec, unsigned long f2_nsec)
 {
-       if (file2 > file1) {
+       if (f2_sec > f1_sec) {
                /* The final comparison makes sure that modify_window doesn't overflow a
-                * time_t, which would mean that file2 must be in the equality window. */
-               if (!modify_window || (file2 > file1 + modify_window && file1 + modify_window > file1))
+                * time_t, which would mean that f2_sec must be in the equality window. */
+               if (modify_window <= 0 || (f2_sec > f1_sec + modify_window && f1_sec + modify_window > f1_sec))
                        return -1;
-       } else if (file1 > file2) {
-               if (!modify_window || (file1 > file2 + modify_window && file2 + modify_window > file2))
+       } else if (f1_sec > f2_sec) {
+               if (modify_window <= 0 || (f1_sec > f2_sec + modify_window && f2_sec + modify_window > f2_sec))
+                       return 1;
+       } else if (modify_window < 0) {
+               if (f2_nsec > f1_nsec)
+                       return -1;
+               else if (f1_nsec > f2_nsec)
                        return 1;
        }
        return 0;