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