1 To use this patch, run these commands for a successful build:
3 patch -p1 <patches/atimes.diff
5 ./configure (optional if already run)
8 TODO: need to fix this to handle 64-bit time_t values!
12 @@ -43,6 +43,7 @@ extern int prune_empty_dirs;
13 extern int protocol_version;
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 preserve_hard_links;
20 @@ -57,7 +58,7 @@ extern char *dest_option;
21 extern struct filter_list_struct filter_list;
23 /* These index values are for the file-list's extra-attribute array. */
24 -int uid_ndx, gid_ndx, acls_ndx, xattrs_ndx;
25 +int uid_ndx, gid_ndx, atimes_ndx, acls_ndx, xattrs_ndx;
27 /* The server makes sure that if either side only supports a pre-release
28 * version of a protocol, that both sides must speak a compatible version
29 @@ -98,6 +99,8 @@ void setup_protocol(int f_out,int f_in)
30 uid_ndx = ++file_extra_cnt;
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;
40 @@ -52,6 +52,7 @@ extern int preserve_devices;
41 extern int preserve_specials;
44 +extern int atimes_ndx;
45 extern int relative_paths;
46 extern int implied_dirs;
47 extern int file_extra_cnt;
48 @@ -158,6 +159,7 @@ void show_flist_stats(void)
49 static void list_file_entry(struct file_struct *f)
51 char permbuf[PERMSTRING_SIZE];
52 + time_t atime = atimes_ndx ? F_ATIME(f) : 0;
55 if (!F_IS_ACTIVE(f)) {
56 @@ -172,14 +174,16 @@ static void list_file_entry(struct file_
59 if (preserve_links && S_ISLNK(f->mode)) {
60 - rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
61 + rprintf(FINFO, "%s %11.0f %s %s %s -> %s\n",
62 permbuf, len, timestring(f->modtime),
63 + atimes_ndx ? timestring(atime) : "",
64 f_name(f, NULL), F_SYMLINK(f));
68 - rprintf(FINFO, "%s %11.0f %s %s\n",
69 + rprintf(FINFO, "%s %11.0f %s %s %s\n",
70 permbuf, len, timestring(f->modtime),
71 + atimes_ndx ? timestring(atime) : "",
75 @@ -347,6 +351,7 @@ int push_pathname(const char *dir, int l
76 static void send_file_entry(int f, struct file_struct *file, int ndx)
78 static time_t modtime;
79 + static time_t atime;
83 @@ -442,6 +447,13 @@ static void send_file_entry(int f, struc
84 flags |= XMIT_SAME_TIME;
86 modtime = file->modtime;
87 + if (atimes_ndx && !S_ISDIR(mode)) {
88 + time_t file_atime = F_ATIME(file);
89 + if (file_atime == atime)
90 + flags |= XMIT_SAME_ATIME;
95 #ifdef SUPPORT_HARD_LINKS
97 @@ -513,6 +525,8 @@ static void send_file_entry(int f, struc
99 if (!(flags & XMIT_SAME_MODE))
100 write_int(f, to_wire_mode(mode));
101 + if (atimes_ndx && !S_ISDIR(mode) && !(flags & XMIT_SAME_ATIME))
102 + write_varlong(f, atime, 4);
103 if (uid_ndx && !(flags & XMIT_SAME_UID)) {
104 if (protocol_version < 30)
106 @@ -599,7 +613,7 @@ static void send_file_entry(int f, struc
107 static struct file_struct *recv_file_entry(struct file_list *flist,
110 - static int64 modtime;
111 + static int64 modtime, atime;
115 @@ -735,6 +749,16 @@ static struct file_struct *recv_file_ent
117 if (!(xflags & XMIT_SAME_MODE))
118 mode = from_wire_mode(read_int(f));
119 + if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME)) {
120 + atime = read_varlong(f, 4);
121 +#if SIZEOF_TIME_T < SIZEOF_INT64
122 + if ((atime > INT_MAX || atime < INT_MIN) && !am_generator) {
124 + "Access time value of %s truncated on receiver.\n",
130 if (chmod_modes && !S_ISLNK(mode))
131 mode = tweak_mode(mode, chmod_modes);
132 @@ -863,6 +887,8 @@ static struct file_struct *recv_file_ent
134 file->flags |= gid_flags;
137 + F_ATIME(file) = (time_t)atime;
140 F_NDX(file) = flist->count + flist->ndx_start;
141 @@ -1182,6 +1208,8 @@ struct file_struct *make_file(const char
142 F_OWNER(file) = st.st_uid;
144 F_GROUP(file) = st.st_gid;
146 + F_ATIME(file) = st.st_atime;
148 if (basename != thisname)
149 file->dirname = lastdir;
152 @@ -43,6 +43,7 @@ extern int preserve_specials;
153 extern int preserve_hard_links;
154 extern int preserve_perms;
155 extern int preserve_times;
156 +extern int preserve_atimes;
157 extern int omit_dir_times;
160 @@ -563,6 +564,9 @@ void itemize(const char *fnamecmp, struc
161 && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
162 || (keep_time && cmp_time(file->modtime, sxp->st.st_mtime) != 0))
163 iflags |= ITEM_REPORT_TIME;
164 + if (preserve_atimes && !S_ISDIR(file->mode) && !S_ISLNK(file->mode)
165 + && cmp_time(F_ATIME(file), sxp->st.st_atime) != 0)
166 + iflags |= ITEM_REPORT_ATIME;
167 if (!BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS))
168 iflags |= ITEM_REPORT_PERMS;
169 if (uid_ndx && am_root && (uid_t)F_OWNER(file) != sxp->st.st_uid)
170 @@ -880,6 +884,8 @@ static int try_dests_reg(struct file_str
172 if (!hard_link_one(file, fname, cmpbuf, 1))
174 + if (preserve_atimes)
175 + set_file_attrs(fname, file, sxp, NULL, 0);
176 if (preserve_hard_links && F_IS_HLINKED(file))
177 finish_hard_link(file, fname, &sxp->st, itemizing, code, j);
178 if (itemizing && (verbose > 1 || stdout_format_has_i > 1)) {
179 @@ -1749,7 +1755,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))
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';
202 @@ -56,6 +56,7 @@ int preserve_uid = 0;
203 int preserve_gid = 0;
204 int preserve_times = 0;
205 int omit_dir_times = 0;
206 +int preserve_atimes = 0;
210 @@ -335,8 +336,9 @@ void usage(enum logcode F)
211 rprintf(F," --devices preserve device files (super-user only)\n");
212 rprintf(F," --specials preserve special files\n");
213 rprintf(F," -D same as --devices --specials\n");
214 - rprintf(F," -t, --times preserve times\n");
215 - rprintf(F," -O, --omit-dir-times omit directories when preserving times\n");
216 + rprintf(F," -t, --times preserve modify times\n");
217 + rprintf(F," -O, --omit-dir-times omit directories when preserving modify times\n");
218 + rprintf(F," -U, --atimes preserve access (last-used) times\n");
219 rprintf(F," --super receiver attempts super-user activities\n");
220 #ifdef SUPPORT_XATTRS
221 rprintf(F," --fake-super store/recover privileged attrs using xattrs\n");
222 @@ -471,6 +473,9 @@ static struct poptOption long_options[]
223 {"times", 't', POPT_ARG_VAL, &preserve_times, 1, 0, 0 },
224 {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
225 {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
226 + {"atimes", 'U', POPT_ARG_VAL, &preserve_atimes, 1, 0, 0 },
227 + {"no-atimes", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 },
228 + {"no-U", 0, POPT_ARG_VAL, &preserve_atimes, 0, 0, 0 },
229 {"omit-dir-times", 'O', POPT_ARG_VAL, &omit_dir_times, 2, 0, 0 },
230 {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 },
231 {"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 },
232 @@ -1668,6 +1673,8 @@ void server_options(char **args,int *arg
236 + if (preserve_atimes)
240 else if (preserve_executability && am_sender)
243 @@ -32,6 +32,7 @@ extern int preserve_acls;
244 extern int preserve_xattrs;
245 extern int preserve_perms;
246 extern int preserve_executability;
247 +extern int preserve_atimes;
248 extern int preserve_times;
249 extern int omit_dir_times;
251 @@ -275,6 +276,7 @@ int set_file_attrs(const char *fname, st
254 int change_uid, change_gid;
255 + time_t atime, mtime;
256 mode_t new_mode = file->mode;
259 @@ -312,18 +314,36 @@ int set_file_attrs(const char *fname, st
260 set_stat_xattr(fname, file);
263 + /* This code must be the first update in the function due to
264 + * how it uses the "updated" variable. */
265 if (!preserve_times || (S_ISDIR(sxp->st.st_mode) && omit_dir_times))
266 flags |= ATTRS_SKIP_MTIME;
267 + if (!preserve_atimes || S_ISDIR(sxp->st.st_mode))
268 + flags |= ATTRS_SKIP_ATIME;
269 if (!(flags & ATTRS_SKIP_MTIME)
270 && cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
271 - int ret = set_modtime(fname, file->modtime, sxp->st.st_mode);
272 + mtime = file->modtime;
275 + mtime = sxp->st.st_mtime;
276 + if (!(flags & ATTRS_SKIP_ATIME)) {
277 + time_t file_atime = F_ATIME(file);
278 + if (cmp_time(sxp->st.st_atime, file_atime) != 0) {
279 + atime = file_atime;
282 + atime = sxp->st.st_atime;
284 + atime = sxp->st.st_atime;
286 + int ret = set_times(fname, mtime, atime, sxp->st.st_mode);
288 rsyserr(FERROR, errno, "failed to set times on %s",
292 - if (ret == 0) /* ret == 1 if symlink could not be set */
294 + if (ret > 0) /* ret == 1 if symlink could not be set */
298 change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
302 #define XMIT_RDEV_MINOR_8_pre30 (1<<11) /* protocols 28 - 29 */
303 #define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - NOW */
304 #define XMIT_HLINK_FIRST (1<<12) /* protocols 30 - NOW */
305 +#define XMIT_SAME_ATIME (1<<13) /* protocols ?? - NOW */
307 /* These flags are used in the live flist data. */
311 #define ATTRS_REPORT (1<<0)
312 #define ATTRS_SKIP_MTIME (1<<1)
313 +#define ATTRS_SKIP_ATIME (1<<2)
316 #define NORMAL_FLUSH 0
317 @@ -582,6 +584,7 @@ struct file_struct {
318 extern int file_extra_cnt;
321 +extern int atimes_ndx;
323 extern int xattrs_ndx;
325 @@ -616,6 +619,7 @@ extern int xattrs_ndx;
326 /* When the associated option is on, all entries will have these present: */
327 #define F_OWNER(f) REQ_EXTRA(f, uid_ndx)->unum
328 #define F_GROUP(f) REQ_EXTRA(f, gid_ndx)->unum
329 +#define F_ATIME(f) REQ_EXTRA(f, atimes_ndx)->unum
330 #define F_ACL(f) REQ_EXTRA(f, acls_ndx)->num
331 #define F_XATTR(f) REQ_EXTRA(f, xattrs_ndx)->num
332 #define F_NDX(f) REQ_EXTRA(f, ic_ndx)->num
335 @@ -336,8 +336,9 @@ to the detailed description below for a
336 --devices preserve device files (super-user only)
337 --specials preserve special files
338 -D same as --devices --specials
339 - -t, --times preserve times
340 - -O, --omit-dir-times omit directories when preserving times
341 + -t, --times preserve modify times
342 + -O, --omit-dir-times omit directories when preserving mod-times
343 + -U, --atimes preserve access (use) times
344 --super receiver attempts super-user activities
345 --fake-super store/recover privileged attrs using xattrs
346 -S, --sparse handle sparse files efficiently
347 @@ -916,6 +917,12 @@ it is preserving modification times (see
348 the directories on the receiving side, it is a good idea to use bf(-O).
349 This option is inferred if you use bf(--backup) without bf(--backup-dir).
351 +dit(bf(-U, --atimes)) This tells rsync to set the access (use) times of the
352 +destination files to the same value as the source files. Note that the
353 +reading of the source file may update the atime of the source files, so
354 +repeated rsync runs with --atimes may be needed if you want to force the
355 +access-time values to be 100% identical on the two systems.
357 dit(bf(--super)) This tells the receiving side to attempt super-user
358 activities even if the receiving rsync wasn't run by the super-user. These
359 activities include: preserving users via the bf(--owner) option, preserving
360 @@ -1530,7 +1537,7 @@ quote(itemization(
361 by the file transfer.
362 it() A bf(t) means the modification time is different and is being updated
363 to the sender's value (requires bf(--times)). An alternate value of bf(T)
364 - means that the time will be set to the transfer time, which happens
365 + means that the modify time will be set to the transfer time, which happens
366 anytime a symlink is transferred, or when a file or device is transferred
368 it() A bf(p) means the permissions are different and are being updated to
369 @@ -1539,8 +1546,10 @@ quote(itemization(
370 sender's value (requires bf(--owner) and super-user privileges).
371 it() A bf(g) means the group is different and is being updated to the
372 sender's value (requires bf(--group) and the authority to set the group).
373 - it() The bf(u) slot is reserved for reporting update (access) time changes
374 - (a feature that is not yet released).
375 + it() A bf(u) means the access (use) time is different and is being updated to
376 + the sender's value (requires bf(--atimes)). An alternate value of bf(U)
377 + means that the access time will be set to the transfer time, which happens
378 + when a symlink or directory is updated.
379 it() The bf(a) means that the ACL information changed.
380 it() The bf(x) slot is reserved for reporting extended attribute changes
381 (a feature that is not yet released).
384 @@ -43,6 +43,7 @@ extern int do_progress;
387 extern int write_batch;
388 +extern unsigned int file_struct_len;
389 extern struct stats stats;
390 extern struct file_list *cur_flist, *first_flist;
392 --- old/testsuite/atimes.test
393 +++ new/testsuite/atimes.test
397 +# Test rsync copying atimes
399 +. "$suitedir/rsync.fns"
405 +touch "$fromdir/foo"
406 +touch -a -t 200102031717.42 "$fromdir/foo"
410 +checkit "$RSYNC -rtUgvvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
412 +# The script would have aborted on error, so getting here means we've won.
414 --- old/testsuite/rsync.fns
415 +++ new/testsuite/rsync.fns
416 @@ -186,6 +186,10 @@ checkit() {
417 # We can just write everything to stdout/stderr, because the
418 # wrapper hides it unless there is a problem.
420 + if test x$TLS_ARGS = x--atime; then
421 + ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
424 echo "Running: \"$1\""
427 @@ -193,10 +197,13 @@ checkit() {
431 + if test x$TLS_ARGS != x--atime; then
432 + ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
436 echo "check how the directory listings compare with diff:"
438 - ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
439 ( cd "$3" && rsync_ls_lR . ) > "$tmpdir/ls-to"
440 diff $diffopt "$tmpdir/ls-from" "$tmpdir/ls-to" || failed=YES
444 @@ -98,6 +98,8 @@ static int stat_xattr(const char *fname,
448 +static int display_atime = 0;
450 static void failed(char const *what, char const *where)
452 fprintf(stderr, PROGRAM ": %s %s: %s\n",
453 @@ -105,12 +107,29 @@ static void failed(char const *what, cha
457 +static void storetime(char *dest, time_t t, size_t destsize)
460 + struct tm *mt = gmtime(&t);
462 + snprintf(dest, destsize,
463 + "%04d-%02d-%02d %02d:%02d:%02d ",
464 + (int)mt->tm_year + 1900,
465 + (int)mt->tm_mon + 1,
471 + strlcpy(dest, " ", destsize);
474 static void list_file(const char *fname)
477 char permbuf[PERMSTRING_SIZE];
484 if (do_lstat(fname, &buf) < 0)
485 @@ -147,19 +166,8 @@ static void list_file(const char *fname)
487 permstring(permbuf, buf.st_mode);
489 - if (buf.st_mtime) {
490 - mt = gmtime(&buf.st_mtime);
492 - snprintf(datebuf, sizeof datebuf,
493 - "%04d-%02d-%02d %02d:%02d:%02d",
494 - (int)mt->tm_year + 1900,
495 - (int)mt->tm_mon + 1,
501 - strlcpy(datebuf, " ", sizeof datebuf);
502 + storetime(mtimebuf, buf.st_mtime, sizeof mtimebuf);
503 + storetime(atimebuf, buf.st_atime, sizeof atimebuf);
505 /* TODO: Perhaps escape special characters in fname? */
507 @@ -170,13 +178,15 @@ static void list_file(const char *fname)
508 (long)minor(buf.st_rdev));
509 } else /* NB: use double for size since it might not fit in a long. */
510 printf("%12.0f", (double)buf.st_size);
511 - printf(" %6ld.%-6ld %6ld %s %s%s\n",
512 + printf(" %6ld.%-6ld %6ld %s%s%s%s\n",
513 (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
514 - datebuf, fname, linkbuf);
515 + mtimebuf, display_atime && !S_ISDIR(buf.st_mode) ? atimebuf : "",
519 static struct poptOption long_options[] = {
520 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
521 + {"atime", 'u', POPT_ARG_NONE, &display_atime, 0, 0, 0},
522 #ifdef SUPPORT_XATTRS
523 {"fake-super", 'f', POPT_ARG_VAL, &am_root, -1, 0, 0 },
525 @@ -190,6 +200,7 @@ static void tls_usage(int ret)
526 fprintf(F,"usage: " PROGRAM " [OPTIONS] FILE ...\n");
527 fprintf(F,"Trivial file listing program for portably checking rsync\n");
528 fprintf(F,"\nOptions:\n");
529 + rprintf(F," -U, --atimes display access (last-used) times\n");
530 #ifdef SUPPORT_XATTRS
531 fprintf(F," -f, --fake-super display attributes including fake-super xattrs\n");
535 @@ -121,7 +121,7 @@ NORETURN void overflow_exit(const char *
536 exit_cleanup(RERR_MALLOC);
539 -int set_modtime(const char *fname, time_t modtime, mode_t mode)
540 +int set_times(const char *fname, time_t modtime, time_t atime, mode_t mode)
542 #if !defined HAVE_LUTIMES || !defined HAVE_UTIMES
544 @@ -129,9 +129,13 @@ int set_modtime(const char *fname, time_
548 - rprintf(FINFO, "set modtime of %s to (%ld) %s",
549 + char mtimebuf[200];
551 + strlcpy(mtimebuf, timestring(modtime), sizeof mtimebuf);
553 + "set modtime, atime of %s to (%ld) %s, (%ld) %s\n",
554 fname, (long)modtime,
555 - asctime(localtime(&modtime)));
556 + mtimebuf, (long)atime, timestring(atime));
560 @@ -140,7 +144,7 @@ int set_modtime(const char *fname, time_
564 - t[0].tv_sec = time(NULL);
565 + t[0].tv_sec = atime;
567 t[1].tv_sec = modtime;
569 @@ -153,12 +157,12 @@ int set_modtime(const char *fname, time_
570 return utimes(fname, t);
571 #elif defined HAVE_UTIMBUF
573 - tbuf.actime = time(NULL);
574 + tbuf.actime = atime;
575 tbuf.modtime = modtime;
576 return utime(fname,&tbuf);
577 #elif defined HAVE_UTIME
582 return utime(fname,t);