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