The patches for 3.3.0.
[rsync-patches.git] / crtimes.diff
1 This patch adds a --crtimes (-N) option that will preserve
2 create times on OS X.
3
4 To use this patch, run these commands for a successful build:
5
6     patch -p1 <patches/fileflags.diff
7     patch -p1 <patches/crtimes.diff
8     ./prepare-source
9     ./configure
10     make
11
12 based-on: patch/master/fileflags
13 diff --git a/compat.c b/compat.c
14 --- a/compat.c
15 +++ b/compat.c
16 @@ -49,6 +49,7 @@ extern int force_change;
17  extern int protect_args;
18  extern int preserve_uid;
19  extern int preserve_gid;
20 +extern int preserve_crtimes;
21  extern int preserve_fileflags;
22  extern int preserve_acls;
23  extern int preserve_xattrs;
24 @@ -67,7 +68,7 @@ extern char *iconv_opt;
25  #endif
26  
27  /* These index values are for the file-list's extra-attribute array. */
28 -int uid_ndx, gid_ndx, fileflags_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
29 +int uid_ndx, gid_ndx, crtimes_ndx, fileflags_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
30  
31  int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
32  int sender_symlink_iconv = 0;  /* sender should convert symlink content */
33 @@ -146,6 +147,8 @@ void setup_protocol(int f_out,int f_in)
34                 uid_ndx = ++file_extra_cnt;
35         if (preserve_gid)
36                 gid_ndx = ++file_extra_cnt;
37 +       if (preserve_crtimes)
38 +               crtimes_ndx = (file_extra_cnt += TIME_EXTRA_CNT);
39         if (preserve_fileflags || (force_change && !am_sender))
40                 fileflags_ndx = ++file_extra_cnt;
41         if (preserve_acls && !am_sender)
42 diff --git a/flist.c b/flist.c
43 --- a/flist.c
44 +++ b/flist.c
45 @@ -56,6 +56,7 @@ extern int preserve_fileflags;
46  extern int delete_during;
47  extern int missing_args;
48  extern int eol_nulls;
49 +extern int crtimes_ndx;
50  extern int relative_paths;
51  extern int implied_dirs;
52  extern int ignore_perishable;
53 @@ -380,7 +381,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
54  #endif
55                             int ndx, int first_ndx)
56  {
57 -       static time_t modtime;
58 +       static time_t modtime, crtime;
59         static mode_t mode;
60  #ifdef SUPPORT_FILEFLAGS
61         static uint32 fileflags;
62 @@ -491,6 +492,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
63                 modtime = file->modtime;
64         if (NSEC_BUMP(file) && protocol_version >= 31)
65                 xflags |= XMIT_MOD_NSEC;
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 != -1) {
76 @@ -575,6 +583,8 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
77         }
78         if (xflags & XMIT_MOD_NSEC)
79                 write_varint(f, F_MOD_NSEC(file));
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));
84  #ifdef SUPPORT_FILEFLAGS
85 @@ -668,7 +678,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
86  
87  static struct file_struct *recv_file_entry(int f, struct file_list *flist, int xflags)
88  {
89 -       static int64 modtime;
90 +       static int64 modtime, crtime;
91         static mode_t mode;
92  #ifdef SUPPORT_FILEFLAGS
93         static uint32 fileflags;
94 @@ -820,6 +830,19 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
95                 modtime_nsec = read_varint(f);
96         else
97                 modtime_nsec = 0;
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  
114 @@ -997,6 +1020,8 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
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  
123 @@ -1398,6 +1423,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
124                 F_GROUP(file) = st.st_gid;
125         if (am_generator && st.st_uid == our_uid)
126                 file->flags |= FLAG_OWNED_BY_US;
127 +       if (crtimes_ndx)
128 +               f_crtime_set(file, get_create_time(fname));
129  
130         if (basename != thisname)
131                 file->dirname = lastdir;
132 diff --git a/generator.c b/generator.c
133 --- a/generator.c
134 +++ b/generator.c
135 @@ -40,6 +40,7 @@ extern int preserve_xattrs;
136  extern int preserve_links;
137  extern int preserve_devices;
138  extern int preserve_specials;
139 +extern int preserve_fileflags;
140  extern int preserve_hard_links;
141  extern int preserve_executability;
142  extern int preserve_fileflags;
143 @@ -394,6 +395,16 @@ static inline int time_diff(STRUCT_STAT *stp, struct file_struct *file)
144  #endif
145  }
146  
147 +static inline int all_time_diff(stat_x *sxp, struct file_struct *file, const char *fname)
148 +{
149 +       int diff = time_diff(&sxp->st, file);
150 +       if (diff || !crtimes_ndx)
151 +               return diff;
152 +       if (sxp->crtime == 0)
153 +               sxp->crtime = get_create_time(fname);
154 +       return cmp_time(sxp->crtime, 0, f_crtime(file), 0);
155 +}
156 +
157  static inline int perms_differ(struct file_struct *file, stat_x *sxp)
158  {
159         if (preserve_perms)
160 @@ -448,7 +459,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
161  {
162         if (S_ISLNK(file->mode)) {
163  #ifdef CAN_SET_SYMLINK_TIMES
164 -               if (preserve_times & PRESERVE_LINK_TIMES && time_diff(&sxp->st, file))
165 +               if (preserve_times & PRESERVE_LINK_TIMES && all_time_diff(sxp, file, fname))
166                         return 0;
167  #endif
168  #ifdef CAN_CHMOD_SYMLINK
169 @@ -468,7 +479,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
170                         return 0;
171  #endif
172         } else {
173 -               if (preserve_times && time_diff(&sxp->st, file))
174 +               if (preserve_times && all_time_diff(sxp, file, fname))
175                         return 0;
176                 if (perms_differ(file, sxp))
177                         return 0;
178 @@ -511,6 +522,12 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
179                  : iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !(iflags & ITEM_MATCHED)
180                   && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
181                         iflags |= ITEM_REPORT_TIME;
182 +               if (crtimes_ndx) {
183 +                       if (sxp->crtime == 0)
184 +                               sxp->crtime = get_create_time(fnamecmp);
185 +                       if (cmp_time(sxp->crtime, f_crtime(file)) != 0)
186 +                               iflags |= ITEM_REPORT_CRTIME;
187 +               }
188  #if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
189                 if (S_ISLNK(file->mode)) {
190                         ;
191 @@ -1135,6 +1152,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
192  static void list_file_entry(struct file_struct *f)
193  {
194         char permbuf[PERMSTRING_SIZE];
195 +       time_t crtime = crtimes_ndx ? f_crtime(f) : 0;
196         int64 len;
197         int colwidth = human_readable ? 14 : 11;
198  
199 @@ -1150,10 +1168,12 @@ static void list_file_entry(struct file_struct *f)
200  
201  #ifdef SUPPORT_LINKS
202         if (preserve_links && S_ISLNK(f->mode)) {
203 -               rprintf(FINFO, "%s %*s %s %s -> %s\n",
204 +               rprintf(FINFO, "%s %*s %s%s%s %s -> %s\n",
205                         permbuf, colwidth, human_num(len),
206 -                       timestring(f->modtime), f_name(f, NULL),
207 -                       F_SYMLINK(f));
208 +                       timestring(f->modtime),
209 +                       crtimes_ndx ? " " : "",
210 +                       crtimes_ndx ? timestring(crtime) : "",
211 +                       f_name(f, NULL), F_SYMLINK(f));
212         } else
213  #endif
214         if (missing_args == 2 && f->mode == 0) {
215 @@ -1161,9 +1181,12 @@ static void list_file_entry(struct file_struct *f)
216                         colwidth + 31, "*missing",
217                         f_name(f, NULL));
218         } else {
219 -               rprintf(FINFO, "%s %*s %s %s\n",
220 +               rprintf(FINFO, "%s %*s %s%s%s %s\n",
221                         permbuf, colwidth, human_num(len),
222 -                       timestring(f->modtime), f_name(f, NULL));
223 +                       timestring(f->modtime),
224 +                       crtimes_ndx ? " " : "",
225 +                       crtimes_ndx ? timestring(crtime) : "",
226 +                       f_name(f, NULL));
227         }
228  }
229  
230 @@ -1258,6 +1281,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
231                         return;
232                 }
233         }
234 +       sx.crtime = 0;
235  
236         if (dry_run > 1 || (dry_missing_dir && is_below(file, dry_missing_dir))) {
237                 int i;
238 diff --git a/ifuncs.h b/ifuncs.h
239 --- a/ifuncs.h
240 +++ b/ifuncs.h
241 @@ -43,6 +43,28 @@ free_xbuf(xbuf *xb)
242         memset(xb, 0, sizeof (xbuf));
243  }
244  
245 +static inline time_t
246 +f_crtime(struct file_struct *fp)
247 +{
248 +#if SIZEOF_TIME_T > 4
249 +       time_t crtime;
250 +       memcpy(&crtime, &REQ_EXTRA(fp, crtimes_ndx)->unum, SIZEOF_TIME_T);
251 +       return crtime;
252 +#else
253 +       return REQ_EXTRA(fp, crtimes_ndx)->unum;
254 +#endif
255 +}
256 +
257 +static inline void
258 +f_crtime_set(struct file_struct *fp, time_t crtime)
259 +{
260 +#if SIZEOF_TIME_T > 4
261 +       memcpy(&REQ_EXTRA(fp, crtimes_ndx)->unum, &crtime, SIZEOF_TIME_T);
262 +#else
263 +       REQ_EXTRA(fp, crtimes_ndx)->unum = (uint32)crtime;
264 +#endif
265 +}
266 +
267  static inline int
268  to_wire_mode(mode_t mode)
269  {
270 diff --git a/log.c b/log.c
271 --- a/log.c
272 +++ b/log.c
273 @@ -716,7 +716,8 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
274                         c[8] = !(iflags & ITEM_REPORT_FFLAGS) ? '.' : 'f';
275                         c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
276                         c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
277 -                       c[11] = '\0';
278 +                       c[11] = !(iflags & ITEM_REPORT_CRTIME) ? '.' : 'n';
279 +                       c[12] = '\0';
280  
281                         if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) {
282                                 char ch = iflags & ITEM_IS_NEW ? '+' : '?';
283 diff --git a/options.c b/options.c
284 --- a/options.c
285 +++ b/options.c
286 @@ -62,6 +62,7 @@ int preserve_specials = 0;
287  int preserve_uid = 0;
288  int preserve_gid = 0;
289  int preserve_times = 0;
290 +int preserve_crtimes = 0;
291  int update_only = 0;
292  int cvs_exclude = 0;
293  int dry_run = 0;
294 @@ -717,6 +718,7 @@ void usage(enum logcode F)
295    rprintf(F,"     --specials              preserve special files\n");
296    rprintf(F," -D                          same as --devices --specials\n");
297    rprintf(F," -t, --times                 preserve modification times\n");
298 +  rprintf(F," -N, --crtimes               preserve create times (newness)\n");
299    rprintf(F," -O, --omit-dir-times        omit directories from --times\n");
300    rprintf(F," -J, --omit-link-times       omit symlinks from --times\n");
301    rprintf(F,"     --super                 receiver attempts super-user activities\n");
302 @@ -885,6 +887,9 @@ static struct poptOption long_options[] = {
303    {"times",           't', POPT_ARG_VAL,    &preserve_times, 1, 0, 0 },
304    {"no-times",         0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
305    {"no-t",             0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
306 +  {"crtimes",         'N', POPT_ARG_VAL,    &preserve_crtimes, 1, 0, 0 },
307 +  {"no-crtimes",       0,  POPT_ARG_VAL,    &preserve_crtimes, 0, 0, 0 },
308 +  {"no-N",             0,  POPT_ARG_VAL,    &preserve_crtimes, 0, 0, 0 },
309    {"omit-dir-times",  'O', POPT_ARG_VAL,    &omit_dir_times, 1, 0, 0 },
310    {"no-omit-dir-times",0,  POPT_ARG_VAL,    &omit_dir_times, 0, 0, 0 },
311    {"no-O",             0,  POPT_ARG_VAL,    &omit_dir_times, 0, 0, 0 },
312 @@ -2491,6 +2496,8 @@ void server_options(char **args, int *argc_p)
313                 argstr[x++] = 'D';
314         if (preserve_times)
315                 argstr[x++] = 't';
316 +       if (preserve_crtimes)
317 +               argstr[x++] = 'N';
318         if (preserve_perms)
319                 argstr[x++] = 'p';
320         else if (preserve_executability && am_sender)
321 diff --git a/rsync.c b/rsync.c
322 --- a/rsync.c
323 +++ b/rsync.c
324 @@ -587,6 +587,9 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
325          || (!(preserve_times & PRESERVE_DIR_TIMES) && S_ISDIR(sxp->st.st_mode))
326          || (!(preserve_times & PRESERVE_LINK_TIMES) && S_ISLNK(sxp->st.st_mode)))
327                 flags |= ATTRS_SKIP_MTIME;
328 +       /* Don't set the creation date on the root folder of an HFS+ volume. */
329 +       if (sxp->st.st_ino == 2 && S_ISDIR(sxp->st.st_mode))
330 +               flags |= ATTRS_SKIP_CRTIME;
331         if (!(flags & ATTRS_SKIP_MTIME)
332          && (sxp->st.st_mtime != file->modtime
333  #ifdef ST_MTIME_NSEC
334 @@ -604,6 +607,14 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
335                 else
336                         file->flags |= FLAG_TIME_FAILED;
337         }
338 +       if (crtimes_ndx && !(flags & ATTRS_SKIP_CRTIME)) {
339 +               time_t file_crtime = f_crtime(file);
340 +               if (sxp->crtime == 0)
341 +                       sxp->crtime = get_create_time(fname);
342 +               if (cmp_time(sxp->crtime, file_crtime) != 0
343 +                && set_create_time(fname, file_crtime) == 0)
344 +                       updated = 1;
345 +       }
346  
347  #ifdef SUPPORT_ACLS
348         /* It's OK to call set_acl() now, even for a dir, as the generator
349 @@ -720,7 +731,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
350         /* Change permissions before putting the file into place. */
351         set_file_attrs(fnametmp, file, NULL, fnamecmp,
352                        ATTRS_DELAY_IMMUTABLE
353 -                      | (ok_to_set_time ? ATTRS_SET_NANO : ATTRS_SKIP_MTIME));
354 +                      | (ok_to_set_time ? ATTRS_SET_NANO : ATTRS_SKIP_MTIME | ATTRS_SKIP_CRTIME));
355  
356         /* move tmp file over real file */
357         if (DEBUG_GTE(RECV, 1))
358 @@ -749,7 +760,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
359  
360    do_set_file_attrs:
361         set_file_attrs(fnametmp, file, NULL, fnamecmp,
362 -                      ok_to_set_time ? ATTRS_SET_NANO : ATTRS_SKIP_MTIME);
363 +                      ok_to_set_time ? ATTRS_SET_NANO : ATTRS_SKIP_MTIME | ATTRS_SKIP_CRTIME);
364  
365         if (temp_copy_name) {
366                 if (do_rename(fnametmp, fname) < 0) {
367 diff --git a/rsync.h b/rsync.h
368 --- a/rsync.h
369 +++ b/rsync.h
370 @@ -62,7 +62,8 @@
371  #define XMIT_HLINK_FIRST (1<<12)       /* protocols 30 - now (HLINKED files only) */
372  #define XMIT_IO_ERROR_ENDLIST (1<<12)  /* protocols 31*- now (w/XMIT_EXTENDED_FLAGS) (also protocol 30 w/'f' compat flag) */
373  #define XMIT_MOD_NSEC (1<<13)          /* protocols 31 - now */
374 -#define XMIT_SAME_FLAGS (1<<14)                /* protocols ?? - now */
375 +#define XMIT_CRTIME_EQ_MTIME (1<<14)   /* protocols ?? - now */
376 +#define XMIT_SAME_FLAGS (1<<15)                /* protocols ?? - now */
377  
378  /* These flags are used in the live flist data. */
379  
380 @@ -168,6 +169,7 @@
381  #define ATTRS_SKIP_MTIME       (1<<1)
382  #define ATTRS_SET_NANO         (1<<2)
383  #define ATTRS_DELAY_IMMUTABLE  (1<<4)
384 +#define ATTRS_SKIP_CRTIME      (1<<5)
385  
386  #define FULL_FLUSH     1
387  #define NORMAL_FLUSH   0
388 @@ -184,7 +186,7 @@
389  #define FNAMECMP_FUZZY         0x83
390  
391  /* For use by the itemize_changes code */
392 -#define ITEM_REPORT_ATIME (1<<0)
393 +#define ITEM_REPORT_CRTIME (1<<0)
394  #define ITEM_REPORT_CHANGE (1<<1)
395  #define ITEM_REPORT_SIZE (1<<2)     /* regular files only */
396  #define ITEM_REPORT_TIMEFAIL (1<<2) /* symlinks only */
397 @@ -741,6 +743,7 @@ extern int file_extra_cnt;
398  extern int inc_recurse;
399  extern int uid_ndx;
400  extern int gid_ndx;
401 +extern int crtimes_ndx;
402  extern int fileflags_ndx;
403  extern int acls_ndx;
404  extern int xattrs_ndx;
405 @@ -748,6 +751,7 @@ extern int xattrs_ndx;
406  #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
407  #define EXTRA_LEN (sizeof (union file_extras))
408  #define PTR_EXTRA_CNT ((sizeof (char *) + EXTRA_LEN - 1) / EXTRA_LEN)
409 +#define TIME_EXTRA_CNT ((SIZEOF_TIME_T + EXTRA_LEN - 1) / EXTRA_LEN)
410  #define DEV_EXTRA_CNT 2
411  #define DIRNODE_EXTRA_CNT 3
412  #define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
413 @@ -1033,6 +1037,7 @@ typedef struct {
414  
415  typedef struct {
416      STRUCT_STAT st;
417 +    time_t crtime;
418  #ifdef SUPPORT_ACLS
419      struct rsync_acl *acc_acl; /* access ACL */
420      struct rsync_acl *def_acl; /* default ACL */
421 diff --git a/rsync.yo b/rsync.yo
422 --- a/rsync.yo
423 +++ b/rsync.yo
424 @@ -373,6 +373,7 @@ to the detailed description below for a complete description.  verb(
425       --specials              preserve special files
426   -D                          same as --devices --specials
427   -t, --times                 preserve modification times
428 + -N, --crtimes               preserve create times (newness)
429   -O, --omit-dir-times        omit directories from --times
430   -J, --omit-link-times       omit symlinks from --times
431       --super                 receiver attempts super-user activities
432 @@ -1242,6 +1243,9 @@ cause the next transfer to behave as if it used bf(-I), causing all files to be
433  updated (though rsync's delta-transfer algorithm will make the update fairly efficient
434  if the files haven't actually changed, you're much better off using bf(-t)).
435  
436 +dit(bf(-N, --crtimes)) This tells rsync to set the create times (newness) of
437 +the destination files to the same value as the source files.
438 +
439  dit(bf(-O, --omit-dir-times)) This tells rsync to omit directories when
440  it is preserving modification times (see bf(--times)).  If NFS is sharing
441  the directories on the receiving side, it is a good idea to use bf(-O).
442 @@ -2173,7 +2177,7 @@ with older versions of rsync, but that also turns on the output of other
443  verbose messages).
444  
445  The "%i" escape has a cryptic output that is 11 letters long.  The general
446 -format is like the string bf(YXcstpogfax), where bf(Y) is replaced by the
447 +format is like the string bf(YXcstpogfaxn), where bf(Y) is replaced by the
448  type of update being done, bf(X) is replaced by the file-type, and the
449  other letters represent attributes that may be output if they are being
450  modified.
451 @@ -2232,6 +2236,8 @@ quote(itemization(
452    it() The bf(f) means that the fileflags information changed.
453    it() The bf(a) means that the ACL information changed.
454    it() The bf(x) means that the extended attribute information changed.
455 +  it() A bf(n) means the create time (newness) is different and is being
456 +  updated to the sender's value (requires bf(--crtimes)).
457  ))
458  
459  One other output is possible:  when deleting files, the "%i" will output
460 diff --git a/syscall.c b/syscall.c
461 --- a/syscall.c
462 +++ b/syscall.c
463 @@ -54,6 +54,13 @@ extern int preserve_executability;
464  # endif
465  #endif
466  
467 +#pragma pack(push, 4)
468 +struct create_time {
469 +       uint32 length;
470 +       struct timespec crtime;
471 +};
472 +#pragma pack(pop)
473 +
474  #define RETURN_ERROR_IF(x,e) \
475         do { \
476                 if (x) { \
477 @@ -491,6 +498,36 @@ int do_setattrlist_times(const char *fname, time_t modtime, uint32 mod_nsec)
478  }
479  #endif
480  
481 +time_t get_create_time(const char *path)
482 +{
483 +       static struct create_time attrBuf;
484 +       struct attrlist attrList;
485 +
486 +       memset(&attrList, 0, sizeof attrList);
487 +       attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
488 +       attrList.commonattr = ATTR_CMN_CRTIME;
489 +       if (getattrlist(path, &attrList, &attrBuf, sizeof attrBuf, FSOPT_NOFOLLOW) < 0)
490 +               return 0;
491 +       return attrBuf.crtime.tv_sec;
492 +}
493 +
494 +int set_create_time(const char *path, time_t crtime)
495 +{
496 +       struct attrlist attrList;
497 +       struct timespec ts;
498 +
499 +       if (dry_run) return 0;
500 +       RETURN_ERROR_IF_RO_OR_LO;
501 +
502 +       ts.tv_sec = crtime;
503 +       ts.tv_nsec = 0;
504 +
505 +       memset(&attrList, 0, sizeof attrList);
506 +       attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
507 +       attrList.commonattr = ATTR_CMN_CRTIME;
508 +       return setattrlist(path, &attrList, &ts, sizeof ts, FSOPT_NOFOLLOW);
509 +}
510 +
511  #ifdef HAVE_UTIMENSAT
512  int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec)
513  {
514 diff --git a/testsuite/crtimes.test b/testsuite/crtimes.test
515 new file mode 100644
516 --- /dev/null
517 +++ b/testsuite/crtimes.test
518 @@ -0,0 +1,24 @@
519 +#! /bin/sh
520 +
521 +# Test rsync copying create times
522 +
523 +. "$suitedir/rsync.fns"
524 +
525 +# Setting an older time via touch sets the create time to the mtime.
526 +# Setting it to a newer time affects just the mtime.
527 +
528 +mkdir "$fromdir"
529 +echo hiho "$fromdir/foo"
530 +
531 +touch -t 200101011111.11 "$fromdir"
532 +touch -t 200202022222.22 "$fromdir"
533 +
534 +touch -t 200111111111.11 "$fromdir/foo"
535 +touch -t 200212122222.22 "$fromdir/foo"
536 +
537 +TLS_ARGS=--crtimes
538 +
539 +checkit "$RSYNC -rtgvvv --crtimes \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
540 +
541 +# The script would have aborted on error, so getting here means we've won.
542 +exit 0
543 diff --git a/testsuite/rsync.fns b/testsuite/rsync.fns
544 --- a/testsuite/rsync.fns
545 +++ b/testsuite/rsync.fns
546 @@ -23,9 +23,9 @@ todir="$tmpdir/to"
547  chkdir="$tmpdir/chk"
548  
549  # For itemized output:
550 -all_plus='+++++++++'
551 -allspace='         '
552 -dots='.....' # trailing dots after changes
553 +all_plus='++++++++++'
554 +allspace='          '
555 +dots='......' # trailing dots after changes
556  tab_ch='       ' # a single tab character
557  
558  # Berkley's nice.
559 diff --git a/tls.c b/tls.c
560 --- a/tls.c
561 +++ b/tls.c
562 @@ -111,6 +111,8 @@ static int stat_xattr(const char *fname, STRUCT_STAT *fst)
563  
564  #endif
565  
566 +static int display_crtimes = 0;
567 +
568  static void failed(char const *what, char const *where)
569  {
570         fprintf(stderr, PROGRAM ": %s %s: %s\n",
571 @@ -118,16 +120,44 @@ static void failed(char const *what, char const *where)
572         exit(1);
573  }
574  
575 +static void storetime(char *dest, size_t destsize, time_t t, int nsecs)
576 +{
577 +       if (t) {
578 +               int len;
579 +               struct tm *mt = gmtime(&t);
580 +
581 +               len = snprintf(dest, destsize,
582 +                       "%04d-%02d-%02d %02d:%02d:%02d",
583 +                       (int)mt->tm_year + 1900,
584 +                       (int)mt->tm_mon + 1,
585 +                       (int)mt->tm_mday,
586 +                       (int)mt->tm_hour,
587 +                       (int)mt->tm_min,
588 +                       (int)mt->tm_sec);
589 +               if (nsecs >= 0 && len >= 0)
590 +                       snprintf(dest + len, destsize - len, ".%09d", nsecs);
591 +       } else {
592 +               int has_nsecs = nsecs >= 0 ? 1 : 0;
593 +               int len = MIN(19 + 9*has_nsecs, (int)destsize - 1);
594 +               memset(dest, ' ', len);
595 +               dest[len] = '\0';
596 +       }
597 +}
598 +
599  static void list_file(const char *fname)
600  {
601         STRUCT_STAT buf;
602 +       time_t crtime = 0;
603         char permbuf[PERMSTRING_SIZE];
604 -       struct tm *mt;
605 -       char datebuf[50];
606 +       char mtimebuf[50];
607 +       char crtimebuf[50];
608         char linkbuf[4096];
609 +       int nsecs;
610  
611         if (do_lstat(fname, &buf) < 0)
612                 failed("stat", fname);
613 +       if (display_crtimes && (crtime = get_create_time(fname)) == 0)
614 +               failed("get_create_time", fname);
615  #ifdef SUPPORT_XATTRS
616         if (am_root < 0)
617                 stat_xattr(fname, &buf);
618 @@ -161,30 +191,17 @@ static void list_file(const char *fname)
619         }
620  
621         permstring(permbuf, buf.st_mode);
622 -
623 -       if (buf.st_mtime) {
624 -               int len;
625 -               mt = gmtime(&buf.st_mtime);
626 -
627 -               len = snprintf(datebuf, sizeof datebuf,
628 -                       "%04d-%02d-%02d %02d:%02d:%02d",
629 -                       (int)mt->tm_year + 1900,
630 -                       (int)mt->tm_mon + 1,
631 -                       (int)mt->tm_mday,
632 -                       (int)mt->tm_hour,
633 -                       (int)mt->tm_min,
634 -                       (int)mt->tm_sec);
635  #ifdef ST_MTIME_NSEC
636 -               if (nsec_times) {
637 -                       snprintf(datebuf + len, sizeof datebuf - len,
638 -                               ".%09d", (int)buf.ST_MTIME_NSEC);
639 -               }
640 +       if (nsec_times)
641 +               nsecs = (int)buf.ST_MTIME_NSEC;
642 +       else
643  #endif
644 -       } else {
645 -               int len = MIN(19 + 9*nsec_times, (int)sizeof datebuf - 1);
646 -               memset(datebuf, ' ', len);
647 -               datebuf[len] = '\0';
648 -       }
649 +               nsecs = -1;
650 +       storetime(mtimebuf, sizeof mtimebuf, buf.st_mtime, nsecs);
651 +       if (display_crtimes)
652 +               storetime(crtimebuf, sizeof crtimebuf, crtime, -1);
653 +       else
654 +               crtimebuf[0] = '\0';
655  
656         /* TODO: Perhaps escape special characters in fname? */
657  
658 @@ -195,13 +212,14 @@ static void list_file(const char *fname)
659                     (long)minor(buf.st_rdev));
660         } else
661                 printf("%15s", do_big_num(buf.st_size, 1, NULL));
662 -       printf(" %6ld.%-6ld %6ld %s %s%s\n",
663 +       printf(" %6ld.%-6ld %6ld %s%s%s%s\n",
664                (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
665 -              datebuf, fname, linkbuf);
666 +              mtimebuf, crtimebuf, fname, linkbuf);
667  }
668  
669  static struct poptOption long_options[] = {
670    /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
671 +  {"crtimes",         'N', POPT_ARG_NONE,   &display_crtimes, 0, 0, 0},
672    {"link-times",      'l', POPT_ARG_NONE,   &link_times, 0, 0, 0 },
673    {"link-owner",      'L', POPT_ARG_NONE,   &link_owner, 0, 0, 0 },
674  #ifdef SUPPORT_XATTRS
675 @@ -220,6 +238,7 @@ static void tls_usage(int ret)
676    fprintf(F,"usage: " PROGRAM " [OPTIONS] FILE ...\n");
677    fprintf(F,"Trivial file listing program for portably checking rsync\n");
678    fprintf(F,"\nOptions:\n");
679 +  fprintf(F," -N, --crtimes               display create times (newness)\n");
680    fprintf(F," -l, --link-times            display the time on a symlink\n");
681    fprintf(F," -L, --link-owner            display the owner+group on a symlink\n");
682  #ifdef SUPPORT_XATTRS