Renamed flags.diff fileflags.diff.
[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
790ba11a 39@@ -53,6 +53,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;
a5e6228a
WD
47@@ -342,7 +343,7 @@ int push_pathname(const char *dir, int len)
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;
a5e6228a 56@@ -450,6 +451,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) {
a5e6228a 70@@ -522,6 +530,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);
99650e0d 76 if (uid_ndx && !(xflags & XMIT_SAME_UID)) {
caf38d8d
WD
77 if (protocol_version < 30)
78 write_int(f, uid);
a5e6228a 79@@ -608,7 +618,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;
a5e6228a 88@@ -741,6 +751,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);
a5e6228a 105@@ -871,6 +891,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
a5e6228a 114@@ -1203,6 +1225,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
fc068916 115 F_OWNER(file) = st.st_uid;
898a2112 116 if (gid_ndx)
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;
85096e5e 134@@ -613,6 +614,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 ;
85096e5e 144@@ -967,6 +971,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
WD
152 if (!maybe_ATTRS_REPORT && (verbose > 1 || stdout_format_has_i > 1)) {
153@@ -1153,6 +1159,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)) {
85096e5e 161@@ -1167,14 +1174,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 }
85096e5e 180@@ -1929,7 +1938,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
85096e5e 404@@ -1689,8 +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).
4306c620
WD
408- it() The bf(u) slot is reserved for reporting update (access) time changes
409- (a feature that is not yet released).
55602791
WD
410+ it() A bf(u) means the access (use) time is different and is being updated to
411+ the sender's value (requires bf(--atimes)). An alternate value of bf(U)
412+ means that the access time will be set to the transfer time, which happens
ccc3a12c 413+ when a symlink or directory is updated.
4306c620
WD
414 it() The bf(a) means that the ACL information changed.
415 it() The bf(x) slot is reserved for reporting extended attribute changes
416 (a feature that is not yet released).
cc3e685d
WD
417diff --git a/testsuite/atimes.test b/testsuite/atimes.test
418new file mode 100644
419--- /dev/null
420+++ b/testsuite/atimes.test
a5e6228a 421@@ -0,0 +1,17 @@
13bed3dd
WD
422+#! /bin/sh
423+
424+# Test rsync copying atimes
425+
426+. "$suitedir/rsync.fns"
427+
13bed3dd
WD
428+mkdir "$fromdir"
429+
430+touch "$fromdir/foo"
431+touch -a -t 200102031717.42 "$fromdir/foo"
432+
a5e6228a 433+TLS_ARGS=--atimes
13bed3dd 434+
55602791 435+checkit "$RSYNC -rtUgvvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
13bed3dd
WD
436+
437+# The script would have aborted on error, so getting here means we've won.
438+exit 0
cc3e685d
WD
439diff --git a/testsuite/rsync.fns b/testsuite/rsync.fns
440--- a/testsuite/rsync.fns
441+++ b/testsuite/rsync.fns
9c25eef5 442@@ -187,6 +187,10 @@ checkit() {
13bed3dd
WD
443 # We can just write everything to stdout/stderr, because the
444 # wrapper hides it unless there is a problem.
445
a5e6228a 446+ if test x$TLS_ARGS = x--atimes; then
81c32ffd
WD
447+ ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
448+ fi
13bed3dd
WD
449+
450 echo "Running: \"$1\""
451 eval "$1"
452 status=$?
9c25eef5 453@@ -194,10 +198,13 @@ checkit() {
81c32ffd
WD
454 failed="YES";
455 fi
456
a5e6228a 457+ if test x$TLS_ARGS != x--atimes; then
81c32ffd
WD
458+ ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
459+ fi
460+
13bed3dd 461 echo "-------------"
b78a6aba
WD
462 echo "check how the directory listings compare with diff:"
463 echo ""
13bed3dd 464- ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
b78a6aba
WD
465 ( cd "$3" && rsync_ls_lR . ) > "$tmpdir/ls-to"
466 diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to" || failed=YES
4da25dad 467
cc3e685d
WD
468diff --git a/tls.c b/tls.c
469--- a/tls.c
470+++ b/tls.c
a5e6228a 471@@ -105,6 +105,8 @@ static int stat_xattr(const char *fname, STRUCT_STAT *fst)
58b399b9
WD
472
473 #endif
43581f16 474
a5e6228a 475+static int display_atimes = 0;
b3593e7c 476+
fe6407b5
WD
477 static void failed(char const *what, char const *where)
478 {
b3593e7c 479 fprintf(stderr, PROGRAM ": %s %s: %s\n",
a5e6228a 480@@ -112,12 +114,29 @@ static void failed(char const *what, char const *where)
fe6407b5 481 exit(1);
43581f16
WD
482 }
483
3f053c45 484+static void storetime(char *dest, time_t t, size_t destsize)
43581f16
WD
485+{
486+ if (t) {
487+ struct tm *mt = gmtime(&t);
b3593e7c 488+
3f053c45
WD
489+ snprintf(dest, destsize,
490+ "%04d-%02d-%02d %02d:%02d:%02d ",
9d95bd65
WD
491+ (int)mt->tm_year + 1900,
492+ (int)mt->tm_mon + 1,
493+ (int)mt->tm_mday,
494+ (int)mt->tm_hour,
495+ (int)mt->tm_min,
496+ (int)mt->tm_sec);
3f053c45
WD
497+ } else
498+ strlcpy(dest, " ", destsize);
b3593e7c
WD
499+}
500+
fe6407b5 501 static void list_file(const char *fname)
43581f16
WD
502 {
503 STRUCT_STAT buf;
504 char permbuf[PERMSTRING_SIZE];
505- struct tm *mt;
506- char datebuf[50];
507+ char mtimebuf[50];
508+ char atimebuf[50];
509 char linkbuf[4096];
510
ba50e96c 511 if (do_lstat(fname, &buf) < 0)
a5e6228a 512@@ -154,19 +173,11 @@ static void list_file(const char *fname)
43581f16
WD
513
514 permstring(permbuf, buf.st_mode);
515
516- if (buf.st_mtime) {
517- mt = gmtime(&buf.st_mtime);
518-
3f053c45
WD
519- snprintf(datebuf, sizeof datebuf,
520- "%04d-%02d-%02d %02d:%02d:%02d",
9d95bd65
WD
521- (int)mt->tm_year + 1900,
522- (int)mt->tm_mon + 1,
523- (int)mt->tm_mday,
524- (int)mt->tm_hour,
525- (int)mt->tm_min,
526- (int)mt->tm_sec);
3f053c45
WD
527- } else
528- strlcpy(datebuf, " ", sizeof datebuf);
529+ storetime(mtimebuf, buf.st_mtime, sizeof mtimebuf);
a5e6228a
WD
530+ if (display_atimes)
531+ storetime(atimebuf, S_ISDIR(buf.st_mode) ? 0 : buf.st_atime, sizeof atimebuf);
532+ else
533+ atimebuf[0] = '\0';
43581f16
WD
534
535 /* TODO: Perhaps escape special characters in fname? */
536
a5e6228a 537@@ -177,13 +188,14 @@ static void list_file(const char *fname)
43581f16
WD
538 (long)minor(buf.st_rdev));
539 } else /* NB: use double for size since it might not fit in a long. */
540 printf("%12.0f", (double)buf.st_size);
541- printf(" %6ld.%-6ld %6ld %s %s%s\n",
542+ printf(" %6ld.%-6ld %6ld %s%s%s%s\n",
543 (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
544- datebuf, fname, linkbuf);
a5e6228a 545+ mtimebuf, atimebuf, fname, linkbuf);
b3593e7c 546 }
43581f16 547
12b04b40
WD
548 static struct poptOption long_options[] = {
549 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
a5e6228a 550+ {"atimes", 'U', POPT_ARG_NONE, &display_atimes, 0, 0, 0},
58b399b9 551 #ifdef SUPPORT_XATTRS
12b04b40 552 {"fake-super", 'f', POPT_ARG_VAL, &am_root, -1, 0, 0 },
58b399b9 553 #endif
a5e6228a 554@@ -197,6 +209,7 @@ static void tls_usage(int ret)
12b04b40
WD
555 fprintf(F,"usage: " PROGRAM " [OPTIONS] FILE ...\n");
556 fprintf(F,"Trivial file listing program for portably checking rsync\n");
557 fprintf(F,"\nOptions:\n");
a5e6228a 558+ fprintf(F," -U, --atimes display access (last-used) times\n");
58b399b9 559 #ifdef SUPPORT_XATTRS
12b04b40 560 fprintf(F," -f, --fake-super display attributes including fake-super xattrs\n");
58b399b9 561 #endif
cc3e685d
WD
562diff --git a/util.c b/util.c
563--- a/util.c
564+++ b/util.c
565@@ -122,7 +122,7 @@ NORETURN void overflow_exit(const char *str)
577db5e2
WD
566 exit_cleanup(RERR_MALLOC);
567 }
43581f16 568
fb11cdd7
WD
569-int set_modtime(const char *fname, time_t modtime, mode_t mode)
570+int set_times(const char *fname, time_t modtime, time_t atime, mode_t mode)
43581f16 571 {
9e355bf1
WD
572 #if !defined HAVE_LUTIMES || !defined HAVE_UTIMES
573 if (S_ISLNK(mode))
cc3e685d 574@@ -130,9 +130,13 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode)
9e355bf1
WD
575 #endif
576
43581f16
WD
577 if (verbose > 2) {
578- rprintf(FINFO, "set modtime of %s to (%ld) %s",
579+ char mtimebuf[200];
43581f16 580+
125d7fca 581+ strlcpy(mtimebuf, timestring(modtime), sizeof mtimebuf);
43581f16
WD
582+ rprintf(FINFO,
583+ "set modtime, atime of %s to (%ld) %s, (%ld) %s\n",
93ca4d27 584 fname, (long)modtime,
43581f16 585- asctime(localtime(&modtime)));
9e355bf1 586+ mtimebuf, (long)atime, timestring(atime));
43581f16
WD
587 }
588
ba50e96c 589 if (dry_run)
cc3e685d 590@@ -141,7 +145,7 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode)
43581f16 591 {
9e355bf1
WD
592 #ifdef HAVE_UTIMES
593 struct timeval t[2];
594- t[0].tv_sec = time(NULL);
595+ t[0].tv_sec = atime;
596 t[0].tv_usec = 0;
597 t[1].tv_sec = modtime;
598 t[1].tv_usec = 0;
4c15e800 599@@ -155,12 +159,12 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode)
9e355bf1 600 return utimes(fname, t);
d4dd2dd5 601 #elif defined HAVE_STRUCT_UTIMBUF
43581f16
WD
602 struct utimbuf tbuf;
603- tbuf.actime = time(NULL);
604+ tbuf.actime = atime;
605 tbuf.modtime = modtime;
606 return utime(fname,&tbuf);
09fb8f03 607 #elif defined HAVE_UTIME
43581f16
WD
608 time_t t[2];
609- t[0] = time(NULL);
610+ t[0] = atime;
611 t[1] = modtime;
612 return utime(fname,t);
613 #else