diff --git a/flist.c b/flist.c index 7bf3030e5..77dfc6224 100644 --- a/flist.c +++ b/flist.c @@ -132,6 +132,20 @@ static int64 tmp_dev = -1, tmp_ino; #endif static char tmp_sum[MAX_DIGEST_LEN]; +#ifdef ST_MTIME_NSEC +/* Return st_mtim nsec if it is in the wire-valid range, else 0. A pre-1970 + * mtime with a sub-second part yields a negative tv_nsec on some platforms + * (e.g. cygwin), which a protocol-31 peer rejects as an out-of-range value. */ +static inline uint32 wire_mtime_nsec_from_stat(const STRUCT_STAT *stp) +{ + unsigned long nsec = (unsigned long)stp->ST_MTIME_NSEC; + + if (nsec > MAX_WIRE_NSEC) + return 0; + return (uint32)nsec; +} +#endif + static char empty_sum[MAX_DIGEST_LEN]; static int flist_count_offset; /* for --delete --progress */ static int show_filelist_progress; @@ -1401,7 +1415,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, } #ifdef ST_MTIME_NSEC - if (st.ST_MTIME_NSEC && protocol_version >= 31) + if (wire_mtime_nsec_from_stat(&st) && protocol_version >= 31) extra_len += EXTRA_LEN; #endif #if SIZEOF_CAPITAL_OFF_T >= 8 @@ -1457,9 +1471,13 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, file->flags = flags; file->modtime = st.st_mtime; #ifdef ST_MTIME_NSEC - if (st.ST_MTIME_NSEC && protocol_version >= 31) { - file->flags |= FLAG_MOD_NSEC; - F_MOD_NSEC(file) = st.ST_MTIME_NSEC; + { + uint32 nsec = wire_mtime_nsec_from_stat(&st); + + if (nsec && protocol_version >= 31) { + file->flags |= FLAG_MOD_NSEC; + F_MOD_NSEC(file) = nsec; + } } #endif file->len32 = (uint32)st.st_size; diff --git a/rsync.h b/rsync.h index 479ac4848..31ceee558 100644 --- a/rsync.h +++ b/rsync.h @@ -173,6 +173,8 @@ #define MAX_BASIS_DIRS 20 #define MAX_SERVER_ARGS (MAX_BASIS_DIRS*2 + 100) +#define MAX_WIRE_NSEC 999999999 + #define COMPARE_DEST 1 #define COPY_DEST 2 #define LINK_DEST 3