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 @@ -46,6 +46,7 @@ extern int preserve_devices;
12 extern int preserve_specials;
13 extern int preserve_uid;
14 extern int preserve_gid;
15 +extern int preserve_atimes;
16 extern int relative_paths;
17 extern int implied_dirs;
18 extern int ignore_perishable;
19 @@ -127,6 +128,7 @@ void show_flist_stats(void)
20 static void list_file_entry(struct file_struct *f)
22 char permbuf[PERMSTRING_SIZE];
23 + time_t atime = preserve_atimes ? IVAL(ATIME_PTR(f), 0) : 0;
26 /* this can happen if duplicate names were removed */
27 @@ -137,16 +139,18 @@ static void list_file_entry(struct file_
30 if (preserve_links && S_ISLNK(f->mode)) {
31 - rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
32 + rprintf(FINFO, "%s %11.0f %s %s %s -> %s\n",
34 (double)f->length, timestring(f->modtime),
35 + preserve_atimes ? timestring(atime) : "",
36 f_name(f, NULL), f->u.link);
40 - rprintf(FINFO, "%s %11.0f %s %s\n",
41 + rprintf(FINFO, "%s %11.0f %s %s %s\n",
43 (double)f->length, timestring(f->modtime),
44 + preserve_atimes ? timestring(atime) : "",
48 @@ -304,6 +308,7 @@ static void send_file_entry(struct file_
51 static time_t modtime;
52 + static time_t atime;
56 @@ -319,7 +324,7 @@ static void send_file_entry(struct file_
60 - modtime = 0, mode = 0;
61 + modtime = 0, atime = 0, mode = 0;
62 dev = 0, rdev = MAKEDEV(0, 0);
65 @@ -365,6 +370,13 @@ static void send_file_entry(struct file_
66 flags |= XMIT_SAME_TIME;
68 modtime = file->modtime;
69 + if (preserve_atimes && !S_ISDIR(mode)) {
70 + time_t file_atime = IVAL(ATIME_PTR(file), 0);
71 + if (file_atime == atime)
72 + flags |= XMIT_SAME_ATIME;
77 #ifdef SUPPORT_HARD_LINKS
78 if (file->link_u.idev) {
79 @@ -418,6 +430,8 @@ static void send_file_entry(struct file_
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)) {
88 @@ -484,6 +498,7 @@ static struct file_struct *receive_file_
89 unsigned short flags, int f)
91 static time_t modtime;
92 + static time_t atime;
96 @@ -497,12 +512,13 @@ static struct file_struct *receive_file_
97 char thisname[MAXPATHLEN];
98 unsigned int l1 = 0, l2 = 0;
99 int alloc_len, basename_len, dirname_len, linkname_len, sum_len;
102 char *basename, *dirname, *bp;
103 struct file_struct *file;
106 - modtime = 0, mode = 0;
107 + modtime = 0, atime = 0, mode = 0;
108 dev = 0, rdev = MAKEDEV(0, 0);
111 @@ -558,6 +574,8 @@ static struct file_struct *receive_file_
112 modtime = (time_t)read_int(f);
113 if (!(flags & XMIT_SAME_MODE))
114 mode = from_wire_mode(read_int(f));
115 + if (preserve_atimes && !S_ISDIR(mode) && !(flags & XMIT_SAME_ATIME))
116 + atime = (time_t)read_int(f);
118 if (chmod_modes && !S_ISLNK(mode))
119 mode = tweak_mode(mode, chmod_modes);
120 @@ -600,19 +618,23 @@ static struct file_struct *receive_file_
122 sum_len = always_checksum && S_ISREG(mode) ? MD4_SUM_LENGTH : 0;
124 + atime_len = preserve_atimes ? 4 : 0;
126 alloc_len = file_struct_len + dirname_len + basename_len
127 - + linkname_len + sum_len;
128 + + atime_len + linkname_len + sum_len;
129 bp = pool_alloc(flist->file_pool, alloc_len, "receive_file_entry");
131 file = (struct file_struct *)bp;
132 memset(bp, 0, file_struct_len);
133 - bp += file_struct_len;
134 + bp += file_struct_len + atime_len;
136 file->modtime = modtime;
137 file->length = file_length;
141 + if (preserve_atimes)
142 + SIVAL(ATIME_PTR(file), 0, atime);
145 file->dirname = lastdir = bp;
146 @@ -731,6 +753,7 @@ struct file_struct *make_file(char *fnam
147 char thisname[MAXPATHLEN];
148 char linkname[MAXPATHLEN];
149 int alloc_len, basename_len, dirname_len, linkname_len, sum_len;
151 char *basename, *dirname, *bp;
153 if (!flist || !flist->count) /* Ignore lastdir when invalid. */
154 @@ -854,8 +877,10 @@ struct file_struct *make_file(char *fnam
155 sum_len = always_checksum && am_sender && S_ISREG(st.st_mode)
156 ? MD4_SUM_LENGTH : 0;
158 + atime_len = preserve_atimes ? 4 : 0;
160 alloc_len = file_struct_len + dirname_len + basename_len
161 - + linkname_len + sum_len;
162 + + atime_len + linkname_len + sum_len;
164 bp = pool_alloc(flist->file_pool, alloc_len, "make_file");
166 @@ -865,7 +890,7 @@ struct file_struct *make_file(char *fnam
168 file = (struct file_struct *)bp;
169 memset(bp, 0, file_struct_len);
170 - bp += file_struct_len;
171 + bp += file_struct_len + atime_len;
174 file->modtime = st.st_mtime;
175 @@ -873,6 +898,8 @@ struct file_struct *make_file(char *fnam
176 file->mode = st.st_mode;
177 file->uid = st.st_uid;
178 file->gid = st.st_gid;
179 + if (preserve_atimes)
180 + SIVAL(ATIME_PTR(file), 0, st.st_atime);
182 #ifdef SUPPORT_HARD_LINKS
183 if (flist && flist->hlink_pool) {
184 @@ -1594,8 +1621,9 @@ static void clean_flist(struct file_list
186 /* Make sure we don't lose track of a user-specified
188 - flist->files[keep]->flags |= flist->files[drop]->flags
189 - & (FLAG_TOP_DIR|FLAG_DEL_HERE);
190 + flist->files[keep]->flags
191 + |= flist->files[drop]->flags
192 + & (FLAG_TOP_DIR|FLAG_DEL_HERE);
194 clear_file(flist->files[drop], flist);
198 @@ -43,6 +43,7 @@ extern int preserve_perms;
199 extern int preserve_uid;
200 extern int preserve_gid;
201 extern int preserve_times;
202 +extern int preserve_atimes;
203 extern int omit_dir_times;
204 extern int delete_mode;
205 extern int delete_before;
206 @@ -90,6 +91,7 @@ extern dev_t filesystem_dev;
207 extern char *backup_dir;
208 extern char *backup_suffix;
209 extern int backup_suffix_len;
210 +extern unsigned int file_struct_len;
211 extern struct file_list *the_file_list;
212 extern struct filter_list_struct server_filter_list;
214 @@ -409,6 +411,9 @@ void itemize(struct file_struct *file, i
215 && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
216 || (keep_time && cmp_time(file->modtime, st->st_mtime) != 0))
217 iflags |= ITEM_REPORT_TIME;
218 + if (preserve_atimes && !S_ISDIR(file->mode) && !S_ISLNK(file->mode)
219 + && cmp_time(IVAL(ATIME_PTR(file), 0), st->st_atime) != 0)
220 + iflags |= ITEM_REPORT_ATIME;
221 if ((file->mode & CHMOD_BITS) != (st->st_mode & CHMOD_BITS))
222 iflags |= ITEM_REPORT_PERMS;
223 if (preserve_uid && am_root && file->uid != st->st_uid)
224 @@ -720,6 +725,8 @@ static int try_dests_reg(struct file_str
225 if (hard_link_one(file, ndx, fname, 0, stp,
226 cmpbuf, 1, i, code) < 0)
228 + if (preserve_atimes)
229 + set_file_attrs(fname, file, stp, 0);
230 if (preserve_hard_links && file->link_u.links) {
232 file->link_u.links->link_dest_used = j + 1;
235 @@ -37,6 +37,7 @@ extern int msg_fd_out;
236 extern int allow_8bit_chars;
237 extern int protocol_version;
238 extern int preserve_times;
239 +extern int preserve_atimes;
240 extern int stdout_format_has_i;
241 extern int stdout_format_has_o_or_i;
242 extern int logfile_format_has_i;
243 @@ -611,7 +612,8 @@ static void log_formatted(enum logcode c
244 c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
245 c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
246 c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
248 + c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.'
249 + : S_ISLNK(file->mode) ? 'U' : 'u';
252 if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) {
255 @@ -55,6 +55,7 @@ int preserve_uid = 0;
256 int preserve_gid = 0;
257 int preserve_times = 0;
258 int omit_dir_times = 0;
259 +int preserve_atimes = 0;
263 @@ -311,8 +312,9 @@ void usage(enum logcode F)
264 rprintf(F," --devices preserve device files (super-user only)\n");
265 rprintf(F," --specials preserve special files\n");
266 rprintf(F," -D same as --devices --specials\n");
267 - rprintf(F," -t, --times preserve times\n");
268 - rprintf(F," -O, --omit-dir-times omit directories when preserving times\n");
269 + rprintf(F," -t, --times preserve modify times\n");
270 + rprintf(F," -O, --omit-dir-times omit directories when preserving modify times\n");
271 + rprintf(F," -U, --atimes preserve access (use) times\n");
272 rprintf(F," --super receiver attempts super-user activities\n");
273 rprintf(F," -S, --sparse handle sparse files efficiently\n");
274 rprintf(F," -n, --dry-run show what would have been transferred\n");
275 @@ -428,6 +430,9 @@ static struct poptOption long_options[]
276 {"times", 't', POPT_ARG_VAL, &preserve_times, 1, 0, 0 },
277 {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
278 {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
279 + {"atimes", 'U', POPT_ARG_VAL, &preserve_atimes, 1, 0, 0 },
280 + {"no-atimes", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 },
281 + {"no-k", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 },
282 {"omit-dir-times", 'O', POPT_ARG_VAL, &omit_dir_times, 2, 0, 0 },
283 {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
284 {"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 },
285 @@ -1538,6 +1543,8 @@ void server_options(char **args,int *arg
289 + if (preserve_atimes)
293 else if (preserve_executability && am_sender)
296 @@ -34,6 +34,7 @@ extern int verbose;
298 extern int preserve_perms;
299 extern int preserve_executability;
300 +extern int preserve_atimes;
301 extern int preserve_times;
302 extern int omit_dir_times;
304 @@ -49,6 +50,7 @@ extern int keep_dirlinks;
305 extern int make_backups;
306 extern mode_t orig_umask;
307 extern struct stats stats;
308 +extern unsigned int file_struct_len;
309 extern struct chmod_mode_struct *daemon_chmod_modes;
311 #if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
312 @@ -129,6 +131,7 @@ int set_file_attrs(char *fname, struct f
315 int change_uid, change_gid;
316 + time_t atime, mtime;
317 mode_t new_mode = file->mode;
320 @@ -148,18 +151,36 @@ int set_file_attrs(char *fname, struct f
324 + /* This code must be the first update in the function due to
325 + * how it uses the "updated" variable. */
326 if (!preserve_times || (S_ISDIR(st->st_mode) && omit_dir_times))
327 flags |= ATTRS_SKIP_MTIME;
328 + if (!preserve_atimes || S_ISDIR(st->st_mode))
329 + flags |= ATTRS_SKIP_ATIME;
330 if (!(flags & ATTRS_SKIP_MTIME)
331 && cmp_time(st->st_mtime, file->modtime) != 0) {
332 - int ret = set_modtime(fname, file->modtime, st->st_mode);
333 + mtime = file->modtime;
336 + mtime = st->st_mtime;
337 + if (!(flags & ATTRS_SKIP_ATIME)) {
338 + time_t file_atime = IVAL(ATIME_PTR(file), 0);
339 + if (cmp_time(st->st_atime, file_atime) != 0) {
340 + atime = file_atime;
343 + atime = st->st_atime;
345 + atime = st->st_atime;
347 + int ret = set_times(fname, mtime, atime, st->st_mode);
349 rsyserr(FERROR, errno, "failed to set times on %s",
353 - if (ret == 0) /* ret == 1 if symlink could not be set */
355 + if (ret > 0) /* ret == 1 if symlink could not be set */
359 change_uid = am_root && preserve_uid && st->st_uid != file->uid;
363 #define XMIT_HAS_IDEV_DATA (1<<9)
364 #define XMIT_SAME_DEV (1<<10)
365 #define XMIT_RDEV_MINOR_IS_SMALL (1<<11)
366 +#define XMIT_SAME_ATIME (1<<12)
368 /* These flags are used in the live flist data. */
372 #define ATTRS_REPORT (1<<0)
373 #define ATTRS_SKIP_MTIME (1<<1)
374 +#define ATTRS_SKIP_ATIME (1<<2)
377 #define NORMAL_FLUSH 0
378 @@ -534,6 +536,8 @@ struct file_struct {
379 uchar flags; /* this item MUST remain last */
382 +#define ATIME_PTR(f) (((uchar*)(f))+file_struct_len)
385 * Start the flist array at FLIST_START entries and grow it
386 * by doubling until FLIST_LINEAR then grow by FLIST_LINEAR
389 @@ -328,8 +328,9 @@ to the detailed description below for a
390 --devices preserve device files (super-user only)
391 --specials preserve special files
392 -D same as --devices --specials
393 - -t, --times preserve times
394 - -O, --omit-dir-times omit directories when preserving times
395 + -t, --times preserve modify times
396 + -O, --omit-dir-times omit directories when preserving mod-times
397 + -U, --atimes preserve access (use) times
398 --super receiver attempts super-user activities
399 -S, --sparse handle sparse files efficiently
400 -n, --dry-run show what would have been transferred
401 @@ -869,6 +870,12 @@ it is preserving modification times (see
402 the directories on the receiving side, it is a good idea to use bf(-O).
403 This option is inferred if you use bf(--backup) without bf(--backup-dir).
405 +dit(bf(-U, --atimes)) This tells rsync to set the access (use) times of the
406 +destination files to the same value as the source files. Note that the
407 +reading of the source file may update the atime of the source files, so
408 +repeated rsync runs with --atimes may be needed if you want to force the
409 +access-time values to be 100% identical on the two systems.
411 dit(bf(--super)) This tells the receiving side to attempt super-user
412 activities even if the receiving rsync wasn't run by the super-user. These
413 activities include: preserving users via the bf(--owner) option, preserving
414 @@ -1396,7 +1403,7 @@ with older versions of rsync, but that a
417 The "%i" escape has a cryptic output that is 9 letters long. The general
418 -format is like the string bf(YXcstpogz), where bf(Y) is replaced by the
419 +format is like the string bf(YXcstpogu), where bf(Y) is replaced by the
420 type of update being done, bf(X) is replaced by the file-type, and the
421 other letters represent attributes that may be output if they are being
423 @@ -1436,7 +1443,7 @@ quote(itemization(
424 by the file transfer.
425 it() A bf(t) means the modification time is different and is being updated
426 to the sender's value (requires bf(--times)). An alternate value of bf(T)
427 - means that the time will be set to the transfer time, which happens
428 + means that the modify time will be set to the transfer time, which happens
429 anytime a symlink is transferred, or when a file or device is transferred
431 it() A bf(p) means the permissions are different and are being updated to
432 @@ -1445,7 +1452,10 @@ quote(itemization(
433 sender's value (requires bf(--owner) and super-user privileges).
434 it() A bf(g) means the group is different and is being updated to the
435 sender's value (requires bf(--group) and the authority to set the group).
436 - it() The bf(z) slot is reserved for future use.
437 + it() A bf(u) means the access (use) time is different and is being updated to
438 + the sender's value (requires bf(--atimes)). An alternate value of bf(U)
439 + means that the access time will be set to the transfer time, which happens
440 + when a symlink or directory is updated.
443 One other output is possible: when deleting files, the "%i" will output
446 @@ -41,6 +41,7 @@ extern int do_progress;
449 extern int write_batch;
450 +extern unsigned int file_struct_len;
451 extern struct stats stats;
452 extern struct file_list *the_file_list;
453 extern char *stdout_format;
454 --- old/testsuite/atimes.test
455 +++ new/testsuite/atimes.test
459 +# Test rsync copying atimes
461 +. "$suitedir/rsync.fns"
467 +touch "$fromdir/foo"
468 +touch -a -t 200102031717.42 "$fromdir/foo"
472 +checkit "$RSYNC -rtUgvvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
474 +# The script would have aborted on error, so getting here means we've won.
476 --- old/testsuite/rsync.fns
477 +++ new/testsuite/rsync.fns
478 @@ -66,7 +66,7 @@ printmsg() {
482 - find "$@" -print | sort | sed 's/ /\\ /g' | xargs "$TOOLDIR/tls"
483 + find "$@" -print | sort | sed 's/ /\\ /g' | xargs "$TOOLDIR/tls" $TLS_ARGS
487 @@ -184,6 +184,10 @@ checkit() {
488 # We can just write everything to stdout/stderr, because the
489 # wrapper hides it unless there is a problem.
491 + if test x$TLS_ARGS = x--atime; then
492 + ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
495 echo "Running: \"$1\""
498 @@ -191,10 +195,13 @@ checkit() {
502 + if test x$TLS_ARGS != x--atime; then
503 + ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
507 echo "check how the directory listings compare with diff:"
509 - ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
510 ( cd "$3" && rsync_ls_lR . ) > "$tmpdir/ls-to"
511 diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to" || failed=YES
521 #define PROGRAM "tls"
523 @@ -43,6 +44,8 @@ int read_only = 1;
525 int preserve_perms = 0;
527 +static int display_atime = 0;
529 static void failed(char const *what, char const *where)
531 fprintf(stderr, PROGRAM ": %s %s: %s\n",
532 @@ -50,12 +53,29 @@ static void failed(char const *what, cha
536 +static void storetime(char *dest, time_t t, size_t destsize)
539 + struct tm *mt = gmtime(&t);
541 + snprintf(dest, destsize,
542 + "%04d-%02d-%02d %02d:%02d:%02d ",
543 + (int)mt->tm_year + 1900,
544 + (int)mt->tm_mon + 1,
550 + strlcpy(dest, " ", destsize);
553 static void list_file(const char *fname)
556 char permbuf[PERMSTRING_SIZE];
563 if (do_lstat(fname, &buf) < 0)
564 @@ -88,19 +108,8 @@ static void list_file(const char *fname)
566 permstring(permbuf, buf.st_mode);
568 - if (buf.st_mtime) {
569 - mt = gmtime(&buf.st_mtime);
571 - snprintf(datebuf, sizeof datebuf,
572 - "%04d-%02d-%02d %02d:%02d:%02d",
573 - (int)mt->tm_year + 1900,
574 - (int)mt->tm_mon + 1,
580 - strlcpy(datebuf, " ", sizeof datebuf);
581 + storetime(mtimebuf, buf.st_mtime, sizeof mtimebuf);
582 + storetime(atimebuf, buf.st_atime, sizeof atimebuf);
584 /* TODO: Perhaps escape special characters in fname? */
586 @@ -111,23 +120,55 @@ static void list_file(const char *fname)
587 (long)minor(buf.st_rdev));
588 } else /* NB: use double for size since it might not fit in a long. */
589 printf("%12.0f", (double)buf.st_size);
590 - printf(" %6ld.%-6ld %6ld %s %s%s\n",
591 + printf(" %6ld.%-6ld %6ld %s%s%s%s\n",
592 (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
593 - datebuf, fname, linkbuf);
594 + mtimebuf, display_atime && !S_ISDIR(buf.st_mode) ? atimebuf : "",
598 +static struct poptOption long_options[] = {
599 + /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
600 + {"atime", 'u', POPT_ARG_NONE, &display_atime, 0, 0, 0},
601 + {"help", 'h', POPT_ARG_NONE, 0, 'h', 0, 0},
605 +static void tls_usage(int ret)
607 + fprintf(stderr, "usage: " PROGRAM " [--atime | -u] DIR ...\n"
608 + "Trivial file listing program for portably checking rsync\n");
613 main(int argc, char *argv[])
616 - fprintf(stderr, "usage: " PROGRAM " DIR ...\n"
617 - "Trivial file listing program for portably checking rsync\n");
621 + const char **extra_args;
624 - for (argv++; *argv; argv++) {
626 + pc = poptGetContext(PROGRAM, argc, (const char **)argv,
628 + while ((opt = poptGetNextOpt(pc)) != -1) {
635 + poptBadOption(pc, POPT_BADOPTION_NOALIAS),
636 + poptStrerror(opt));
641 + extra_args = poptGetArgs(pc);
642 + if (*extra_args == NULL)
645 + for (; *extra_args; extra_args++)
646 + list_file(*extra_args);
647 + poptFreeContext(pc);
653 @@ -121,7 +121,7 @@ NORETURN void overflow_exit(const char *
654 exit_cleanup(RERR_MALLOC);
657 -int set_modtime(const char *fname, time_t modtime, mode_t mode)
658 +int set_times(const char *fname, time_t modtime, time_t atime, mode_t mode)
660 #if !defined HAVE_LUTIMES || !defined HAVE_UTIMES
662 @@ -129,9 +129,13 @@ int set_modtime(const char *fname, time_
666 - rprintf(FINFO, "set modtime of %s to (%ld) %s",
667 + char mtimebuf[200];
669 + strlcpy(mtimebuf, timestring(modtime), sizeof mtimebuf);
671 + "set modtime, atime of %s to (%ld) %s, (%ld) %s\n",
672 fname, (long)modtime,
673 - asctime(localtime(&modtime)));
674 + mtimebuf, (long)atime, timestring(atime));
678 @@ -140,7 +144,7 @@ int set_modtime(const char *fname, time_
682 - t[0].tv_sec = time(NULL);
683 + t[0].tv_sec = atime;
685 t[1].tv_sec = modtime;
687 @@ -153,12 +157,12 @@ int set_modtime(const char *fname, time_
688 return utimes(fname, t);
689 #elif defined HAVE_UTIMBUF
691 - tbuf.actime = time(NULL);
692 + tbuf.actime = atime;
693 tbuf.modtime = modtime;
694 return utime(fname,&tbuf);
695 #elif defined HAVE_UTIME
700 return utime(fname,t);