1 To use this patch, run these commands for a successful build:
3 patch -p1 <patches/atimes.diff
5 ./configure (optional if already run)
11 @@ -62,6 +62,8 @@ void setup_protocol(int f_out,int f_in)
12 preserve_uid = ++file_extra_cnt;
14 preserve_gid = ++file_extra_cnt;
15 + if (preserve_atimes)
16 + preserve_atimes = ++file_extra_cnt;
17 if (preserve_acls && !am_sender)
18 preserve_acls = ++file_extra_cnt;
22 @@ -48,6 +48,7 @@ extern int preserve_devices;
23 extern int preserve_specials;
24 extern int preserve_uid;
25 extern int preserve_gid;
26 +extern int preserve_atimes;
27 extern int relative_paths;
28 extern int implied_dirs;
29 extern int file_extra_cnt;
30 @@ -143,6 +144,7 @@ void show_flist_stats(void)
31 static void list_file_entry(struct file_struct *f)
33 char permbuf[PERMSTRING_SIZE];
34 + time_t atime = preserve_atimes ? F_ATIME(f) : 0;
37 if (!F_IS_ACTIVE(f)) {
38 @@ -157,14 +159,16 @@ static void list_file_entry(struct file_
41 if (preserve_links && S_ISLNK(f->mode)) {
42 - rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
43 + rprintf(FINFO, "%s %11.0f %s %s %s -> %s\n",
44 permbuf, len, timestring(f->modtime),
45 + preserve_atimes ? timestring(atime) : "",
46 f_name(f, NULL), F_SYMLINK(f));
50 - rprintf(FINFO, "%s %11.0f %s %s\n",
51 + rprintf(FINFO, "%s %11.0f %s %s %s\n",
52 permbuf, len, timestring(f->modtime),
53 + preserve_atimes ? timestring(atime) : "",
57 @@ -348,6 +352,7 @@ int push_flist_dir(const char *dir, int
58 static void send_file_entry(int f, struct file_struct *file, int ndx)
60 static time_t modtime;
61 + static time_t atime;
65 @@ -415,6 +420,13 @@ static void send_file_entry(int f, struc
66 flags |= XMIT_SAME_TIME;
68 modtime = file->modtime;
69 + if (preserve_atimes && !S_ISDIR(mode)) {
70 + time_t file_atime = F_ATIME(file);
71 + if (file_atime == atime)
72 + flags |= XMIT_SAME_ATIME;
77 #ifdef SUPPORT_HARD_LINKS
79 @@ -482,6 +494,8 @@ static void send_file_entry(int f, struc
80 write_int(f, modtime);
81 if (!(flags & XMIT_SAME_MODE))
82 write_int(f, to_wire_mode(mode));
83 + if (preserve_atimes && !S_ISDIR(mode) && !(flags & XMIT_SAME_ATIME))
84 + write_int(f, atime);
85 if (preserve_uid && !(flags & XMIT_SAME_UID)) {
86 write_abbrevint30(f, uid);
87 if (flags & XMIT_USER_NAME_FOLLOWS) {
88 @@ -558,7 +572,7 @@ static void send_file_entry(int f, struc
89 static struct file_struct *recv_file_entry(struct file_list *flist,
92 - static time_t modtime;
93 + static time_t modtime, atime;
97 @@ -655,6 +669,8 @@ static struct file_struct *recv_file_ent
98 modtime = (time_t)read_int(f);
99 if (!(flags & XMIT_SAME_MODE))
100 mode = from_wire_mode(read_int(f));
101 + if (preserve_atimes && !S_ISDIR(mode) && !(flags & XMIT_SAME_ATIME))
102 + atime = (time_t)read_int(f);
104 if (chmod_modes && !S_ISLNK(mode))
105 mode = tweak_mode(mode, chmod_modes);
106 @@ -769,6 +785,8 @@ static struct file_struct *recv_file_ent
110 + if (preserve_atimes)
111 + F_ATIME(file) = atime;
113 if (basename != thisname) {
114 file->dirname = lastdir;
115 @@ -1074,6 +1092,8 @@ struct file_struct *make_file(const char
116 F_OWNER(file) = st.st_uid;
118 F_GROUP(file) = st.st_gid;
119 + if (preserve_atimes)
120 + F_ATIME(file) = st.st_atime;
122 if (basename != thisname)
123 file->dirname = lastdir;
126 @@ -44,6 +44,7 @@ extern int preserve_perms;
127 extern int preserve_uid;
128 extern int preserve_gid;
129 extern int preserve_times;
130 +extern int preserve_atimes;
131 extern int omit_dir_times;
132 extern int delete_mode;
133 extern int delete_before;
134 @@ -552,6 +553,9 @@ void itemize(const char *fname, struct f
135 && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
136 || (keep_time && cmp_time(file->modtime, sxp->st.st_mtime) != 0))
137 iflags |= ITEM_REPORT_TIME;
138 + if (preserve_atimes && !S_ISDIR(file->mode) && !S_ISLNK(file->mode)
139 + && cmp_time(F_ATIME(file), st->st_atime) != 0)
140 + iflags |= ITEM_REPORT_ATIME;
141 if (!BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS))
142 iflags |= ITEM_REPORT_PERMS;
143 if (preserve_uid && am_root && F_UID(file) != sxp->st.st_uid)
144 @@ -852,6 +856,8 @@ static int try_dests_reg(struct file_str
146 if (!hard_link_one(file, fname, cmpbuf, 1))
148 + if (preserve_atimes)
149 + set_file_attrs(fname, file, stp, 0);
150 if (preserve_hard_links && F_IS_HLINKED(file))
151 finish_hard_link(file, fname, &sxp->st, itemizing, code, j);
152 if (itemizing && (verbose > 1 || stdout_format_has_i > 1)) {
155 @@ -36,6 +36,7 @@ extern int msg_fd_out;
156 extern int allow_8bit_chars;
157 extern int protocol_version;
158 extern int preserve_times;
159 +extern int preserve_atimes;
160 extern int preserve_uid;
161 extern int preserve_gid;
162 extern int stdout_format_has_i;
163 @@ -624,7 +625,8 @@ static void log_formatted(enum logcode c
164 c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
165 c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
166 c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
167 - c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' : 'u';
168 + c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.'
169 + : S_ISLNK(file->mode) ? 'U' : 'u';
170 c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
171 c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
175 @@ -55,6 +55,7 @@ int preserve_uid = 0;
176 int preserve_gid = 0;
177 int preserve_times = 0;
178 int omit_dir_times = 0;
179 +int preserve_atimes = 0;
183 @@ -315,8 +316,9 @@ void usage(enum logcode F)
184 rprintf(F," --devices preserve device files (super-user only)\n");
185 rprintf(F," --specials preserve special files\n");
186 rprintf(F," -D same as --devices --specials\n");
187 - rprintf(F," -t, --times preserve times\n");
188 - rprintf(F," -O, --omit-dir-times omit directories when preserving times\n");
189 + rprintf(F," -t, --times preserve modify times\n");
190 + rprintf(F," -O, --omit-dir-times omit directories when preserving modify times\n");
191 + rprintf(F," -U, --atimes preserve access (use) times\n");
192 rprintf(F," --super receiver attempts super-user activities\n");
193 rprintf(F," -S, --sparse handle sparse files efficiently\n");
194 rprintf(F," -n, --dry-run show what would have been transferred\n");
195 @@ -436,6 +438,9 @@ static struct poptOption long_options[]
196 {"times", 't', POPT_ARG_VAL, &preserve_times, 1, 0, 0 },
197 {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
198 {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
199 + {"atimes", 'U', POPT_ARG_VAL, &preserve_atimes, 1, 0, 0 },
200 + {"no-atimes", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 },
201 + {"no-U", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 },
202 {"omit-dir-times", 'O', POPT_ARG_VAL, &omit_dir_times, 2, 0, 0 },
203 {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
204 {"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 },
205 @@ -1577,6 +1582,8 @@ void server_options(char **args,int *arg
209 + if (preserve_atimes)
213 else if (preserve_executability && am_sender)
216 @@ -34,6 +34,7 @@ extern int dry_run;
217 extern int preserve_acls;
218 extern int preserve_perms;
219 extern int preserve_executability;
220 +extern int preserve_atimes;
221 extern int preserve_times;
222 extern int omit_dir_times;
224 @@ -234,6 +235,7 @@ int set_file_attrs(char *fname, struct f
227 int change_uid, change_gid;
228 + time_t atime, mtime;
229 mode_t new_mode = file->mode;
232 @@ -261,18 +263,36 @@ int set_file_attrs(char *fname, struct f
236 + /* This code must be the first update in the function due to
237 + * how it uses the "updated" variable. */
238 if (!preserve_times || (S_ISDIR(sxp->st.st_mode) && omit_dir_times))
239 flags |= ATTRS_SKIP_MTIME;
240 + if (!preserve_atimes || S_ISDIR(sxp->st.st_mode))
241 + flags |= ATTRS_SKIP_ATIME;
242 if (!(flags & ATTRS_SKIP_MTIME)
243 && cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
244 - int ret = set_modtime(fname, file->modtime, sxp->st.st_mode);
245 + mtime = file->modtime;
248 + mtime = sxp->st.st_mtime;
249 + if (!(flags & ATTRS_SKIP_ATIME)) {
250 + time_t file_atime = F_ATIME(file);
251 + if (cmp_time(sxp->st.st_atime, file_atime) != 0) {
252 + atime = file_atime;
255 + atime = sxp->st.st_atime;
257 + atime = sxp->st.st_atime;
259 + int ret = set_times(fname, mtime, atime, sxp->st.st_mode);
261 rsyserr(FERROR, errno, "failed to set times on %s",
265 - if (ret == 0) /* ret == 1 if symlink could not be set */
267 + if (ret > 0) /* ret == 1 if symlink could not be set */
271 change_uid = am_root && preserve_uid && sxp->st.st_uid != F_UID(file);
275 #define XMIT_RDEV_MINOR_IS_SMALL (1<<11)
276 #define XMIT_USER_NAME_FOLLOWS (1<<12) /* protocols >= 30 */
277 #define XMIT_GROUP_NAME_FOLLOWS (1<<13) /* protocols >= 30 */
278 +#define XMIT_SAME_ATIME (1<<14) /* protocols >= 30 */
280 /* These flags are used in the live flist data. */
284 #define ATTRS_REPORT (1<<0)
285 #define ATTRS_SKIP_MTIME (1<<1)
286 +#define ATTRS_SKIP_ATIME (1<<2)
289 #define NORMAL_FLUSH 0
290 @@ -571,6 +573,7 @@ extern int file_extra_cnt;
291 extern int preserve_uid;
292 extern int preserve_gid;
293 extern int preserve_acls;
294 +extern int preserve_atimes;
296 #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
297 #define EXTRA_LEN (sizeof (union file_extras))
298 @@ -603,6 +606,7 @@ extern int preserve_acls;
299 /* When the associated option is on, all entries will have these present: */
300 #define F_OWNER(f) REQ_EXTRA(f, preserve_uid)->unum
301 #define F_GROUP(f) REQ_EXTRA(f, preserve_gid)->unum
302 +#define F_ATIME(f) REQ_EXTRA(f, preserve_atimes)->unum
303 #define F_ACL(f) REQ_EXTRA(f, preserve_acls)->unum
305 /* These items are per-entry optional and mutally exclusive: */
308 @@ -329,8 +329,9 @@ to the detailed description below for a
309 --devices preserve device files (super-user only)
310 --specials preserve special files
311 -D same as --devices --specials
312 - -t, --times preserve times
313 - -O, --omit-dir-times omit directories when preserving times
314 + -t, --times preserve modify times
315 + -O, --omit-dir-times omit directories when preserving mod-times
316 + -U, --atimes preserve access (use) times
317 --super receiver attempts super-user activities
318 -S, --sparse handle sparse files efficiently
319 -n, --dry-run show what would have been transferred
320 @@ -896,6 +897,12 @@ it is preserving modification times (see
321 the directories on the receiving side, it is a good idea to use bf(-O).
322 This option is inferred if you use bf(--backup) without bf(--backup-dir).
324 +dit(bf(-U, --atimes)) This tells rsync to set the access (use) times of the
325 +destination files to the same value as the source files. Note that the
326 +reading of the source file may update the atime of the source files, so
327 +repeated rsync runs with --atimes may be needed if you want to force the
328 +access-time values to be 100% identical on the two systems.
330 dit(bf(--super)) This tells the receiving side to attempt super-user
331 activities even if the receiving rsync wasn't run by the super-user. These
332 activities include: preserving users via the bf(--owner) option, preserving
333 @@ -1482,7 +1489,7 @@ quote(itemization(
334 by the file transfer.
335 it() A bf(t) means the modification time is different and is being updated
336 to the sender's value (requires bf(--times)). An alternate value of bf(T)
337 - means that the time will be set to the transfer time, which happens
338 + means that the modify time will be set to the transfer time, which happens
339 anytime a symlink is transferred, or when a file or device is transferred
341 it() A bf(p) means the permissions are different and are being updated to
342 @@ -1491,8 +1498,10 @@ quote(itemization(
343 sender's value (requires bf(--owner) and super-user privileges).
344 it() A bf(g) means the group is different and is being updated to the
345 sender's value (requires bf(--group) and the authority to set the group).
346 - it() The bf(u) slot is reserved for reporting update (access) time changes
347 - (a feature that is not yet released).
348 + it() A bf(u) means the access (use) time is different and is being updated to
349 + the sender's value (requires bf(--atimes)). An alternate value of bf(U)
350 + means that the access time will be set to the transfer time, which happens
351 + when a symlink or directory is updated.
352 it() The bf(a) means that the ACL information changed.
353 it() The bf(x) slot is reserved for reporting extended attribute changes
354 (a feature that is not yet released).
357 @@ -41,6 +41,7 @@ extern int do_progress;
360 extern int write_batch;
361 +extern unsigned int file_struct_len;
362 extern struct stats stats;
363 extern struct file_list *cur_flist, *first_flist;
365 --- old/testsuite/atimes.test
366 +++ new/testsuite/atimes.test
370 +# Test rsync copying atimes
372 +. "$suitedir/rsync.fns"
378 +touch "$fromdir/foo"
379 +touch -a -t 200102031717.42 "$fromdir/foo"
383 +checkit "$RSYNC -rtUgvvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
385 +# The script would have aborted on error, so getting here means we've won.
387 --- old/testsuite/rsync.fns
388 +++ new/testsuite/rsync.fns
389 @@ -66,7 +66,7 @@ printmsg() {
393 - find "$@" -print | sort | sed 's/ /\\ /g' | xargs "$TOOLDIR/tls"
394 + find "$@" -print | sort | sed 's/ /\\ /g' | xargs "$TOOLDIR/tls" $TLS_ARGS
398 @@ -184,6 +184,10 @@ checkit() {
399 # We can just write everything to stdout/stderr, because the
400 # wrapper hides it unless there is a problem.
402 + if test x$TLS_ARGS = x--atime; then
403 + ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
406 echo "Running: \"$1\""
409 @@ -191,10 +195,13 @@ checkit() {
413 + if test x$TLS_ARGS != x--atime; then
414 + ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
418 echo "check how the directory listings compare with diff:"
420 - ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
421 ( cd "$3" && rsync_ls_lR . ) > "$tmpdir/ls-to"
422 diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to" || failed=YES
432 #define PROGRAM "tls"
434 @@ -43,6 +44,8 @@ int read_only = 1;
436 int preserve_perms = 0;
438 +static int display_atime = 0;
440 static void failed(char const *what, char const *where)
442 fprintf(stderr, PROGRAM ": %s %s: %s\n",
443 @@ -50,12 +53,29 @@ static void failed(char const *what, cha
447 +static void storetime(char *dest, time_t t, size_t destsize)
450 + struct tm *mt = gmtime(&t);
452 + snprintf(dest, destsize,
453 + "%04d-%02d-%02d %02d:%02d:%02d ",
454 + (int)mt->tm_year + 1900,
455 + (int)mt->tm_mon + 1,
461 + strlcpy(dest, " ", destsize);
464 static void list_file(const char *fname)
467 char permbuf[PERMSTRING_SIZE];
474 if (do_lstat(fname, &buf) < 0)
475 @@ -88,19 +108,8 @@ static void list_file(const char *fname)
477 permstring(permbuf, buf.st_mode);
479 - if (buf.st_mtime) {
480 - mt = gmtime(&buf.st_mtime);
482 - snprintf(datebuf, sizeof datebuf,
483 - "%04d-%02d-%02d %02d:%02d:%02d",
484 - (int)mt->tm_year + 1900,
485 - (int)mt->tm_mon + 1,
491 - strlcpy(datebuf, " ", sizeof datebuf);
492 + storetime(mtimebuf, buf.st_mtime, sizeof mtimebuf);
493 + storetime(atimebuf, buf.st_atime, sizeof atimebuf);
495 /* TODO: Perhaps escape special characters in fname? */
497 @@ -111,23 +120,55 @@ static void list_file(const char *fname)
498 (long)minor(buf.st_rdev));
499 } else /* NB: use double for size since it might not fit in a long. */
500 printf("%12.0f", (double)buf.st_size);
501 - printf(" %6ld.%-6ld %6ld %s %s%s\n",
502 + printf(" %6ld.%-6ld %6ld %s%s%s%s\n",
503 (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
504 - datebuf, fname, linkbuf);
505 + mtimebuf, display_atime && !S_ISDIR(buf.st_mode) ? atimebuf : "",
509 +static struct poptOption long_options[] = {
510 + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
511 + {"atime", 'u', POPT_ARG_NONE, &display_atime, 0, 0, 0},
512 + {"help", 'h', POPT_ARG_NONE, 0, 'h', 0, 0},
516 +static void tls_usage(int ret)
518 + fprintf(stderr, "usage: " PROGRAM " [--atime | -u] FILE ...\n"
519 + "Trivial file listing program for portably checking rsync\n");
524 main(int argc, char *argv[])
527 - fprintf(stderr, "usage: " PROGRAM " FILE ...\n"
528 - "Trivial file listing program for portably checking rsync\n");
532 + const char **extra_args;
535 - for (argv++; *argv; argv++) {
537 + pc = poptGetContext(PROGRAM, argc, (const char **)argv,
539 + while ((opt = poptGetNextOpt(pc)) != -1) {
546 + poptBadOption(pc, POPT_BADOPTION_NOALIAS),
547 + poptStrerror(opt));
552 + extra_args = poptGetArgs(pc);
553 + if (*extra_args == NULL)
556 + for (; *extra_args; extra_args++)
557 + list_file(*extra_args);
558 + poptFreeContext(pc);
564 @@ -120,7 +120,7 @@ NORETURN void overflow_exit(const char *
565 exit_cleanup(RERR_MALLOC);
568 -int set_modtime(const char *fname, time_t modtime, mode_t mode)
569 +int set_times(const char *fname, time_t modtime, time_t atime, mode_t mode)
571 #if !defined HAVE_LUTIMES || !defined HAVE_UTIMES
573 @@ -128,9 +128,13 @@ int set_modtime(const char *fname, time_
577 - rprintf(FINFO, "set modtime of %s to (%ld) %s",
578 + char mtimebuf[200];
580 + strlcpy(mtimebuf, timestring(modtime), sizeof mtimebuf);
582 + "set modtime, atime of %s to (%ld) %s, (%ld) %s\n",
583 fname, (long)modtime,
584 - asctime(localtime(&modtime)));
585 + mtimebuf, (long)atime, timestring(atime));
589 @@ -139,7 +143,7 @@ int set_modtime(const char *fname, time_
593 - t[0].tv_sec = time(NULL);
594 + t[0].tv_sec = atime;
596 t[1].tv_sec = modtime;
598 @@ -152,12 +156,12 @@ int set_modtime(const char *fname, time_
599 return utimes(fname, t);
600 #elif defined HAVE_UTIMBUF
602 - tbuf.actime = time(NULL);
603 + tbuf.actime = atime;
604 tbuf.modtime = modtime;
605 return utime(fname,&tbuf);
606 #elif defined HAVE_UTIME
611 return utime(fname,t);