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