From 1a2e41af941573e69c02370329afe0192b61f213 Mon Sep 17 00:00:00 2001 From: Wayne Davison Date: Mon, 7 Sep 2009 13:49:52 -0700 Subject: [PATCH] Add support for transferring & setting nsec time values. --- configure.in | 15 +++++++++++++-- flist.c | 42 ++++++++++++++++++++++++++++++++++++++++-- generator.c | 2 +- rsync.c | 2 +- rsync.h | 29 +++++++++++++++++------------ tls.c | 28 +++++++++++++++++++++++++--- util.c | 15 ++++++++++++--- 7 files changed, 109 insertions(+), 24 deletions(-) diff --git a/configure.in b/configure.in index fcde0cab..b5bebe5f 100644 --- a/configure.in +++ b/configure.in @@ -370,7 +370,18 @@ AC_TYPE_SIGNAL AC_TYPE_UID_T AC_CHECK_TYPES([mode_t,off_t,size_t,pid_t,id_t]) AC_TYPE_GETGROUPS -AC_CHECK_MEMBERS([struct stat.st_rdev]) +AC_CHECK_MEMBERS([struct stat.st_rdev, + struct stat.st_mtimensec, + struct stat.st_mtim.tv_nsec],,,[ +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif]) TYPE_SOCKLEN_T @@ -563,7 +574,7 @@ AC_CHECK_FUNCS(waitpid wait4 getcwd strdup chown chmod lchmod mknod mkfifo \ setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \ strerror putenv iconv_open locale_charset nl_langinfo getxattr \ extattr_get_link sigaction sigprocmask setattrlist getgrouplist \ - initgroups) + initgroups utimensat) dnl cygwin iconv.h defines iconv_open as libiconv_open if test x"$ac_cv_func_iconv_open" != x"yes"; then diff --git a/flist.c b/flist.c index 8b08ff6f..7a28b73b 100644 --- a/flist.c +++ b/flist.c @@ -87,6 +87,14 @@ extern int filesfrom_convert; extern iconv_t ic_send, ic_recv; #endif +#ifdef HAVE_UTIMENSAT +#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC +#define ST_MTIME_NSEC st_mtim.tv_nsec +#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC) +#define ST_MTIME_NSEC st_mtimensec +#endif +#endif + #define PTR_SIZE (sizeof (struct file_struct *)) int io_error; @@ -495,6 +503,8 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file, xflags |= XMIT_SAME_TIME; else modtime = file->modtime; + if (NSEC_BUMP(file) && protocol_version >= 31) + xflags |= XMIT_MOD_NSEC; #ifdef SUPPORT_HARD_LINKS if (tmp_dev != 0) { @@ -577,6 +587,8 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file, else write_int(f, modtime); } + if (xflags & XMIT_MOD_NSEC) + write_varint(f, F_MOD_NSEC(file)); if (!(xflags & XMIT_SAME_MODE)) write_int(f, to_wire_mode(mode)); if (preserve_uid && !(xflags & XMIT_SAME_UID)) { @@ -685,6 +697,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist, int extra_len = file_extra_cnt * EXTRA_LEN; int first_hlink_ndx = -1; int64 file_length; + uint32 modtime_nsec; const char *basename; struct file_struct *file; alloc_pool_t *pool; @@ -768,6 +781,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist, struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start]; file_length = F_LENGTH(first); modtime = first->modtime; + modtime_nsec = F_MOD_NSEC(first); mode = first->mode; if (preserve_uid) uid = F_OWNER(first); @@ -801,6 +815,10 @@ static struct file_struct *recv_file_entry(struct file_list *flist, } else modtime = read_int(f); } + if (xflags & XMIT_MOD_NSEC) + modtime_nsec = read_varint(f); + else + modtime_nsec = 0; if (!(xflags & XMIT_SAME_MODE)) mode = from_wire_mode(read_int(f)); @@ -898,6 +916,10 @@ static struct file_struct *recv_file_entry(struct file_list *flist, #if SIZEOF_INT64 >= 8 if (file_length > 0xFFFFFFFFu && S_ISREG(mode)) extra_len += EXTRA_LEN; +#endif +#ifdef HAVE_UTIMENSAT + if (modtime_nsec) + extra_len += EXTRA_LEN; #endif if (file_length < 0) { rprintf(FERROR, "Offset underflow: file-length is negative\n"); @@ -934,6 +956,12 @@ static struct file_struct *recv_file_entry(struct file_list *flist, file->flags |= FLAG_HLINKED; #endif file->modtime = (time_t)modtime; +#ifdef HAVE_UTIMENSAT + if (modtime_nsec) { + file->flags |= FLAG_MOD_NSEC; + OPT_EXTRA(file, 0)->unum = modtime_nsec; + } +#endif file->len32 = (uint32)file_length; #if SIZEOF_INT64 >= 8 if (file_length > 0xFFFFFFFFu && S_ISREG(mode)) { @@ -942,7 +970,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist, exit_cleanup(RERR_UNSUPPORTED); #else file->flags |= FLAG_LENGTH64; - OPT_EXTRA(file, 0)->unum = (uint32)(file_length >> 32); + OPT_EXTRA(file, NSEC_BUMP(file))->unum = (uint32)(file_length >> 32); #endif } #endif @@ -1272,6 +1300,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist, linkname_len = 0; #endif +#ifdef ST_MTIME_NSEC + if (st.ST_MTIME_NSEC && protocol_version >= 31) + extra_len += EXTRA_LEN; +#endif #if SIZEOF_CAPITAL_OFF_T >= 8 if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode)) extra_len += EXTRA_LEN; @@ -1326,11 +1358,17 @@ 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; + OPT_EXTRA(file, 0)->unum = st.ST_MTIME_NSEC; + } +#endif file->len32 = (uint32)st.st_size; #if SIZEOF_CAPITAL_OFF_T >= 8 if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode)) { file->flags |= FLAG_LENGTH64; - OPT_EXTRA(file, 0)->unum = (uint32)(st.st_size >> 32); + OPT_EXTRA(file, NSEC_BUMP(file))->unum = (uint32)(st.st_size >> 32); } #endif file->mode = st.st_mode; diff --git a/generator.c b/generator.c index 11a7cb9d..836633e8 100644 --- a/generator.c +++ b/generator.c @@ -1923,7 +1923,7 @@ static void touch_up_dirs(struct file_list *flist, int ndx) STRUCT_STAT st; if (link_stat(fname, &st, 0) == 0 && cmp_time(st.st_mtime, file->modtime) != 0) - set_modtime(fname, file->modtime, file->mode); + set_modtime(fname, file->modtime, F_MOD_NSEC(file), file->mode); } if (counter >= loopchk_limit) { if (allowed_lull) diff --git a/rsync.c b/rsync.c index d8bc469e..db7abcc5 100644 --- a/rsync.c +++ b/rsync.c @@ -435,7 +435,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp, flags |= ATTRS_SKIP_MTIME; if (!(flags & ATTRS_SKIP_MTIME) && cmp_time(sxp->st.st_mtime, file->modtime) != 0) { - int ret = set_modtime(fname, file->modtime, sxp->st.st_mode); + int ret = set_modtime(fname, file->modtime, F_MOD_NSEC(file), sxp->st.st_mode); if (ret < 0) { rsyserr(FERROR_XFER, errno, "failed to set times on %s", full_fname(fname)); diff --git a/rsync.h b/rsync.h index cf2a2dd7..b0ac7a9f 100644 --- a/rsync.h +++ b/rsync.h @@ -61,6 +61,7 @@ #define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */ #define XMIT_HLINK_FIRST (1<<12) /* protocols 30 - now (HLINKED files only) */ #define XMIT_IO_ERROR_ENDLIST (1<<12) /* protocols 31 - now (w/XMIT_EXTENDED_FLAGS) */ +#define XMIT_MOD_NSEC (1<<13) /* protocols 31 - now */ /* These flags are used in the live flist data. */ @@ -80,6 +81,7 @@ #define FLAG_LENGTH64 (1<<9) /* sender/receiver/generator */ #define FLAG_SKIP_GROUP (1<<10) /* receiver/generator */ #define FLAG_TIME_FAILED (1<<11)/* generator */ +#define FLAG_MOD_NSEC (1<<12) /* sender/receiver/generator */ /* These flags are passed to functions but not stored. */ @@ -96,7 +98,7 @@ /* This is used when working on a new protocol version in CVS, and should * be a new non-zero value for each CVS change that affects the protocol. * It must ALWAYS be 0 when the protocol goes final (and NEVER before)! */ -#define SUBPROTOCOL_VERSION 6 +#define SUBPROTOCOL_VERSION 7 /* We refuse to interoperate with versions that are not in this range. * Note that we assume we'll work with later versions: the onus is on @@ -359,7 +361,7 @@ enum delret { #include #endif -#ifdef HAVE_LUTIMES +#if defined HAVE_UTIMENSAT || defined HAVE_LUTIMES #define CAN_SET_SYMLINK_TIMES 1 #endif @@ -674,7 +676,9 @@ extern int xattrs_ndx; #define REQ_EXTRA(f,ndx) ((union file_extras*)(f) - (ndx)) #define OPT_EXTRA(f,bump) ((union file_extras*)(f) - file_extra_cnt - 1 - (bump)) +#define NSEC_BUMP(f) ((f)->flags & FLAG_MOD_NSEC ? 1 : 0) #define LEN64_BUMP(f) ((f)->flags & FLAG_LENGTH64 ? 1 : 0) +#define START_BUMP(f) (NSEC_BUMP(f) + LEN64_BUMP(f)) #define HLINK_BUMP(f) ((f)->flags & (FLAG_HLINKED|FLAG_HLINK_DONE) ? inc_recurse+1 : 0) #define ACL_BUMP(f) (acls_ndx ? 1 : 0) @@ -686,6 +690,8 @@ extern int xattrs_ndx; ? (int64)OPT_EXTRA(f, 0)->unum << 32 : 0)) #endif +#define F_MOD_NSEC(f) ((f)->flags & FLAG_MOD_NSEC ? OPT_EXTRA(f, LEN64_BUMP(f))->unum : 0) + /* If there is a symlink string, it is always right after the basename */ #define F_SYMLINK(f) ((f)->basename + strlen((f)->basename) + 1) @@ -703,22 +709,21 @@ extern int xattrs_ndx; #define F_NDX(f) REQ_EXTRA(f, unsort_ndx)->num /* These items are per-entry optional: */ -#define F_HL_GNUM(f) OPT_EXTRA(f, LEN64_BUMP(f))->num /* non-dirs */ -#define F_HL_PREV(f) OPT_EXTRA(f, LEN64_BUMP(f)+inc_recurse)->num /* non-dirs */ -#define F_DIR_NODE_P(f) (&OPT_EXTRA(f, LEN64_BUMP(f) \ +#define F_HL_GNUM(f) OPT_EXTRA(f, START_BUMP(f))->num /* non-dirs */ +#define F_HL_PREV(f) OPT_EXTRA(f, START_BUMP(f)+inc_recurse)->num /* non-dirs */ +#define F_DIR_NODE_P(f) (&OPT_EXTRA(f, START_BUMP(f) \ + DIRNODE_EXTRA_CNT - 1)->num) /* sender dirs */ -#define F_DIR_RELNAMES_P(f) (&OPT_EXTRA(f, LEN64_BUMP(f) + DIRNODE_EXTRA_CNT \ +#define F_DIR_RELNAMES_P(f) (&OPT_EXTRA(f, START_BUMP(f) + DIRNODE_EXTRA_CNT \ + PTR_EXTRA_CNT - 1)->num) /* sender dirs */ -#define F_DIR_DEFACL(f) OPT_EXTRA(f, LEN64_BUMP(f))->unum /* receiver dirs */ -#define F_DIR_DEV_P(f) (&OPT_EXTRA(f, LEN64_BUMP(f) + ACL_BUMP(f) \ +#define F_DIR_DEFACL(f) OPT_EXTRA(f, START_BUMP(f))->unum /* receiver dirs */ +#define F_DIR_DEV_P(f) (&OPT_EXTRA(f, START_BUMP(f) + ACL_BUMP(f) \ + DEV_EXTRA_CNT - 1)->unum) /* receiver dirs */ -/* This optional item might follow an F_HL_*() item. - * (Note: a device doesn't need to check LEN64_BUMP(f).) */ -#define F_RDEV_P(f) (&OPT_EXTRA(f, HLINK_BUMP(f) + DEV_EXTRA_CNT - 1)->unum) +/* This optional item might follow an F_HL_*() item. */ +#define F_RDEV_P(f) (&OPT_EXTRA(f, START_BUMP(f) + HLINK_BUMP(f) + DEV_EXTRA_CNT - 1)->unum) /* The sum is only present on regular files. */ -#define F_SUM(f) ((char*)OPT_EXTRA(f, LEN64_BUMP(f) + HLINK_BUMP(f) \ +#define F_SUM(f) ((char*)OPT_EXTRA(f, START_BUMP(f) + HLINK_BUMP(f) \ + SUM_EXTRA_CNT - 1)) /* Some utility defines: */ diff --git a/tls.c b/tls.c index 1c5953d7..aa43b912 100644 --- a/tls.c +++ b/tls.c @@ -48,10 +48,19 @@ int read_only = 1; int list_only = 0; int link_times = 0; int link_owner = 0; +int nsec_times = 0; int preserve_perms = 0; int preserve_executability = 0; char number_separator; +#ifdef HAVE_UTIMENSAT +#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC +#define ST_MTIME_NSEC st_mtim.tv_nsec +#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC) +#define ST_MTIME_NSEC st_mtimensec +#endif +#endif + #ifdef SUPPORT_XATTRS #ifdef HAVE_LINUX_XATTRS @@ -161,9 +170,10 @@ static void list_file(const char *fname) permstring(permbuf, buf.st_mode); if (buf.st_mtime) { + int len; mt = gmtime(&buf.st_mtime); - snprintf(datebuf, sizeof datebuf, + len = snprintf(datebuf, sizeof datebuf, "%04d-%02d-%02d %02d:%02d:%02d", (int)mt->tm_year + 1900, (int)mt->tm_mon + 1, @@ -171,8 +181,17 @@ static void list_file(const char *fname) (int)mt->tm_hour, (int)mt->tm_min, (int)mt->tm_sec); - } else - strlcpy(datebuf, " ", sizeof datebuf); +#ifdef ST_MTIME_NSEC + if (nsec_times) { + snprintf(datebuf + len, sizeof datebuf - len, + ".%09d", (int)buf.ST_MTIME_NSEC); + } +#endif + } else { + int len = MIN(19 + 9*nsec_times, (int)sizeof datebuf - 1); + memset(datebuf, ' ', len); + datebuf[len] = '\0'; + } /* TODO: Perhaps escape special characters in fname? */ @@ -194,6 +213,9 @@ static struct poptOption long_options[] = { {"link-owner", 'L', POPT_ARG_NONE, &link_owner, 0, 0, 0 }, #ifdef SUPPORT_XATTRS {"fake-super", 'f', POPT_ARG_VAL, &am_root, -1, 0, 0 }, +#endif +#ifdef ST_MTIME_NSEC + {"nsec", 's', POPT_ARG_NONE, &nsec_times, 0, 0, 0 }, #endif {"help", 'h', POPT_ARG_NONE, 0, 'h', 0, 0 }, {0,0,0,0,0,0,0} diff --git a/util.c b/util.c index d7efd239..6019fccb 100644 --- a/util.c +++ b/util.c @@ -123,7 +123,7 @@ NORETURN void overflow_exit(const char *str) exit_cleanup(RERR_MALLOC); } -int set_modtime(const char *fname, time_t modtime, mode_t mode) +int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode) { #ifndef CAN_SET_SYMLINK_TIMES if (S_ISLNK(mode)) @@ -140,12 +140,21 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode) return 0; { -#if defined HAVE_UTIMES || defined HAVE_LUTIMES +#ifdef HAVE_UTIMENSAT + struct timespec t[2]; + t[0].tv_sec = 0; + t[0].tv_nsec = UTIME_NOW; + t[1].tv_sec = modtime; + t[1].tv_nsec = mod_nsec; + if (utimensat(AT_FDCWD, fname, t, AT_SYMLINK_NOFOLLOW) < 0) + return S_ISLNK(mode) && errno == ENOSYS ? 1 : -1; + return 0; +#elif defined HAVE_UTIMES || defined HAVE_LUTIMES struct timeval t[2]; t[0].tv_sec = time(NULL); t[0].tv_usec = 0; t[1].tv_sec = modtime; - t[1].tv_usec = 0; + t[1].tv_usec = mod_nsec / 1000; # ifdef HAVE_LUTIMES if (lutimes(fname, t) < 0) return S_ISLNK(mode) && errno == ENOSYS ? 1 : -1; -- 2.34.1