The patches for 3.0.1 and 3.0.2.
[rsync/rsync-patches.git] / crtimes.diff
CommitLineData
a5e6228a
WD
1This patch adds a --crtimes (-N) option that will preserve
2create times on OS X.
3
4To use this patch, run these commands for a successful build:
5
f9df736a 6 patch -p1 <patches/fileflags.diff
a5e6228a
WD
7 patch -p1 <patches/crtimes.diff
8 ./configure (optional if already run)
9 make
10
11diff --git a/compat.c b/compat.c
12--- a/compat.c
13+++ b/compat.c
f9df736a 14@@ -45,6 +45,7 @@ extern int force_change;
a5e6228a
WD
15 extern int protect_args;
16 extern int preserve_uid;
17 extern int preserve_gid;
18+extern int preserve_crtimes;
19 extern int preserve_fileflags;
20 extern int preserve_acls;
21 extern int preserve_xattrs;
f9df736a 22@@ -62,7 +63,7 @@ extern iconv_t ic_send, ic_recv;
a5e6228a
WD
23 #endif
24
25 /* These index values are for the file-list's extra-attribute array. */
26-int uid_ndx, gid_ndx, fileflags_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
27+int uid_ndx, gid_ndx, crtimes_ndx, fileflags_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
28
85096e5e
WD
29 int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
30
f9df736a 31@@ -136,6 +137,8 @@ void setup_protocol(int f_out,int f_in)
a5e6228a
WD
32 uid_ndx = ++file_extra_cnt;
33 if (preserve_gid)
34 gid_ndx = ++file_extra_cnt;
35+ if (preserve_crtimes)
36+ crtimes_ndx = (file_extra_cnt += TIME_EXTRA_CNT);
f9df736a 37 if (preserve_fileflags || (force_change && !am_sender))
a5e6228a
WD
38 fileflags_ndx = ++file_extra_cnt;
39 if (preserve_acls && !am_sender)
40diff --git a/flist.c b/flist.c
41--- a/flist.c
42+++ b/flist.c
f9df736a 43@@ -56,6 +56,7 @@ extern int preserve_fileflags;
a5e6228a
WD
44 extern int uid_ndx;
45 extern int gid_ndx;
46 extern int eol_nulls;
47+extern int crtimes_ndx;
48 extern int relative_paths;
49 extern int implied_dirs;
50 extern int file_extra_cnt;
bc3fcf1d 51@@ -389,7 +390,7 @@ int change_pathname(struct file_struct *file, const char *dir, int dirlen)
a5e6228a 52
65ecbe35 53 static void send_file_entry(int f, const char *fname, struct file_struct *file, int ndx, int first_ndx)
a5e6228a
WD
54 {
55- static time_t modtime;
56+ static time_t modtime, crtime;
57 static mode_t mode;
f9df736a 58 #ifdef SUPPORT_FILEFLAGS
a5e6228a 59 static uint32 fileflags;
bc3fcf1d 60@@ -474,6 +475,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
a5e6228a
WD
61 xflags |= XMIT_SAME_TIME;
62 else
63 modtime = file->modtime;
64+ if (crtimes_ndx) {
65+ time_t file_crtime = f_crtime(file);
66+ if (file_crtime == modtime)
67+ xflags |= XMIT_CRTIME_EQ_MTIME;
68+ else
69+ crtime = file_crtime;
70+ }
71
72 #ifdef SUPPORT_HARD_LINKS
73 if (tmp_dev != 0) {
bc3fcf1d 74@@ -543,6 +551,8 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
a5e6228a
WD
75 else
76 write_int(f, modtime);
77 }
78+ if (crtimes_ndx && !(xflags & XMIT_CRTIME_EQ_MTIME))
79+ write_varlong(f, crtime, 4);
80 if (!(xflags & XMIT_SAME_MODE))
81 write_int(f, to_wire_mode(mode));
f9df736a 82 #ifdef SUPPORT_FILEFLAGS
bc3fcf1d 83@@ -635,7 +645,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
a5e6228a
WD
84 static struct file_struct *recv_file_entry(struct file_list *flist,
85 int xflags, int f)
86 {
87- static int64 modtime;
88+ static int64 modtime, crtime;
89 static mode_t mode;
f9df736a 90 #ifdef SUPPORT_FILEFLAGS
a5e6228a 91 static uint32 fileflags;
bc3fcf1d 92@@ -770,6 +780,19 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
a5e6228a
WD
93 } else
94 modtime = read_int(f);
95 }
96+ if (crtimes_ndx) {
97+ if (!(xflags & XMIT_CRTIME_EQ_MTIME)) {
98+ crtime = read_varlong(f, 4);
99+#if SIZEOF_TIME_T < SIZEOF_INT64
100+ if (!am_generator && (int64)(time_t)crtime != crtime) {
101+ rprintf(FERROR_XFER,
102+ "Create time value of %s truncated on receiver.\n",
103+ lastname);
104+ }
105+#endif
106+ } else
107+ crtime = modtime;
108+ }
109 if (!(xflags & XMIT_SAME_MODE))
110 mode = from_wire_mode(read_int(f));
111
bc3fcf1d 112@@ -909,6 +932,8 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
a5e6228a
WD
113 F_GROUP(file) = gid;
114 file->flags |= gid_flags;
115 }
116+ if (crtimes_ndx)
117+ f_crtime_set(file, (time_t)crtime);
118 if (unsort_ndx)
119 F_NDX(file) = flist->used + flist->ndx_start;
120
bc3fcf1d 121@@ -1261,6 +1286,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
a5e6228a 122 F_OWNER(file) = st.st_uid;
f9df736a 123 if (gid_ndx) /* Check gid_ndx instead of preserve_gid for del support */
a5e6228a
WD
124 F_GROUP(file) = st.st_gid;
125+ if (crtimes_ndx)
126+ f_crtime_set(file, get_create_time(fname));
127
128 if (basename != thisname)
129 file->dirname = lastdir;
130diff --git a/generator.c b/generator.c
131--- a/generator.c
132+++ b/generator.c
133@@ -21,6 +21,7 @@
134 */
135
136 #include "rsync.h"
137+#include "ifuncs.h"
138
139 extern int verbose;
140 extern int dry_run;
c0c7984e 141@@ -40,6 +41,7 @@ extern int preserve_xattrs;
85096e5e 142 extern int preserve_links;
a5e6228a
WD
143 extern int preserve_devices;
144 extern int preserve_specials;
a5e6228a 145+extern int preserve_fileflags;
85096e5e
WD
146 extern int preserve_hard_links;
147 extern int preserve_executability;
f9df736a 148 extern int preserve_fileflags;
65ecbe35 149@@ -620,6 +622,13 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
a5e6228a
WD
150 if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP) && sxp->st.st_gid != (gid_t)F_GROUP(file))
151 return 0;
152
153+ if (crtimes_ndx) {
154+ if (sxp->crtime == 0)
155+ sxp->crtime = get_create_time(fname);
156+ if (cmp_time(sxp->crtime, f_crtime(file)) != 0)
157+ return 0;
158+ }
159+
160 #ifdef SUPPORT_ACLS
161 if (preserve_acls && !S_ISLNK(file->mode)) {
162 if (!ACL_READY(*sxp))
65ecbe35 163@@ -663,6 +672,12 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
c0c7984e 164 : iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !(iflags & ITEM_MATCHED)
a5e6228a 165 && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
a5e6228a
WD
166 iflags |= ITEM_REPORT_TIME;
167+ if (crtimes_ndx) {
168+ if (sxp->crtime == 0)
169+ sxp->crtime = get_create_time(fnamecmp);
170+ if (cmp_time(sxp->crtime, f_crtime(file)) != 0)
171+ iflags |= ITEM_REPORT_CRTIME;
172+ }
173 #if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
174 if (S_ISLNK(file->mode)) {
175 ;
65ecbe35 176@@ -1210,6 +1225,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
a5e6228a
WD
177 static void list_file_entry(struct file_struct *f)
178 {
179 char permbuf[PERMSTRING_SIZE];
180+ time_t crtime = crtimes_ndx ? f_crtime(f) : 0;
181 double len;
182
183 if (!F_IS_ACTIVE(f)) {
65ecbe35 184@@ -1224,14 +1240,16 @@ static void list_file_entry(struct file_struct *f)
a5e6228a
WD
185
186 #ifdef SUPPORT_LINKS
187 if (preserve_links && S_ISLNK(f->mode)) {
188- rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
189+ rprintf(FINFO, "%s %11.0f %s %s %s -> %s\n",
190 permbuf, len, timestring(f->modtime),
191+ crtimes_ndx ? timestring(crtime) : "",
192 f_name(f, NULL), F_SYMLINK(f));
193 } else
194 #endif
195 {
196- rprintf(FINFO, "%s %11.0f %s %s\n",
197+ rprintf(FINFO, "%s %11.0f %s %s %s\n",
198 permbuf, len, timestring(f->modtime),
199+ crtimes_ndx ? timestring(crtime) : "",
200 f_name(f, NULL));
201 }
202 }
65ecbe35 203@@ -1323,6 +1341,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
a5e6228a
WD
204 return;
205 }
206 }
207+ sx.crtime = 0;
c0c7984e 208
a5e6228a
WD
209 #ifdef SUPPORT_ACLS
210 sx.acc_acl = sx.def_acl = NULL;
a5e6228a
WD
211diff --git a/ifuncs.h b/ifuncs.h
212--- a/ifuncs.h
213+++ b/ifuncs.h
c0c7984e
WD
214@@ -67,6 +67,28 @@ d_name(struct dirent *di)
215 #endif
a5e6228a
WD
216 }
217
218+static inline time_t
219+f_crtime(struct file_struct *fp)
220+{
221+#if SIZEOF_TIME_T > 4
222+ time_t crtime;
223+ memcpy(&crtime, &REQ_EXTRA(fp, crtimes_ndx)->unum, SIZEOF_TIME_T);
224+ return crtime;
225+#else
226+ return REQ_EXTRA(fp, crtimes_ndx)->unum;
227+#endif
228+}
229+
230+static inline void
231+f_crtime_set(struct file_struct *fp, time_t crtime)
232+{
233+#if SIZEOF_TIME_T > 4
234+ memcpy(&REQ_EXTRA(fp, crtimes_ndx)->unum, &crtime, SIZEOF_TIME_T);
235+#else
236+ REQ_EXTRA(fp, crtimes_ndx)->unum = (uint32)crtime;
237+#endif
238+}
239+
240 static inline int
241 isDigit(const char *ptr)
242 {
243diff --git a/log.c b/log.c
244--- a/log.c
245+++ b/log.c
c0c7984e 246@@ -653,7 +653,8 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
f9df736a 247 c[8] = !(iflags & ITEM_REPORT_FFLAGS) ? '.' : 'f';
a5e6228a
WD
248 c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
249 c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
f9df736a
WD
250- c[11] = '\0';
251+ c[11] = !(iflags & ITEM_REPORT_CRTIME) ? '.' : 'n';
252+ c[12] = '\0';
253
254 if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) {
255 char ch = iflags & ITEM_IS_NEW ? '+' : '?';
a5e6228a
WD
256diff --git a/options.c b/options.c
257--- a/options.c
258+++ b/options.c
c0c7984e 259@@ -60,6 +60,7 @@ int preserve_specials = 0;
a5e6228a
WD
260 int preserve_uid = 0;
261 int preserve_gid = 0;
262 int preserve_times = 0;
263+int preserve_crtimes = 0;
264 int update_only = 0;
265 int cvs_exclude = 0;
266 int dry_run = 0;
c0c7984e 267@@ -362,6 +363,7 @@ void usage(enum logcode F)
a5e6228a
WD
268 rprintf(F," -D same as --devices --specials\n");
269 rprintf(F," -t, --times preserve modification times\n");
270 rprintf(F," -O, --omit-dir-times omit directories from --times\n");
f9df736a 271+ rprintf(F," -N, --crtimes preserve create times (newness)\n");
a5e6228a
WD
272 rprintf(F," --super receiver attempts super-user activities\n");
273 #ifdef SUPPORT_XATTRS
274 rprintf(F," --fake-super store/recover privileged attrs using xattrs\n");
c0c7984e 275@@ -508,6 +510,9 @@ static struct poptOption long_options[] = {
a5e6228a
WD
276 {"times", 't', POPT_ARG_VAL, &preserve_times, 2, 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+ {"crtimes", 'N', POPT_ARG_VAL, &preserve_crtimes, 1, 0, 0 },
280+ {"no-crtimes", 0, POPT_ARG_VAL, &preserve_crtimes, 0, 0, 0 },
281+ {"no-N", 0, POPT_ARG_VAL, &preserve_crtimes, 0, 0, 0 },
282 {"omit-dir-times", 'O', POPT_ARG_VAL, &omit_dir_times, 1, 0, 0 },
283 {"no-omit-dir-times",0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 },
284 {"no-O", 0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 },
c0c7984e 285@@ -1799,6 +1804,8 @@ void server_options(char **args, int *argc_p)
a5e6228a
WD
286 argstr[x++] = 'D';
287 if (preserve_times)
288 argstr[x++] = 't';
289+ if (preserve_crtimes)
290+ argstr[x++] = 'N';
291 if (preserve_perms)
292 argstr[x++] = 'p';
293 else if (preserve_executability && am_sender)
294diff --git a/rsync.c b/rsync.c
295--- a/rsync.c
296+++ b/rsync.c
c0c7984e
WD
297@@ -472,6 +472,14 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
298 else
85096e5e 299 file->flags |= FLAG_TIME_FAILED;
a5e6228a
WD
300 }
301+ if (crtimes_ndx && !(flags & ATTRS_SKIP_CRTIME)) {
302+ time_t file_crtime = f_crtime(file);
303+ if (sxp->crtime == 0)
304+ sxp->crtime = get_create_time(fname);
305+ if (cmp_time(sxp->crtime, file_crtime) != 0
306+ && set_create_time(fname, file_crtime) == 0)
307+ updated = 1;
308+ }
309
310 change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
311 change_gid = gid_ndx && !(file->flags & FLAG_SKIP_GROUP)
c0c7984e 312@@ -615,7 +623,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
a5e6228a
WD
313 /* Change permissions before putting the file into place. */
314 set_file_attrs(fnametmp, file, NULL, fnamecmp,
f9df736a
WD
315 ATTRS_DELAY_IMMUTABLE
316- | (ok_to_set_time ? 0 : ATTRS_SKIP_MTIME));
317+ | (ok_to_set_time ? 0 : ATTRS_SKIP_MTIME | ATTRS_SKIP_CRTIME));
a5e6228a 318
f9df736a
WD
319 /* move tmp file over real file */
320 if (verbose > 2)
c0c7984e 321@@ -646,7 +654,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
a5e6228a
WD
322
323 do_set_file_attrs:
324 set_file_attrs(fnametmp, file, NULL, fnamecmp,
325- ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
326+ ok_to_set_time ? 0 : ATTRS_SKIP_MTIME | ATTRS_SKIP_CRTIME);
327
328 if (temp_copy_name) {
329 if (do_rename(fnametmp, fname) < 0) {
330diff --git a/rsync.h b/rsync.h
331--- a/rsync.h
332+++ b/rsync.h
333@@ -60,6 +60,7 @@
334 #define XMIT_RDEV_MINOR_8_pre30 (1<<11) /* protocols 28 - 29 */
335 #define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */
336 #define XMIT_HLINK_FIRST (1<<12) /* protocols 30 - now (HLINKED files only) */
337+#define XMIT_CRTIME_EQ_MTIME (1<<13) /* protocols ?? - now */
338 #define XMIT_SAME_FLAGS (1<<14) /* protocols ?? - now */
339
340 /* These flags are used in the live flist data. */
cbdf862c 341@@ -154,6 +155,7 @@
a5e6228a
WD
342 #define ATTRS_REPORT (1<<0)
343 #define ATTRS_SKIP_MTIME (1<<1)
f9df736a
WD
344 #define ATTRS_DELAY_IMMUTABLE (1<<2)
345+#define ATTRS_SKIP_CRTIME (1<<3)
a5e6228a
WD
346
347 #define FULL_FLUSH 1
348 #define NORMAL_FLUSH 0
cbdf862c 349@@ -170,7 +172,7 @@
a5e6228a
WD
350 #define FNAMECMP_FUZZY 0x83
351
352 /* For use by the itemize_changes code */
353-#define ITEM_REPORT_ATIME (1<<0)
354+#define ITEM_REPORT_CRTIME (1<<0)
c0c7984e
WD
355 #define ITEM_REPORT_CHANGE (1<<1)
356 #define ITEM_REPORT_SIZE (1<<2) /* regular files only */
357 #define ITEM_REPORT_TIMEFAIL (1<<2) /* symlinks only */
cbdf862c 358@@ -651,6 +653,7 @@ extern int file_extra_cnt;
a5e6228a
WD
359 extern int inc_recurse;
360 extern int uid_ndx;
361 extern int gid_ndx;
362+extern int crtimes_ndx;
363 extern int fileflags_ndx;
364 extern int acls_ndx;
365 extern int xattrs_ndx;
cbdf862c 366@@ -658,6 +661,7 @@ extern int xattrs_ndx;
a5e6228a
WD
367 #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
368 #define EXTRA_LEN (sizeof (union file_extras))
369 #define PTR_EXTRA_CNT ((sizeof (char *) + EXTRA_LEN - 1) / EXTRA_LEN)
370+#define TIME_EXTRA_CNT ((SIZEOF_TIME_T + EXTRA_LEN - 1) / EXTRA_LEN)
371 #define DEV_EXTRA_CNT 2
372 #define DIRNODE_EXTRA_CNT 3
373 #define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
cbdf862c 374@@ -916,6 +920,7 @@ typedef struct {
a5e6228a
WD
375
376 typedef struct {
377 STRUCT_STAT st;
378+ time_t crtime;
379 #ifdef SUPPORT_ACLS
380 struct rsync_acl *acc_acl; /* access ACL */
381 struct rsync_acl *def_acl; /* default ACL */
382diff --git a/rsync.yo b/rsync.yo
383--- a/rsync.yo
384+++ b/rsync.yo
385@@ -350,6 +350,7 @@ to the detailed description below for a complete description. verb(
386 -D same as --devices --specials
387 -t, --times preserve modification times
388 -O, --omit-dir-times omit directories from --times
f9df736a 389+ -N, --crtimes preserve create times (newness)
a5e6228a
WD
390 --super receiver attempts super-user activities
391 --fake-super store/recover privileged attrs using xattrs
392 -S, --sparse handle sparse files efficiently
c0c7984e 393@@ -1022,6 +1023,9 @@ it is preserving modification times (see bf(--times)). If NFS is sharing
a5e6228a
WD
394 the directories on the receiving side, it is a good idea to use bf(-O).
395 This option is inferred if you use bf(--backup) without bf(--backup-dir).
396
f9df736a 397+dit(bf(-N, --crtimes)) This tells rsync to set the create times (newness) of
a5e6228a
WD
398+the destination files to the same value as the source files.
399+
400 dit(bf(--super)) This tells the receiving side to attempt super-user
401 activities even if the receiving rsync wasn't run by the super-user. These
402 activities include: preserving users via the bf(--owner) option, preserving
c0c7984e 403@@ -1674,7 +1678,7 @@ with older versions of rsync, but that also turns on the output of other
a5e6228a
WD
404 verbose messages).
405
406 The "%i" escape has a cryptic output that is 11 letters long. The general
f9df736a
WD
407-format is like the string bf(YXcstpogfax), where bf(Y) is replaced by the
408+format is like the string bf(YXcstpogfaxn), where bf(Y) is replaced by the
a5e6228a
WD
409 type of update being done, bf(X) is replaced by the file-type, and the
410 other letters represent attributes that may be output if they are being
411 modified.
c0c7984e 412@@ -1733,6 +1737,8 @@ quote(itemization(
f9df736a 413 it() The bf(f) means that the fileflags information changed.
a5e6228a 414 it() The bf(a) means that the ACL information changed.
f9df736a
WD
415 it() The bf(x) means that the extended attribute information changed.
416+ it() A bf(n) means the create time (newness) is different and is being
417+ updated to the sender's value (requires bf(--crtimes)).
418 ))
419
420 One other output is possible: when deleting files, the "%i" will output
a5e6228a
WD
421diff --git a/syscall.c b/syscall.c
422--- a/syscall.c
423+++ b/syscall.c
f9df736a 424@@ -37,6 +37,11 @@ extern int force_change;
a5e6228a
WD
425 extern int preserve_perms;
426 extern int preserve_executability;
427
428+struct create_time {
429+ unsigned long length;
430+ struct timespec crtime;
431+};
432+
433 #define RETURN_ERROR_IF(x,e) \
434 do { \
435 if (x) { \
c0c7984e
WD
436@@ -394,3 +399,33 @@ OFF_T do_lseek(int fd, OFF_T offset, int whence)
437 return lseek(fd, offset, whence);
a5e6228a
WD
438 #endif
439 }
440+
441+time_t get_create_time(const char *path)
442+{
443+ static struct create_time attrBuf;
444+ struct attrlist attrList;
445+
446+ memset(&attrList, 0, sizeof attrList);
447+ attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
448+ attrList.commonattr = ATTR_CMN_CRTIME;
449+ if (getattrlist(path, &attrList, &attrBuf, sizeof attrBuf, FSOPT_NOFOLLOW) < 0)
450+ return 0;
451+ return attrBuf.crtime.tv_sec;
452+}
453+
454+int set_create_time(const char *path, time_t crtime)
455+{
456+ struct attrlist attrList;
457+ struct timespec ts;
458+
459+ if (dry_run) return 0;
460+ RETURN_ERROR_IF_RO_OR_LO;
461+
462+ ts.tv_sec = crtime;
463+ ts.tv_nsec = 0;
464+
465+ memset(&attrList, 0, sizeof attrList);
466+ attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
467+ attrList.commonattr = ATTR_CMN_CRTIME;
468+ return setattrlist(path, &attrList, &ts, sizeof ts, FSOPT_NOFOLLOW);
469+}
470diff --git a/testsuite/crtimes.test b/testsuite/crtimes.test
471new file mode 100644
472--- /dev/null
473+++ b/testsuite/crtimes.test
474@@ -0,0 +1,24 @@
475+#! /bin/sh
476+
477+# Test rsync copying create times
478+
479+. "$suitedir/rsync.fns"
480+
481+# Setting an older time via touch sets the create time to the mtime.
482+# Setting it to a newer time affects just the mtime.
483+
484+mkdir "$fromdir"
485+echo hiho "$fromdir/foo"
486+
487+touch -t 200101011111.11 "$fromdir"
488+touch -t 200202022222.22 "$fromdir"
489+
490+touch -t 200111111111.11 "$fromdir/foo"
491+touch -t 200212122222.22 "$fromdir/foo"
492+
493+TLS_ARGS=--crtimes
494+
495+checkit "$RSYNC -rtgvvv --crtimes \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
496+
497+# The script would have aborted on error, so getting here means we've won.
498+exit 0
f9df736a
WD
499diff --git a/testsuite/rsync.fns b/testsuite/rsync.fns
500--- a/testsuite/rsync.fns
501+++ b/testsuite/rsync.fns
502@@ -24,9 +24,9 @@ todir="$tmpdir/to"
503 chkdir="$tmpdir/chk"
504
505 # For itemized output:
506-all_plus='+++++++++'
507-allspace=' '
508-dots='.....' # trailing dots after changes
509+all_plus='++++++++++'
510+allspace=' '
511+dots='......' # trailing dots after changes
512
513 # Berkley's nice.
514 PATH="$PATH:/usr/ucb"
a5e6228a
WD
515diff --git a/tls.c b/tls.c
516--- a/tls.c
517+++ b/tls.c
518@@ -105,6 +105,8 @@ static int stat_xattr(const char *fname, STRUCT_STAT *fst)
519
520 #endif
521
522+static int display_crtimes = 0;
f9df736a 523+
a5e6228a
WD
524 static void failed(char const *what, char const *where)
525 {
526 fprintf(stderr, PROGRAM ": %s %s: %s\n",
527@@ -112,16 +114,36 @@ static void failed(char const *what, char const *where)
528 exit(1);
529 }
530
531+static void storetime(char *dest, time_t t, size_t destsize)
532+{
533+ if (t) {
534+ struct tm *mt = gmtime(&t);
535+
536+ snprintf(dest, destsize,
537+ "%04d-%02d-%02d %02d:%02d:%02d ",
538+ (int)mt->tm_year + 1900,
539+ (int)mt->tm_mon + 1,
540+ (int)mt->tm_mday,
541+ (int)mt->tm_hour,
542+ (int)mt->tm_min,
543+ (int)mt->tm_sec);
544+ } else
545+ strlcpy(dest, " ", destsize);
546+}
547+
548 static void list_file(const char *fname)
549 {
550 STRUCT_STAT buf;
551+ time_t crtime = 0;
552 char permbuf[PERMSTRING_SIZE];
553- struct tm *mt;
554- char datebuf[50];
555+ char mtimebuf[50];
556+ char crtimebuf[50];
557 char linkbuf[4096];
558
559 if (do_lstat(fname, &buf) < 0)
560 failed("stat", fname);
561+ if (display_crtimes && (crtime = get_create_time(fname)) == 0)
562+ failed("get_create_time", fname);
563 #ifdef SUPPORT_XATTRS
564 if (am_root < 0)
565 stat_xattr(fname, &buf);
566@@ -154,19 +176,11 @@ static void list_file(const char *fname)
567
568 permstring(permbuf, buf.st_mode);
569
570- if (buf.st_mtime) {
571- mt = gmtime(&buf.st_mtime);
572-
573- snprintf(datebuf, sizeof datebuf,
574- "%04d-%02d-%02d %02d:%02d:%02d",
575- (int)mt->tm_year + 1900,
576- (int)mt->tm_mon + 1,
577- (int)mt->tm_mday,
578- (int)mt->tm_hour,
579- (int)mt->tm_min,
580- (int)mt->tm_sec);
581- } else
582- strlcpy(datebuf, " ", sizeof datebuf);
583+ storetime(mtimebuf, buf.st_mtime, sizeof mtimebuf);
584+ if (display_crtimes)
585+ storetime(crtimebuf, crtime, sizeof crtimebuf);
586+ else
587+ crtimebuf[0] = '\0';
588
589 /* TODO: Perhaps escape special characters in fname? */
590
591@@ -177,13 +191,14 @@ static void list_file(const char *fname)
592 (long)minor(buf.st_rdev));
593 } else /* NB: use double for size since it might not fit in a long. */
594 printf("%12.0f", (double)buf.st_size);
595- printf(" %6ld.%-6ld %6ld %s %s%s\n",
596+ printf(" %6ld.%-6ld %6ld %s%s%s%s\n",
597 (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
598- datebuf, fname, linkbuf);
599+ mtimebuf, crtimebuf, fname, linkbuf);
600 }
601
602 static struct poptOption long_options[] = {
603 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
604+ {"crtimes", 'N', POPT_ARG_NONE, &display_crtimes, 0, 0, 0},
605 #ifdef SUPPORT_XATTRS
606 {"fake-super", 'f', POPT_ARG_VAL, &am_root, -1, 0, 0 },
607 #endif
608@@ -197,6 +212,7 @@ static void tls_usage(int ret)
609 fprintf(F,"usage: " PROGRAM " [OPTIONS] FILE ...\n");
610 fprintf(F,"Trivial file listing program for portably checking rsync\n");
611 fprintf(F,"\nOptions:\n");
f9df736a 612+ fprintf(F," -N, --crtimes display create times (newness)\n");
a5e6228a
WD
613 #ifdef SUPPORT_XATTRS
614 fprintf(F," -f, --fake-super display attributes including fake-super xattrs\n");
615 #endif