Added --atimes and --set-noatime options.
authorWayne Davison <wayne@opencoder.net>
Thu, 23 Apr 2020 20:17:41 +0000 (13:17 -0700)
committerWayne Davison <wayne@opencoder.net>
Thu, 23 Apr 2020 20:24:15 +0000 (13:24 -0700)
17 files changed:
NEWS
compat.c
flist.c
generator.c
log.c
options.c
rsync.c
rsync.h
rsync.yo
syscall.c
t_stub.c
testsuite/atimes.test [new file with mode: 0644]
testsuite/daemon.test
testsuite/rsync.fns
tls.c
trimslash.c
util.c

diff --git a/NEWS b/NEWS
index 53b61aa24697cff63d3bfdfdf696bab04ee6243c..e3323c61d1c43de06d751bc4384b4eae7050c45b 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -30,6 +30,10 @@ Changes since 3.1.3:
 
   ENHANCEMENTS:
 
 
   ENHANCEMENTS:
 
+    - Improved the --atimes patch and promoted it to be in the release.
+
+    - Added --set-noatime option to open files using O_NOATIME.
+
     - Improved the --write-devices patch and promoted it to be in the release.
 
     - Added openssl support to the rsync-ssl script via its renamed helper
     - Improved the --write-devices patch and promoted it to be in the release.
 
     - Added openssl support to the rsync-ssl script via its renamed helper
index 17113ae4145eb629cf20b0d04728245dc3cb4b70..6298b904a98777c7775b775bcf25459582768ba2 100644 (file)
--- a/compat.c
+++ b/compat.c
@@ -48,6 +48,7 @@ extern int protocol_version;
 extern int protect_args;
 extern int preserve_uid;
 extern int preserve_gid;
 extern int protect_args;
 extern int preserve_uid;
 extern int preserve_gid;
+extern int preserve_atimes;
 extern int preserve_acls;
 extern int preserve_xattrs;
 extern int need_messages_from_generator;
 extern int preserve_acls;
 extern int preserve_xattrs;
 extern int need_messages_from_generator;
@@ -65,7 +66,7 @@ extern char *iconv_opt;
 #endif
 
 /* These index values are for the file-list's extra-attribute array. */
 #endif
 
 /* These index values are for the file-list's extra-attribute array. */
-int uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
+int pathname_ndx, depth_ndx, atimes_ndx, uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
 
 int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
 int sender_symlink_iconv = 0;  /* sender should convert symlink content */
 
 int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
 int sender_symlink_iconv = 0;  /* sender should convert symlink content */
@@ -136,10 +137,17 @@ void set_allow_inc_recurse(void)
 
 void setup_protocol(int f_out,int f_in)
 {
 
 void setup_protocol(int f_out,int f_in)
 {
-       if (am_sender)
-               file_extra_cnt += PTR_EXTRA_CNT;
+       assert(file_extra_cnt == 0);
+       assert(EXTRA64_CNT == 2 || EXTRA64_CNT == 1);
+
+       /* All int64 values must be set first so that they are guaranteed to be
+        * aligned for direct int64-pointer memory access. */
+       if (preserve_atimes)
+               atimes_ndx = (file_extra_cnt += EXTRA64_CNT);
+       if (am_sender) /* This is most likely in the in64 union as well. */
+               pathname_ndx = (file_extra_cnt += PTR_EXTRA_CNT);
        else
        else
-               file_extra_cnt++;
+               depth_ndx = ++file_extra_cnt;
        if (preserve_uid)
                uid_ndx = ++file_extra_cnt;
        if (preserve_gid)
        if (preserve_uid)
                uid_ndx = ++file_extra_cnt;
        if (preserve_gid)
diff --git a/flist.c b/flist.c
index a67e36531ad73e3ab8de6449c07f039db05cce79..a756fb67e7b8269063abe9001ece4a3e0771c720 100644 (file)
--- a/flist.c
+++ b/flist.c
@@ -55,6 +55,7 @@ extern int preserve_specials;
 extern int delete_during;
 extern int missing_args;
 extern int eol_nulls;
 extern int delete_during;
 extern int missing_args;
 extern int eol_nulls;
+extern int atimes_ndx;
 extern int relative_paths;
 extern int implied_dirs;
 extern int ignore_perishable;
 extern int relative_paths;
 extern int implied_dirs;
 extern int ignore_perishable;
@@ -379,7 +380,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
 #endif
                            int ndx, int first_ndx)
 {
 #endif
                            int ndx, int first_ndx)
 {
-       static time_t modtime;
+       static time_t modtime, atime;
        static mode_t mode;
 #ifdef SUPPORT_HARD_LINKS
        static int64 dev;
        static mode_t mode;
 #ifdef SUPPORT_HARD_LINKS
        static int64 dev;
@@ -479,6 +480,12 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
                modtime = file->modtime;
        if (NSEC_BUMP(file) && protocol_version >= 31)
                xflags |= XMIT_MOD_NSEC;
                modtime = file->modtime;
        if (NSEC_BUMP(file) && protocol_version >= 31)
                xflags |= XMIT_MOD_NSEC;
+       if (atimes_ndx && !S_ISDIR(mode)) {
+               if (F_ATIME(file) == atime)
+                       xflags |= XMIT_SAME_ATIME;
+               else
+                       atime = F_ATIME(file);
+       }
 
 #ifdef SUPPORT_HARD_LINKS
        if (tmp_dev != -1) {
 
 #ifdef SUPPORT_HARD_LINKS
        if (tmp_dev != -1) {
@@ -565,6 +572,8 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
                write_varint(f, F_MOD_NSEC(file));
        if (!(xflags & XMIT_SAME_MODE))
                write_int(f, to_wire_mode(mode));
                write_varint(f, F_MOD_NSEC(file));
        if (!(xflags & XMIT_SAME_MODE))
                write_int(f, to_wire_mode(mode));
+       if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME))
+               write_varlong(f, atime, 4);
        if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
                if (protocol_version < 30)
                        write_int(f, uid);
        if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
                if (protocol_version < 30)
                        write_int(f, uid);
@@ -652,7 +661,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
 
 static struct file_struct *recv_file_entry(int f, struct file_list *flist, int xflags)
 {
 
 static struct file_struct *recv_file_entry(int f, struct file_list *flist, int xflags)
 {
-       static int64 modtime;
+       static int64 modtime, atime;
        static mode_t mode;
 #ifdef SUPPORT_HARD_LINKS
        static int64 dev;
        static mode_t mode;
 #ifdef SUPPORT_HARD_LINKS
        static int64 dev;
@@ -799,6 +808,16 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
                modtime_nsec = 0;
        if (!(xflags & XMIT_SAME_MODE))
                mode = from_wire_mode(read_int(f));
                modtime_nsec = 0;
        if (!(xflags & XMIT_SAME_MODE))
                mode = from_wire_mode(read_int(f));
+       if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME)) {
+               atime = read_varlong(f, 4);
+#if SIZEOF_TIME_T < SIZEOF_INT64
+               if (!am_generator && (int64)(time_t)atime != atime) {
+                       rprintf(FERROR_XFER,
+                               "Access time value of %s truncated on receiver.\n",
+                               lastname);
+               }
+#endif
+       }
 
        if (chmod_modes && !S_ISLNK(mode) && mode)
                mode = tweak_mode(mode, chmod_modes);
 
        if (chmod_modes && !S_ISLNK(mode) && mode)
                mode = tweak_mode(mode, chmod_modes);
@@ -966,6 +985,8 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
                F_GROUP(file) = gid;
                file->flags |= gid_flags;
        }
                F_GROUP(file) = gid;
                file->flags |= gid_flags;
        }
