mirror of
https://github.com/RsyncProject/rsync.git
synced 2026-10-20 12:54:15 -04:00
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:
1 parent
f6f5ea4173
commit
839dbff2aa
4 files changed
+46
-28
No files matched your search
+13
-11
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user