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