Use "use warnings" rather than -w on the #! line.
[rsync/rsync-patches.git] / crtimes.diff
CommitLineData
a5e6228a
WD
1This patch adds a --crtimes (-N) option that will preserve
2create times on OS X.
3
4To use this patch, run these commands for a successful build:
5
f9df736a 6 patch -p1 <patches/fileflags.diff
a5e6228a
WD
7 patch -p1 <patches/crtimes.diff
8 ./configure (optional if already run)
9 make
10
11diff --git a/compat.c b/compat.c
12--- a/compat.c
13+++ b/compat.c
f9df736a 14@@ -45,6 +45,7 @@ extern int force_change;
a5e6228a
WD
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;
ae306a29 22@@ -63,7 +64,7 @@ extern char *iconv_opt;
a5e6228a
WD
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
85096e5e 29 int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
ae306a29
WD
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)
a5e6228a
WD
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);
f9df736a 37 if (preserve_fileflags || (force_change && !am_sender))
a5e6228a
WD
38 fileflags_ndx = ++file_extra_cnt;
39 if (preserve_acls && !am_sender)
40diff --git a/flist.c b/flist.c
41--- a/flist.c
42+++ b/flist.c
f9df736a 43@@ -56,6 +56,7 @@ extern int preserve_fileflags;
a5e6228a
WD
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;
ae306a29
WD
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)
a5e6228a
WD
54 {
55- static time_t modtime;
56+ static time_t modtime, crtime;
57 static mode_t mode;
f9df736a 58 #ifdef SUPPORT_FILEFLAGS
a5e6228a 59 static uint32 fileflags;
ae306a29 60@@ -479,6 +480,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
a5e6228a
WD
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) {
ae306a29 74@@ -548,6 +556,8 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
a5e6228a
WD
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));
f9df736a 82 #ifdef SUPPORT_FILEFLAGS
ae306a29 83@@ -638,7 +648,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
a5e6228a
WD
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;
f9df736a 90 #ifdef SUPPORT_FILEFLAGS
a5e6228a 91 static uint32 fileflags;
248786a4
WD
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,
a5e6228a
WD
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
248786a4 121@@ -932,6 +957,8 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
a5e6228a
WD
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
248786a4 130@@ -1308,6 +1335,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
a5e6228a 131 F_OWNER(file) = st.st_uid;
f9df736a 132 if (gid_ndx) /* Check gid_ndx instead of preserve_gid for del support */
a5e6228a
WD
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;
139diff --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;
c0c7984e 150@@ -40,6 +41,7 @@ extern int preserve_xattrs;
85096e5e 151 extern int preserve_links;
a5e6228a
WD
152 extern int preserve_devices;
153 extern int preserve_specials;
a5e6228a 154+extern int preserve_fileflags;
85096e5e
WD
155 extern int preserve_hard_links;
156 extern int preserve_executability;
f9df736a 157 extern int preserve_fileflags;
abd3adb8 158@@ -623,6 +625,13 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
a5e6228a
WD
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))
abd3adb8 172@@ -666,6 +675,12 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
c0c7984e 173 : iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !(iflags & ITEM_MATCHED)
a5e6228a 174 && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
a5e6228a
WD
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 ;
abd3adb8 185@@ -1226,6 +1241,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
a5e6228a
WD
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)) {
abd3adb8 193@@ -1240,14 +1256,16 @@ static void list_file_entry(struct file_struct *f)
a5e6228a
WD
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 }
abd3adb8 212@@ -1339,6 +1357,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
a5e6228a
WD
213 return;
214 }
215 }
216+ sx.crtime = 0;
c0c7984e 217
a5e6228a
WD
218 #ifdef SUPPORT_ACLS
219 sx.acc_acl = sx.def_acl = NULL;
248786a4
WD
220diff --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
a5e6228a
WD
239diff --git a/ifuncs.h b/ifuncs.h
240--- a/ifuncs.h
241+++ b/ifuncs.h
c0c7984e
WD
242@@ -67,6 +67,28 @@ d_name(struct dirent *di)
243 #endif
a5e6228a
WD
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 {
271diff --git a/log.c b/log.c
272--- a/log.c
273+++ b/log.c
ae306a29 274@@ -663,7 +663,8 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
f9df736a 275 c[8] = !(iflags & ITEM_REPORT_FFLAGS) ? '.' : 'f';
a5e6228a
WD
276 c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
277 c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
f9df736a
WD
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 ? '+' : '?';
a5e6228a
WD
284diff --git a/options.c b/options.c
285--- a/options.c
286+++ b/options.c
c0c7984e 287@@ -60,6 +60,7 @@ int preserve_specials = 0;
a5e6228a
WD
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;
abd3adb8 295@@ -363,6 +364,7 @@ void usage(enum logcode F)
a5e6228a
WD
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");
f9df736a 299+ rprintf(F," -N, --crtimes preserve create times (newness)\n");
a5e6228a
WD
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");
abd3adb8 303@@ -509,6 +511,9 @@ static struct poptOption long_options[] = {
a5e6228a
WD
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 },
abd3adb8 313@@ -1800,6 +1805,8 @@ void server_options(char **args, int *argc_p)
a5e6228a
WD
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)
322diff --git a/rsync.c b/rsync.c
323--- a/rsync.c
324+++ b/rsync.c
248786a4
WD
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,
c0c7984e 334 else
85096e5e 335 file->flags |= FLAG_TIME_FAILED;
a5e6228a
WD
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)
248786a4 348@@ -622,7 +631,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
a5e6228a
WD
349 /* Change permissions before putting the file into place. */
350 set_file_attrs(fnametmp, file, NULL, fnamecmp,
f9df736a
WD
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));
a5e6228a 354
f9df736a
WD
355 /* move tmp file over real file */
356 if (verbose > 2)
248786a4 357@@ -653,7 +662,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
a5e6228a
WD
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) {
366diff --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. */
e8972101 377@@ -156,6 +157,7 @@
a5e6228a
WD
378 #define ATTRS_REPORT (1<<0)
379 #define ATTRS_SKIP_MTIME (1<<1)
f9df736a
WD
380 #define ATTRS_DELAY_IMMUTABLE (1<<2)
381+#define ATTRS_SKIP_CRTIME (1<<3)
a5e6228a
WD
382
383 #define FULL_FLUSH 1
384 #define NORMAL_FLUSH 0
e8972101 385@@ -172,7 +174,7 @@
a5e6228a
WD
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)
c0c7984e
WD
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 */
ae306a29 394@@ -657,6 +659,7 @@ extern int file_extra_cnt;
a5e6228a
WD
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;
ae306a29 402@@ -664,6 +667,7 @@ extern int xattrs_ndx;
a5e6228a
WD
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)
248786a4 410@@ -931,6 +935,7 @@ typedef struct {
a5e6228a
WD
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 */
418diff --git a/rsync.yo b/rsync.yo
419--- a/rsync.yo
420+++ b/rsync.yo
abd3adb8 421@@ -354,6 +354,7 @@ to the detailed description below for a complete description. verb(
a5e6228a
WD
422 -D same as --devices --specials
423 -t, --times preserve modification times
424 -O, --omit-dir-times omit directories from --times
f9df736a 425+ -N, --crtimes preserve create times (newness)
a5e6228a
WD
426 --super receiver attempts super-user activities
427 --fake-super store/recover privileged attrs using xattrs
428 -S, --sparse handle sparse files efficiently
abd3adb8 429@@ -1032,6 +1033,9 @@ it is preserving modification times (see bf(--times)). If NFS is sharing
a5e6228a
WD
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
f9df736a 433+dit(bf(-N, --crtimes)) This tells rsync to set the create times (newness) of
a5e6228a
WD
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
abd3adb8 439@@ -1696,7 +1700,7 @@ with older versions of rsync, but that also turns on the output of other
a5e6228a
WD
440 verbose messages).
441
442 The "%i" escape has a cryptic output that is 11 letters long. The general
f9df736a
WD
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
a5e6228a
WD
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.
abd3adb8 448@@ -1755,6 +1759,8 @@ quote(itemization(
f9df736a 449 it() The bf(f) means that the fileflags information changed.
a5e6228a 450 it() The bf(a) means that the ACL information changed.
f9df736a
WD
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
a5e6228a
WD
457diff --git a/syscall.c b/syscall.c
458--- a/syscall.c
459+++ b/syscall.c
f9df736a 460@@ -37,6 +37,11 @@ extern int force_change;
a5e6228a
WD
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) { \
c0c7984e
WD
472@@ -394,3 +399,33 @@ OFF_T do_lseek(int fd, OFF_T offset, int whence)
473 return lseek(fd, offset, whence);
a5e6228a
WD
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+}
506diff --git a/testsuite/crtimes.test b/testsuite/crtimes.test
507new 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
f9df736a
WD
535diff --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"
a5e6228a
WD
551diff --git a/tls.c b/tls.c
552--- a/tls.c
553+++ b/tls.c
e66d6d51 554@@ -107,6 +107,8 @@ static int stat_xattr(const char *fname, STRUCT_STAT *fst)
a5e6228a
WD
555
556 #endif
557
558+static int display_crtimes = 0;
f9df736a 559+
a5e6228a
WD
560 static void failed(char const *what, char const *where)
561 {
562 fprintf(stderr, PROGRAM ": %s %s: %s\n",
e66d6d51 563@@ -114,16 +116,36 @@ static void failed(char const *what, char const *where)
a5e6228a
WD
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);
e66d6d51 602@@ -158,19 +180,11 @@ static void list_file(const char *fname)
a5e6228a
WD
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
e66d6d51 627@@ -181,13 +195,14 @@ static void list_file(const char *fname)
a5e6228a
WD
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},
e66d6d51
WD
641 {"link-times", 'l', POPT_ARG_NONE, &link_times, 0, 0, 0 },
642 {"link-owner", 'L', POPT_ARG_NONE, &link_owner, 0, 0, 0 },
a5e6228a 643 #ifdef SUPPORT_XATTRS
e66d6d51 644@@ -203,6 +218,7 @@ static void tls_usage(int ret)
a5e6228a
WD
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");
f9df736a 648+ fprintf(F," -N, --crtimes display create times (newness)\n");
e66d6d51
WD
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");
a5e6228a 651 #ifdef SUPPORT_XATTRS