The patches for 3.0.9.
[rsync.git/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/b3.0.x/fileflags
13 diff --git a/compat.c b/compat.c
14 --- a/compat.c
15 +++ b/compat.c
16 @@ -47,6 +47,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 @@ -65,7 +66,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 @@ -142,6 +143,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 @@ -54,6 +54,7 @@ extern int preserve_specials;
46  extern int preserve_fileflags;
47  extern int delete_during;
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 @@ -393,7 +394,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 @@ -488,6 +489,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
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 != -1) {
76 @@ -557,6 +565,8 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
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));
84  #ifdef SUPPORT_FILEFLAGS
85 @@ -648,7 +658,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 @@ -758,6 +768,8 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
95                                 uid = F_OWNER(first);
96                         if (preserve_gid)
97                                 gid = F_GROUP(first);
98 +                       if (crtimes_ndx)
99 +                               crtime = f_crtime(first);
100                         if (preserve_devices && IS_DEVICE(mode)) {
101                                 uint32 *devp = F_RDEV_P(first);
102                                 rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
103 @@ -786,6 +798,19 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
104                 } else
105                         modtime = read_int(f);
106         }
107 +       if (crtimes_ndx) {
108 +               if (!(xflags & XMIT_CRTIME_EQ_MTIME)) {
109 +                       crtime = read_varlong(f, 4);
110 +#if SIZEOF_TIME_T < SIZEOF_INT64
111 +                       if (!am_generator && (int64)(time_t)crtime != crtime) {
112 +                               rprintf(FERROR_XFER,
113 +                                   "Create time value of %s truncated on receiver.\n",
114 +                                   lastname);
115 +                       }
116 +#endif
117 +               } else
118 +                       crtime = modtime;
119 +       }
120         if (!(xflags & XMIT_SAME_MODE))
121                 mode = from_wire_mode(read_int(f));
122  
123 @@ -946,6 +971,8 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
124                 F_GROUP(file) = gid;
125                 file->flags |= gid_flags;
126         }
127 +       if (crtimes_ndx)
128 +               f_crtime_set(file, (time_t)crtime);
129         if (unsort_ndx)
130                 F_NDX(file) = flist->used + flist->ndx_start;
131  
132 @@ -1324,6 +1351,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
133                 F_GROUP(file) = st.st_gid;
134         if (am_generator && st.st_uid == our_uid)
135                 file->flags |= FLAG_OWNED_BY_US;
136 +       if (crtimes_ndx)
137 +               f_crtime_set(file, get_create_time(fname));
138  
139         if (basename != thisname)
140                 file->dirname = lastdir;
141 diff --git a/generator.c b/generator.c
142 --- a/generator.c
143 +++ b/generator.c
144 @@ -21,6 +21,7 @@
145   */
146  
147  #include "rsync.h"
148 +#include "ifuncs.h"
149  
150  extern int verbose;
151  extern int dry_run;
152 @@ -41,6 +42,7 @@ extern int preserve_xattrs;
153  extern int preserve_links;
154  extern int preserve_devices;
155  extern int preserve_specials;
156 +extern int preserve_fileflags;
157  extern int preserve_hard_links;
158  extern int preserve_executability;
159  extern int preserve_fileflags;
160 @@ -576,8 +578,15 @@ static void do_delete_pass(void)
161                 rprintf(FINFO, "                    \r");
162  }
163  
164 -static inline int time_differs(struct file_struct *file, stat_x *sxp)
165 +static inline int time_differs(struct file_struct *file, stat_x *sxp, const char *fname)
166  {
167 +       if (crtimes_ndx) {
168 +               if (sxp->crtime == 0)
169 +                       sxp->crtime = get_create_time(fname);
170 +               if (cmp_time(sxp->crtime, f_crtime(file)) != 0)
171 +                       return 1;
172 +       }
173 +
174         return cmp_time(sxp->st.st_mtime, file->modtime);
175  }
176  
177 @@ -635,7 +644,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
178  {
179         if (S_ISLNK(file->mode)) {
180  #ifdef CAN_SET_SYMLINK_TIMES
181 -               if (preserve_times & PRESERVE_LINK_TIMES && time_differs(file, sxp))
182 +               if (preserve_times & PRESERVE_LINK_TIMES && time_differs(file, sxp, fname))
183                         return 0;
184  #endif
185  #ifdef CAN_CHMOD_SYMLINK
186 @@ -655,7 +664,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
187                         return 0;
188  #endif
189         } else {
190 -               if (preserve_times && time_differs(file, sxp))
191 +               if (preserve_times && time_differs(file, sxp, fname))
192                         return 0;
193                 if (perms_differ(file, sxp))
194                         return 0;
195 @@ -698,6 +707,12 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
196                  : iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !(iflags & ITEM_MATCHED)
197                   && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
198                         iflags |= ITEM_REPORT_TIME;
199 +               if (crtimes_ndx) {
200 +                       if (sxp->crtime == 0)
201 +                               sxp->crtime = get_create_time(fnamecmp);
202 +                       if (cmp_time(sxp->crtime, f_crtime(file)) != 0)
203 +                               iflags |= ITEM_REPORT_CRTIME;
204 +               }
205  #if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
206                 if (S_ISLNK(file->mode)) {
207                         ;
208 @@ -1263,7 +1278,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
209  
210  static void list_file_entry(struct file_struct *f)
211  {
212 -       char permbuf[PERMSTRING_SIZE];
213 +       char permbuf[PERMSTRING_SIZE], crtime_buf[32];
214         double len;
215  
216         if (!F_IS_ACTIVE(f)) {
217 @@ -1274,19 +1289,24 @@ static void list_file_entry(struct file_struct *f)
218         permstring(permbuf, f->mode);
219         len = F_LENGTH(f);
220  
221 +       if (crtimes_ndx)
222 +               snprintf(crtime_buf, sizeof crtime_buf, " %s", timestring(f_crtime(f)));
223 +       else
224 +               *crtime_buf = '\0';
225 +
226         /* TODO: indicate '+' if the entry has an ACL. */
227  
228  #ifdef SUPPORT_LINKS
229         if (preserve_links && S_ISLNK(f->mode)) {
230 -               rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
231 +               rprintf(FINFO, "%s %11.0f %s%s %s -> %s\n",
232                         permbuf, len, timestring(f->modtime),
233 -                       f_name(f, NULL), F_SYMLINK(f));
234 +                       crtime_buf, f_name(f, NULL), F_SYMLINK(f));
235         } else
236  #endif
237         {
238 -               rprintf(FINFO, "%s %11.0f %s %s\n",
239 +               rprintf(FINFO, "%s %11.0f %s%s %s\n",
240                         permbuf, len, timestring(f->modtime),
241 -                       f_name(f, NULL));
242 +                       crtime_buf, f_name(f, NULL));
243         }
244  }
245  
246 @@ -1383,6 +1403,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
247                         return;
248                 }
249         }
250 +       sx.crtime = 0;
251  
252         if (dry_run > 1 || (dry_missing_dir && is_below(file, dry_missing_dir))) {
253           parent_is_dry_missing:
254 diff --git a/hlink.c b/hlink.c
255 --- a/hlink.c
256 +++ b/hlink.c
257 @@ -371,6 +371,7 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
258                 char cmpbuf[MAXPATHLEN];
259                 stat_x alt_sx;
260                 int j = 0;
261 +               alt_sx.crtime = 0;
262  #ifdef SUPPORT_ACLS
263                 alt_sx.acc_acl = alt_sx.def_acl = NULL;
264  #endif
265 @@ -499,6 +500,7 @@ void finish_hard_link(struct file_struct *file, const char *fname, int fin_ndx,
266         } else
267                 our_name = fname;
268  
269 +       prev_sx.crtime = 0;
270  #ifdef SUPPORT_ACLS
271         prev_sx.acc_acl = prev_sx.def_acl = NULL;
272  #endif
273 diff --git a/ifuncs.h b/ifuncs.h
274 --- a/ifuncs.h
275 +++ b/ifuncs.h
276 @@ -67,6 +67,28 @@ d_name(struct dirent *di)
277  #endif
278  }
279  
280 +static inline time_t
281 +f_crtime(struct file_struct *fp)
282 +{
283 +#if SIZEOF_TIME_T > 4
284 +       time_t crtime;
285 +       memcpy(&crtime, &REQ_EXTRA(fp, crtimes_ndx)->unum, SIZEOF_TIME_T);
286 +       return crtime;
287 +#else
288 +       return REQ_EXTRA(fp, crtimes_ndx)->unum;
289 +#endif
290 +}
291 +
292 +static inline void
293 +f_crtime_set(struct file_struct *fp, time_t crtime)
294 +{
295 +#if SIZEOF_TIME_T > 4
296 +       memcpy(&REQ_EXTRA(fp, crtimes_ndx)->unum, &crtime, SIZEOF_TIME_T);
297 +#else
298 +       REQ_EXTRA(fp, crtimes_ndx)->unum = (uint32)crtime;
299 +#endif
300 +}
301 +
302  static inline int
303  isDigit(const char *ptr)
304  {
305 diff --git a/log.c b/log.c
306 --- a/log.c
307 +++ b/log.c
308 @@ -661,7 +661,8 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
309                         c[8] = !(iflags & ITEM_REPORT_FFLAGS) ? '.' : 'f';
310                         c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
311                         c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
312 -                       c[11] = '\0';
313 +                       c[11] = !(iflags & ITEM_REPORT_CRTIME) ? '.' : 'n';
314 +                       c[12] = '\0';
315  
316                         if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) {
317                                 char ch = iflags & ITEM_IS_NEW ? '+' : '?';
318 diff --git a/options.c b/options.c
319 --- a/options.c
320 +++ b/options.c
321 @@ -60,6 +60,7 @@ int preserve_specials = 0;
322  int preserve_uid = 0;
323  int preserve_gid = 0;
324  int preserve_times = 0;
325 +int preserve_crtimes = 0;
326  int update_only = 0;
327  int cvs_exclude = 0;
328  int dry_run = 0;
329 @@ -361,6 +362,7 @@ void usage(enum logcode F)
330    rprintf(F," -D                          same as --devices --specials\n");
331    rprintf(F," -t, --times                 preserve modification times\n");
332    rprintf(F," -O, --omit-dir-times        omit directories from --times\n");
333 +  rprintf(F," -N, --crtimes               preserve create times (newness)\n");
334    rprintf(F,"     --super                 receiver attempts super-user activities\n");
335  #ifdef SUPPORT_XATTRS
336    rprintf(F,"     --fake-super            store/recover privileged attrs using xattrs\n");
337 @@ -507,6 +509,9 @@ static struct poptOption long_options[] = {
338    {"times",           't', POPT_ARG_VAL,    &preserve_times, 1, 0, 0 },
339    {"no-times",         0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
340    {"no-t",             0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
341 +  {"crtimes",         'N', POPT_ARG_VAL,    &preserve_crtimes, 1, 0, 0 },
342 +  {"no-crtimes",       0,  POPT_ARG_VAL,    &preserve_crtimes, 0, 0, 0 },
343 +  {"no-N",             0,  POPT_ARG_VAL,    &preserve_crtimes, 0, 0, 0 },
344    {"omit-dir-times",  'O', POPT_ARG_VAL,    &omit_dir_times, 1, 0, 0 },
345    {"no-omit-dir-times",0,  POPT_ARG_VAL,    &omit_dir_times, 0, 0, 0 },
346    {"no-O",             0,  POPT_ARG_VAL,    &omit_dir_times, 0, 0, 0 },
347 @@ -1810,6 +1815,8 @@ void server_options(char **args, int *argc_p)
348                 argstr[x++] = 'D';
349         if (preserve_times)
350                 argstr[x++] = 't';
351 +       if (preserve_crtimes)
352 +               argstr[x++] = 'N';
353         if (preserve_perms)
354                 argstr[x++] = 'p';
355         else if (preserve_executability && am_sender)
356 diff --git a/rsync.c b/rsync.c
357 --- a/rsync.c
358 +++ b/rsync.c
359 @@ -464,6 +464,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
360                                 full_fname(fname));
361                         return 0;
362                 }
363 +               sx2.crtime = 0;
364  #ifdef SUPPORT_ACLS
365                 sx2.acc_acl = sx2.def_acl = NULL;
366  #endif
367 @@ -505,6 +506,9 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
368          || (!(preserve_times & PRESERVE_DIR_TIMES) && S_ISDIR(sxp->st.st_mode))
369          || (!(preserve_times & PRESERVE_LINK_TIMES) && S_ISLNK(sxp->st.st_mode)))
370                 flags |= ATTRS_SKIP_MTIME;
371 +       /* Don't set the creation date on the root folder of an HFS+ volume. */
372 +       if (sxp->st.st_ino == 2 && S_ISDIR(sxp->st.st_mode))
373 +               flags |= ATTRS_SKIP_CRTIME;
374         if (!(flags & ATTRS_SKIP_MTIME)
375             && cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
376                 int ret = set_modtime(fname, file->modtime, sxp->st.st_mode, ST_FLAGS(sxp->st));
377 @@ -518,6 +522,14 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
378                 else
379                         file->flags |= FLAG_TIME_FAILED;
380         }
381 +       if (crtimes_ndx && !(flags & ATTRS_SKIP_CRTIME)) {
382 +               time_t file_crtime = f_crtime(file);
383 +               if (sxp->crtime == 0)
384 +                       sxp->crtime = get_create_time(fname);
385 +               if (cmp_time(sxp->crtime, file_crtime) != 0
386 +                && set_create_time(fname, file_crtime) == 0)
387 +                       updated = 1;
388 +       }
389  
390         change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
391         change_gid = gid_ndx && !(file->flags & FLAG_SKIP_GROUP)
392 @@ -675,7 +687,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
393         /* Change permissions before putting the file into place. */
394         set_file_attrs(fnametmp, file, NULL, fnamecmp,
395                        ATTRS_DELAY_IMMUTABLE
396 -                      | (ok_to_set_time ? 0 : ATTRS_SKIP_MTIME));
397 +                      | (ok_to_set_time ? 0 : ATTRS_SKIP_MTIME | ATTRS_SKIP_CRTIME));
398  
399         /* move tmp file over real file */
400         if (verbose > 2)
401 @@ -706,7 +718,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
402  
403    do_set_file_attrs:
404         set_file_attrs(fnametmp, file, NULL, fnamecmp,
405 -                      ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
406 +                      ok_to_set_time ? 0 : ATTRS_SKIP_MTIME | ATTRS_SKIP_CRTIME);
407  
408         if (temp_copy_name) {
409                 if (do_rename(fnametmp, fname) < 0) {
410 diff --git a/rsync.h b/rsync.h
411 --- a/rsync.h
412 +++ b/rsync.h
413 @@ -61,6 +61,7 @@
414  #define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */
415  #define XMIT_HLINK_FIRST (1<<12)       /* protocols 30 - now (HLINKED files only) */
416  #define XMIT_IO_ERROR_ENDLIST (1<<12)  /* protocols 31*- now (w/XMIT_EXTENDED_FLAGS) (also protocol 30 w/'f' compat flag) */
417 +#define XMIT_CRTIME_EQ_MTIME (1<<13)   /* protocols ?? - now */
418  #define XMIT_SAME_FLAGS (1<<14)                /* protocols ?? - now */
419  
420  /* These flags are used in the live flist data. */
421 @@ -162,6 +163,7 @@
422  #define ATTRS_REPORT           (1<<0)
423  #define ATTRS_SKIP_MTIME       (1<<1)
424  #define ATTRS_DELAY_IMMUTABLE  (1<<2)
425 +#define ATTRS_SKIP_CRTIME      (1<<3)
426  
427  #define FULL_FLUSH     1
428  #define NORMAL_FLUSH   0
429 @@ -178,7 +180,7 @@
430  #define FNAMECMP_FUZZY         0x83
431  
432  /* For use by the itemize_changes code */
433 -#define ITEM_REPORT_ATIME (1<<0)
434 +#define ITEM_REPORT_CRTIME (1<<0)
435  #define ITEM_REPORT_CHANGE (1<<1)
436  #define ITEM_REPORT_SIZE (1<<2)     /* regular files only */
437  #define ITEM_REPORT_TIMEFAIL (1<<2) /* symlinks only */
438 @@ -677,6 +679,7 @@ extern int file_extra_cnt;
439  extern int inc_recurse;
440  extern int uid_ndx;
441  extern int gid_ndx;
442 +extern int crtimes_ndx;
443  extern int fileflags_ndx;
444  extern int acls_ndx;
445  extern int xattrs_ndx;
446 @@ -684,6 +687,7 @@ extern int xattrs_ndx;
447  #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
448  #define EXTRA_LEN (sizeof (union file_extras))
449  #define PTR_EXTRA_CNT ((sizeof (char *) + EXTRA_LEN - 1) / EXTRA_LEN)
450 +#define TIME_EXTRA_CNT ((SIZEOF_TIME_T + EXTRA_LEN - 1) / EXTRA_LEN)
451  #define DEV_EXTRA_CNT 2
452  #define DIRNODE_EXTRA_CNT 3
453  #define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
454 @@ -951,6 +955,7 @@ typedef struct {
455  
456  typedef struct {
457      STRUCT_STAT st;
458 +    time_t crtime;
459  #ifdef SUPPORT_ACLS
460      struct rsync_acl *acc_acl; /* access ACL */
461      struct rsync_acl *def_acl; /* default ACL */
462 diff --git a/rsync.yo b/rsync.yo
463 --- a/rsync.yo
464 +++ b/rsync.yo
465 @@ -367,6 +367,7 @@ to the detailed description below for a complete description.  verb(
466   -D                          same as --devices --specials
467   -t, --times                 preserve modification times
468   -O, --omit-dir-times        omit directories from --times
469 + -N, --crtimes               preserve create times (newness)
470       --super                 receiver attempts super-user activities
471       --fake-super            store/recover privileged attrs using xattrs
472   -S, --sparse                handle sparse files efficiently
473 @@ -1105,6 +1106,9 @@ it is preserving modification times (see bf(--times)).  If NFS is sharing
474  the directories on the receiving side, it is a good idea to use bf(-O).
475  This option is inferred if you use bf(--backup) without bf(--backup-dir).
476  
477 +dit(bf(-N, --crtimes)) This tells rsync to set the create times (newness) of
478 +the destination files to the same value as the source files.
479 +
480  dit(bf(--super)) This tells the receiving side to attempt super-user
481  activities even if the receiving rsync wasn't run by the super-user.  These
482  activities include: preserving users via the bf(--owner) option, preserving
483 @@ -1811,7 +1815,7 @@ with older versions of rsync, but that also turns on the output of other
484  verbose messages).
485  
486  The "%i" escape has a cryptic output that is 11 letters long.  The general
487 -format is like the string bf(YXcstpogfax), where bf(Y) is replaced by the
488 +format is like the string bf(YXcstpogfaxn), where bf(Y) is replaced by the
489  type of update being done, bf(X) is replaced by the file-type, and the
490  other letters represent attributes that may be output if they are being
491  modified.
492 @@ -1870,6 +1874,8 @@ quote(itemization(
493    it() The bf(f) means that the fileflags information changed.
494    it() The bf(a) means that the ACL information changed.
495    it() The bf(x) means that the extended attribute information changed.
496 +  it() A bf(n) means the create time (newness) is different and is being
497 +  updated to the sender's value (requires bf(--crtimes)).
498  ))
499  
500  One other output is possible:  when deleting files, the "%i" will output
501 diff --git a/syscall.c b/syscall.c
502 --- a/syscall.c
503 +++ b/syscall.c
504 @@ -37,6 +37,13 @@ extern int force_change;
505  extern int preserve_perms;
506  extern int preserve_executability;
507  
508 +#pragma pack(push, 4)
509 +struct create_time {
510 +       uint32 length;
511 +       struct timespec crtime;
512 +};
513 +#pragma pack(pop)
514 +
515  #define RETURN_ERROR_IF(x,e) \
516         do { \
517                 if (x) { \
518 @@ -529,6 +536,36 @@ OFF_T do_lseek(int fd, OFF_T offset, int whence)
519  #endif
520  }
521  
522 +time_t get_create_time(const char *path)
523 +{
524 +       static struct create_time attrBuf;
525 +       struct attrlist attrList;
526 +
527 +       memset(&attrList, 0, sizeof attrList);
528 +       attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
529 +       attrList.commonattr = ATTR_CMN_CRTIME;
530 +       if (getattrlist(path, &attrList, &attrBuf, sizeof attrBuf, FSOPT_NOFOLLOW) < 0)
531 +               return 0;
532 +       return attrBuf.crtime.tv_sec;
533 +}
534 +
535 +int set_create_time(const char *path, time_t crtime)
536 +{
537 +       struct attrlist attrList;
538 +       struct timespec ts;
539 +
540 +       if (dry_run) return 0;
541 +       RETURN_ERROR_IF_RO_OR_LO;
542 +
543 +       ts.tv_sec = crtime;
544 +       ts.tv_nsec = 0;
545 +
546 +       memset(&attrList, 0, sizeof attrList);
547 +       attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
548 +       attrList.commonattr = ATTR_CMN_CRTIME;
549 +       return setattrlist(path, &attrList, &ts, sizeof ts, FSOPT_NOFOLLOW);
550 +}
551 +
552  #ifdef HAVE_UTIMENSAT
553  int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode, uint32 fileflags)
554  {
555 diff --git a/testsuite/crtimes.test b/testsuite/crtimes.test
556 new file mode 100644
557 --- /dev/null
558 +++ b/testsuite/crtimes.test
559 @@ -0,0 +1,24 @@
560 +#! /bin/sh
561 +
562 +# Test rsync copying create times
563 +
564 +. "$suitedir/rsync.fns"
565 +
566 +# Setting an older time via touch sets the create time to the mtime.
567 +# Setting it to a newer time affects just the mtime.
568 +
569 +mkdir "$fromdir"
570 +echo hiho "$fromdir/foo"
571 +
572 +touch -t 200101011111.11 "$fromdir"
573 +touch -t 200202022222.22 "$fromdir"
574 +
575 +touch -t 200111111111.11 "$fromdir/foo"
576 +touch -t 200212122222.22 "$fromdir/foo"
577 +
578 +TLS_ARGS=--crtimes
579 +
580 +checkit "$RSYNC -rtgvvv --crtimes \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
581 +
582 +# The script would have aborted on error, so getting here means we've won.
583 +exit 0
584 diff --git a/testsuite/rsync.fns b/testsuite/rsync.fns
585 --- a/testsuite/rsync.fns
586 +++ b/testsuite/rsync.fns
587 @@ -24,9 +24,9 @@ todir="$tmpdir/to"
588  chkdir="$tmpdir/chk"
589  
590  # For itemized output:
591 -all_plus='+++++++++'
592 -allspace='         '
593 -dots='.....' # trailing dots after changes
594 +all_plus='++++++++++'
595 +allspace='          '
596 +dots='......' # trailing dots after changes
597  tab_ch='       ' # a single tab character
598  
599  # Berkley's nice.
600 diff --git a/tls.c b/tls.c
601 --- a/tls.c
602 +++ b/tls.c
603 @@ -107,6 +107,8 @@ static int stat_xattr(const char *fname, STRUCT_STAT *fst)
604  
605  #endif
606  
607 +static int display_crtimes = 0;
608 +
609  static void failed(char const *what, char const *where)
610  {
611         fprintf(stderr, PROGRAM ": %s %s: %s\n",
612 @@ -114,16 +116,36 @@ static void failed(char const *what, char const *where)
613         exit(1);
614  }
615  
616 +static void storetime(char *dest, time_t t, size_t destsize)
617 +{
618 +       if (t) {
619 +               struct tm *mt = gmtime(&t);
620 +
621 +               snprintf(dest, destsize,
622 +                       "%04d-%02d-%02d %02d:%02d:%02d ",
623 +                       (int)mt->tm_year + 1900,
624 +                       (int)mt->tm_mon + 1,
625 +                       (int)mt->tm_mday,
626 +                       (int)mt->tm_hour,
627 +                       (int)mt->tm_min,
628 +                       (int)mt->tm_sec);
629 +       } else
630 +               strlcpy(dest, "                    ", destsize);
631 +}
632 +
633  static void list_file(const char *fname)
634  {
635         STRUCT_STAT buf;
636 +       time_t crtime = 0;
637         char permbuf[PERMSTRING_SIZE];
638 -       struct tm *mt;
639 -       char datebuf[50];
640 +       char mtimebuf[50];
641 +       char crtimebuf[50];
642         char linkbuf[4096];
643  
644         if (do_lstat(fname, &buf) < 0)
645                 failed("stat", fname);
646 +       if (display_crtimes && (crtime = get_create_time(fname)) == 0)
647 +               failed("get_create_time", fname);
648  #ifdef SUPPORT_XATTRS
649         if (am_root < 0)
650                 stat_xattr(fname, &buf);
651 @@ -158,19 +180,11 @@ static void list_file(const char *fname)
652  
653         permstring(permbuf, buf.st_mode);
654  
655 -       if (buf.st_mtime) {
656 -               mt = gmtime(&buf.st_mtime);
657 -
658 -               snprintf(datebuf, sizeof datebuf,
659 -                       "%04d-%02d-%02d %02d:%02d:%02d",
660 -                       (int)mt->tm_year + 1900,
661 -                       (int)mt->tm_mon + 1,
662 -                       (int)mt->tm_mday,
663 -                       (int)mt->tm_hour,
664 -                       (int)mt->tm_min,
665 -                       (int)mt->tm_sec);
666 -       } else
667 -               strlcpy(datebuf, "                   ", sizeof datebuf);
668 +       storetime(mtimebuf, buf.st_mtime, sizeof mtimebuf);
669 +       if (display_crtimes)
670 +               storetime(crtimebuf, crtime, sizeof crtimebuf);
671 +       else
672 +               crtimebuf[0] = '\0';
673  
674         /* TODO: Perhaps escape special characters in fname? */
675  
676 @@ -181,13 +195,14 @@ static void list_file(const char *fname)
677                     (long)minor(buf.st_rdev));
678         } else /* NB: use double for size since it might not fit in a long. */
679                 printf("%12.0f", (double)buf.st_size);
680 -       printf(" %6ld.%-6ld %6ld %s %s%s\n",
681 +       printf(" %6ld.%-6ld %6ld %s%s%s%s\n",
682                (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
683 -              datebuf, fname, linkbuf);
684 +              mtimebuf, crtimebuf, fname, linkbuf);
685  }
686  
687  static struct poptOption long_options[] = {
688    /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
689 +  {"crtimes",         'N', POPT_ARG_NONE,   &display_crtimes, 0, 0, 0},
690    {"link-times",      'l', POPT_ARG_NONE,   &link_times, 0, 0, 0 },
691    {"link-owner",      'L', POPT_ARG_NONE,   &link_owner, 0, 0, 0 },
692  #ifdef SUPPORT_XATTRS
693 @@ -203,6 +218,7 @@ static void tls_usage(int ret)
694    fprintf(F,"usage: " PROGRAM " [OPTIONS] FILE ...\n");
695    fprintf(F,"Trivial file listing program for portably checking rsync\n");
696    fprintf(F,"\nOptions:\n");
697 +  fprintf(F," -N, --crtimes               display create times (newness)\n");
698    fprintf(F," -l, --link-times            display the time on a symlink\n");
699    fprintf(F," -L, --link-owner            display the owner+group on a symlink\n");
700  #ifdef SUPPORT_XATTRS