+       if (atimes_ndx)
+               F_ATIME(file) = atime;
        if (unsort_ndx)
                F_NDX(file) = flist->used + flist->ndx_start;
 
        if (unsort_ndx)
                F_NDX(file) = flist->used + flist->ndx_start;
 
@@ -1363,6 +1384,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                F_GROUP(file) = st.st_gid;
        if (am_generator && st.st_uid == our_uid)
                file->flags |= FLAG_OWNED_BY_US;
                F_GROUP(file) = st.st_gid;
        if (am_generator && st.st_uid == our_uid)
                file->flags |= FLAG_OWNED_BY_US;
+       if (atimes_ndx)
+               F_ATIME(file) = st.st_atime;
 
        if (basename != thisname)
                file->dirname = lastdir;
 
        if (basename != thisname)
                file->dirname = lastdir;
index 7ec924cf4e7566f84f4903489d447e2a2d5434b4..3c50f63f2e1024c9f84c7596c64ce94160239da1 100644 (file)
@@ -506,6 +506,9 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
                 : iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !(iflags & ITEM_MATCHED)
                  && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
                        iflags |= ITEM_REPORT_TIME;
                 : iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !(iflags & ITEM_MATCHED)
                  && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
                        iflags |= ITEM_REPORT_TIME;
