The patches for 3.0.0.
[rsync/rsync-patches.git] / atimes.diff
CommitLineData
03019e41 1To use this patch, run these commands for a successful build:
8a529471 2
03019e41 3 patch -p1 <patches/atimes.diff
27e96866
WD
4 ./configure (optional if already run)
5 make
8a529471 6
cc3e685d
WD
7diff --git a/compat.c b/compat.c
8--- a/compat.c
9+++ b/compat.c
ccdb48f6
WD
10@@ -44,6 +44,7 @@ extern int protocol_version;
11 extern int protect_args;
898a2112
WD
12 extern int preserve_uid;
13 extern int preserve_gid;
14+extern int preserve_atimes;
15 extern int preserve_acls;
16 extern int preserve_xattrs;
9c25eef5 17 extern int need_messages_from_generator;
cdcd2137 18@@ -60,7 +61,7 @@ extern iconv_t ic_send, ic_recv;
ccdb48f6 19 #endif
898a2112
WD
20
21 /* These index values are for the file-list's extra-attribute array. */
d4dd2dd5
WD
22-int uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
23+int uid_ndx, gid_ndx, atimes_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
898a2112 24
85096e5e
WD
25 int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
26
27@@ -134,6 +135,8 @@ void setup_protocol(int f_out,int f_in)
898a2112 28 uid_ndx = ++file_extra_cnt;
fdf967c7 29 if (preserve_gid)
898a2112 30 gid_ndx = ++file_extra_cnt;
fdf967c7 31+ if (preserve_atimes)
a5e6228a 32+ atimes_ndx = (file_extra_cnt += TIME_EXTRA_CNT);
4306c620 33 if (preserve_acls && !am_sender)
898a2112 34 acls_ndx = ++file_extra_cnt;
5795bf59 35 if (preserve_xattrs)
cc3e685d
WD
36diff --git a/flist.c b/flist.c
37--- a/flist.c
38+++ b/flist.c
f9df736a 39@@ -55,6 +55,7 @@ extern int preserve_specials;
898a2112
WD
40 extern int uid_ndx;
41 extern int gid_ndx;
790ba11a 42 extern int eol_nulls;
898a2112 43+extern int atimes_ndx;
81c32ffd 44 extern int relative_paths;
a5e0f697 45 extern int implied_dirs;
7e27b6c0 46 extern int file_extra_cnt;
f9df736a 47@@ -344,7 +345,7 @@ int push_pathname(const char *dir, int len)
a5e6228a 48
9c25eef5 49 static void send_file_entry(int f, struct file_struct *file, int ndx, int first_ndx)
43581f16 50 {
a5e6228a
WD
51- static time_t modtime;
52+ static time_t modtime, atime;
43581f16 53 static mode_t mode;
f1f4dbd1 54 #ifdef SUPPORT_HARD_LINKS
ba50e96c 55 static int64 dev;
f9df736a 56@@ -452,6 +453,13 @@ static void send_file_entry(int f, struct file_struct *file, int ndx, int first_
99650e0d 57 xflags |= XMIT_SAME_TIME;
43581f16
WD
58 else
59 modtime = file->modtime;
898a2112 60+ if (atimes_ndx && !S_ISDIR(mode)) {
a5e6228a 61+ time_t file_atime = f_atime(file);
672ad041 62+ if (file_atime == atime)
99650e0d 63+ xflags |= XMIT_SAME_ATIME;
43581f16 64+ else
672ad041 65+ atime = file_atime;
43581f16
WD
66+ }
67
09fb8f03 68 #ifdef SUPPORT_HARD_LINKS
fdf967c7 69 if (tmp_dev != 0) {
f9df736a 70@@ -524,6 +532,8 @@ static void send_file_entry(int f, struct file_struct *file, int ndx, int first_
a2dabe5e 71 }
99650e0d 72 if (!(xflags & XMIT_SAME_MODE))
43581f16 73 write_int(f, to_wire_mode(mode));
99650e0d 74+ if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME))
a2dabe5e 75+ write_varlong(f, atime, 4);
f9df736a 76 if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
caf38d8d
WD
77 if (protocol_version < 30)
78 write_int(f, uid);
f9df736a 79@@ -610,7 +620,7 @@ static void send_file_entry(int f, struct file_struct *file, int ndx, int first_
70891d26 80 static struct file_struct *recv_file_entry(struct file_list *flist,
761f1b71 81 int xflags, int f)
43581f16 82 {
a2dabe5e
WD
83- static int64 modtime;
84+ static int64 modtime, atime;
43581f16 85 static mode_t mode;
f1f4dbd1 86 #ifdef SUPPORT_HARD_LINKS
ba50e96c 87 static int64 dev;
f9df736a 88@@ -743,6 +753,16 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
a2dabe5e 89 }
761f1b71 90 if (!(xflags & XMIT_SAME_MODE))
43581f16 91 mode = from_wire_mode(read_int(f));
898a2112 92+ if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME)) {
a2dabe5e
WD
93+ atime = read_varlong(f, 4);
94+#if SIZEOF_TIME_T < SIZEOF_INT64
a5e6228a 95+ if (!am_generator && (int64)(time_t)atime != atime) {
cc3e685d 96+ rprintf(FERROR_XFER,
a2dabe5e
WD
97+ "Access time value of %s truncated on receiver.\n",
98+ lastname);
99+ }
100+#endif
101+ }
43581f16 102
1cb60481
WD
103 if (chmod_modes && !S_ISLNK(mode))
104 mode = tweak_mode(mode, chmod_modes);
f9df736a 105@@ -873,6 +893,8 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
fc068916 106 F_GROUP(file) = gid;
761f1b71
WD
107 file->flags |= gid_flags;
108 }
898a2112 109+ if (atimes_ndx)
a5e6228a 110+ f_atime_set(file, (time_t)atime);
d4dd2dd5 111 if (unsort_ndx)
671e613f 112 F_NDX(file) = flist->used + flist->ndx_start;
d4dd2dd5 113
f9df736a 114@@ -1205,6 +1227,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
fc068916 115 F_OWNER(file) = st.st_uid;
f9df736a 116 if (gid_ndx) /* Check gid_ndx instead of preserve_gid for del support */
fc068916 117 F_GROUP(file) = st.st_gid;
898a2112 118+ if (atimes_ndx)
a5e6228a 119+ f_atime_set(file, st.st_atime);
1cb60481 120
fc068916
WD
121 if (basename != thisname)
122 file->dirname = lastdir;
cc3e685d
WD
123diff --git a/generator.c b/generator.c
124--- a/generator.c
125+++ b/generator.c
a5e6228a
WD
126@@ -21,6 +21,7 @@
127 */
128
129 #include "rsync.h"
130+#include "ifuncs.h"
131
132 extern int verbose;
133 extern int dry_run;
f9df736a 134@@ -633,6 +634,9 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
9a70b743 135 && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
4306c620 136 || (keep_time && cmp_time(file->modtime, sxp->st.st_mtime) != 0))
f20eb450 137 iflags |= ITEM_REPORT_TIME;
a5e6228a
WD
138+ if (atimes_ndx && !S_ISDIR(file->mode) && !S_ISLNK(file->mode)
139+ && cmp_time(f_atime(file), sxp->st.st_atime) != 0)
55602791 140+ iflags |= ITEM_REPORT_ATIME;
7c4c2959 141 #if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
03edfc6b
WD
142 if (S_ISLNK(file->mode)) {
143 ;
f9df736a 144@@ -987,6 +991,8 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
8aec0853
WD
145 if (link_dest) {
146 if (!hard_link_one(file, fname, cmpbuf, 1))
8a04c9a7 147 goto try_a_copy;
a5e6228a 148+ if (atimes_ndx)
97669ae9 149+ set_file_attrs(fname, file, sxp, NULL, 0);
1aa236e1 150 if (preserve_hard_links && F_IS_HLINKED(file))
ccdb48f6 151 finish_hard_link(file, fname, ndx, &sxp->st, itemizing, code, j);
85096e5e 152 if (!maybe_ATTRS_REPORT && (verbose > 1 || stdout_format_has_i > 1)) {
f9df736a 153@@ -1173,6 +1179,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
cdcd2137
WD
154 static void list_file_entry(struct file_struct *f)
155 {
156 char permbuf[PERMSTRING_SIZE];
a5e6228a 157+ time_t atime = atimes_ndx ? f_atime(f) : 0;
cdcd2137
WD
158 double len;
159
160 if (!F_IS_ACTIVE(f)) {
f9df736a 161@@ -1187,14 +1194,16 @@ static void list_file_entry(struct file_struct *f)
cdcd2137
WD
162
163 #ifdef SUPPORT_LINKS
164 if (preserve_links && S_ISLNK(f->mode)) {
165- rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
166+ rprintf(FINFO, "%s %11.0f %s %s %s -> %s\n",
167 permbuf, len, timestring(f->modtime),
168+ atimes_ndx ? timestring(atime) : "",
169 f_name(f, NULL), F_SYMLINK(f));
170 } else
171 #endif
172 {
173- rprintf(FINFO, "%s %11.0f %s %s\n",
174+ rprintf(FINFO, "%s %11.0f %s %s %s\n",
175 permbuf, len, timestring(f->modtime),
176+ atimes_ndx ? timestring(atime) : "",
177 f_name(f, NULL));
178 }
179 }
f9df736a 180@@ -1950,7 +1959,7 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
85a65b82
WD
181 if (!(file->mode & S_IWUSR))
182 do_chmod(fname, file->mode);
183 if (need_retouch_dir_times)
184- set_modtime(fname, file->modtime, file->mode);
185+ set_times(fname, file->modtime, file->modtime, file->mode);
6cbbe66d 186 if (allowed_lull && !(counter % lull_mod))
85a65b82 187 maybe_send_keepalive();
6cbbe66d 188 else if (!(counter & 0xFF))
a5e6228a
WD
189diff --git a/ifuncs.h b/ifuncs.h
190--- a/ifuncs.h
191+++ b/ifuncs.h
192@@ -57,6 +57,28 @@ from_wire_mode(int mode)
193 return mode;
194 }
195
196+static inline time_t
197+f_atime(struct file_struct *fp)
198+{
199+#if SIZEOF_TIME_T > 4
200+ time_t atime;
201+ memcpy(&atime, &REQ_EXTRA(fp, atimes_ndx)->unum, SIZEOF_TIME_T);
202+ return atime;
203+#else
204+ return REQ_EXTRA(fp, atimes_ndx)->unum;
205+#endif
206+}
207+
208+static inline void
209+f_atime_set(struct file_struct *fp, time_t atime)
210+{
211+#if SIZEOF_TIME_T > 4
212+ memcpy(&REQ_EXTRA(fp, atimes_ndx)->unum, &atime, SIZEOF_TIME_T);
213+#else
214+ REQ_EXTRA(fp, atimes_ndx)->unum = (uint32)atime;
215+#endif
216+}
217+
218 static inline int
219 isDigit(const char *ptr)
220 {
cc3e685d
WD
221diff --git a/log.c b/log.c
222--- a/log.c
223+++ b/log.c
85096e5e 224@@ -644,7 +644,8 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
fb11cdd7
WD
225 c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
226 c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
227 c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
4306c620 228- c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' : 'u';
fb11cdd7 229+ c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.'
55602791 230+ : S_ISLNK(file->mode) ? 'U' : 'u';
4306c620
WD
231 c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
232 c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
233 c[11] = '\0';
cc3e685d
WD
234diff --git a/options.c b/options.c
235--- a/options.c
236+++ b/options.c
f2863bc0 237@@ -58,6 +58,7 @@ int preserve_specials = 0;
a54a2c4d 238 int preserve_uid = 0;
43581f16
WD
239 int preserve_gid = 0;
240 int preserve_times = 0;
81c32ffd 241+int preserve_atimes = 0;
43581f16
WD
242 int update_only = 0;
243 int cvs_exclude = 0;
244 int dry_run = 0;
85096e5e 245@@ -352,6 +353,7 @@ void usage(enum logcode F)
4a65fe72 246 rprintf(F," -D same as --devices --specials\n");
671e613f
WD
247 rprintf(F," -t, --times preserve modification times\n");
248 rprintf(F," -O, --omit-dir-times omit directories from --times\n");
12b04b40 249+ rprintf(F," -U, --atimes preserve access (last-used) times\n");
4a65fe72 250 rprintf(F," --super receiver attempts super-user activities\n");
12b04b40
WD
251 #ifdef SUPPORT_XATTRS
252 rprintf(F," --fake-super store/recover privileged attrs using xattrs\n");
85096e5e 253@@ -487,6 +489,9 @@ static struct poptOption long_options[] = {
a54a2c4d 254 {"times", 't', POPT_ARG_VAL, &preserve_times, 2, 0, 0 },
489b0a72
WD
255 {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
256 {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
55602791 257+ {"atimes", 'U', POPT_ARG_VAL, &preserve_atimes, 1, 0, 0 },
489b0a72 258+ {"no-atimes", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 },
70891d26 259+ {"no-U", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 },
a54a2c4d
WD
260 {"omit-dir-times", 'O', POPT_ARG_VAL, &omit_dir_times, 1, 0, 0 },
261 {"no-omit-dir-times",0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 },
262 {"no-O", 0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 },
85096e5e 263@@ -1747,6 +1752,8 @@ void server_options(char **args, int *argc_p)
43581f16
WD
264 argstr[x++] = 'D';
265 if (preserve_times)
266 argstr[x++] = 't';
81c32ffd 267+ if (preserve_atimes)
55602791 268+ argstr[x++] = 'U';
43581f16 269 if (preserve_perms)
4a65fe72
WD
270 argstr[x++] = 'p';
271 else if (preserve_executability && am_sender)
cc3e685d
WD
272diff --git a/rsync.c b/rsync.c
273--- a/rsync.c
274+++ b/rsync.c
85096e5e 275@@ -345,6 +345,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
9a21ad72 276 int updated = 0;
c8a8b4a7 277 stat_x sx2;
9a21ad72
WD
278 int change_uid, change_gid;
279+ time_t atime, mtime;
1ed0b5c9 280 mode_t new_mode = file->mode;
ccdb48f6 281 int inherit;
9a21ad72 282
85096e5e 283@@ -388,20 +389,39 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
cc3e685d 284 set_xattr(fname, file, fnamecmp, sxp);
4306c620 285 #endif
9a21ad72 286
8a04c9a7
WD
287+ /* This code must be the first update in the function due to
288+ * how it uses the "updated" variable. */
a54a2c4d 289 if (!preserve_times || (S_ISDIR(sxp->st.st_mode) && preserve_times == 1))
4a65fe72 290 flags |= ATTRS_SKIP_MTIME;
a5e6228a 291+ if (!atimes_ndx || S_ISDIR(sxp->st.st_mode))
4a65fe72
WD
292+ flags |= ATTRS_SKIP_ATIME;
293 if (!(flags & ATTRS_SKIP_MTIME)
4306c620
WD
294 && cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
295- int ret = set_modtime(fname, file->modtime, sxp->st.st_mode);
9a21ad72 296+ mtime = file->modtime;
9e355bf1 297+ updated = 1;
9a21ad72 298+ } else
4306c620 299+ mtime = sxp->st.st_mtime;
672ad041 300+ if (!(flags & ATTRS_SKIP_ATIME)) {
a5e6228a 301+ time_t file_atime = f_atime(file);
4306c620 302+ if (cmp_time(sxp->st.st_atime, file_atime) != 0) {
672ad041
WD
303+ atime = file_atime;
304+ updated = 1;
305+ } else
4306c620 306+ atime = sxp->st.st_atime;
81c32ffd 307+ } else
4306c620 308+ atime = sxp->st.st_atime;
9e355bf1 309+ if (updated) {
4306c620 310+ int ret = set_times(fname, mtime, atime, sxp->st.st_mode);
9e355bf1 311 if (ret < 0) {
cc3e685d 312 rsyserr(FERROR_XFER, errno, "failed to set times on %s",
9e355bf1 313 full_fname(fname));
4306c620 314 goto cleanup;
9e355bf1
WD
315 }
316- if (ret == 0) /* ret == 1 if symlink could not be set */
317- updated = 1;
85096e5e
WD
318- else if (receiver_symlink_times)
319- file->flags |= FLAG_TIME_FAILED;
320+ if (ret > 0) { /* ret == 1 if symlink could not be set */
9e355bf1 321+ updated = 0;
85096e5e
WD
322+ if (receiver_symlink_times)
323+ file->flags |= FLAG_TIME_FAILED;
324+ }
43581f16
WD
325 }
326
898a2112 327 change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
85096e5e 328@@ -531,7 +551,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
a5e6228a
WD
329
330 /* Change permissions before putting the file into place. */
331 set_file_attrs(fnametmp, file, NULL, fnamecmp,
332- ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
333+ ok_to_set_time ? 0 : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME);
334
335 /* move tmp file over real file */
336 if (verbose > 2)
85096e5e 337@@ -558,7 +578,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
a5e6228a
WD
338
339 do_set_file_attrs:
340 set_file_attrs(fnametmp, file, NULL, fnamecmp,
341- ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
342+ ok_to_set_time ? 0 : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME);
343
344 if (temp_copy_name) {
345 if (do_rename(fnametmp, fname) < 0) {
cc3e685d
WD
346diff --git a/rsync.h b/rsync.h
347--- a/rsync.h
348+++ b/rsync.h
349@@ -60,6 +60,7 @@
caf38d8d 350 #define XMIT_RDEV_MINOR_8_pre30 (1<<11) /* protocols 28 - 29 */
9c25eef5
WD
351 #define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */
352 #define XMIT_HLINK_FIRST (1<<12) /* protocols 30 - now (HLINKED files only) */
353+#define XMIT_SAME_ATIME (1<<13) /* protocols ?? - now */
43581f16
WD
354
355 /* These flags are used in the live flist data. */
356
85096e5e 357@@ -149,6 +150,7 @@
8a529471 358
4a65fe72
WD
359 #define ATTRS_REPORT (1<<0)
360 #define ATTRS_SKIP_MTIME (1<<1)
361+#define ATTRS_SKIP_ATIME (1<<2)
8a529471
WD
362
363 #define FULL_FLUSH 1
364 #define NORMAL_FLUSH 0
85096e5e 365@@ -620,12 +622,14 @@ extern int file_extra_cnt;
9c25eef5 366 extern int inc_recurse;
898a2112
WD
367 extern int uid_ndx;
368 extern int gid_ndx;
369+extern int atimes_ndx;
370 extern int acls_ndx;
371 extern int xattrs_ndx;
fdf967c7 372
a5e6228a
WD
373 #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
374 #define EXTRA_LEN (sizeof (union file_extras))
375 #define PTR_EXTRA_CNT ((sizeof (char *) + EXTRA_LEN - 1) / EXTRA_LEN)
376+#define TIME_EXTRA_CNT ((SIZEOF_TIME_T + EXTRA_LEN - 1) / EXTRA_LEN)
377 #define DEV_EXTRA_CNT 2
378 #define DIRNODE_EXTRA_CNT 3
379 #define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
cc3e685d
WD
380diff --git a/rsync.yo b/rsync.yo
381--- a/rsync.yo
382+++ b/rsync.yo
383@@ -349,6 +349,7 @@ to the detailed description below for a complete description. verb(
4a65fe72 384 -D same as --devices --specials
671e613f
WD
385 -t, --times preserve modification times
386 -O, --omit-dir-times omit directories from --times
55602791 387+ -U, --atimes preserve access (use) times
4a65fe72 388 --super receiver attempts super-user activities
12b04b40 389 --fake-super store/recover privileged attrs using xattrs
43581f16 390 -S, --sparse handle sparse files efficiently
a5e6228a 391@@ -987,6 +988,12 @@ it is preserving modification times (see bf(--times)). If NFS is sharing
a7219d20 392 the directories on the receiving side, it is a good idea to use bf(-O).
333b8af4 393 This option is inferred if you use bf(--backup) without bf(--backup-dir).
7b675ff5 394
55602791
WD
395+dit(bf(-U, --atimes)) This tells rsync to set the access (use) times of the
396+destination files to the same value as the source files. Note that the
81c32ffd
WD
397+reading of the source file may update the atime of the source files, so
398+repeated rsync runs with --atimes may be needed if you want to force the
399+access-time values to be 100% identical on the two systems.
7b675ff5 400+
4a65fe72
WD
401 dit(bf(--super)) This tells the receiving side to attempt super-user
402 activities even if the receiving rsync wasn't run by the super-user. These
403 activities include: preserving users via the bf(--owner) option, preserving
f9df736a 404@@ -1689,7 +1696,10 @@ quote(itemization(
ccc3a12c
WD
405 sender's value (requires bf(--owner) and super-user privileges).
406 it() A bf(g) means the group is different and is being updated to the
407 sender's value (requires bf(--group) and the authority to set the group).
f9df736a 408- it() The bf(u) slot is reserved for future use.
55602791
WD
409+ it() A bf(u) means the access (use) time is different and is being updated to
410+ the sender's value (requires bf(--atimes)). An alternate value of bf(U)
411+ means that the access time will be set to the transfer time, which happens
ccc3a12c 412+ when a symlink or directory is updated.
4306c620 413 it() The bf(a) means that the ACL information changed.
f9df736a
WD
414 it() The bf(x) means that the extended attribute information changed.
415 ))
cc3e685d
WD
416diff --git a/testsuite/atimes.test b/testsuite/atimes.test
417new file mode 100644
418--- /dev/null
419+++ b/testsuite/atimes.test
a5e6228a 420@@ -0,0 +1,17 @@
13bed3dd
WD
421+#! /bin/sh
422+
423+# Test rsync copying atimes
424+
425+. "$suitedir/rsync.fns"
426+
13bed3dd
WD
427+mkdir "$fromdir"
428+
429+touch "$fromdir/foo"
430+touch -a -t 200102031717.42 "$fromdir/foo"
431+
a5e6228a 432+TLS_ARGS=--atimes
13bed3dd 433+
55602791 434+checkit "$RSYNC -rtUgvvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
13bed3dd
WD
435+
436+# The script would have aborted on error, so getting here means we've won.
437+exit 0
cc3e685d
WD
438diff --git a/testsuite/rsync.fns b/testsuite/rsync.fns
439--- a/testsuite/rsync.fns
440+++ b/testsuite/rsync.fns
f9df736a 441@@ -192,6 +192,10 @@ checkit() {
13bed3dd
WD
442 # We can just write everything to stdout/stderr, because the
443 # wrapper hides it unless there is a problem.
444
a5e6228a 445+ if test x$TLS_ARGS = x--atimes; then
81c32ffd
WD
446+ ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
447+ fi
13bed3dd
WD
448+
449 echo "Running: \"$1\""
450 eval "$1"
451 status=$?
f9df736a 452@@ -199,10 +203,13 @@ checkit() {
81c32ffd
WD
453 failed="YES";
454 fi
455
a5e6228a 456+ if test x$TLS_ARGS != x--atimes; then
81c32ffd
WD
457+ ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
458+ fi
459+
13bed3dd 460 echo "-------------"
b78a6aba
WD
461 echo "check how the directory listings compare with diff:"
462 echo ""
13bed3dd 463- ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
b78a6aba
WD
464 ( cd "$3" && rsync_ls_lR . ) > "$tmpdir/ls-to"
465 diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to" || failed=YES
4da25dad 466
cc3e685d
WD
467diff --git a/tls.c b/tls.c
468--- a/tls.c
469+++ b/tls.c
a5e6228a 470@@ -105,6 +105,8 @@ static int stat_xattr(const char *fname, STRUCT_STAT *fst)
58b399b9
WD
471
472 #endif
43581f16 473
a5e6228a 474+static int display_atimes = 0;
b3593e7c 475+
fe6407b5
WD
476 static void failed(char const *what, char const *where)
477 {
b3593e7c 478 fprintf(stderr, PROGRAM ": %s %s: %s\n",
a5e6228a 479@@ -112,12 +114,29 @@ static void failed(char const *what, char const *where)
fe6407b5 480 exit(1);
43581f16
WD
481 }
482
3f053c45 483+static void storetime(char *dest, time_t t, size_t destsize)
43581f16
WD
484+{
485+ if (t) {
486+ struct tm *mt = gmtime(&t);
b3593e7c 487+
3f053c45
WD
488+ snprintf(dest, destsize,
489+ "%04d-%02d-%02d %02d:%02d:%02d ",
9d95bd65
WD
490+ (int)mt->tm_year + 1900,
491+ (int)mt->tm_mon + 1,
492+ (int)mt->tm_mday,
493+ (int)mt->tm_hour,
494+ (int)mt->tm_min,
495+ (int)mt->tm_sec);
3f053c45
WD
496+ } else
497+ strlcpy(dest, " ", destsize);
b3593e7c
WD
498+}
499+
fe6407b5 500 static void list_file(const char *fname)
43581f16
WD
501 {
502 STRUCT_STAT buf;
503 char permbuf[PERMSTRING_SIZE];
504- struct tm *mt;
505- char datebuf[50];
506+ char mtimebuf[50];
507+ char atimebuf[50];
508 char linkbuf[4096];
509
ba50e96c 510 if (do_lstat(fname, &buf) < 0)
a5e6228a 511@@ -154,19 +173,11 @@ static void list_file(const char *fname)
43581f16
WD
512
513 permstring(permbuf, buf.st_mode);
514
515- if (buf.st_mtime) {
516- mt = gmtime(&buf.st_mtime);
517-
3f053c45
WD
518- snprintf(datebuf, sizeof datebuf,
519- "%04d-%02d-%02d %02d:%02d:%02d",
9d95bd65
WD
520- (int)mt->tm_year + 1900,
521- (int)mt->tm_mon + 1,
522- (int)mt->tm_mday,
523- (int)mt->tm_hour,
524- (int)mt->tm_min,
525- (int)mt->tm_sec);
3f053c45
WD
526- } else
527- strlcpy(datebuf, " ", sizeof datebuf);
528+ storetime(mtimebuf, buf.st_mtime, sizeof mtimebuf);
a5e6228a
WD
529+ if (display_atimes)
530+ storetime(atimebuf, S_ISDIR(buf.st_mode) ? 0 : buf.st_atime, sizeof atimebuf);
531+ else
532+ atimebuf[0] = '\0';
43581f16
WD
533
534 /* TODO: Perhaps escape special characters in fname? */
535
a5e6228a 536@@ -177,13 +188,14 @@ static void list_file(const char *fname)
43581f16
WD
537 (long)minor(buf.st_rdev));
538 } else /* NB: use double for size since it might not fit in a long. */
539 printf("%12.0f", (double)buf.st_size);
540- printf(" %6ld.%-6ld %6ld %s %s%s\n",
541+ printf(" %6ld.%-6ld %6ld %s%s%s%s\n",
542 (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
543- datebuf, fname, linkbuf);
a5e6228a 544+ mtimebuf, atimebuf, fname, linkbuf);
b3593e7c 545 }
43581f16 546
12b04b40
WD
547 static struct poptOption long_options[] = {
548 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
a5e6228a 549+ {"atimes", 'U', POPT_ARG_NONE, &display_atimes, 0, 0, 0},
58b399b9 550 #ifdef SUPPORT_XATTRS
12b04b40 551 {"fake-super", 'f', POPT_ARG_VAL, &am_root, -1, 0, 0 },
58b399b9 552 #endif
a5e6228a 553@@ -197,6 +209,7 @@ static void tls_usage(int ret)
12b04b40
WD
554 fprintf(F,"usage: " PROGRAM " [OPTIONS] FILE ...\n");
555 fprintf(F,"Trivial file listing program for portably checking rsync\n");
556 fprintf(F,"\nOptions:\n");
a5e6228a 557+ fprintf(F," -U, --atimes display access (last-used) times\n");
58b399b9 558 #ifdef SUPPORT_XATTRS
12b04b40 559 fprintf(F," -f, --fake-super display attributes including fake-super xattrs\n");
58b399b9 560 #endif
cc3e685d
WD
561diff --git a/util.c b/util.c
562--- a/util.c
563+++ b/util.c
564@@ -122,7 +122,7 @@ NORETURN void overflow_exit(const char *str)
577db5e2
WD
565 exit_cleanup(RERR_MALLOC);
566 }
43581f16 567
fb11cdd7
WD
568-int set_modtime(const char *fname, time_t modtime, mode_t mode)
569+int set_times(const char *fname, time_t modtime, time_t atime, mode_t mode)
43581f16 570 {
9e355bf1
WD
571 #if !defined HAVE_LUTIMES || !defined HAVE_UTIMES
572 if (S_ISLNK(mode))
cc3e685d 573@@ -130,9 +130,13 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode)
9e355bf1
WD
574 #endif
575
43581f16
WD
576 if (verbose > 2) {
577- rprintf(FINFO, "set modtime of %s to (%ld) %s",
578+ char mtimebuf[200];
43581f16 579+
125d7fca 580+ strlcpy(mtimebuf, timestring(modtime), sizeof mtimebuf);
43581f16
WD
581+ rprintf(FINFO,
582+ "set modtime, atime of %s to (%ld) %s, (%ld) %s\n",
93ca4d27 583 fname, (long)modtime,
43581f16 584- asctime(localtime(&modtime)));
9e355bf1 585+ mtimebuf, (long)atime, timestring(atime));
43581f16
WD
586 }
587
ba50e96c 588 if (dry_run)
cc3e685d 589@@ -141,7 +145,7 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode)
43581f16 590 {
9e355bf1
WD
591 #ifdef HAVE_UTIMES
592 struct timeval t[2];
593- t[0].tv_sec = time(NULL);
594+ t[0].tv_sec = atime;
595 t[0].tv_usec = 0;
596 t[1].tv_sec = modtime;
597 t[1].tv_usec = 0;
4c15e800 598@@ -155,12 +159,12 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode)
9e355bf1 599 return utimes(fname, t);
d4dd2dd5 600 #elif defined HAVE_STRUCT_UTIMBUF
43581f16
WD
601 struct utimbuf tbuf;
602- tbuf.actime = time(NULL);
603+ tbuf.actime = atime;
604 tbuf.modtime = modtime;
605 return utime(fname,&tbuf);
09fb8f03 606 #elif defined HAVE_UTIME
43581f16
WD
607 time_t t[2];
608- t[0] = time(NULL);
609+ t[0] = atime;
610 t[1] = modtime;
611 return utime(fname,t);
612 #else