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 @@ -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;
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;
29 @@ -122,6 +123,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 @@ -53,6 +53,7 @@ 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 @@ -334,6 +335,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)
51 static time_t modtime;
52 + static time_t atime;
56 @@ -438,6 +440,13 @@ static void send_file_entry(int f, struc
57 xflags |= XMIT_SAME_TIME;
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;
68 #ifdef SUPPORT_HARD_LINKS
70 @@ -511,6 +520,8 @@ static void send_file_entry(int f, struc
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)
79 @@ -597,7 +608,7 @@ static void send_file_entry(int f, struc
80 static struct file_struct *recv_file_entry(struct file_list *flist,
83 - static int64 modtime;
84 + static int64 modtime, atime;
88 @@ -728,6 +739,16 @@ static struct file_struct *recv_file_ent
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) {
97 + "Access time value of %s truncated on receiver.\n",
103 if (chmod_modes && !S_ISLNK(mode))
104 mode = tweak_mode(mode, chmod_modes);
105 @@ -856,6 +877,8 @@ static struct file_struct *recv_file_ent
107 file->flags |= gid_flags;
110 + F_ATIME(file) = (time_t)atime;
113 F_NDX(file) = flist->used + flist->ndx_start;
114 @@ -1182,6 +1205,8 @@ struct file_struct *make_file(const char
115 F_OWNER(file) = st.st_uid;
117 F_GROUP(file) = st.st_gid;
119 + F_ATIME(file) = st.st_atime;
121 if (basename != thisname)
122 file->dirname = lastdir;
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;
132 extern int delete_mode;
133 @@ -562,6 +563,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)) {
143 @@ -884,6 +888,8 @@ static int try_dests_reg(struct file_str
145 if (!hard_link_one(file, fname, cmpbuf, 1))
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 @@ -1080,6 +1086,7 @@ static int try_dests_non(struct file_str
153 static void list_file_entry(struct file_struct *f)
155 char permbuf[PERMSTRING_SIZE];
156 + time_t atime = atimes_ndx ? F_ATIME(f) : 0;
159 if (!F_IS_ACTIVE(f)) {
160 @@ -1094,14 +1101,16 @@ static void list_file_entry(struct file_
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));
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) : "",
179 @@ -1851,7 +1860,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 @@ -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;
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
232 + if (preserve_atimes)
236 else if (preserve_executability && am_sender)
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;
246 extern int am_server;
247 @@ -343,6 +344,7 @@ int set_file_attrs(const char *fname, st
250 int change_uid, change_gid;
251 + time_t atime, mtime;
252 mode_t new_mode = file->mode;
255 @@ -383,18 +385,36 @@ int set_file_attrs(const char *fname, st
256 set_stat_xattr(fname, file);
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;
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;
278 + atime = sxp->st.st_atime;
280 + atime = sxp->st.st_atime;
282 + int ret = set_times(fname, mtime, atime, sxp->st.st_mode);
284 rsyserr(FERROR, errno, "failed to set times on %s",
288 - if (ret == 0) /* ret == 1 if symlink could not be set */
290 + if (ret > 0) /* ret == 1 if symlink could not be set */
294 change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
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 */
303 /* These flags are used in the live flist data. */
307 #define ATTRS_REPORT (1<<0)
308 #define ATTRS_SKIP_MTIME (1<<1)
309 +#define ATTRS_SKIP_ATIME (1<<2)
312 #define NORMAL_FLUSH 0
313 @@ -586,6 +588,7 @@ extern int file_extra_cnt;
314 extern int inc_recurse;
317 +extern int atimes_ndx;
319 extern int xattrs_ndx;
321 @@ -623,6 +626,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, ic_ndx)->num
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 @@ -965,6 +966,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).
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.
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 @@ -1646,8 +1653,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).
367 @@ -43,6 +43,7 @@ extern int do_progress;
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;
375 --- old/testsuite/atimes.test
376 +++ new/testsuite/atimes.test
380 +# Test rsync copying atimes
382 +. "$suitedir/rsync.fns"
388 +touch "$fromdir/foo"
389 +touch -a -t 200102031717.42 "$fromdir/foo"
393 +checkit "$RSYNC -rtUgvvv \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
395 +# The script would have aborted on error, so getting here means we've won.
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.
403 + if test x$TLS_ARGS = x--atime; then
404 + ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
407 echo "Running: \"$1\""
410 @@ -194,10 +198,13 @@ checkit() {
414 + if test x$TLS_ARGS != x--atime; then
415 + ( cd "$2" && rsync_ls_lR . ) > "$tmpdir/ls-from"
419 echo "check how the directory listings compare with diff:"
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
427 @@ -104,6 +104,8 @@ static int stat_xattr(const char *fname,
431 +static int display_atime = 0;
433 static void failed(char const *what, char const *where)
435 fprintf(stderr, PROGRAM ": %s %s: %s\n",
436 @@ -111,12 +113,29 @@ static void failed(char const *what, cha
440 +static void storetime(char *dest, time_t t, size_t destsize)
443 + struct tm *mt = gmtime(&t);
445 + snprintf(dest, destsize,
446 + "%04d-%02d-%02d %02d:%02d:%02d ",
447 + (int)mt->tm_year + 1900,
448 + (int)mt->tm_mon + 1,
454 + strlcpy(dest, " ", destsize);
457 static void list_file(const char *fname)
460 char permbuf[PERMSTRING_SIZE];
467 if (do_lstat(fname, &buf) < 0)
468 @@ -153,19 +172,8 @@ static void list_file(const char *fname)
470 permstring(permbuf, buf.st_mode);
472 - if (buf.st_mtime) {
473 - mt = gmtime(&buf.st_mtime);
475 - snprintf(datebuf, sizeof datebuf,
476 - "%04d-%02d-%02d %02d:%02d:%02d",
477 - (int)mt->tm_year + 1900,
478 - (int)mt->tm_mon + 1,
484 - strlcpy(datebuf, " ", sizeof datebuf);
485 + storetime(mtimebuf, buf.st_mtime, sizeof mtimebuf);
486 + storetime(atimebuf, buf.st_atime, sizeof atimebuf);
488 /* TODO: Perhaps escape special characters in fname? */
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 : "",
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 },
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");
518 @@ -122,7 +122,7 @@ NORETURN void overflow_exit(const char *
519 exit_cleanup(RERR_MALLOC);
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)
525 #if !defined HAVE_LUTIMES || !defined HAVE_UTIMES
527 @@ -130,9 +130,13 @@ int set_modtime(const char *fname, time_
531 - rprintf(FINFO, "set modtime of %s to (%ld) %s",
532 + char mtimebuf[200];
534 + strlcpy(mtimebuf, timestring(modtime), sizeof mtimebuf);
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));
543 @@ -141,7 +145,7 @@ int set_modtime(const char *fname, time_
547 - t[0].tv_sec = time(NULL);
548 + t[0].tv_sec = atime;
550 t[1].tv_sec = modtime;
552 @@ -154,12 +158,12 @@ int set_modtime(const char *fname, time_
553 return utimes(fname, t);
554 #elif defined HAVE_UTIMBUF
556 - tbuf.actime = time(NULL);
557 + tbuf.actime = atime;
558 tbuf.modtime = modtime;
559 return utime(fname,&tbuf);
560 #elif defined HAVE_UTIME
565 return utime(fname,t);