+               if (atimes_ndx && !S_ISDIR(file->mode) && !S_ISLNK(file->mode)
+                && cmp_time(F_ATIME(file), 0, sxp->st.st_atime, 0) != 0)
+                       iflags |= ITEM_REPORT_ATIME;
 #if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
                if (S_ISLNK(file->mode)) {
                        ;
 #if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
                if (S_ISLNK(file->mode)) {
                        ;
@@ -916,6 +919,8 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
                if (link_dest) {
                        if (!hard_link_one(file, fname, cmpbuf, 1))
                                goto try_a_copy;
                if (link_dest) {
                        if (!hard_link_one(file, fname, cmpbuf, 1))
                                goto try_a_copy;
+                       if (atimes_ndx)
+                               set_file_attrs(fname, file, sxp, NULL, 0);
                        if (preserve_hard_links && F_IS_HLINKED(file))
                                finish_hard_link(file, fname, ndx, &sxp->st, itemizing, code, j);
                        if (!maybe_ATTRS_REPORT && (INFO_GTE(NAME, 2) || stdout_format_has_i > 1)) {
                        if (preserve_hard_links && F_IS_HLINKED(file))
                                finish_hard_link(file, fname, ndx, &sxp->st, itemizing, code, j);
                        if (!maybe_ATTRS_REPORT && (INFO_GTE(NAME, 2) || stdout_format_has_i > 1)) {
@@ -1120,35 +1125,40 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
 static void list_file_entry(struct file_struct *f)
 {
        char permbuf[PERMSTRING_SIZE];
 static void list_file_entry(struct file_struct *f)
 {
        char permbuf[PERMSTRING_SIZE];
-       int64 len;
-       int colwidth = human_readable ? 14 : 11;
+       const char *mtime_str = timestring(f->modtime);
+       int size_width = human_readable ? 14 : 11;
+       int mtime_width = 1 + strlen(mtime_str);
+       int atime_width = atimes_ndx ? mtime_width : 0;
 
        if (!F_IS_ACTIVE(f)) {
                /* this can happen if duplicate names were removed */
                return;
        }
 
 
        if (!F_IS_ACTIVE(f)) {
                /* this can happen if duplicate names were removed */
                return;
        }
 
-       permstring(permbuf, f->mode);
-       len = F_LENGTH(f);
-
        /* TODO: indicate '+' if the entry has an ACL. */
 
        /* TODO: indicate '+' if the entry has an ACL. */
 
-#ifdef SUPPORT_LINKS
-       if (preserve_links && S_ISLNK(f->mode)) {
-               rprintf(FINFO, "%s %*s %s %s -> %s\n",
-                       permbuf, colwidth, human_num(len),
-                       timestring(f->modtime), f_name(f, NULL),
-                       F_SYMLINK(f));
-       } else
-#endif
        if (missing_args == 2 && f->mode == 0) {
                rprintf(FINFO, "%-*s %s\n",
        if (missing_args == 2 && f->mode == 0) {
                rprintf(FINFO, "%-*s %s\n",
-                       colwidth + 31, "*missing",
+                       10 + 1 + size_width + mtime_width + atime_width, "*missing",
                        f_name(f, NULL));
        } else {
                        f_name(f, NULL));
        } else {
-               rprintf(FINFO, "%s %*s %s %s\n",
-                       permbuf, colwidth, human_num(len),
-                       timestring(f->modtime), f_name(f, NULL));
+               const char *atime_str = atimes_ndx && !S_ISDIR(f->mode) ? timestring(F_ATIME(f)) : "";
+               const char *arrow, *lnk;
+
+               permstring(permbuf, f->mode);
+
+#ifdef SUPPORT_LINKS
+               if (preserve_links && S_ISLNK(f->mode)) {
+                       arrow = " -> ";
+                       lnk = F_SYMLINK(f);
+               } else
+#endif
+                       arrow = lnk = "";
+
+               rprintf(FINFO, "%s %*s %s%*s %s%s%s\n",
+                       permbuf, size_width, human_num(F_LENGTH(f)),
+                       timestring(f->modtime), atime_width, atime_str,
+                       f_name(f, NULL), arrow, lnk);
        }
 }
 
        }
 }
 
@@ -2064,8 +2074,13 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
                        do_chmod(fname, file->mode);
                if (need_retouch_dir_times) {
                        STRUCT_STAT st;
                        do_chmod(fname, file->mode);
                if (need_retouch_dir_times) {
                        STRUCT_STAT st;
-                       if (link_stat(fname, &st, 0) == 0 && time_diff(&st, file))
-                               set_modtime(fname, file->modtime, F_MOD_NSEC_or_0(file), file->mode);
+                       if (link_stat(fname, &st, 0) == 0 && time_diff(&st, file)) {
+                               st.st_mtime = file->modtime;
+#ifdef ST_MTIME_NSEC
+                               st.ST_MTIME_NSEC = F_MOD_NSEC_or_0(file);
+#endif
+                               set_times(fname, &st);
+                       }
                }
                if (counter >= loopchk_limit) {
                        if (allowed_lull)
                }
                if (counter >= loopchk_limit) {
                        if (allowed_lull)
diff --git a/log.c b/log.c
index 3c6b1b9b3832b03c283fbfe56c4aed2aa78427fd..ae6214da91a876908150b74e68ca912e33050e0a 100644 (file)
--- a/log.c
+++ b/log.c
@@ -716,7 +716,8 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
                        c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
                        c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
                        c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
                        c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
                        c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
                        c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
-                       c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' : 'u';
+                       c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.'
+                            : S_ISLNK(file->mode) ? 'U' : 'u';
                        c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
                        c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
                        c[11] = '\0';
                        c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
                        c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
                        c[11] = '\0';
index d567d6495113d2bbdaa639a719185253b11056e9..95ec6da0c64207c6d1941f5be79955900546ae7b 100644 (file)
--- a/options.c
+++ b/options.c
@@ -63,7 +63,9 @@ int preserve_specials = 0;
 int preserve_uid = 0;
 int preserve_gid = 0;
 int preserve_times = 0;
 int preserve_uid = 0;
 int preserve_gid = 0;
 int preserve_times = 0;
+int preserve_atimes = 0;
 int update_only = 0;
 int update_only = 0;
+int set_noatime = 0;
 int cvs_exclude = 0;
 int dry_run = 0;
 int do_xfers = 1;
 int cvs_exclude = 0;
 int dry_run = 0;
 int do_xfers = 1;
@@ -711,6 +713,8 @@ void usage(enum logcode F)
   rprintf(F,"     --specials              preserve special files\n");
   rprintf(F," -D                          same as --devices --specials\n");
   rprintf(F," -t, --times                 preserve modification times\n");
   rprintf(F,"     --specials              preserve special files\n");
   rprintf(F," -D                          same as --devices --specials\n");
   rprintf(F," -t, --times                 preserve modification times\n");
+  rprintf(F," -U, --atimes                preserve access (last-used) times\n");
+  rprintf(F,"     --set-noatime           avoid changing the atime on accessed files\n");
   rprintf(F," -O, --omit-dir-times        omit directories from --times\n");
   rprintf(F," -J, --omit-link-times       omit symlinks from --times\n");
   rprintf(F,"     --super                 receiver attempts super-user activities\n");
   rprintf(F," -O, --omit-dir-times        omit directories from --times\n");
   rprintf(F," -J, --omit-link-times       omit symlinks from --times\n");
   rprintf(F,"     --super                 receiver attempts super-user activities\n");
@@ -872,6 +876,11 @@ static struct poptOption long_options[] = {
   {"times",           't', POPT_ARG_VAL,    &preserve_times, 1, 0, 0 },
   {"no-times",         0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
   {"no-t",             0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
   {"times",           't', POPT_ARG_VAL,    &preserve_times, 1, 0, 0 },
   {"no-times",         0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
   {"no-t",             0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
+  {"atimes",          'U', POPT_ARG_NONE,   0, 'U', 0, 0 },
+  {"no-atimes",        0,  POPT_ARG_VAL,    &preserve_atimes, 0, 0, 0 },
+  {"no-U",             0,  POPT_ARG_VAL,    &preserve_atimes, 0, 0, 0 },
+  {"set-noatime",      0,  POPT_ARG_VAL,    &set_noatime, 1, 0, 0 },
+  {"no-set-noatime",   0,  POPT_ARG_VAL,    &set_noatime, 0, 0, 0 },
   {"omit-dir-times",  'O', POPT_ARG_VAL,    &omit_dir_times, 1, 0, 0 },
   {"no-omit-dir-times",0,  POPT_ARG_VAL,    &omit_dir_times, 0, 0, 0 },
   {"no-O",             0,  POPT_ARG_VAL,    &omit_dir_times, 0, 0, 0 },
   {"omit-dir-times",  'O', POPT_ARG_VAL,    &omit_dir_times, 1, 0, 0 },
   {"no-omit-dir-times",0,  POPT_ARG_VAL,    &omit_dir_times, 0, 0, 0 },
   {"no-O",             0,  POPT_ARG_VAL,    &omit_dir_times, 0, 0, 0 },
@@ -1543,6 +1552,11 @@ int parse_arguments(int *argc_p, const char ***argv_p)
                        itemize_changes++;
                        break;
 
                        itemize_changes++;
                        break;
 
+               case 'U':
+                       if (++preserve_atimes > 1)
+                           set_noatime = 1;
+                       break;
+
                case 'v':
                        verbose++;
                        break;
                case 'v':
                        verbose++;
                        break;
@@ -2493,6 +2507,11 @@ void server_options(char **args, int *argc_p)
                argstr[x++] = 'D';
        if (preserve_times)
                argstr[x++] = 't';
                argstr[x++] = 'D';
        if (preserve_times)
                argstr[x++] = 't';
+       if (preserve_atimes) {
+               argstr[x++] = 'U';
+               if (preserve_atimes > 1)
+                       argstr[x++] = 'U';
+       }
        if (preserve_perms)
                argstr[x++] = 'p';
        else if (preserve_executability && am_sender)
        if (preserve_perms)
                argstr[x++] = 'p';
        else if (preserve_executability && am_sender)
@@ -2831,6 +2850,9 @@ void server_options(char **args, int *argc_p)
        if (preallocate_files && am_sender)
                args[ac++] = "--preallocate";
 
        if (preallocate_files && am_sender)
                args[ac++] = "--preallocate";
 
+       if (set_noatime && preserve_atimes <= 1)
+               args[ac++] = "--set-noatime";
+
        if (ac > MAX_SERVER_ARGS) { /* Not possible... */
                rprintf(FERROR, "argc overflow in server_options().\n");
                exit_cleanup(RERR_MALLOC);
        if (ac > MAX_SERVER_ARGS) { /* Not possible... */
                rprintf(FERROR, "argc overflow in server_options().\n");
                exit_cleanup(RERR_MALLOC);
diff --git a/rsync.c b/rsync.c
index 9479f24c77b0687f4bf35345c55ee0a0bd3ae853..19f09c358551fbe22226234179c0a65b6bd9382c 100644 (file)
--- a/rsync.c
+++ b/rsync.c
@@ -63,6 +63,15 @@ iconv_t ic_chck = (iconv_t)-1;
 iconv_t ic_send = (iconv_t)-1, ic_recv = (iconv_t)-1;
 # endif
 
 iconv_t ic_send = (iconv_t)-1, ic_recv = (iconv_t)-1;
 # endif
 
+#define UPDATED_OWNER (1<<0)
+#define UPDATED_GROUP (1<<1)
+#define UPDATED_MTIME (1<<2)
+#define UPDATED_ATIME (1<<3)
+#define UPDATED_ACLS  (1<<4)
+#define UPDATED_MODE  (1<<5)
+
+#define UPDATED_TIMES (UPDATED_MTIME|UPDATED_ATIME)
+
 static const char *default_charset(void)
 {
 # if defined HAVE_LIBCHARSET_H && defined HAVE_LOCALE_CHARSET
 static const char *default_charset(void)
 {
 # if defined HAVE_LIBCHARSET_H && defined HAVE_LOCALE_CHARSET
@@ -540,7 +549,10 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
                                          keep_dirlinks && S_ISDIR(sxp->st.st_mode));
                        }
                }
                                          keep_dirlinks && S_ISDIR(sxp->st.st_mode));
                        }
                }
-               updated = 1;
+               if (change_uid)
+                   updated |= UPDATED_OWNER;
+               if (change_gid)
+                   updated |= UPDATED_GROUP;
        }
 
 #ifdef SUPPORT_XATTRS
        }
 
 #ifdef SUPPORT_XATTRS
@@ -553,23 +565,44 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
        if (!preserve_times
         || (!(preserve_times & PRESERVE_DIR_TIMES) && S_ISDIR(sxp->st.st_mode))
         || (!(preserve_times & PRESERVE_LINK_TIMES) && S_ISLNK(sxp->st.st_mode)))
        if (!preserve_times
         || (!(preserve_times & PRESERVE_DIR_TIMES) && S_ISDIR(sxp->st.st_mode))
         || (!(preserve_times & PRESERVE_LINK_TIMES) && S_ISLNK(sxp->st.st_mode)))
-               flags |= ATTRS_SKIP_MTIME;
+               flags |= ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME;
+       else if (sxp != &sx2)
+               memcpy(&sx2.st, &sxp->st, sizeof (sx2.st));
+       if (!atimes_ndx || S_ISDIR(sxp->st.st_mode))
+               flags |= ATTRS_SKIP_ATIME;
        if (!(flags & ATTRS_SKIP_MTIME)
         && (sxp->st.st_mtime != file->modtime
 #ifdef ST_MTIME_NSEC
          || (flags & ATTRS_SET_NANO && NSEC_BUMP(file) && (uint32)sxp->st.ST_MTIME_NSEC != F_MOD_NSEC(file))
 #endif
          )) {
        if (!(flags & ATTRS_SKIP_MTIME)
         && (sxp->st.st_mtime != file->modtime
 #ifdef ST_MTIME_NSEC
          || (flags & ATTRS_SET_NANO && NSEC_BUMP(file) && (uint32)sxp->st.ST_MTIME_NSEC != F_MOD_NSEC(file))
 #endif
          )) {
-               int ret = set_modtime(fname, file->modtime, F_MOD_NSEC_or_0(file), sxp->st.st_mode);
+               sx2.st.st_mtime = file->modtime;
+#ifdef ST_MTIME_NSEC
+               sx2.st.ST_MTIME_NSEC = F_MOD_NSEC_or_0(file);
+#endif
+               updated |= UPDATED_MTIME;
+       }
+       if (!(flags & ATTRS_SKIP_ATIME)) {
+               time_t file_atime = F_ATIME(file);
+               if (cmp_time(sxp->st.st_atime, 0, file_atime, 0) != 0) {
+                       sx2.st.st_atime = file_atime;
+#ifdef ST_ATIME_NSEC
+                       sx2.st.ST_ATIME_NSEC = 0;
+#endif
+                       updated |= UPDATED_ATIME;
+               }
+       }
+       if (updated & UPDATED_TIMES) {
+               int ret = set_times(fname, &sx2.st);
                if (ret < 0) {
                        rsyserr(FERROR_XFER, errno, "failed to set times on %s",
                                full_fname(fname));
                        goto cleanup;
                }
                if (ret < 0) {
                        rsyserr(FERROR_XFER, errno, "failed to set times on %s",
                                full_fname(fname));
                        goto cleanup;
                }
-               if (ret == 0) /* ret == 1 if symlink could not be set */
-                       updated = 1;
-               else
+               if (ret > 0) { /* ret == 1 if symlink could not be set */
+                       updated &= ~UPDATED_TIMES;
                        file->flags |= FLAG_TIME_FAILED;
                        file->flags |= FLAG_TIME_FAILED;
+               }
        }
 
 #ifdef SUPPORT_ACLS
        }
 
 #ifdef SUPPORT_ACLS
@@ -581,7 +614,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
         * need to chmod(). */
        if (preserve_acls && !S_ISLNK(new_mode)) {
                if (set_acl(fname, file, sxp, new_mode) > 0)
         * need to chmod(). */
        if (preserve_acls && !S_ISLNK(new_mode)) {
                if (set_acl(fname, file, sxp, new_mode) > 0)
-                       updated = 1;
+                       updated |= UPDATED_ACLS;
        }
 #endif
 
        }
 #endif
 
@@ -595,7 +628,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
                        goto cleanup;
                }
                if (ret == 0) /* ret == 1 if symlink could not be set */
                        goto cleanup;
                }
                if (ret == 0) /* ret == 1 if symlink could not be set */
-                       updated = 1;
+                       updated |= UPDATED_MODE;
        }
 #endif
 
        }
 #endif
 
@@ -676,7 +709,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
 
        /* Change permissions before putting the file into place. */
        set_file_attrs(fnametmp, file, NULL, fnamecmp,
 
        /* Change permissions before putting the file into place. */
        set_file_attrs(fnametmp, file, NULL, fnamecmp,
-                      ok_to_set_time ? ATTRS_SET_NANO : ATTRS_SKIP_MTIME);
+                      ok_to_set_time ? ATTRS_SET_NANO : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME);
 
        /* move tmp file over real file */
        if (DEBUG_GTE(RECV, 1))
 
        /* move tmp file over real file */
        if (DEBUG_GTE(RECV, 1))
@@ -701,7 +734,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
 
   do_set_file_attrs:
        set_file_attrs(fnametmp, file, NULL, fnamecmp,
 
   do_set_file_attrs:
        set_file_attrs(fnametmp, file, NULL, fnamecmp,
-                      ok_to_set_time ? ATTRS_SET_NANO : ATTRS_SKIP_MTIME);
+                      ok_to_set_time ? ATTRS_SET_NANO : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME);
 
        if (temp_copy_name) {
                if (do_rename(fnametmp, fname) < 0) {
 
        if (temp_copy_name) {
                if (do_rename(fnametmp, fname) < 0) {
diff --git a/rsync.h b/rsync.h
index c6cb0992cc99e0ea748bf9fa54a436e2d50c4426..2a558e43072c79ddb836e77905e586ba3c9077d2 100644 (file)
--- a/rsync.h
+++ b/rsync.h
@@ -62,6 +62,7 @@
 #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) (also protocol 30 w/'f' compat flag) */
 #define XMIT_MOD_NSEC (1<<13)          /* protocols 31 - 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) (also protocol 30 w/'f' compat flag) */
 #define XMIT_MOD_NSEC (1<<13)          /* protocols 31 - now */
+#define XMIT_SAME_ATIME (1<<14)        /* protocols ?? - now */
 
 /* These flags are used in the live flist data. */
 
 
 /* These flags are used in the live flist data. */
 
 #define ATTRS_REPORT           (1<<0)
 #define ATTRS_SKIP_MTIME       (1<<1)
 #define ATTRS_SET_NANO         (1<<2)
 #define ATTRS_REPORT           (1<<0)
 #define ATTRS_SKIP_MTIME       (1<<1)
 #define ATTRS_SET_NANO         (1<<2)
+#define ATTRS_SKIP_ATIME       (1<<3)
 
 #define FULL_FLUSH     1
 #define NORMAL_FLUSH   0
 
 #define FULL_FLUSH     1
 #define NORMAL_FLUSH   0
@@ -394,10 +396,13 @@ enum delret {
 #ifdef CAN_SET_NSEC
 #ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
 #define ST_MTIME_NSEC st_mtim.tv_nsec
 #ifdef CAN_SET_NSEC
 #ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
 #define ST_MTIME_NSEC st_mtim.tv_nsec
+#define ST_ATIME_NSEC st_atim.tv_nsec
 #elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
 #define ST_MTIME_NSEC st_mtimensec
 #elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
 #define ST_MTIME_NSEC st_mtimensec
+#define ST_ATIME_NSEC st_atimensec
 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC)
 #define ST_MTIME_NSEC st_mtimespec.tv_nsec
 #elif defined(HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC)
 #define ST_MTIME_NSEC st_mtimespec.tv_nsec
+#define ST_ATIME_NSEC st_atimespec.tv_nsec
 #endif
 #endif
 
 #endif
 #endif
 
@@ -700,9 +705,29 @@ struct ht_int64_node {
 #endif
 #endif
 
 #endif
 #endif
 
+#if SIZEOF_CHARP == 4
+# define PTRS_ARE_32 1
+# define PTR_EXTRA_CNT 1
+#elif SIZEOF_CHARP == 8
+# define PTRS_ARE_64 1
+# define PTR_EXTRA_CNT EXTRA64_CNT
+#else
+# error Character pointers are not 4 or 8 bytes.
+#endif
+
 union file_extras {
        int32 num;
        uint32 unum;
 union file_extras {
        int32 num;
        uint32 unum;
+#ifdef PTRS_ARE_32
+       const char* ptr;
+#endif
+};
+
+union file_extras64 {
+       int64 num;
+#ifdef PTRS_ARE_64
+       const char* ptr;
+#endif
 };
 
 struct file_struct {
 };
 
 struct file_struct {
@@ -716,6 +741,9 @@ struct file_struct {
 
 extern int file_extra_cnt;
 extern int inc_recurse;
 
 extern int file_extra_cnt;
 extern int inc_recurse;
+extern int atimes_ndx;
+extern int pathname_ndx;
+extern int depth_ndx;
 extern int uid_ndx;
 extern int gid_ndx;
 extern int acls_ndx;
 extern int uid_ndx;
 extern int gid_ndx;
 extern int acls_ndx;
@@ -723,14 +751,18 @@ extern int xattrs_ndx;
 
 #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
 #define EXTRA_LEN (sizeof (union file_extras))
 
 #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
 #define EXTRA_LEN (sizeof (union file_extras))
-#define PTR_EXTRA_CNT ((sizeof (char *) + EXTRA_LEN - 1) / EXTRA_LEN)
 #define DEV_EXTRA_CNT 2
 #define DIRNODE_EXTRA_CNT 3
 #define DEV_EXTRA_CNT 2
 #define DIRNODE_EXTRA_CNT 3
+#define EXTRA64_CNT ((sizeof (union file_extras64) + EXTRA_LEN - 1) / EXTRA_LEN)
 #define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
 
 #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 SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
 
 #define REQ_EXTRA(f,ndx) ((union file_extras*)(f) - (ndx))
 #define OPT_EXTRA(f,bump) ((union file_extras*)(f) - file_extra_cnt - 1 - (bump))
 
+/* These are guaranteed to be allocated first in the array so that they
+ * are aligned for direct int64-pointer access. */
+#define REQ_EXTRA64(f,ndx) ((union file_extras64*)REQ_EXTRA(f,ndx))
+
 #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 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))
@@ -752,10 +784,14 @@ extern int xattrs_ndx;
 #define F_SYMLINK(f) ((f)->basename + strlen((f)->basename) + 1)
 
 /* The sending side always has this available: */
 #define F_SYMLINK(f) ((f)->basename + strlen((f)->basename) + 1)
 
 /* The sending side always has this available: */
-#define F_PATHNAME(f) (*(const char**)REQ_EXTRA(f, PTR_EXTRA_CNT))
+#ifdef PTRS_ARE_32
+#define F_PATHNAME(f) REQ_EXTRA(f, pathname_ndx)->ptr
+#else
+#define F_PATHNAME(f) REQ_EXTRA64(f, pathname_ndx)->ptr
+#endif
 
 /* The receiving side always has this available: */
 
 /* The receiving side always has this available: */
-#define F_DEPTH(f) REQ_EXTRA(f, 1)->num
+#define F_DEPTH(f) REQ_EXTRA(f, depth_ndx)->num
 
 /* When the associated option is on, all entries will have these present: */
 #define F_OWNER(f) REQ_EXTRA(f, uid_ndx)->unum
 
 /* When the associated option is on, all entries will have these present: */
 #define F_OWNER(f) REQ_EXTRA(f, uid_ndx)->unum
@@ -763,6 +799,7 @@ extern int xattrs_ndx;
 #define F_ACL(f) REQ_EXTRA(f, acls_ndx)->num
 #define F_XATTR(f) REQ_EXTRA(f, xattrs_ndx)->num
 #define F_NDX(f) REQ_EXTRA(f, unsort_ndx)->num
 #define F_ACL(f) REQ_EXTRA(f, acls_ndx)->num
 #define F_XATTR(f) REQ_EXTRA(f, xattrs_ndx)->num
 #define F_NDX(f) REQ_EXTRA(f, unsort_ndx)->num
+#define F_ATIME(f) REQ_EXTRA64(f, atimes_ndx)->num
 
 /* These items are per-entry optional: */
 #define F_HL_GNUM(f) OPT_EXTRA(f, START_BUMP(f))->num /* non-dirs */
 
 /* These items are per-entry optional: */
 #define F_HL_GNUM(f) OPT_EXTRA(f, START_BUMP(f))->num /* non-dirs */
index 8cc3e0a7da660d6979aa030aadea9eb7c281d09c..443c37daaaba96bb9fb3158644c3d70a5599800d 100644 (file)
--- a/rsync.yo
+++ b/rsync.yo
@@ -378,6 +378,8 @@ to the detailed description below for a complete description.  verb(
      --specials              preserve special files
  -D                          same as --devices --specials
  -t, --times                 preserve modification times
      --specials              preserve special files
  -D                          same as --devices --specials
  -t, --times                 preserve modification times
+ -U, --atimes                preserve access (use) times
+     --set-noatime           avoid changing the atime on accessed files
  -O, --omit-dir-times        omit directories from --times
  -J, --omit-link-times       omit symlinks from --times
      --super                 receiver attempts super-user activities
  -O, --omit-dir-times        omit directories from --times
  -J, --omit-link-times       omit symlinks from --times
      --super                 receiver attempts super-user activities
@@ -1240,6 +1242,25 @@ cause the next transfer to behave as if it used bf(-I), causing all files to be
 updated (though rsync's delta-transfer algorithm will make the update fairly efficient
 if the files haven't actually changed, you're much better off using bf(-t)).
 
 updated (though rsync's delta-transfer algorithm will make the update fairly efficient
 if the files haven't actually changed, you're much better off using bf(-t)).
 
+dit(bf(-U, --atimes)) This tells rsync to set the access (use) times of the
+destination files to the same value as the source files.
+
+If repeated, it also sets the bf(--set-noatime) option, which can help you
+to make the sending and receiving systems have the same access times on the
+transferred files without needing to run rsync an extra time after a file is
+transferred.
+
+Note that some older rsync versions (prior to 3.1.4) may have been built with
+a pre-release bf(--atimes) patch that does not imply bf(--set-noatime) when
+this option is repeated.
+
+dit(bf(--set-noatime)) This tells rsync to open files with the O_NOATIME
+flag (on systems that support it) to avoid changing the access time of the
+files that are being transferred. If your OS does not support the O_NOATIME
+flag then rsync will silently ignore this option. Note also that some
+filesystems are mounted to avoid updating the atime on read access even
+without the O_NOATIME flag being set.
+
 dit(bf(-O, --omit-dir-times)) This tells rsync to omit directories when
 it is preserving modification times (see bf(--times)).  If NFS is sharing
 the directories on the receiving side, it is a good idea to use bf(-O).
 dit(bf(-O, --omit-dir-times)) This tells rsync to omit directories when
 it is preserving modification times (see bf(--times)).  If NFS is sharing
 the directories on the receiving side, it is a good idea to use bf(-O).
@@ -2263,7 +2284,10 @@ quote(itemization(
   sender's value (requires bf(--owner) and super-user privileges).
   it() A bf(g) means the group is different and is being updated to the
   sender's value (requires bf(--group) and the authority to set the group).
   sender's value (requires bf(--owner) and super-user privileges).
   it() A bf(g) means the group is different and is being updated to the
   sender's value (requires bf(--group) and the authority to set the group).
-  it() The bf(u) slot is reserved for future use.
+  it() A bf(u) means the access (use) time is different and is being updated to
+  the sender's value (requires bf(--atimes)).  An alternate value of bf(U)
+  means that the access time will be set to the transfer time, which happens
+  when a symlink or directory is updated.
   it() The bf(a) means that the ACL information changed.
   it() The bf(x) means that the extended attribute information changed.
 ))
   it() The bf(a) means that the ACL information changed.
   it() The bf(x) means that the extended attribute information changed.
 ))
index 7f29336740f8a5947c0ab79b058e135501d8b0e1..927f71d6b461141fde15b3e89c12400f006c0593 100644 (file)
--- a/syscall.c
+++ b/syscall.c
@@ -42,6 +42,7 @@ extern int inplace;
 extern int preallocate_files;
 extern int preserve_perms;
 extern int preserve_executability;
 extern int preallocate_files;
 extern int preserve_perms;
 extern int preserve_executability;
+extern int set_noatime;
 
 #ifndef S_BLKSIZE
 # if defined hpux || defined __hpux__ || defined __hpux
 
 #ifndef S_BLKSIZE
 # if defined hpux || defined __hpux__ || defined __hpux
@@ -202,6 +203,11 @@ int do_open(const char *pathname, int flags, mode_t mode)
                RETURN_ERROR_IF_RO_OR_LO;
        }
 
                RETURN_ERROR_IF_RO_OR_LO;
        }
 
+#ifdef O_NOATIME
+       if (set_noatime)
+               flags |= O_NOATIME;
+#endif
+
        return open(pathname, flags | O_BINARY, mode);
 }
 
        return open(pathname, flags | O_BINARY, mode);
 }
 
@@ -380,54 +386,78 @@ int do_setattrlist_times(const char *fname, time_t modtime, uint32 mod_nsec)
 #endif
 
 #ifdef HAVE_UTIMENSAT
 #endif
 
 #ifdef HAVE_UTIMENSAT
-int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec)
+int do_utimensat(const char *fname, STRUCT_STAT *stp)
 {
        struct timespec t[2];
 
        if (dry_run) return 0;
        RETURN_ERROR_IF_RO_OR_LO;
 
 {
        struct timespec t[2];
 
        if (dry_run) return 0;
        RETURN_ERROR_IF_RO_OR_LO;
 
-       t[0].tv_sec = 0;
-       t[0].tv_nsec = UTIME_NOW;
-       t[1].tv_sec = modtime;
-       t[1].tv_nsec = mod_nsec;
+       t[0].tv_sec = stp->st_atime;
+#ifdef ST_ATIME_NSEC
+       t[0].tv_nsec = stp->ST_ATIME_NSEC;
+#else
+       t[0].tv_nsec = 0;
+#endif
+       t[1].tv_sec = stp->st_mtime;
+#ifdef ST_MTIME_NSEC
+       t[1].tv_nsec = stp->ST_MTIME_NSEC;
+#else
+       t[1].tv_nsec = 0;
+#endif
        return utimensat(AT_FDCWD, fname, t, AT_SYMLINK_NOFOLLOW);
 }
 #endif
 
 #ifdef HAVE_LUTIMES
        return utimensat(AT_FDCWD, fname, t, AT_SYMLINK_NOFOLLOW);
 }
 #endif
 
 #ifdef HAVE_LUTIMES
-int do_lutimes(const char *fname, time_t modtime, uint32 mod_nsec)
+int do_lutimes(const char *fname, STRUCT_STAT *stp)
 {
        struct timeval t[2];
 
        if (dry_run) return 0;
        RETURN_ERROR_IF_RO_OR_LO;
 
 {
        struct timeval t[2];
 
        if (dry_run) return 0;
        RETURN_ERROR_IF_RO_OR_LO;
 
-       t[0].tv_sec = time(NULL);
+       t[0].tv_sec = stp->st_atime;
+#ifdef ST_ATIME_NSEC
+       t[0].tv_usec = stp->ST_ATIME_NSEC / 1000;
+#else
        t[0].tv_usec = 0;
        t[0].tv_usec = 0;
-       t[1].tv_sec = modtime;
-       t[1].tv_usec = mod_nsec / 1000;
+#endif
+       t[1].tv_sec = stp->st_mtime;
+#ifdef ST_MTIME_NSEC
+       t[1].tv_usec = stp->ST_MTIME_NSEC / 1000;
+#else
+       t[1].tv_usec = 0;
+#endif
        return lutimes(fname, t);
 }
 #endif
 
 #ifdef HAVE_UTIMES
        return lutimes(fname, t);
 }
 #endif
 
 #ifdef HAVE_UTIMES
-int do_utimes(const char *fname, time_t modtime, uint32 mod_nsec)
+int do_utimes(const char *fname, STRUCT_STAT *stp)
 {
        struct timeval t[2];
 
        if (dry_run) return 0;
        RETURN_ERROR_IF_RO_OR_LO;
 
 {
        struct timeval t[2];
 
        if (dry_run) return 0;
        RETURN_ERROR_IF_RO_OR_LO;
 
-       t[0].tv_sec = time(NULL);
+       t[0].tv_sec = stp->st_atime;
+#ifdef ST_ATIME_NSEC
+       t[0].tv_usec = stp->ST_ATIME_NSEC / 1000;
+#else
        t[0].tv_usec = 0;
        t[0].tv_usec = 0;
-       t[1].tv_sec = modtime;
-       t[1].tv_usec = mod_nsec / 1000;
+#endif
+       t[1].tv_sec = stp->st_mtime;
+#ifdef ST_MTIME_NSEC
+       t[1].tv_usec = stp->ST_MTIME_NSEC / 1000;
+#else
+       t[1].tv_usec = 0;
+#endif
        return utimes(fname, t);
 }
 
 #elif defined HAVE_UTIME
        return utimes(fname, t);
 }
 
 #elif defined HAVE_UTIME
-int do_utime(const char *fname, time_t modtime, UNUSED(uint32 mod_nsec))
+int do_utime(const char *fname, STRUCT_STAT *stp)
 {
 #ifdef HAVE_STRUCT_UTIMBUF
        struct utimbuf tbuf;
 {
 #ifdef HAVE_STRUCT_UTIMBUF
        struct utimbuf tbuf;
@@ -439,12 +469,12 @@ int do_utime(const char *fname, time_t modtime, UNUSED(uint32 mod_nsec))
        RETURN_ERROR_IF_RO_OR_LO;
 
 # ifdef HAVE_STRUCT_UTIMBUF
        RETURN_ERROR_IF_RO_OR_LO;
 
 # ifdef HAVE_STRUCT_UTIMBUF
-       tbuf.actime = time(NULL);
-       tbuf.modtime = modtime;
+       tbuf.actime = stp->st_atime;
+       tbuf.modtime = stp->st_mtime;
        return utime(fname, &tbuf);
 # else
        return utime(fname, &tbuf);
 # else
-       t[0] = time(NULL);
-       t[1] = modtime;
+       t[0] = stp->st_atime;
+       t[1] = stp->st_mtime;
        return utime(fname, t);
 # endif
 }
        return utime(fname, t);
 # endif
 }
index fc0785b6a3cf0d2f0056fd4f20d8ed4ad1e7efab..cfe8727accc2995e96cb0c2eb1b36744b0a742e3 100644 (file)
--- a/t_stub.c
+++ b/t_stub.c
@@ -31,6 +31,7 @@ int module_dirlen = 0;
 int preserve_acls = 0;
 int preserve_times = 0;
 int preserve_xattrs = 0;
 int preserve_acls = 0;
 int preserve_times = 0;
 int preserve_xattrs = 0;
+int set_noatime = 0;
 char *partial_dir;
 char *module_dir;
 filter_rule_list daemon_filter_list;
 char *partial_dir;
 char *module_dir;
 filter_rule_list daemon_filter_list;
diff --git a/testsuite/atimes.test b/testsuite/atimes.test
new file mode 100644 (file)
index 0000000..bd3f292
--- /dev/null
@@ -0,0 +1,17 @@
+#! /bin/sh
+
+# Test rsync copying atimes
+
+. "$suitedir/rsync.fns"
+
+mkdir "$fromdir"
+
+touch "$fromdir/foo"
+touch -a -t 200102031717.42 "$fromdir/foo"
+
+TLS_ARGS=--atimes
+
+checkit "$RSYNC -rtUgvvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
+
+# The script would have aborted on error, so getting here means we've won.
+exit 0
index 06728e263355e02f98b1ed7766e6c904de55057d..a8acd04e6648cdfe102114347b4d82e5544fb8c5 100644 (file)
@@ -27,7 +27,7 @@ outfile="$scratchdir/rsync.out"
 SSH="src/support/lsh.sh --no-cd"
 FILE_REPL='s/^\([^d][^ ]*\) *\(..........[0-9]\) /\1 \2 /'
 DIR_REPL='s/^\(d[^ ]*\)  *[0-9][.,0-9]* /\1         DIR /'
 SSH="src/support/lsh.sh --no-cd"
 FILE_REPL='s/^\([^d][^ ]*\) *\(..........[0-9]\) /\1 \2 /'
 DIR_REPL='s/^\(d[^ ]*\)  *[0-9][.,0-9]* /\1         DIR /'
-LS_REPL='s;[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9];####/##/## ##:##:##;'
+LS_REPL='s;[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ;####/##/## ##:##:## ;g'
 
 build_rsyncd_conf
 
 
 build_rsyncd_conf
 
@@ -91,3 +91,12 @@ drwxr-xr-x         DIR ####/##/## ##:##:## foo
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed"
 
 EOT
 diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed"
 
+$RSYNC -rU localhost::test-from/f* \
+    | sed "$FILE_REPL" | sed "$DIR_REPL" | sed "$LS_REPL" \
+    | tee "$outfile"
+cat <<EOT >"$chkfile"
+drwxr-xr-x         DIR ####/##/## ##:##:##                     foo
+-rw-r--r--           4 ####/##/## ##:##:## ####/##/## ##:##:## foo/one
+EOT
+diff $diffopt "$chkfile" "$outfile" || test_fail "test 3 failed"
+
index bd5768b96ffb33414a66d984503196cb086c3f98..65d071cae90c711897958a5b43781fcabd0de620 100644 (file)
@@ -219,6 +219,14 @@ checkit() {
     # We can just write everything to stdout/stderr, because the
     # wrapper hides it unless there is a problem.
 
     # We can just write everything to stdout/stderr, because the
     # wrapper hides it unless there is a problem.
 
+    case "x$TLS_ARGS" in
+    *--atimes*)
+       ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
+       ;;
+    *)
+       ;;
+    esac
+
     echo "Running: \"$1\""  
     eval "$1" 
     status=$?
     echo "Running: \"$1\""  
     eval "$1" 
     status=$?
@@ -226,10 +234,17 @@ checkit() {
        failed="$failed status=$status"
     fi
 
        failed="$failed status=$status"
     fi
 
+    case "x$TLS_ARGS" in
+    *--atimes*)
+       ;;
+    *)
+       ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
+       ;;
+    esac
+
     echo "-------------"
     echo "check how the directory listings compare with diff:"
     echo ""
     echo "-------------"
     echo "check how the directory listings compare with diff:"
     echo ""
-    ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
     ( cd "$3" && rsync_ls_lR . ) > "$tmpdir/ls-to"
     diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to" || failed="$failed dir-diff"
 
     ( cd "$3" && rsync_ls_lR . ) > "$tmpdir/ls-to"
     diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to" || failed="$failed dir-diff"
 
diff --git a/tls.c b/tls.c
index 637ba1dae8fe56fc01475cb30a710c1953b7e921..9e15c96cf46c667ee1c1bbca40cfe4ac05976bd4 100644 (file)
--- a/tls.c
+++ b/tls.c
@@ -52,6 +52,7 @@ int nsec_times = 0;
 int preserve_perms = 0;
 int preserve_executability = 0;
 int preallocate_files = 0;
 int preserve_perms = 0;
 int preserve_executability = 0;
 int preallocate_files = 0;
+int set_noatime = 0;
 int inplace = 0;
 
 #ifdef SUPPORT_XATTRS
 int inplace = 0;
 
 #ifdef SUPPORT_XATTRS
@@ -111,6 +112,8 @@ static int stat_xattr(const char *fname, STRUCT_STAT *fst)
 
 #endif
 
 
 #endif
 
+static int display_atimes = 0;
+
 static void failed(char const *what, char const *where)
 {
        fprintf(stderr, PROGRAM ": %s %s: %s\n",
 static void failed(char const *what, char const *where)
 {
        fprintf(stderr, PROGRAM ": %s %s: %s\n",
@@ -118,13 +121,38 @@ static void failed(char const *what, char const *where)
        exit(1);
 }
 
        exit(1);
 }
 
+static void storetime(char *dest, size_t destsize, time_t t, int nsecs)
+{
+       if (t) {
+               int len;
+               struct tm *mt = gmtime(&t);
+
+               len = snprintf(dest, destsize,
+                       " %04d-%02d-%02d %02d:%02d:%02d",
+                       (int)mt->tm_year + 1900,
+                       (int)mt->tm_mon + 1,
+                       (int)mt->tm_mday,
+                       (int)mt->tm_hour,
+                       (int)mt->tm_min,
+                       (int)mt->tm_sec);
+               if (nsecs >= 0 && len >= 0)
+                       snprintf(dest + len, destsize - len, ".%09d", nsecs);
+       } else {
+               int has_nsecs = nsecs >= 0 ? 1 : 0;
+               int len = MIN(20 + 10*has_nsecs, (int)destsize - 1);
+               memset(dest, ' ', len);
+               dest[len] = '\0';
+       }
+}
+
 static void list_file(const char *fname)
 {
        STRUCT_STAT buf;
        char permbuf[PERMSTRING_SIZE];
 static void list_file(const char *fname)
 {
        STRUCT_STAT buf;
        char permbuf[PERMSTRING_SIZE];
-       struct tm *mt;
-       char datebuf[50];
+       char mtimebuf[50];
+       char atimebuf[50];
        char linkbuf[4096];
        char linkbuf[4096];
+       int nsecs;
 
        if (do_lstat(fname, &buf) < 0)
                failed("stat", fname);
 
        if (do_lstat(fname, &buf) < 0)
                failed("stat", fname);
@@ -150,58 +178,47 @@ static void list_file(const char *fname)
                        buf.st_uid = buf.st_gid = 0;
                strlcpy(linkbuf, " -> ", sizeof linkbuf);
                /* const-cast required for silly UNICOS headers */
                        buf.st_uid = buf.st_gid = 0;
                strlcpy(linkbuf, " -> ", sizeof linkbuf);
                /* const-cast required for silly UNICOS headers */
-               len = do_readlink((char *) fname, linkbuf+4, sizeof(linkbuf) - 4);
+               len = do_readlink((char*)fname, linkbuf+4, sizeof linkbuf - 4);
                if (len == -1)
                        failed("do_readlink", fname);
                else
                        /* it's not nul-terminated */
                        linkbuf[4+len] = 0;
        } else {
                if (len == -1)
                        failed("do_readlink", fname);
                else
                        /* it's not nul-terminated */
                        linkbuf[4+len] = 0;
        } else {
-               linkbuf[0] = 0;
+               linkbuf[0] = '\0';
        }
 
        permstring(permbuf, buf.st_mode);
        }
 
        permstring(permbuf, buf.st_mode);
-
-       if (buf.st_mtime) {
-               int len;
-               mt = gmtime(&buf.st_mtime);
-
-               len = snprintf(datebuf, sizeof datebuf,
-                       "%04d-%02d-%02d %02d:%02d:%02d",
-                       (int)mt->tm_year + 1900,
-                       (int)mt->tm_mon + 1,
-                       (int)mt->tm_mday,
-                       (int)mt->tm_hour,
-                       (int)mt->tm_min,
-                       (int)mt->tm_sec);
 #ifdef ST_MTIME_NSEC
 #ifdef ST_MTIME_NSEC
-               if (nsec_times) {
-                       snprintf(datebuf + len, sizeof datebuf - len,
-                               ".%09d", (int)buf.ST_MTIME_NSEC);
-               }
+       if (nsec_times)
+               nsecs = (int)buf.ST_MTIME_NSEC;
+       else
 #endif
 #endif
-       } else {
-               int len = MIN(19 + 9*nsec_times, (int)sizeof datebuf - 1);
-               memset(datebuf, ' ', len);
-               datebuf[len] = '\0';
-       }
+               nsecs = -1;
+       storetime(mtimebuf, sizeof mtimebuf, buf.st_mtime, nsecs);
+       if (display_atimes)
+               storetime(atimebuf, sizeof atimebuf, S_ISDIR(buf.st_mode) ? 0 : buf.st_atime, -1);
+       else
+               atimebuf[0] = '\0';
 
        /* TODO: Perhaps escape special characters in fname? */
 
        /* TODO: Perhaps escape special characters in fname? */
-
        printf("%s ", permbuf);
        printf("%s ", permbuf);
+
        if (S_ISCHR(buf.st_mode) || S_ISBLK(buf.st_mode)) {
                printf("%5ld,%6ld",
                    (long)major(buf.st_rdev),
                    (long)minor(buf.st_rdev));
        } else
                printf("%15s", do_big_num(buf.st_size, 1, NULL));
        if (S_ISCHR(buf.st_mode) || S_ISBLK(buf.st_mode)) {
                printf("%5ld,%6ld",
                    (long)major(buf.st_rdev),
                    (long)minor(buf.st_rdev));
        } else
                printf("%15s", do_big_num(buf.st_size, 1, NULL));
-       printf(" %6ld.%-6ld %6ld %s %s%s\n",
+
+       printf(" %6ld.%-6ld %6ld%s%s %s%s\n",
               (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
               (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
-              datebuf, fname, linkbuf);
+              mtimebuf, atimebuf, fname, linkbuf);
 }
 
 static struct poptOption long_options[] = {
   /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
 }
 
 static struct poptOption long_options[] = {
   /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
+  {"atimes",          'U', POPT_ARG_NONE,   &display_atimes, 0, 0, 0},
   {"link-times",      'l', POPT_ARG_NONE,   &link_times, 0, 0, 0 },
   {"link-owner",      'L', POPT_ARG_NONE,   &link_owner, 0, 0, 0 },
 #ifdef SUPPORT_XATTRS
   {"link-times",      'l', POPT_ARG_NONE,   &link_times, 0, 0, 0 },
   {"link-owner",      'L', POPT_ARG_NONE,   &link_owner, 0, 0, 0 },
 #ifdef SUPPORT_XATTRS
@@ -220,6 +237,7 @@ static void NORETURN tls_usage(int ret)
   fprintf(F,"usage: " PROGRAM " [OPTIONS] FILE ...\n");
   fprintf(F,"Trivial file listing program for portably checking rsync\n");
   fprintf(F,"\nOptions:\n");
   fprintf(F,"usage: " PROGRAM " [OPTIONS] FILE ...\n");
   fprintf(F,"Trivial file listing program for portably checking rsync\n");
   fprintf(F,"\nOptions:\n");
+  fprintf(F," -U, --atimes                display access (last-used) times\n");
   fprintf(F," -l, --link-times            display the time on a symlink\n");
   fprintf(F," -L, --link-owner            display the owner+group on a symlink\n");
 #ifdef SUPPORT_XATTRS
   fprintf(F," -l, --link-times            display the time on a symlink\n");
   fprintf(F," -L, --link-owner            display the owner+group on a symlink\n");
 #ifdef SUPPORT_XATTRS
index 49331fad3602fd05f23c1e959f87659b4a10ae46..2891279eef6846e73e3f8e2b449ea91f0e454294 100644 (file)
@@ -29,6 +29,7 @@ int list_only = 0;
 int preserve_perms = 0;
 int preserve_executability = 0;
 int preallocate_files = 0;
 int preserve_perms = 0;
 int preserve_executability = 0;
 int preallocate_files = 0;
+int set_noatime = 0;
 int inplace = 0;
 
 int
 int inplace = 0;
 
 int
diff --git a/util.c b/util.c
index c5569f6d54be1e9ce71b3ee1fd4f29610fcd46a3..1935a7711e8f47f0aa6ff0c0041cf2720d772a04 100644 (file)
--- a/util.c
+++ b/util.c
@@ -117,14 +117,15 @@ void print_child_argv(const char *prefix, char **cmd)
 
 /* This returns 0 for success, 1 for a symlink if symlink time-setting
  * is not possible, or -1 for any other error. */
 
 /* This returns 0 for success, 1 for a symlink if symlink time-setting
  * is not possible, or -1 for any other error. */
-int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
+int set_times(const char *fname, STRUCT_STAT *stp)
 {
        static int switch_step = 0;
 
        if (DEBUG_GTE(TIME, 1)) {
 {
        static int switch_step = 0;
 
        if (DEBUG_GTE(TIME, 1)) {
-               rprintf(FINFO, "set modtime of %s to (%ld) %s",
-                       fname, (long)modtime,
-                       asctime(localtime(&modtime)));
+               rprintf(FINFO,
+                       "set modtime, atime of %s to (%ld) %s, (%ld) %s\n",
+                       fname, (long)stp->st_mtime,
+                       timestring(stp->st_mtime), (long)stp->st_atime, timestring(stp->st_atime));
        }
 
        switch (switch_step) {
        }
 
        switch (switch_step) {
@@ -139,7 +140,7 @@ int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
 
 #ifdef HAVE_UTIMENSAT
 #include "case_N.h"
 
 #ifdef HAVE_UTIMENSAT
 #include "case_N.h"
-               if (do_utimensat(fname, modtime, mod_nsec) == 0)
+               if (do_utimensat(fname, stp) == 0)
                        break;
                if (errno != ENOSYS)
                        return -1;
                        break;
                if (errno != ENOSYS)
                        return -1;
@@ -148,7 +149,7 @@ int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
 
 #ifdef HAVE_LUTIMES
 #include "case_N.h"
 
 #ifdef HAVE_LUTIMES
 #include "case_N.h"
-               if (do_lutimes(fname, modtime, mod_nsec) == 0)
+               if (do_lutimes(fname, stp) == 0)
                        break;
                if (errno != ENOSYS)
                        return -1;
                        break;
                if (errno != ENOSYS)
                        return -1;
@@ -159,16 +160,16 @@ int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
                switch_step++;
                if (preserve_times & PRESERVE_LINK_TIMES) {
                        preserve_times &= ~PRESERVE_LINK_TIMES;
                switch_step++;
                if (preserve_times & PRESERVE_LINK_TIMES) {
                        preserve_times &= ~PRESERVE_LINK_TIMES;
-                       if (S_ISLNK(mode))
+                       if (S_ISLNK(stp->st_mode))
                                return 1;
                }
 
 #include "case_N.h"
 #ifdef HAVE_UTIMES
                                return 1;
                }
 
 #include "case_N.h"
 #ifdef HAVE_UTIMES
-               if (do_utimes(fname, modtime, mod_nsec) == 0)
+               if (do_utimes(fname, stp) == 0)
                        break;
 #else
                        break;
 #else
-               if (do_utime(fname, modtime, mod_nsec) == 0)
+               if (do_utime(fname, stp) == 0)
                        break;
 #endif
 
                        break;
 #endif
 
@@ -1332,18 +1333,14 @@ int unsafe_symlink(const char *dest, const char *src)
 /* Return the date and time as a string.  Some callers tweak returned buf. */
 char *timestring(time_t t)
 {
 /* Return the date and time as a string.  Some callers tweak returned buf. */
 char *timestring(time_t t)
 {
-       static char TimeBuf[200];
+       static int ndx = 0;
+       static char buffers[4][20]; /* We support 4 simultaneous timestring results. */
+       char *TimeBuf = buffers[ndx = (ndx + 1) % 4];
        struct tm *tm = localtime(&t);
        struct tm *tm = localtime(&t);
-       char *p;
 
 
-#ifdef HAVE_STRFTIME
-       strftime(TimeBuf, sizeof TimeBuf - 1, "%Y/%m/%d %H:%M:%S", tm);
-#else
-       strlcpy(TimeBuf, asctime(tm), sizeof TimeBuf);
-#endif
-
-       if ((p = strchr(TimeBuf, '\n')) != NULL)
-               *p = '\0';
+       snprintf(TimeBuf, sizeof buffers[0], "%4d/%02d/%02d %02d:%02d:%02d",
+                (int)tm->tm_year + 1900, (int)tm->tm_mon + 1, (int)tm->tm_mday,
+                (int)tm->tm_hour, (int)tm->tm_min, (int)tm->tm_sec);
 
        return TimeBuf;
 }
 
        return TimeBuf;
 }