1 To use this patch, run these commands for a successful build:
3 patch -p1 <patches/atimes.diff
4 ./configure (optional if already run)
7 based-on: 1e9ee19a716b72454dfeab663802c626b81cdf2e
8 diff --git a/compat.c b/compat.c
11 @@ -46,6 +46,7 @@ extern int protocol_version;
12 extern int protect_args;
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;
18 extern int need_messages_from_generator;
19 @@ -63,7 +64,7 @@ extern char *iconv_opt;
22 /* These index values are for the file-list's extra-attribute array. */
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;
26 int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
27 int sender_symlink_iconv = 0; /* sender should convert symlink content */
28 @@ -140,6 +141,8 @@ void setup_protocol(int f_out,int f_in)
29 uid_ndx = ++file_extra_cnt;
31 gid_ndx = ++file_extra_cnt;
32 + if (preserve_atimes)
33 + atimes_ndx = (file_extra_cnt += TIME_EXTRA_CNT);
34 if (preserve_acls && !am_sender)
35 acls_ndx = ++file_extra_cnt;
37 diff --git a/flist.c b/flist.c
40 @@ -53,6 +53,7 @@ extern int preserve_specials;
41 extern int delete_during;
42 extern int missing_args;
44 +extern int atimes_ndx;
45 extern int relative_paths;
46 extern int implied_dirs;
47 extern int ignore_perishable;
48 @@ -397,7 +398,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
50 int ndx, int first_ndx)
52 - static time_t modtime;
53 + static time_t modtime, atime;
55 #ifdef SUPPORT_HARD_LINKS
57 @@ -497,6 +498,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
58 modtime = file->modtime;
59 if (NSEC_BUMP(file) && protocol_version >= 31)
60 xflags |= XMIT_MOD_NSEC;
61 + if (atimes_ndx && !S_ISDIR(mode)) {
62 + time_t file_atime = f_atime(file);
63 + if (file_atime == atime)
64 + xflags |= XMIT_SAME_ATIME;
69 #ifdef SUPPORT_HARD_LINKS
71 @@ -583,6 +591,8 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
72 write_varint(f, F_MOD_NSEC(file));
73 if (!(xflags & XMIT_SAME_MODE))
74 write_int(f, to_wire_mode(mode));
75 + if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME))
76 + write_varlong(f, atime, 4);
77 if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
78 if (protocol_version < 30)
80 @@ -670,7 +680,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
82 static struct file_struct *recv_file_entry(int f, struct file_list *flist, int xflags)
84 - static int64 modtime;
85 + static int64 modtime, atime;
87 #ifdef SUPPORT_HARD_LINKS
89 @@ -814,6 +824,16 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
91 if (!(xflags & XMIT_SAME_MODE))
92 mode = from_wire_mode(read_int(f));
93 + if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME)) {
94 + atime = read_varlong(f, 4);
95 +#if SIZEOF_TIME_T < SIZEOF_INT64
96 + if (!am_generator && (int64)(time_t)atime != atime) {
97 + rprintf(FERROR_XFER,
98 + "Access time value of %s truncated on receiver.\n",
104 if (chmod_modes && !S_ISLNK(mode) && mode)
105 mode = tweak_mode(mode, chmod_modes);
106 @@ -974,6 +994,8 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
108 file->flags |= gid_flags;
111 + f_atime_set(file, (time_t)atime);
113 F_NDX(file) = flist->used + flist->ndx_start;
115 @@ -1371,6 +1393,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
116 F_GROUP(file) = st.st_gid;
117 if (am_generator && st.st_uid == our_uid)
118 file->flags |= FLAG_OWNED_BY_US;
120 + f_atime_set(file, st.st_atime);
122 if (basename != thisname)
123 file->dirname = lastdir;
124 diff --git a/generator.c b/generator.c
127 @@ -496,6 +496,9 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
128 : iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !(iflags & ITEM_MATCHED)
129 && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
130 iflags |= ITEM_REPORT_TIME;
131 + if (atimes_ndx && !S_ISDIR(file->mode) && !S_ISLNK(file->mode)
132 + && cmp_time(f_atime(file), sxp->st.st_atime) != 0)
133 + iflags |= ITEM_REPORT_ATIME;
134 #if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
135 if (S_ISLNK(file->mode)) {
137 @@ -907,6 +910,8 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
139 if (!hard_link_one(file, fname, cmpbuf, 1))
142 + set_file_attrs(fname, file, sxp, NULL, 0);
143 if (preserve_hard_links && F_IS_HLINKED(file))
144 finish_hard_link(file, fname, ndx, &sxp->st, itemizing, code, j);
145 if (!maybe_ATTRS_REPORT && (INFO_GTE(NAME, 2) || stdout_format_has_i > 1)) {
146 @@ -1109,6 +1114,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
147 static void list_file_entry(struct file_struct *f)
149 char permbuf[PERMSTRING_SIZE];
150 + time_t atime = atimes_ndx ? f_atime(f) : 0;
152 int colwidth = human_readable ? 14 : 11;
154 @@ -1124,10 +1130,11 @@ static void list_file_entry(struct file_struct *f)
157 if (preserve_links && S_ISLNK(f->mode)) {
158 - rprintf(FINFO, "%s %*s %s %s -> %s\n",
159 + rprintf(FINFO, "%s %*s %s %s %s -> %s\n",
160 permbuf, colwidth, human_num(len),
161 - timestring(f->modtime), f_name(f, NULL),
163 + timestring(f->modtime),
164 + atimes_ndx ? timestring(atime) : "",
165 + f_name(f, NULL), F_SYMLINK(f));
168 if (missing_args == 2 && f->mode == 0) {
169 @@ -1135,9 +1142,11 @@ static void list_file_entry(struct file_struct *f)
170 colwidth + 31, "*missing",
173 - rprintf(FINFO, "%s %*s %s %s\n",
174 + rprintf(FINFO, "%s %*s %s %s %s\n",
175 permbuf, colwidth, human_num(len),
176 - timestring(f->modtime), f_name(f, NULL));
177 + timestring(f->modtime),
178 + atimes_ndx ? timestring(atime) : "",
183 @@ -2034,7 +2043,7 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
185 if (link_stat(fname, &st, 0) == 0
186 && cmp_time(st.st_mtime, file->modtime) != 0)
187 - set_modtime(fname, file->modtime, F_MOD_NSEC(file), file->mode);
188 + set_times(fname, file->modtime, F_MOD_NSEC(file), file->modtime, file->mode);
190 if (counter >= loopchk_limit) {
192 diff --git a/ifuncs.h b/ifuncs.h
195 @@ -43,6 +43,28 @@ free_xbuf(xbuf *xb)
196 memset(xb, 0, sizeof (xbuf));
199 +static inline time_t
200 +f_atime(struct file_struct *fp)
202 +#if SIZEOF_TIME_T > 4
204 + memcpy(&atime, &REQ_EXTRA(fp, atimes_ndx)->unum, SIZEOF_TIME_T);
207 + return REQ_EXTRA(fp, atimes_ndx)->unum;
212 +f_atime_set(struct file_struct *fp, time_t atime)
214 +#if SIZEOF_TIME_T > 4
215 + memcpy(&REQ_EXTRA(fp, atimes_ndx)->unum, &atime, SIZEOF_TIME_T);
217 + REQ_EXTRA(fp, atimes_ndx)->unum = (uint32)atime;
222 to_wire_mode(mode_t mode)
224 diff --git a/log.c b/log.c
227 @@ -729,7 +729,8 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
228 c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
229 c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
230 c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
231 - c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' : 'u';
232 + c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.'
233 + : S_ISLNK(file->mode) ? 'U' : 'u';
234 c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
235 c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
237 diff --git a/options.c b/options.c
240 @@ -59,6 +59,7 @@ int preserve_specials = 0;
241 int preserve_uid = 0;
242 int preserve_gid = 0;
243 int preserve_times = 0;
244 +int preserve_atimes = 0;
248 @@ -708,6 +709,7 @@ void usage(enum logcode F)
249 rprintf(F," --specials preserve special files\n");
250 rprintf(F," -D same as --devices --specials\n");
251 rprintf(F," -t, --times preserve modification times\n");
252 + rprintf(F," -U, --atimes preserve access (last-used) times\n");
253 rprintf(F," -O, --omit-dir-times omit directories from --times\n");
254 rprintf(F," -J, --omit-link-times omit symlinks from --times\n");
255 rprintf(F," --super receiver attempts super-user activities\n");
256 @@ -865,6 +867,9 @@ static struct poptOption long_options[] = {
257 {"times", 't', POPT_ARG_VAL, &preserve_times, 1, 0, 0 },
258 {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
259 {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
260 + {"atimes", 'U', POPT_ARG_VAL, &preserve_atimes, 1, 0, 0 },
261 + {"no-atimes", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 },
262 + {"no-U", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 },
263 {"omit-dir-times", 'O', POPT_ARG_VAL, &omit_dir_times, 1, 0, 0 },
264 {"no-omit-dir-times",0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 },
265 {"no-O", 0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 },
266 @@ -2428,6 +2433,8 @@ void server_options(char **args, int *argc_p)
270 + if (preserve_atimes)
274 else if (preserve_executability && am_sender)
275 diff --git a/rsync.c b/rsync.c
278 @@ -458,6 +458,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
281 int change_uid, change_gid;
282 + time_t atime, mtime;
283 mode_t new_mode = file->mode;
286 @@ -496,22 +497,40 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
287 set_xattr(fname, file, fnamecmp, sxp);
290 + /* This code must be the first update in the function due to
291 + * how it uses the "updated" variable. */
293 || (!(preserve_times & PRESERVE_DIR_TIMES) && S_ISDIR(sxp->st.st_mode))
294 || (!(preserve_times & PRESERVE_LINK_TIMES) && S_ISLNK(sxp->st.st_mode)))
295 flags |= ATTRS_SKIP_MTIME;
296 + if (!atimes_ndx || S_ISDIR(sxp->st.st_mode))
297 + flags |= ATTRS_SKIP_ATIME;
298 if (!(flags & ATTRS_SKIP_MTIME)
299 && cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
300 - int ret = set_modtime(fname, file->modtime, F_MOD_NSEC(file), sxp->st.st_mode);
301 + mtime = file->modtime;
304 + mtime = sxp->st.st_mtime;
305 + if (!(flags & ATTRS_SKIP_ATIME)) {
306 + time_t file_atime = f_atime(file);
307 + if (cmp_time(sxp->st.st_atime, file_atime) != 0) {
308 + atime = file_atime;
311 + atime = sxp->st.st_atime;
313 + atime = sxp->st.st_atime;
315 + int ret = set_times(fname, mtime, F_MOD_NSEC(file), atime, sxp->st.st_mode);
317 rsyserr(FERROR_XFER, errno, "failed to set times on %s",
321 - if (ret == 0) /* ret == 1 if symlink could not be set */
324 + if (ret > 0) { /* ret == 1 if symlink could not be set */
326 file->flags |= FLAG_TIME_FAILED;
330 change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
331 @@ -662,7 +681,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
333 /* Change permissions before putting the file into place. */
334 set_file_attrs(fnametmp, file, NULL, fnamecmp,
335 - ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
336 + ok_to_set_time ? 0 : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME);
338 /* move tmp file over real file */
339 if (DEBUG_GTE(RECV, 1))
340 @@ -687,7 +706,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
343 set_file_attrs(fnametmp, file, NULL, fnamecmp,
344 - ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
345 + ok_to_set_time ? 0 : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME);
347 if (temp_copy_name) {
348 if (do_rename(fnametmp, fname) < 0) {
349 diff --git a/rsync.h b/rsync.h
353 #define XMIT_HLINK_FIRST (1<<12) /* protocols 30 - now (HLINKED files only) */
354 #define XMIT_IO_ERROR_ENDLIST (1<<12) /* protocols 31*- now (w/XMIT_EXTENDED_FLAGS) (also protocol 30 w/'f' compat flag) */
355 #define XMIT_MOD_NSEC (1<<13) /* protocols 31 - now */
356 +#define XMIT_SAME_ATIME (1<<14) /* protocols ?? - now */
358 /* These flags are used in the live flist data. */
362 #define ATTRS_REPORT (1<<0)
363 #define ATTRS_SKIP_MTIME (1<<1)
364 +#define ATTRS_SKIP_ATIME (1<<2)
367 #define NORMAL_FLUSH 0
368 @@ -708,12 +710,14 @@ extern int file_extra_cnt;
369 extern int inc_recurse;
372 +extern int atimes_ndx;
374 extern int xattrs_ndx;
376 #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
377 #define EXTRA_LEN (sizeof (union file_extras))
378 #define PTR_EXTRA_CNT ((sizeof (char *) + EXTRA_LEN - 1) / EXTRA_LEN)
379 +#define TIME_EXTRA_CNT ((SIZEOF_TIME_T + EXTRA_LEN - 1) / EXTRA_LEN)
380 #define DEV_EXTRA_CNT 2
381 #define DIRNODE_EXTRA_CNT 3
382 #define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
383 diff --git a/rsync.yo b/rsync.yo
386 @@ -369,6 +369,7 @@ to the detailed description below for a complete description. verb(
387 --specials preserve special files
388 -D same as --devices --specials
389 -t, --times preserve modification times
390 + -U, --atimes preserve access (use) times
391 -O, --omit-dir-times omit directories from --times
392 -J, --omit-link-times omit symlinks from --times
393 --super receiver attempts super-user activities
394 @@ -1162,6 +1163,12 @@ cause the next transfer to behave as if it used bf(-I), causing all files to be
395 updated (though rsync's delta-transfer algorithm will make the update fairly efficient
396 if the files haven't actually changed, you're much better off using bf(-t)).
398 +dit(bf(-U, --atimes)) This tells rsync to set the access (use) times of the
399 +destination files to the same value as the source files. Note that the
400 +reading of the source file may update the atime of the source files, so
401 +repeated rsync runs with --atimes may be needed if you want to force the
402 +access-time values to be 100% identical on the two systems.
404 dit(bf(-O, --omit-dir-times)) This tells rsync to omit directories when
405 it is preserving modification times (see bf(--times)). If NFS is sharing
406 the directories on the receiving side, it is a good idea to use bf(-O).
407 @@ -2092,7 +2099,10 @@ quote(itemization(
408 sender's value (requires bf(--owner) and super-user privileges).
409 it() A bf(g) means the group is different and is being updated to the
410 sender's value (requires bf(--group) and the authority to set the group).
411 - it() The bf(u) slot is reserved for future use.
412 + it() A bf(u) means the access (use) time is different and is being updated to
413 + the sender's value (requires bf(--atimes)). An alternate value of bf(U)
414 + means that the access time will be set to the transfer time, which happens
415 + when a symlink or directory is updated.
416 it() The bf(a) means that the ACL information changed.
417 it() The bf(x) means that the extended attribute information changed.
419 diff --git a/syscall.c b/syscall.c
422 @@ -349,15 +349,15 @@ OFF_T do_lseek(int fd, OFF_T offset, int whence)
425 #ifdef HAVE_UTIMENSAT
426 -int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec)
427 +int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec, time_t atime, uint32 a_nsec)
429 struct timespec t[2];
431 if (dry_run) return 0;
432 RETURN_ERROR_IF_RO_OR_LO;
435 - t[0].tv_nsec = UTIME_NOW;
436 + t[0].tv_sec = atime;
437 + t[0].tv_nsec = a_nsec;
438 t[1].tv_sec = modtime;
439 t[1].tv_nsec = mod_nsec;
440 return utimensat(AT_FDCWD, fname, t, AT_SYMLINK_NOFOLLOW);
441 @@ -365,15 +365,15 @@ int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec)
445 -int do_lutimes(const char *fname, time_t modtime, uint32 mod_nsec)
446 +int do_lutimes(const char *fname, time_t modtime, uint32 mod_nsec, time_t atime, uint32 a_nsec)
450 if (dry_run) return 0;
451 RETURN_ERROR_IF_RO_OR_LO;
453 - t[0].tv_sec = time(NULL);
455 + t[0].tv_sec = atime;
456 + t[0].tv_usec = a_nsec;
457 t[1].tv_sec = modtime;
458 t[1].tv_usec = mod_nsec / 1000;
459 return lutimes(fname, t);
460 @@ -381,22 +381,22 @@ int do_lutimes(const char *fname, time_t modtime, uint32 mod_nsec)
464 -int do_utimes(const char *fname, time_t modtime, uint32 mod_nsec)
465 +int do_utimes(const char *fname, time_t modtime, uint32 mod_nsec, time_t atime, uint32 a_nsec)
469 if (dry_run) return 0;
470 RETURN_ERROR_IF_RO_OR_LO;
472 - t[0].tv_sec = time(NULL);
474 + t[0].tv_sec = atime;
475 + t[0].tv_usec = a_nsec;
476 t[1].tv_sec = modtime;
477 t[1].tv_usec = mod_nsec / 1000;
478 return utimes(fname, t);
481 #elif defined HAVE_UTIME
482 -int do_utime(const char *fname, time_t modtime, UNUSED(uint32 mod_nsec))
483 +int do_utime(const char *fname, time_t modtime, UNUSED(uint32 mod_nsec), time_t atime, UNUSED(uint32 a_nsec))
485 #ifdef HAVE_STRUCT_UTIMBUF
487 @@ -408,11 +408,11 @@ int do_utime(const char *fname, time_t modtime, UNUSED(uint32 mod_nsec))
488 RETURN_ERROR_IF_RO_OR_LO;
490 # ifdef HAVE_STRUCT_UTIMBUF
491 - tbuf.actime = time(NULL);
492 + tbuf.actime = atime;
493 tbuf.modtime = modtime;
494 return utime(fname, &tbuf);
499 return utime(fname, t);
501 diff --git a/testsuite/atimes.test b/testsuite/atimes.test
504 +++ b/testsuite/atimes.test
508 +# Test rsync copying atimes
510 +. "$suitedir/rsync.fns"
514 +touch "$fromdir/foo"
515 +touch -a -t 200102031717.42 "$fromdir/foo"
519 +checkit "$RSYNC -rtUgvvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
521 +# The script would have aborted on error, so getting here means we've won.
523 diff --git a/testsuite/rsync.fns b/testsuite/rsync.fns
524 --- a/testsuite/rsync.fns
525 +++ b/testsuite/rsync.fns
526 @@ -219,6 +219,10 @@ checkit() {
527 # We can just write everything to stdout/stderr, because the
528 # wrapper hides it unless there is a problem.
530 + if test x$TLS_ARGS = x--atimes; then
531 + ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
534 echo "Running: \"$1\""
537 @@ -226,10 +230,13 @@ checkit() {
538 failed="$failed status=$status"
541 + if test x$TLS_ARGS != x--atimes; then
542 + ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
546 echo "check how the directory listings compare with diff:"
548 - ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
549 ( cd "$3" && rsync_ls_lR . ) > "$tmpdir/ls-to"
550 diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to" || failed="$failed dir-diff"
552 diff --git a/tls.c b/tls.c
555 @@ -110,6 +110,8 @@ static int stat_xattr(const char *fname, STRUCT_STAT *fst)
559 +static int display_atimes = 0;
561 static void failed(char const *what, char const *where)
563 fprintf(stderr, PROGRAM ": %s %s: %s\n",
564 @@ -117,12 +119,37 @@ static void failed(char const *what, char const *where)
568 +static void storetime(char *dest, size_t destsize, time_t t, int nsecs)
572 + struct tm *mt = gmtime(&t);
574 + len = snprintf(dest, destsize,
575 + "%04d-%02d-%02d %02d:%02d:%02d",
576 + (int)mt->tm_year + 1900,
577 + (int)mt->tm_mon + 1,
583 + snprintf(datebuf + len, sizeof datebuf - len, ".%09d", nsecs);
586 + int has_nsecs = nsecs >= 0 ? 1 : 0;
587 + int len = MIN(19 + 9*nsec_times, (int)sizeof datebuf - 1);
588 + memset(datebuf, ' ', len);
589 + datebuf[len] = '\0';
593 static void list_file(const char *fname)
596 char permbuf[PERMSTRING_SIZE];
603 if (do_lstat(fname, &buf) < 0)
604 @@ -160,30 +187,17 @@ static void list_file(const char *fname)
607 permstring(permbuf, buf.st_mode);
609 - if (buf.st_mtime) {
611 - mt = gmtime(&buf.st_mtime);
613 - len = snprintf(datebuf, sizeof datebuf,
614 - "%04d-%02d-%02d %02d:%02d:%02d",
615 - (int)mt->tm_year + 1900,
616 - (int)mt->tm_mon + 1,
623 - snprintf(datebuf + len, sizeof datebuf - len,
624 - ".%09d", (int)buf.ST_MTIME_NSEC);
627 + nsecs = (int)buf.ST_MTIME_NSEC
631 - int len = MIN(19 + 9*nsec_times, (int)sizeof datebuf - 1);
632 - memset(datebuf, ' ', len);
633 - datebuf[len] = '\0';
636 + storetime(mtimebuf, sizeof mtimebuf, buf.st_mtime, nsecs);
637 + if (display_atimes)
638 + storetime(atimebuf, sizeof atimebuf, S_ISDIR(buf.st_mode) ? 0 : buf.st_atime, -1);
640 + atimebuf[0] = '\0';
642 /* TODO: Perhaps escape special characters in fname? */
644 @@ -194,13 +208,14 @@ static void list_file(const char *fname)
645 (long)minor(buf.st_rdev));
647 printf("%15s", do_big_num(buf.st_size, 1, NULL));
648 - printf(" %6ld.%-6ld %6ld %s %s%s\n",
649 + printf(" %6ld.%-6ld %6ld %s%s%s%s\n",
650 (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
651 - datebuf, fname, linkbuf);
652 + mtimebuf, atimebuf, fname, linkbuf);
655 static struct poptOption long_options[] = {
656 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
657 + {"atimes", 'U', POPT_ARG_NONE, &display_atimes, 0, 0, 0},
658 {"link-times", 'l', POPT_ARG_NONE, &link_times, 0, 0, 0 },
659 {"link-owner", 'L', POPT_ARG_NONE, &link_owner, 0, 0, 0 },
660 #ifdef SUPPORT_XATTRS
661 @@ -219,6 +234,7 @@ static void tls_usage(int ret)
662 fprintf(F,"usage: " PROGRAM " [OPTIONS] FILE ...\n");
663 fprintf(F,"Trivial file listing program for portably checking rsync\n");
664 fprintf(F,"\nOptions:\n");
665 + fprintf(F," -U, --atimes display access (last-used) times\n");
666 fprintf(F," -l, --link-times display the time on a symlink\n");
667 fprintf(F," -L, --link-owner display the owner+group on a symlink\n");
668 #ifdef SUPPORT_XATTRS
669 diff --git a/util.c b/util.c
672 @@ -127,20 +127,24 @@ NORETURN void overflow_exit(const char *str)
674 /* This returns 0 for success, 1 for a symlink if symlink time-setting
675 * is not possible, or -1 for any other error. */
676 -int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
677 +int set_times(const char *fname, time_t modtime, uint32 mod_nsec, time_t atime, mode_t mode)
679 static int switch_step = 0;
681 if (DEBUG_GTE(TIME, 1)) {
682 - rprintf(FINFO, "set modtime of %s to (%ld) %s",
683 + char mtimebuf[200];
685 + strlcpy(mtimebuf, timestring(modtime), sizeof mtimebuf);
687 + "set modtime, atime of %s to (%ld) %s, (%ld) %s\n",
688 fname, (long)modtime,
689 - asctime(localtime(&modtime)));
690 + mtimebuf, (long)atime, timestring(atime));
693 switch (switch_step) {
694 #ifdef HAVE_UTIMENSAT
696 - if (do_utimensat(fname, modtime, mod_nsec) == 0)
697 + if (do_utimensat(fname, modtime, mod_nsec, atime, 0) == 0)
701 @@ -150,7 +154,7 @@ int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
705 - if (do_lutimes(fname, modtime, mod_nsec) == 0)
706 + if (do_lutimes(fname, modtime, mod_nsec, atime, 0) == 0)
710 @@ -169,10 +173,10 @@ int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
714 - if (do_utimes(fname, modtime, mod_nsec) == 0)
715 + if (do_utimes(fname, modtime, mod_nsec, atime, 0) == 0)
718 - if (do_utime(fname, modtime, mod_nsec) == 0)
719 + if (do_utime(fname, modtime, mod_nsec, atime, 0) == 0)