Add support for comparing nanoseconds on the receiver.

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.
This commit is contained in:
Wayne Davison committed 2016-01-24 11:16:10 -08:00
1 parent f6f5ea4173
commit 839dbff2aa
4 files changed
+46 -28

No files matched your search

+13 -11
View 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) {
+5 -4
View File
@@ -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;
}
+17 -7
View File
@@ -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
+11 -6
View File
@@ -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;