Use "use warnings" rather than -w on the #! line.
[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;
ae306a29 18@@ -61,7 +62,7 @@ extern char *iconv_opt;
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 25 int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
ae306a29
WD
26 int sender_symlink_iconv = 0; /* sender should convert symlink content */
27@@ -137,6 +138,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;
ae306a29
WD
47@@ -393,7 +394,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
48 #endif
49 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;
ae306a29 56@@ -467,6 +468,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
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) {
ae306a29 70@@ -538,6 +546,8 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
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);
ae306a29 79@@ -622,7 +632,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
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;
ae306a29 88@@ -756,6 +766,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);
ae306a29 105@@ -905,6 +925,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
ae306a29 114@@ -1277,6 +1299,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;
abd3adb8 134@@ -648,6 +649,9 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
c0c7984e 135 : iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !(iflags & ITEM_MATCHED)
9a70b743 136 && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
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 ;
abd3adb8 144@@ -1018,6 +1022,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)) {
abd3adb8 153@@ -1203,6 +1209,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)) {
abd3adb8 161@@ -1217,14 +1224,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 }
abd3adb8 180@@ -2059,7 +2068,7 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
c0c7984e
WD
181 STRUCT_STAT st;
182 if (link_stat(fname, &st, 0) == 0
183 && cmp_time(st.st_mtime, file->modtime) != 0)
184- set_modtime(fname, file->modtime, file->mode);
185+ set_times(fname, file->modtime, file->modtime, file->mode);
186 }
e2e42a01
WD
187 if (counter >= loopchk_limit) {
188 if (allowed_lull)
a5e6228a
WD
189diff --git a/ifuncs.h b/ifuncs.h
190--- a/ifuncs.h
191+++ b/ifuncs.h
c0c7984e
WD
192@@ -67,6 +67,28 @@ d_name(struct dirent *di)
193 #endif
a5e6228a
WD
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
ae306a29 224@@ -660,7 +660,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
c0c7984e 237@@ -59,6 +59,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;
abd3adb8 245@@ -354,6 +355,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");
abd3adb8 253@@ -491,6 +493,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 },
abd3adb8 263@@ -1774,6 +1779,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
963ca808 275@@ -383,6 +383,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
963ca808 283@@ -426,20 +427,38 @@ 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;
c0c7984e 318- else
85096e5e 319+ if (ret > 0) { /* ret == 1 if symlink could not be set */
9e355bf1 320+ updated = 0;
c0c7984e 321 file->flags |= FLAG_TIME_FAILED;
85096e5e 322+ }
43581f16
WD
323 }
324
898a2112 325 change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
963ca808 326@@ -573,7 +592,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
a5e6228a
WD
327
328 /* Change permissions before putting the file into place. */
329 set_file_attrs(fnametmp, file, NULL, fnamecmp,
330- ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
331+ ok_to_set_time ? 0 : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME);
332
333 /* move tmp file over real file */
334 if (verbose > 2)
963ca808 335@@ -600,7 +619,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
a5e6228a
WD
336
337 do_set_file_attrs:
338 set_file_attrs(fnametmp, file, NULL, fnamecmp,
339- ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
340+ ok_to_set_time ? 0 : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME);
341
342 if (temp_copy_name) {
343 if (do_rename(fnametmp, fname) < 0) {
cc3e685d
WD
344diff --git a/rsync.h b/rsync.h
345--- a/rsync.h
346+++ b/rsync.h
347@@ -60,6 +60,7 @@
caf38d8d 348 #define XMIT_RDEV_MINOR_8_pre30 (1<<11) /* protocols 28 - 29 */
9c25eef5
WD
349 #define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */
350 #define XMIT_HLINK_FIRST (1<<12) /* protocols 30 - now (HLINKED files only) */
351+#define XMIT_SAME_ATIME (1<<13) /* protocols ?? - now */
43581f16
WD
352
353 /* These flags are used in the live flist data. */
354
e8972101 355@@ -154,6 +155,7 @@
8a529471 356
4a65fe72
WD
357 #define ATTRS_REPORT (1<<0)
358 #define ATTRS_SKIP_MTIME (1<<1)
359+#define ATTRS_SKIP_ATIME (1<<2)
8a529471
WD
360
361 #define FULL_FLUSH 1
362 #define NORMAL_FLUSH 0
ae306a29 363@@ -632,12 +634,14 @@ extern int file_extra_cnt;
9c25eef5 364 extern int inc_recurse;
898a2112
WD
365 extern int uid_ndx;
366 extern int gid_ndx;
367+extern int atimes_ndx;
368 extern int acls_ndx;
369 extern int xattrs_ndx;
fdf967c7 370
a5e6228a
WD
371 #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
372 #define EXTRA_LEN (sizeof (union file_extras))
373 #define PTR_EXTRA_CNT ((sizeof (char *) + EXTRA_LEN - 1) / EXTRA_LEN)
374+#define TIME_EXTRA_CNT ((SIZEOF_TIME_T + EXTRA_LEN - 1) / EXTRA_LEN)
375 #define DEV_EXTRA_CNT 2
376 #define DIRNODE_EXTRA_CNT 3
377 #define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
cc3e685d
WD
378diff --git a/rsync.yo b/rsync.yo
379--- a/rsync.yo
380+++ b/rsync.yo
abd3adb8 381@@ -353,6 +353,7 @@ to the detailed description below for a complete description. verb(
4a65fe72 382 -D same as --devices --specials
671e613f
WD
383 -t, --times preserve modification times
384 -O, --omit-dir-times omit directories from --times
55602791 385+ -U, --atimes preserve access (use) times
4a65fe72 386 --super receiver attempts super-user activities
12b04b40 387 --fake-super store/recover privileged attrs using xattrs
43581f16 388 -S, --sparse handle sparse files efficiently
abd3adb8 389@@ -1004,6 +1005,12 @@ it is preserving modification times (see bf(--times)). If NFS is sharing
a7219d20 390 the directories on the receiving side, it is a good idea to use bf(-O).
333b8af4 391 This option is inferred if you use bf(--backup) without bf(--backup-dir).
7b675ff5 392
55602791
WD
393+dit(bf(-U, --atimes)) This tells rsync to set the access (use) times of the
394+destination files to the same value as the source files. Note that the
81c32ffd
WD
395+reading of the source file may update the atime of the source files, so
396+repeated rsync runs with --atimes may be needed if you want to force the
397+access-time values to be 100% identical on the two systems.
7b675ff5 398+
4a65fe72
WD
399 dit(bf(--super)) This tells the receiving side to attempt super-user
400 activities even if the receiving rsync wasn't run by the super-user. These
401 activities include: preserving users via the bf(--owner) option, preserving
abd3adb8 402@@ -1723,7 +1730,10 @@ quote(itemization(
ccc3a12c
WD
403 sender's value (requires bf(--owner) and super-user privileges).
404 it() A bf(g) means the group is different and is being updated to the
405 sender's value (requires bf(--group) and the authority to set the group).
f9df736a 406- it() The bf(u) slot is reserved for future use.
55602791
WD
407+ it() A bf(u) means the access (use) time is different and is being updated to
408+ the sender's value (requires bf(--atimes)). An alternate value of bf(U)
409+ means that the access time will be set to the transfer time, which happens
ccc3a12c 410+ when a symlink or directory is updated.
4306c620 411 it() The bf(a) means that the ACL information changed.
f9df736a
WD
412 it() The bf(x) means that the extended attribute information changed.
413 ))
cc3e685d
WD
414diff --git a/testsuite/atimes.test b/testsuite/atimes.test
415new file mode 100644
416--- /dev/null
417+++ b/testsuite/atimes.test
a5e6228a 418@@ -0,0 +1,17 @@
13bed3dd
WD
419+#! /bin/sh
420+
421+# Test rsync copying atimes
422+
423+. "$suitedir/rsync.fns"
424+
13bed3dd
WD
425+mkdir "$fromdir"
426+
427+touch "$fromdir/foo"
428+touch -a -t 200102031717.42 "$fromdir/foo"
429+
a5e6228a 430+TLS_ARGS=--atimes
13bed3dd 431+
55602791 432+checkit "$RSYNC -rtUgvvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
13bed3dd
WD
433+
434+# The script would have aborted on error, so getting here means we've won.
435+exit 0
cc3e685d
WD
436diff --git a/testsuite/rsync.fns b/testsuite/rsync.fns
437--- a/testsuite/rsync.fns
438+++ b/testsuite/rsync.fns
7f0bf1cb 439@@ -215,6 +215,10 @@ checkit() {
13bed3dd
WD
440 # We can just write everything to stdout/stderr, because the
441 # wrapper hides it unless there is a problem.
442
a5e6228a 443+ if test x$TLS_ARGS = x--atimes; then
81c32ffd
WD
444+ ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
445+ fi
13bed3dd
WD
446+
447 echo "Running: \"$1\""
448 eval "$1"
449 status=$?
7f0bf1cb 450@@ -222,10 +226,13 @@ checkit() {
81c32ffd
WD
451 failed="YES";
452 fi
453
a5e6228a 454+ if test x$TLS_ARGS != x--atimes; then
81c32ffd
WD
455+ ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
456+ fi
457+
13bed3dd 458 echo "-------------"
b78a6aba
WD
459 echo "check how the directory listings compare with diff:"
460 echo ""
13bed3dd 461- ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
b78a6aba
WD
462 ( cd "$3" && rsync_ls_lR . ) > "$tmpdir/ls-to"
463 diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to" || failed=YES
4da25dad 464
cc3e685d
WD
465diff --git a/tls.c b/tls.c
466--- a/tls.c
467+++ b/tls.c
e66d6d51 468@@ -107,6 +107,8 @@ static int stat_xattr(const char *fname, STRUCT_STAT *fst)
58b399b9
WD
469
470 #endif
43581f16 471
a5e6228a 472+static int display_atimes = 0;
e2e42a01 473+
fe6407b5
WD
474 static void failed(char const *what, char const *where)
475 {
b3593e7c 476 fprintf(stderr, PROGRAM ": %s %s: %s\n",
e66d6d51 477@@ -114,12 +116,29 @@ static void failed(char const *what, char const *where)
fe6407b5 478 exit(1);
43581f16
WD
479 }
480
3f053c45 481+static void storetime(char *dest, time_t t, size_t destsize)
43581f16
WD
482+{
483+ if (t) {
484+ struct tm *mt = gmtime(&t);
b3593e7c 485+
3f053c45
WD
486+ snprintf(dest, destsize,
487+ "%04d-%02d-%02d %02d:%02d:%02d ",
9d95bd65
WD
488+ (int)mt->tm_year + 1900,
489+ (int)mt->tm_mon + 1,
490+ (int)mt->tm_mday,
491+ (int)mt->tm_hour,
492+ (int)mt->tm_min,
493+ (int)mt->tm_sec);
3f053c45
WD
494+ } else
495+ strlcpy(dest, " ", destsize);
b3593e7c
WD
496+}
497+
fe6407b5 498 static void list_file(const char *fname)
43581f16
WD
499 {
500 STRUCT_STAT buf;
501 char permbuf[PERMSTRING_SIZE];
502- struct tm *mt;
503- char datebuf[50];
504+ char mtimebuf[50];
505+ char atimebuf[50];
506 char linkbuf[4096];
507
ba50e96c 508 if (do_lstat(fname, &buf) < 0)
e66d6d51 509@@ -158,19 +177,11 @@ static void list_file(const char *fname)
43581f16
WD
510
511 permstring(permbuf, buf.st_mode);
512
513- if (buf.st_mtime) {
514- mt = gmtime(&buf.st_mtime);
515-
3f053c45
WD
516- snprintf(datebuf, sizeof datebuf,
517- "%04d-%02d-%02d %02d:%02d:%02d",
9d95bd65
WD
518- (int)mt->tm_year + 1900,
519- (int)mt->tm_mon + 1,
520- (int)mt->tm_mday,
521- (int)mt->tm_hour,
522- (int)mt->tm_min,
523- (int)mt->tm_sec);
3f053c45
WD
524- } else
525- strlcpy(datebuf, " ", sizeof datebuf);
526+ storetime(mtimebuf, buf.st_mtime, sizeof mtimebuf);
a5e6228a
WD
527+ if (display_atimes)
528+ storetime(atimebuf, S_ISDIR(buf.st_mode) ? 0 : buf.st_atime, sizeof atimebuf);
529+ else
530+ atimebuf[0] = '\0';
43581f16
WD
531
532 /* TODO: Perhaps escape special characters in fname? */
533
e66d6d51 534@@ -181,13 +192,14 @@ static void list_file(const char *fname)
43581f16
WD
535 (long)minor(buf.st_rdev));
536 } else /* NB: use double for size since it might not fit in a long. */
537 printf("%12.0f", (double)buf.st_size);
538- printf(" %6ld.%-6ld %6ld %s %s%s\n",
539+ printf(" %6ld.%-6ld %6ld %s%s%s%s\n",
540 (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
541- datebuf, fname, linkbuf);
a5e6228a 542+ mtimebuf, atimebuf, fname, linkbuf);
b3593e7c 543 }
43581f16 544
12b04b40
WD
545 static struct poptOption long_options[] = {
546 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
a5e6228a 547+ {"atimes", 'U', POPT_ARG_NONE, &display_atimes, 0, 0, 0},
e66d6d51
WD
548 {"link-times", 'l', POPT_ARG_NONE, &link_times, 0, 0, 0 },
549 {"link-owner", 'L', POPT_ARG_NONE, &link_owner, 0, 0, 0 },
58b399b9 550 #ifdef SUPPORT_XATTRS
e66d6d51 551@@ -203,6 +215,7 @@ static void tls_usage(int ret)
12b04b40
WD
552 fprintf(F,"usage: " PROGRAM " [OPTIONS] FILE ...\n");
553 fprintf(F,"Trivial file listing program for portably checking rsync\n");
554 fprintf(F,"\nOptions:\n");
a5e6228a 555+ fprintf(F," -U, --atimes display access (last-used) times\n");
e66d6d51
WD
556 fprintf(F," -l, --link-times display the time on a symlink\n");
557 fprintf(F," -L, --link-owner display the owner+group on a symlink\n");
58b399b9 558 #ifdef SUPPORT_XATTRS
cc3e685d
WD
559diff --git a/util.c b/util.c
560--- a/util.c
561+++ b/util.c
91270139 562@@ -123,7 +123,7 @@ NORETURN void overflow_exit(const char *str)
577db5e2
WD
563 exit_cleanup(RERR_MALLOC);
564 }
43581f16 565
fb11cdd7
WD
566-int set_modtime(const char *fname, time_t modtime, mode_t mode)
567+int set_times(const char *fname, time_t modtime, time_t atime, mode_t mode)
43581f16 568 {
9e355bf1
WD
569 #if !defined HAVE_LUTIMES || !defined HAVE_UTIMES
570 if (S_ISLNK(mode))
91270139 571@@ -131,9 +131,13 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode)
9e355bf1
WD
572 #endif
573
43581f16
WD
574 if (verbose > 2) {
575- rprintf(FINFO, "set modtime of %s to (%ld) %s",
576+ char mtimebuf[200];
43581f16 577+
125d7fca 578+ strlcpy(mtimebuf, timestring(modtime), sizeof mtimebuf);
43581f16
WD
579+ rprintf(FINFO,
580+ "set modtime, atime of %s to (%ld) %s, (%ld) %s\n",
93ca4d27 581 fname, (long)modtime,
43581f16 582- asctime(localtime(&modtime)));
9e355bf1 583+ mtimebuf, (long)atime, timestring(atime));
43581f16
WD
584 }
585
ba50e96c 586 if (dry_run)
91270139 587@@ -142,7 +146,7 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode)
43581f16 588 {
9e355bf1
WD
589 #ifdef HAVE_UTIMES
590 struct timeval t[2];
591- t[0].tv_sec = time(NULL);
592+ t[0].tv_sec = atime;
593 t[0].tv_usec = 0;
594 t[1].tv_sec = modtime;
595 t[1].tv_usec = 0;
91270139 596@@ -156,12 +160,12 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode)
9e355bf1 597 return utimes(fname, t);
d4dd2dd5 598 #elif defined HAVE_STRUCT_UTIMBUF
43581f16
WD
599 struct utimbuf tbuf;
600- tbuf.actime = time(NULL);
601+ tbuf.actime = atime;
602 tbuf.modtime = modtime;
603 return utime(fname,&tbuf);
09fb8f03 604 #elif defined HAVE_UTIME
43581f16
WD
605 time_t t[2];
606- t[0] = time(NULL);
607+ t[0] = atime;
608 t[1] = modtime;
609 return utime(fname,t);
610 #else