9e811d964b2b68a3938c7c28d53bfec79c27ac90
[rsync-patches.git] / atimes.diff
1 To use this patch, run these commands for a successful build:
2
3     patch -p1 <patches/atimes.diff
4     ./prepare-source
5     ./configure                      (optional if already run)
6     make
7
8 TODO:  need to fix this to handle 64-bit time_t values!
9
10 --- old/compat.c
11 +++ new/compat.c
12 @@ -44,6 +44,7 @@ extern int protocol_version;
13  extern int protect_args;
14  extern int preserve_uid;
15  extern int preserve_gid;
16 +extern int preserve_atimes;
17  extern int preserve_acls;
18  extern int preserve_xattrs;
19  extern int need_messages_from_generator;
20 @@ -60,7 +61,7 @@ extern iconv_t ic_send, ic_recv;
21  #endif
22  
23  /* These index values are for the file-list's extra-attribute array. */
24 -int uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
25 +int uid_ndx, gid_ndx, atimes_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
26  
27  #ifdef ICONV_OPTION
28  int filesfrom_convert = 0;
29 @@ -124,6 +125,8 @@ void setup_protocol(int f_out,int f_in)
30                 uid_ndx = ++file_extra_cnt;
31         if (preserve_gid)
32                 gid_ndx = ++file_extra_cnt;
33 +       if (preserve_atimes)
34 +               atimes_ndx = ++file_extra_cnt;
35         if (preserve_acls && !am_sender)
36                 acls_ndx = ++file_extra_cnt;
37         if (preserve_xattrs)
38 --- old/flist.c
39 +++ new/flist.c
40 @@ -53,6 +53,7 @@ extern int preserve_specials;
41  extern int uid_ndx;
42  extern int gid_ndx;
43  extern int eol_nulls;
44 +extern int atimes_ndx;
45  extern int relative_paths;
46  extern int implied_dirs;
47  extern int file_extra_cnt;
48 @@ -338,6 +339,7 @@ int push_pathname(const char *dir, int l
49  static void send_file_entry(int f, struct file_struct *file, int ndx, int first_ndx)
50  {
51         static time_t modtime;
52 +       static time_t atime;
53         static mode_t mode;
54  #ifdef SUPPORT_HARD_LINKS
55         static int64 dev;
56 @@ -444,6 +446,13 @@ static void send_file_entry(int f, struc
57                 xflags |= XMIT_SAME_TIME;
58         else
59                 modtime = file->modtime;
60 +       if (atimes_ndx && !S_ISDIR(mode)) {
61 +               time_t file_atime = F_ATIME(file);
62 +               if (file_atime == atime)
63 +                       xflags |= XMIT_SAME_ATIME;
64 +               else
65 +                       atime = file_atime;
66 +       }
67  
68  #ifdef SUPPORT_HARD_LINKS
69         if (tmp_dev != 0) {
70 @@ -517,6 +526,8 @@ static void send_file_entry(int f, struc
71         }
72         if (!(xflags & XMIT_SAME_MODE))
73                 write_int(f, to_wire_mode(mode));
74 +       if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME))
75 +               write_varlong(f, atime, 4);
76         if (uid_ndx && !(xflags & XMIT_SAME_UID)) {
77                 if (protocol_version < 30)
78                         write_int(f, uid);
79 @@ -603,7 +614,7 @@ static void send_file_entry(int f, struc
80  static struct file_struct *recv_file_entry(struct file_list *flist,
81                                            int xflags, int f)
82  {
83 -       static int64 modtime;
84 +       static int64 modtime, atime;
85         static mode_t mode;
86  #ifdef SUPPORT_HARD_LINKS
87         static int64 dev;
88 @@ -736,6 +747,16 @@ static struct file_struct *recv_file_ent
89         }
90         if (!(xflags & XMIT_SAME_MODE))
91                 mode = from_wire_mode(read_int(f));
92 +       if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME)) {
93 +               atime = read_varlong(f, 4);
94 +#if SIZEOF_TIME_T < SIZEOF_INT64
95 +               if ((atime > INT_MAX || atime < INT_MIN) && !am_generator) {
96 +                       rprintf(FERROR,
97 +                               "Access time value of %s truncated on receiver.\n",
98 +                               lastname);
99 +               }
100 +#endif
101 +       }
102  
103         if (chmod_modes && !S_ISLNK(mode))
104                 mode = tweak_mode(mode, chmod_modes);
105 @@ -864,6 +885,8 @@ static struct file_struct *recv_file_ent
106                 F_GROUP(file) = gid;
107                 file->flags |= gid_flags;
108         }
109 +       if (atimes_ndx)
110 +               F_ATIME(file) = (uint32)atime;
111         if (unsort_ndx)
112                 F_NDX(file) = flist->used + flist->ndx_start;
113  
114 @@ -1186,6 +1209,8 @@ struct file_struct *make_file(const char
115                 F_OWNER(file) = st.st_uid;
116         if (gid_ndx)
117                 F_GROUP(file) = st.st_gid;
118 +       if (atimes_ndx)
119 +               F_ATIME(file) = (uint32)st.st_atime;
120  
121         if (basename != thisname)
122                 file->dirname = lastdir;
123 --- old/generator.c
124 +++ new/generator.c
125 @@ -43,6 +43,7 @@ extern int preserve_specials;
126  extern int preserve_hard_links;
127  extern int preserve_perms;
128  extern int preserve_times;
129 +extern int preserve_atimes;
130  extern int uid_ndx;
131  extern int gid_ndx;
132  extern int delete_mode;
133 @@ -568,6 +569,9 @@ void itemize(const char *fnamecmp, struc
134                   && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
135                  || (keep_time && cmp_time(file->modtime, sxp->st.st_mtime) != 0))
136                         iflags |= ITEM_REPORT_TIME;
137 +               if (preserve_atimes && !S_ISDIR(file->mode) && !S_ISLNK(file->mode)
138 +                && cmp_time(F_ATIME(file), sxp->st.st_atime) != 0)
139 +                       iflags |= ITEM_REPORT_ATIME;
140  #if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
141                 if (S_ISLNK(file->mode)) {
142                         ;
143 @@ -923,6 +927,8 @@ static int try_dests_reg(struct file_str
144                 if (link_dest) {
145                         if (!hard_link_one(file, fname, cmpbuf, 1))
146                                 goto try_a_copy;
147 +                       if (preserve_atimes)
148 +                               set_file_attrs(fname, file, sxp, NULL, 0);
149                         if (preserve_hard_links && F_IS_HLINKED(file))
150                                 finish_hard_link(file, fname, ndx, &sxp->st, itemizing, code, j);
151                         if (itemizing && (verbose > 1 || stdout_format_has_i > 1)) {
152 @@ -1113,6 +1119,7 @@ static int try_dests_non(struct file_str
153  static void list_file_entry(struct file_struct *f)
154  {
155         char permbuf[PERMSTRING_SIZE];
156 +       time_t atime = atimes_ndx ? F_ATIME(f) : 0;
157         double len;
158  
159         if (!F_IS_ACTIVE(f)) {
160 @@ -1127,14 +1134,16 @@ static void list_file_entry(struct file_
161  
162  #ifdef SUPPORT_LINKS
163         if (preserve_links && S_ISLNK(f->mode)) {
164 -               rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
165 +               rprintf(FINFO, "%s %11.0f %s %s %s -> %s\n",
166                         permbuf, len, timestring(f->modtime),
167 +                       atimes_ndx ? timestring(atime) : "",
168                         f_name(f, NULL), F_SYMLINK(f));
169         } else
170  #endif
171         {
172 -               rprintf(FINFO, "%s %11.0f %s %s\n",
173 +               rprintf(FINFO, "%s %11.0f %s %s %s\n",
174                         permbuf, len, timestring(f->modtime),
175 +                       atimes_ndx ? timestring(atime) : "",
176                         f_name(f, NULL));
177         }
178  }
179 @@ -1884,7 +1893,7 @@ static void touch_up_dirs(struct file_li
180                 if (!(file->mode & S_IWUSR))
181                         do_chmod(fname, file->mode);
182                 if (need_retouch_dir_times)
183 -                       set_modtime(fname, file->modtime, file->mode);
184 +                       set_times(fname, file->modtime, file->modtime, file->mode);
185                 if (allowed_lull && !(counter % lull_mod))
186                         maybe_send_keepalive();
187                 else if (!(counter & 0xFF))
188 --- old/log.c
189 +++ new/log.c
190 @@ -631,7 +631,8 @@ static void log_formatted(enum logcode c
191                         c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
192                         c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
193                         c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
194 -                       c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' : 'u';
195 +                       c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.'
196 +                            : S_ISLNK(file->mode) ? 'U' : 'u';
197                         c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
198                         c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
199                         c[11] = '\0';
200 --- old/options.c
201 +++ new/options.c
202 @@ -57,6 +57,7 @@ int preserve_specials = 0;
203  int preserve_uid = 0;
204  int preserve_gid = 0;
205  int preserve_times = 0;
206 +int preserve_atimes = 0;
207  int update_only = 0;
208  int cvs_exclude = 0;
209  int dry_run = 0;
210 @@ -346,6 +347,7 @@ void usage(enum logcode F)
211    rprintf(F," -D                          same as --devices --specials\n");
212    rprintf(F," -t, --times                 preserve modification times\n");
213    rprintf(F," -O, --omit-dir-times        omit directories from --times\n");
214 +  rprintf(F," -U, --atimes                preserve access (last-used) times\n");
215    rprintf(F,"     --super                 receiver attempts super-user activities\n");
216  #ifdef SUPPORT_XATTRS
217    rprintf(F,"     --fake-super            store/recover privileged attrs using xattrs\n");
218 @@ -480,6 +482,9 @@ static struct poptOption long_options[] 
219    {"times",           't', POPT_ARG_VAL,    &preserve_times, 2, 0, 0 },
220    {"no-times",         0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
221    {"no-t",             0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
222 +  {"atimes",          'U', POPT_ARG_VAL,    &preserve_atimes, 1, 0, 0 },
223 +  {"no-atimes",        0,  POPT_ARG_VAL,    &preserve_atimes, 0, 0, 0 },
224 +  {"no-U",             0,  POPT_ARG_VAL,    &preserve_atimes, 0, 0, 0 },
225    {"omit-dir-times",  'O', POPT_ARG_VAL,    &omit_dir_times, 1, 0, 0 },
226    {"no-omit-dir-times",0,  POPT_ARG_VAL,    &omit_dir_times, 0, 0, 0 },
227    {"no-O",             0,  POPT_ARG_VAL,    &omit_dir_times, 0, 0, 0 },
228 @@ -1713,6 +1718,8 @@ void server_options(char **args, int *ar
229                 argstr[x++] = 'D';
230         if (preserve_times)
231                 argstr[x++] = 't';
232 +       if (preserve_atimes)
233 +               argstr[x++] = 'U';
234         if (preserve_perms)
235                 argstr[x++] = 'p';
236         else if (preserve_executability && am_sender)
237 --- old/rsync.c
238 +++ new/rsync.c
239 @@ -33,6 +33,7 @@ extern int preserve_acls;
240  extern int preserve_xattrs;
241  extern int preserve_perms;
242  extern int preserve_executability;
243 +extern int preserve_atimes;
244  extern int preserve_times;
245  extern int am_root;
246  extern int am_server;
247 @@ -343,6 +344,7 @@ int set_file_attrs(const char *fname, st
248         int updated = 0;
249         stat_x sx2;
250         int change_uid, change_gid;
251 +       time_t atime, mtime;
252         mode_t new_mode = file->mode;
253         int inherit;
254  
255 @@ -383,18 +385,36 @@ int set_file_attrs(const char *fname, st
256                 set_stat_xattr(fname, file);
257  #endif
258  
259 +       /* This code must be the first update in the function due to
260 +        * how it uses the "updated" variable. */
261         if (!preserve_times || (S_ISDIR(sxp->st.st_mode) && preserve_times == 1))
262                 flags |= ATTRS_SKIP_MTIME;
263 +       if (!preserve_atimes || S_ISDIR(sxp->st.st_mode))
264 +               flags |= ATTRS_SKIP_ATIME;
265         if (!(flags & ATTRS_SKIP_MTIME)
266             && cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
267 -               int ret = set_modtime(fname, file->modtime, sxp->st.st_mode);
268 +               mtime = file->modtime;
269 +               updated = 1;
270 +       } else
271 +               mtime = sxp->st.st_mtime;
272 +       if (!(flags & ATTRS_SKIP_ATIME)) {
273 +               time_t file_atime = F_ATIME(file);
274 +               if (cmp_time(sxp->st.st_atime, file_atime) != 0) {
275 +                       atime = file_atime;
276 +                       updated = 1;
277 +               } else
278 +                       atime = sxp->st.st_atime;
279 +       } else
280 +               atime = sxp->st.st_atime;
281 +       if (updated) {
282 +               int ret = set_times(fname, mtime, atime, sxp->st.st_mode);
283                 if (ret < 0) {
284                         rsyserr(FERROR, errno, "failed to set times on %s",
285                                 full_fname(fname));
286                         goto cleanup;
287                 }
288 -               if (ret == 0) /* ret == 1 if symlink could not be set */
289 -                       updated = 1;
290 +               if (ret > 0) /* ret == 1 if symlink could not be set */
291 +                       updated = 0;
292         }
293  
294         change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
295 --- old/rsync.h
296 +++ new/rsync.h
297 @@ -57,6 +57,7 @@
298  #define XMIT_RDEV_MINOR_8_pre30 (1<<11)        /* protocols 28 - 29  */
299  #define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */
300  #define XMIT_HLINK_FIRST (1<<12)       /* protocols 30 - now (HLINKED files only) */
301 +#define XMIT_SAME_ATIME (1<<13)                /* protocols ?? - now */
302  
303  /* These flags are used in the live flist data. */
304  
305 @@ -145,6 +146,7 @@
306  
307  #define ATTRS_REPORT           (1<<0)
308  #define ATTRS_SKIP_MTIME       (1<<1)
309 +#define ATTRS_SKIP_ATIME       (1<<2)
310  
311  #define FULL_FLUSH     1
312  #define NORMAL_FLUSH   0
313 @@ -608,6 +610,7 @@ extern int file_extra_cnt;
314  extern int inc_recurse;
315  extern int uid_ndx;
316  extern int gid_ndx;
317 +extern int atimes_ndx;
318  extern int acls_ndx;
319  extern int xattrs_ndx;
320  
321 @@ -645,6 +648,7 @@ extern int xattrs_ndx;
322  /* When the associated option is on, all entries will have these present: */
323  #define F_OWNER(f) REQ_EXTRA(f, uid_ndx)->unum
324  #define F_GROUP(f) REQ_EXTRA(f, gid_ndx)->unum
325 +#define F_ATIME(f) REQ_EXTRA(f, atimes_ndx)->unum
326  #define F_ACL(f) REQ_EXTRA(f, acls_ndx)->num
327  #define F_XATTR(f) REQ_EXTRA(f, xattrs_ndx)->num
328  #define F_NDX(f) REQ_EXTRA(f, unsort_ndx)->num
329 --- old/rsync.yo
330 +++ new/rsync.yo
331 @@ -349,6 +349,7 @@ to the detailed description below for a 
332   -D                          same as --devices --specials
333   -t, --times                 preserve modification times
334   -O, --omit-dir-times        omit directories from --times
335 + -U, --atimes                preserve access (use) times
336       --super                 receiver attempts super-user activities
337       --fake-super            store/recover privileged attrs using xattrs
338   -S, --sparse                handle sparse files efficiently
339 @@ -973,6 +974,12 @@ it is preserving modification times (see
340  the directories on the receiving side, it is a good idea to use bf(-O).
341  This option is inferred if you use bf(--backup) without bf(--backup-dir).
342  
343 +dit(bf(-U, --atimes)) This tells rsync to set the access (use) times of the
344 +destination files to the same value as the source files.  Note that the
345 +reading of the source file may update the atime of the source files, so
346 +repeated rsync runs with --atimes may be needed if you want to force the
347 +access-time values to be 100% identical on the two systems.
348 +
349  dit(bf(--super)) This tells the receiving side to attempt super-user
350  activities even if the receiving rsync wasn't run by the super-user.  These
351  activities include: preserving users via the bf(--owner) option, preserving
352 @@ -1654,8 +1661,10 @@ quote(itemization(
353    sender's value (requires bf(--owner) and super-user privileges).
354    it() A bf(g) means the group is different and is being updated to the
355    sender's value (requires bf(--group) and the authority to set the group).
356 -  it() The bf(u) slot is reserved for reporting update (access) time changes
357 -  (a feature that is not yet released).
358 +  it() A bf(u) means the access (use) time is different and is being updated to
359 +  the sender's value (requires bf(--atimes)).  An alternate value of bf(U)
360 +  means that the access time will be set to the transfer time, which happens
361 +  when a symlink or directory is updated.
362    it() The bf(a) means that the ACL information changed.
363    it() The bf(x) slot is reserved for reporting extended attribute changes
364    (a feature that is not yet released).
365 --- old/sender.c
366 +++ new/sender.c
367 @@ -43,6 +43,7 @@ extern int do_progress;
368  extern int inplace;
369  extern int batch_fd;
370  extern int write_batch;
371 +extern unsigned int file_struct_len;
372  extern struct stats stats;
373  extern struct file_list *cur_flist, *first_flist, *dir_flist;
374  
375 --- old/testsuite/atimes.test
376 +++ new/testsuite/atimes.test
377 @@ -0,0 +1,19 @@
378 +#! /bin/sh
379 +
380 +# Test rsync copying atimes
381 +
382 +. "$suitedir/rsync.fns"
383 +
384 +set -x
385 +
386 +mkdir "$fromdir"
387 +
388 +touch "$fromdir/foo"
389 +touch -a -t 200102031717.42 "$fromdir/foo"
390 +
391 +TLS_ARGS=--atime
392 +
393 +checkit "$RSYNC -rtUgvvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
394 +
395 +# The script would have aborted on error, so getting here means we've won.
396 +exit 0
397 --- old/testsuite/rsync.fns
398 +++ new/testsuite/rsync.fns
399 @@ -187,6 +187,10 @@ checkit() {
400      # We can just write everything to stdout/stderr, because the
401      # wrapper hides it unless there is a problem.
402  
403 +    if test x$TLS_ARGS = x--atime; then
404 +       ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
405 +    fi
406 +
407      echo "Running: \"$1\""  
408      eval "$1" 
409      status=$?
410 @@ -194,10 +198,13 @@ checkit() {
411         failed="YES";
412      fi
413  
414 +    if test x$TLS_ARGS != x--atime; then
415 +       ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
416 +    fi
417 +
418      echo "-------------"
419      echo "check how the directory listings compare with diff:"
420      echo ""
421 -    ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
422      ( cd "$3" && rsync_ls_lR . ) > "$tmpdir/ls-to"
423      diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to" || failed=YES
424  
425 --- old/tls.c
426 +++ new/tls.c
427 @@ -104,6 +104,8 @@ static int stat_xattr(const char *fname,
428  
429  #endif
430  
431 +static int display_atime = 0;
432
433  static void failed(char const *what, char const *where)
434  {
435         fprintf(stderr, PROGRAM ": %s %s: %s\n",
436 @@ -111,12 +113,29 @@ static void failed(char const *what, cha
437         exit(1);
438  }
439  
440 +static void storetime(char *dest, time_t t, size_t destsize)
441 +{
442 +       if (t) {
443 +               struct tm *mt = gmtime(&t);
444 +
445 +               snprintf(dest, destsize,
446 +                       "%04d-%02d-%02d %02d:%02d:%02d ",
447 +                       (int)mt->tm_year + 1900,
448 +                       (int)mt->tm_mon + 1,
449 +                       (int)mt->tm_mday,
450 +                       (int)mt->tm_hour,
451 +                       (int)mt->tm_min,
452 +                       (int)mt->tm_sec);
453 +       } else
454 +               strlcpy(dest, "                    ", destsize);
455 +}
456 +
457  static void list_file(const char *fname)
458  {
459         STRUCT_STAT buf;
460         char permbuf[PERMSTRING_SIZE];
461 -       struct tm *mt;
462 -       char datebuf[50];
463 +       char mtimebuf[50];
464 +       char atimebuf[50];
465         char linkbuf[4096];
466  
467         if (do_lstat(fname, &buf) < 0)
468 @@ -153,19 +172,8 @@ static void list_file(const char *fname)
469  
470         permstring(permbuf, buf.st_mode);
471  
472 -       if (buf.st_mtime) {
473 -               mt = gmtime(&buf.st_mtime);
474 -
475 -               snprintf(datebuf, sizeof datebuf,
476 -                       "%04d-%02d-%02d %02d:%02d:%02d",
477 -                       (int)mt->tm_year + 1900,
478 -                       (int)mt->tm_mon + 1,
479 -                       (int)mt->tm_mday,
480 -                       (int)mt->tm_hour,
481 -                       (int)mt->tm_min,
482 -                       (int)mt->tm_sec);
483 -       } else
484 -               strlcpy(datebuf, "                   ", sizeof datebuf);
485 +       storetime(mtimebuf, buf.st_mtime, sizeof mtimebuf);
486 +       storetime(atimebuf, buf.st_atime, sizeof atimebuf);
487  
488         /* TODO: Perhaps escape special characters in fname? */
489  
490 @@ -176,13 +184,15 @@ static void list_file(const char *fname)
491                     (long)minor(buf.st_rdev));
492         } else /* NB: use double for size since it might not fit in a long. */
493                 printf("%12.0f", (double)buf.st_size);
494 -       printf(" %6ld.%-6ld %6ld %s %s%s\n",
495 +       printf(" %6ld.%-6ld %6ld %s%s%s%s\n",
496                (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
497 -              datebuf, fname, linkbuf);
498 +              mtimebuf, display_atime && !S_ISDIR(buf.st_mode) ? atimebuf : "",
499 +              fname, linkbuf);
500  }
501  
502  static struct poptOption long_options[] = {
503    /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
504 +  {"atime",           'u', POPT_ARG_NONE,   &display_atime, 0, 0, 0},
505  #ifdef SUPPORT_XATTRS
506    {"fake-super",      'f', POPT_ARG_VAL,    &am_root, -1, 0, 0 },
507  #endif
508 @@ -196,6 +206,7 @@ static void tls_usage(int ret)
509    fprintf(F,"usage: " PROGRAM " [OPTIONS] FILE ...\n");
510    fprintf(F,"Trivial file listing program for portably checking rsync\n");
511    fprintf(F,"\nOptions:\n");
512 +  rprintf(F," -U, --atimes                display access (last-used) times\n");
513  #ifdef SUPPORT_XATTRS
514    fprintf(F," -f, --fake-super            display attributes including fake-super xattrs\n");
515  #endif
516 --- old/util.c
517 +++ new/util.c
518 @@ -122,7 +122,7 @@ NORETURN void overflow_exit(const char *
519         exit_cleanup(RERR_MALLOC);
520  }
521  
522 -int set_modtime(const char *fname, time_t modtime, mode_t mode)
523 +int set_times(const char *fname, time_t modtime, time_t atime, mode_t mode)
524  {
525  #if !defined HAVE_LUTIMES || !defined HAVE_UTIMES
526         if (S_ISLNK(mode))
527 @@ -130,9 +130,13 @@ int set_modtime(const char *fname, time_
528  #endif
529  
530         if (verbose > 2) {
531 -               rprintf(FINFO, "set modtime of %s to (%ld) %s",
532 +               char mtimebuf[200];
533 +
534 +               strlcpy(mtimebuf, timestring(modtime), sizeof mtimebuf);
535 +               rprintf(FINFO,
536 +                       "set modtime, atime of %s to (%ld) %s, (%ld) %s\n",
537                         fname, (long)modtime,
538 -                       asctime(localtime(&modtime)));
539 +                       mtimebuf, (long)atime, timestring(atime));
540         }
541  
542         if (dry_run)
543 @@ -141,7 +145,7 @@ int set_modtime(const char *fname, time_
544         {
545  #ifdef HAVE_UTIMES
546                 struct timeval t[2];
547 -               t[0].tv_sec = time(NULL);
548 +               t[0].tv_sec = atime;
549                 t[0].tv_usec = 0;
550                 t[1].tv_sec = modtime;
551                 t[1].tv_usec = 0;
552 @@ -154,12 +158,12 @@ int set_modtime(const char *fname, time_
553                 return utimes(fname, t);
554  #elif defined HAVE_STRUCT_UTIMBUF
555                 struct utimbuf tbuf;
556 -               tbuf.actime = time(NULL);
557 +               tbuf.actime = atime;
558                 tbuf.modtime = modtime;
559                 return utime(fname,&tbuf);
560  #elif defined HAVE_UTIME
561                 time_t t[2];
562 -               t[0] = time(NULL);
563 +               t[0] = atime;
564                 t[1] = modtime;
565                 return utime(fname,t);
566  #else