Add support for transferring & setting nsec time values.
authorWayne Davison <wayned@samba.org>
Mon, 7 Sep 2009 20:49:52 +0000 (13:49 -0700)
committerWayne Davison <wayned@samba.org>
Mon, 7 Sep 2009 21:11:58 +0000 (14:11 -0700)
configure.in
flist.c
generator.c
rsync.c
rsync.h
tls.c
util.c

index fcde0ca..b5bebe5 100644 (file)
@@ -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 <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#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 8b08ff6..7a28b73 100644 (file)
--- 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;
index 11a7cb9..836633e 100644 (file)
@@ -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 d8bc469..db7abcc 100644 (file)
--- 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 cf2a2dd..b0ac7a9 100644 (file)
--- 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 <utime.h>
 #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 1c5953d..aa43b91 100644 (file)
--- 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 d7efd23..6019fcc 100644 (file)
--- 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;