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