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