The patches for 3.0.0pre10.
[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
6 patch -p1 <patches/flags.diff
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
14@@ -44,6 +44,7 @@ extern int protocol_version;
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@@ -61,7 +62,7 @@ extern iconv_t ic_send, ic_recv;
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
WD
29 int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
30
31@@ -135,6 +136,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);
37 if (preserve_fileflags)
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
43@@ -54,6 +54,7 @@ extern int fileflags_ndx;
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@@ -343,7 +344,7 @@ int push_pathname(const char *dir, int len)
52
53 static void send_file_entry(int f, struct file_struct *file, 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_FLAGS
59 static uint32 fileflags;
60@@ -462,6 +463,13 @@ static void send_file_entry(int f, struct file_struct *file, int ndx, int first_
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@@ -532,6 +540,8 @@ static void send_file_entry(int f, struct file_struct *file, int ndx, int first_
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_FLAGS
83@@ -624,7 +634,7 @@ static void send_file_entry(int f, struct file_struct *file, int ndx, int first_
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_FLAGS
91 static uint32 fileflags;
92@@ -758,6 +768,19 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
93 } else
94 modtime = read_int(f);
95 }
96+ if (crtimes_ndx) {
97+ if (!(xflags & XMIT_CRTIME_EQ_MTIME)) {
98+ crtime = read_varlong(f, 4);
99+#if SIZEOF_TIME_T < SIZEOF_INT64
100+ if (!am_generator && (int64)(time_t)crtime != crtime) {
101+ rprintf(FERROR_XFER,
102+ "Create time value of %s truncated on receiver.\n",
103+ lastname);
104+ }
105+#endif
106+ } else
107+ crtime = modtime;
108+ }
109 if (!(xflags & XMIT_SAME_MODE))
110 mode = from_wire_mode(read_int(f));
111
112@@ -898,6 +921,8 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
113 F_GROUP(file) = gid;
114 file->flags |= gid_flags;
115 }
116+ if (crtimes_ndx)
117+ f_crtime_set(file, (time_t)crtime);
118 if (unsort_ndx)
119 F_NDX(file) = flist->used + flist->ndx_start;
120
121@@ -1234,6 +1259,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
122 F_OWNER(file) = st.st_uid;
123 if (gid_ndx)
124 F_GROUP(file) = st.st_gid;
125+ if (crtimes_ndx)
126+ f_crtime_set(file, get_create_time(fname));
127
128 if (basename != thisname)
129 file->dirname = lastdir;
130diff --git a/generator.c b/generator.c
131--- a/generator.c
132+++ b/generator.c
133@@ -21,6 +21,7 @@
134 */
135
136 #include "rsync.h"
137+#include "ifuncs.h"
138
139 extern int verbose;
140 extern int dry_run;
85096e5e
WD
141@@ -41,6 +42,7 @@ extern int preserve_xattrs;
142 extern int preserve_links;
a5e6228a
WD
143 extern int preserve_devices;
144 extern int preserve_specials;
a5e6228a 145+extern int preserve_fileflags;
85096e5e
WD
146 extern int preserve_hard_links;
147 extern int preserve_executability;
a5e6228a 148 extern int preserve_perms;
85096e5e 149@@ -139,6 +141,7 @@ enum delret {
a5e6228a
WD
150 /* Forward declaration for delete_item(). */
151 static enum delret delete_dir_contents(char *fname, int flags);
152
153+
154 static int is_backup_file(char *fn)
155 {
156 int k = strlen(fn) - backup_suffix_len;
85096e5e 157@@ -603,6 +606,13 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
a5e6228a
WD
158 if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP) && sxp->st.st_gid != (gid_t)F_GROUP(file))
159 return 0;
160
161+ if (crtimes_ndx) {
162+ if (sxp->crtime == 0)
163+ sxp->crtime = get_create_time(fname);
164+ if (cmp_time(sxp->crtime, f_crtime(file)) != 0)
165+ return 0;
166+ }
167+
168 #ifdef SUPPORT_ACLS
169 if (preserve_acls && !S_ISLNK(file->mode)) {
170 if (!ACL_READY(*sxp))
85096e5e 171@@ -642,6 +652,12 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
a5e6228a
WD
172 && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
173 || (keep_time && cmp_time(file->modtime, sxp->st.st_mtime) != 0))
174 iflags |= ITEM_REPORT_TIME;
175+ if (crtimes_ndx) {
176+ if (sxp->crtime == 0)
177+ sxp->crtime = get_create_time(fnamecmp);
178+ if (cmp_time(sxp->crtime, f_crtime(file)) != 0)
179+ iflags |= ITEM_REPORT_CRTIME;
180+ }
181 #if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
182 if (S_ISLNK(file->mode)) {
183 ;
85096e5e 184@@ -1182,6 +1198,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
a5e6228a
WD
185 static void list_file_entry(struct file_struct *f)
186 {
187 char permbuf[PERMSTRING_SIZE];
188+ time_t crtime = crtimes_ndx ? f_crtime(f) : 0;
189 double len;
190
191 if (!F_IS_ACTIVE(f)) {
85096e5e 192@@ -1196,14 +1213,16 @@ static void list_file_entry(struct file_struct *f)
a5e6228a
WD
193
194 #ifdef SUPPORT_LINKS
195 if (preserve_links && S_ISLNK(f->mode)) {
196- rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
197+ rprintf(FINFO, "%s %11.0f %s %s %s -> %s\n",
198 permbuf, len, timestring(f->modtime),
199+ crtimes_ndx ? timestring(crtime) : "",
200 f_name(f, NULL), F_SYMLINK(f));
201 } else
202 #endif
203 {
204- rprintf(FINFO, "%s %11.0f %s %s\n",
205+ rprintf(FINFO, "%s %11.0f %s %s %s\n",
206 permbuf, len, timestring(f->modtime),
207+ crtimes_ndx ? timestring(crtime) : "",
208 f_name(f, NULL));
209 }
210 }
85096e5e 211@@ -1290,6 +1309,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
a5e6228a
WD
212 return;
213 }
214 }
215+ sx.crtime = 0;
216 #ifdef SUPPORT_ACLS
217 sx.acc_acl = sx.def_acl = NULL;
218 #endif
219diff --git a/ifuncs.h b/ifuncs.h
220--- a/ifuncs.h
221+++ b/ifuncs.h
222@@ -57,6 +57,28 @@ from_wire_mode(int mode)
223 return mode;
224 }
225
226+static inline time_t
227+f_crtime(struct file_struct *fp)
228+{
229+#if SIZEOF_TIME_T > 4
230+ time_t crtime;
231+ memcpy(&crtime, &REQ_EXTRA(fp, crtimes_ndx)->unum, SIZEOF_TIME_T);
232+ return crtime;
233+#else
234+ return REQ_EXTRA(fp, crtimes_ndx)->unum;
235+#endif
236+}
237+
238+static inline void
239+f_crtime_set(struct file_struct *fp, time_t crtime)
240+{
241+#if SIZEOF_TIME_T > 4
242+ memcpy(&REQ_EXTRA(fp, crtimes_ndx)->unum, &crtime, SIZEOF_TIME_T);
243+#else
244+ REQ_EXTRA(fp, crtimes_ndx)->unum = (uint32)crtime;
245+#endif
246+}
247+
248 static inline int
249 isDigit(const char *ptr)
250 {
251diff --git a/log.c b/log.c
252--- a/log.c
253+++ b/log.c
85096e5e 254@@ -644,7 +644,7 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
a5e6228a
WD
255 c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
256 c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
257 c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
258- c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' : 'u';
259+ c[8] = !(iflags & ITEM_REPORT_CRTIME) ? '.' : 'n';
260 c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
261 c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
262 c[11] = '\0';
263diff --git a/options.c b/options.c
264--- a/options.c
265+++ b/options.c
266@@ -59,6 +59,7 @@ int preserve_specials = 0;
267 int preserve_uid = 0;
268 int preserve_gid = 0;
269 int preserve_times = 0;
270+int preserve_crtimes = 0;
271 int update_only = 0;
272 int cvs_exclude = 0;
273 int dry_run = 0;
85096e5e 274@@ -358,6 +359,7 @@ void usage(enum logcode F)
a5e6228a
WD
275 rprintf(F," -D same as --devices --specials\n");
276 rprintf(F," -t, --times preserve modification times\n");
277 rprintf(F," -O, --omit-dir-times omit directories from --times\n");
278+ rprintf(F," -N, --crtimes preserve create (newness) times\n");
279 rprintf(F," --super receiver attempts super-user activities\n");
280 #ifdef SUPPORT_XATTRS
281 rprintf(F," --fake-super store/recover privileged attrs using xattrs\n");
85096e5e 282@@ -495,6 +497,9 @@ static struct poptOption long_options[] = {
a5e6228a
WD
283 {"times", 't', POPT_ARG_VAL, &preserve_times, 2, 0, 0 },
284 {"no-times", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
285 {"no-t", 0, POPT_ARG_VAL, &preserve_times, 0, 0, 0 },
286+ {"crtimes", 'N', POPT_ARG_VAL, &preserve_crtimes, 1, 0, 0 },
287+ {"no-crtimes", 0, POPT_ARG_VAL, &preserve_crtimes, 0, 0, 0 },
288+ {"no-N", 0, POPT_ARG_VAL, &preserve_crtimes, 0, 0, 0 },
289 {"omit-dir-times", 'O', POPT_ARG_VAL, &omit_dir_times, 1, 0, 0 },
290 {"no-omit-dir-times",0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 },
291 {"no-O", 0, POPT_ARG_VAL, &omit_dir_times, 0, 0, 0 },
85096e5e 292@@ -1764,6 +1769,8 @@ void server_options(char **args, int *argc_p)
a5e6228a
WD
293 argstr[x++] = 'D';
294 if (preserve_times)
295 argstr[x++] = 't';
296+ if (preserve_crtimes)
297+ argstr[x++] = 'N';
298 if (preserve_perms)
299 argstr[x++] = 'p';
300 else if (preserve_executability && am_sender)
301diff --git a/rsync.c b/rsync.c
302--- a/rsync.c
303+++ b/rsync.c
85096e5e
WD
304@@ -439,6 +439,14 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
305 else if (receiver_symlink_times)
306 file->flags |= FLAG_TIME_FAILED;
a5e6228a
WD
307 }
308+ if (crtimes_ndx && !(flags & ATTRS_SKIP_CRTIME)) {
309+ time_t file_crtime = f_crtime(file);
310+ if (sxp->crtime == 0)
311+ sxp->crtime = get_create_time(fname);
312+ if (cmp_time(sxp->crtime, file_crtime) != 0
313+ && set_create_time(fname, file_crtime) == 0)
314+ updated = 1;
315+ }
316
317 change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
318 change_gid = gid_ndx && !(file->flags & FLAG_SKIP_GROUP)
85096e5e 319@@ -576,7 +584,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
a5e6228a
WD
320
321 /* Change permissions before putting the file into place. */
322 set_file_attrs(fnametmp, file, NULL, fnamecmp,
323- ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
324+ ok_to_set_time ? 0 : ATTRS_SKIP_MTIME | ATTRS_SKIP_CRTIME);
325
326 #ifdef SUPPORT_FLAGS
327 if (preserve_fileflags)
85096e5e 328@@ -611,7 +619,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
a5e6228a
WD
329
330 do_set_file_attrs:
331 set_file_attrs(fnametmp, file, NULL, fnamecmp,
332- ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
333+ ok_to_set_time ? 0 : ATTRS_SKIP_MTIME | ATTRS_SKIP_CRTIME);
334
335 if (temp_copy_name) {
336 if (do_rename(fnametmp, fname) < 0) {
337diff --git a/rsync.h b/rsync.h
338--- a/rsync.h
339+++ b/rsync.h
340@@ -60,6 +60,7 @@
341 #define XMIT_RDEV_MINOR_8_pre30 (1<<11) /* protocols 28 - 29 */
342 #define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */
343 #define XMIT_HLINK_FIRST (1<<12) /* protocols 30 - now (HLINKED files only) */
344+#define XMIT_CRTIME_EQ_MTIME (1<<13) /* protocols ?? - now */
345 #define XMIT_SAME_FLAGS (1<<14) /* protocols ?? - now */
346
347 /* These flags are used in the live flist data. */
85096e5e 348@@ -150,6 +151,7 @@
a5e6228a
WD
349
350 #define ATTRS_REPORT (1<<0)
351 #define ATTRS_SKIP_MTIME (1<<1)
352+#define ATTRS_SKIP_CRTIME (1<<2)
353
354 #define FULL_FLUSH 1
355 #define NORMAL_FLUSH 0
85096e5e 356@@ -166,7 +168,7 @@
a5e6228a
WD
357 #define FNAMECMP_FUZZY 0x83
358
359 /* For use by the itemize_changes code */
360-#define ITEM_REPORT_ATIME (1<<0)
361+#define ITEM_REPORT_CRTIME (1<<0)
362 #define ITEM_REPORT_CHECKSUM (1<<1)
363 #define ITEM_REPORT_SIZE (1<<2)
364 #define ITEM_REPORT_TIME (1<<3)
85096e5e 365@@ -636,6 +638,7 @@ extern int file_extra_cnt;
a5e6228a
WD
366 extern int inc_recurse;
367 extern int uid_ndx;
368 extern int gid_ndx;
369+extern int crtimes_ndx;
370 extern int fileflags_ndx;
371 extern int acls_ndx;
372 extern int xattrs_ndx;
85096e5e 373@@ -643,6 +646,7 @@ extern int xattrs_ndx;
a5e6228a
WD
374 #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
375 #define EXTRA_LEN (sizeof (union file_extras))
376 #define PTR_EXTRA_CNT ((sizeof (char *) + EXTRA_LEN - 1) / EXTRA_LEN)
377+#define TIME_EXTRA_CNT ((SIZEOF_TIME_T + EXTRA_LEN - 1) / EXTRA_LEN)
378 #define DEV_EXTRA_CNT 2
379 #define DIRNODE_EXTRA_CNT 3
380 #define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
85096e5e 381@@ -897,6 +901,7 @@ typedef struct {
a5e6228a
WD
382
383 typedef struct {
384 STRUCT_STAT st;
385+ time_t crtime;
386 #ifdef SUPPORT_ACLS
387 struct rsync_acl *acc_acl; /* access ACL */
388 struct rsync_acl *def_acl; /* default ACL */
389diff --git a/rsync.yo b/rsync.yo
390--- a/rsync.yo
391+++ b/rsync.yo
392@@ -350,6 +350,7 @@ to the detailed description below for a complete description. verb(
393 -D same as --devices --specials
394 -t, --times preserve modification times
395 -O, --omit-dir-times omit directories from --times
396+ -N, --crtimes preserve create (newness) times
397 --super receiver attempts super-user activities
398 --fake-super store/recover privileged attrs using xattrs
399 -S, --sparse handle sparse files efficiently
400@@ -996,6 +997,9 @@ it is preserving modification times (see bf(--times)). If NFS is sharing
401 the directories on the receiving side, it is a good idea to use bf(-O).
402 This option is inferred if you use bf(--backup) without bf(--backup-dir).
403
404+dit(bf(-N, --crtimes)) This tells rsync to set the create (newness) times of
405+the destination files to the same value as the source files.
406+
407 dit(bf(--super)) This tells the receiving side to attempt super-user
408 activities even if the receiving rsync wasn't run by the super-user. These
409 activities include: preserving users via the bf(--owner) option, preserving
410@@ -1647,7 +1651,7 @@ with older versions of rsync, but that also turns on the output of other
411 verbose messages).
412
413 The "%i" escape has a cryptic output that is 11 letters long. The general
414-format is like the string bf(YXcstpoguax), where bf(Y) is replaced by the
415+format is like the string bf(YXcstpognax), where bf(Y) is replaced by the
416 type of update being done, bf(X) is replaced by the file-type, and the
417 other letters represent attributes that may be output if they are being
418 modified.
85096e5e 419@@ -1698,8 +1702,8 @@ quote(itemization(
a5e6228a
WD
420 sender's value (requires bf(--owner) and super-user privileges).
421 it() A bf(g) means the group is different and is being updated to the
422 sender's value (requires bf(--group) and the authority to set the group).
423- it() The bf(u) slot is reserved for reporting update (access) time changes
424- (a feature that is not yet released).
425+ it() A bf(n) means the create (newness) time is different and is being
426+ updated to the sender's value (requires bf(--crtimes)).
427 it() The bf(a) means that the ACL information changed.
428 it() The bf(x) slot is reserved for reporting extended attribute changes
429 (a feature that is not yet released).
430diff --git a/syscall.c b/syscall.c
431--- a/syscall.c
432+++ b/syscall.c
433@@ -36,6 +36,11 @@ extern int list_only;
434 extern int preserve_perms;
435 extern int preserve_executability;
436
437+struct create_time {
438+ unsigned long length;
439+ struct timespec crtime;
440+};
441+
442 #define RETURN_ERROR_IF(x,e) \
443 do { \
444 if (x) { \
445@@ -300,3 +305,33 @@ char *d_name(struct dirent *di)
446 return di->d_name;
447 #endif
448 }
449+
450+time_t get_create_time(const char *path)
451+{
452+ static struct create_time attrBuf;
453+ struct attrlist attrList;
454+
455+ memset(&attrList, 0, sizeof attrList);
456+ attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
457+ attrList.commonattr = ATTR_CMN_CRTIME;
458+ if (getattrlist(path, &attrList, &attrBuf, sizeof attrBuf, FSOPT_NOFOLLOW) < 0)
459+ return 0;
460+ return attrBuf.crtime.tv_sec;
461+}
462+
463+int set_create_time(const char *path, time_t crtime)
464+{
465+ struct attrlist attrList;
466+ struct timespec ts;
467+
468+ if (dry_run) return 0;
469+ RETURN_ERROR_IF_RO_OR_LO;
470+
471+ ts.tv_sec = crtime;
472+ ts.tv_nsec = 0;
473+
474+ memset(&attrList, 0, sizeof attrList);
475+ attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
476+ attrList.commonattr = ATTR_CMN_CRTIME;
477+ return setattrlist(path, &attrList, &ts, sizeof ts, FSOPT_NOFOLLOW);
478+}
479diff --git a/testsuite/crtimes.test b/testsuite/crtimes.test
480new file mode 100644
481--- /dev/null
482+++ b/testsuite/crtimes.test
483@@ -0,0 +1,24 @@
484+#! /bin/sh
485+
486+# Test rsync copying create times
487+
488+. "$suitedir/rsync.fns"
489+
490+# Setting an older time via touch sets the create time to the mtime.
491+# Setting it to a newer time affects just the mtime.
492+
493+mkdir "$fromdir"
494+echo hiho "$fromdir/foo"
495+
496+touch -t 200101011111.11 "$fromdir"
497+touch -t 200202022222.22 "$fromdir"
498+
499+touch -t 200111111111.11 "$fromdir/foo"
500+touch -t 200212122222.22 "$fromdir/foo"
501+
502+TLS_ARGS=--crtimes
503+
504+checkit "$RSYNC -rtgvvv --crtimes \"$fromdir/\" \"$todir/\"" "$fromdir" "$todir"
505+
506+# The script would have aborted on error, so getting here means we've won.
507+exit 0
508diff --git a/tls.c b/tls.c
509--- a/tls.c
510+++ b/tls.c
511@@ -105,6 +105,8 @@ static int stat_xattr(const char *fname, STRUCT_STAT *fst)
512
513 #endif
514
515+static int display_crtimes = 0;
516+
517 static void failed(char const *what, char const *where)
518 {
519 fprintf(stderr, PROGRAM ": %s %s: %s\n",
520@@ -112,16 +114,36 @@ static void failed(char const *what, char const *where)
521 exit(1);
522 }
523
524+static void storetime(char *dest, time_t t, size_t destsize)
525+{
526+ if (t) {
527+ struct tm *mt = gmtime(&t);
528+
529+ snprintf(dest, destsize,
530+ "%04d-%02d-%02d %02d:%02d:%02d ",
531+ (int)mt->tm_year + 1900,
532+ (int)mt->tm_mon + 1,
533+ (int)mt->tm_mday,
534+ (int)mt->tm_hour,
535+ (int)mt->tm_min,
536+ (int)mt->tm_sec);
537+ } else
538+ strlcpy(dest, " ", destsize);
539+}
540+
541 static void list_file(const char *fname)
542 {
543 STRUCT_STAT buf;
544+ time_t crtime = 0;
545 char permbuf[PERMSTRING_SIZE];
546- struct tm *mt;
547- char datebuf[50];
548+ char mtimebuf[50];
549+ char crtimebuf[50];
550 char linkbuf[4096];
551
552 if (do_lstat(fname, &buf) < 0)
553 failed("stat", fname);
554+ if (display_crtimes && (crtime = get_create_time(fname)) == 0)
555+ failed("get_create_time", fname);
556 #ifdef SUPPORT_XATTRS
557 if (am_root < 0)
558 stat_xattr(fname, &buf);
559@@ -154,19 +176,11 @@ static void list_file(const char *fname)
560
561 permstring(permbuf, buf.st_mode);
562
563- if (buf.st_mtime) {
564- mt = gmtime(&buf.st_mtime);
565-
566- snprintf(datebuf, sizeof datebuf,
567- "%04d-%02d-%02d %02d:%02d:%02d",
568- (int)mt->tm_year + 1900,
569- (int)mt->tm_mon + 1,
570- (int)mt->tm_mday,
571- (int)mt->tm_hour,
572- (int)mt->tm_min,
573- (int)mt->tm_sec);
574- } else
575- strlcpy(datebuf, " ", sizeof datebuf);
576+ storetime(mtimebuf, buf.st_mtime, sizeof mtimebuf);
577+ if (display_crtimes)
578+ storetime(crtimebuf, crtime, sizeof crtimebuf);
579+ else
580+ crtimebuf[0] = '\0';
581
582 /* TODO: Perhaps escape special characters in fname? */
583
584@@ -177,13 +191,14 @@ static void list_file(const char *fname)
585 (long)minor(buf.st_rdev));
586 } else /* NB: use double for size since it might not fit in a long. */
587 printf("%12.0f", (double)buf.st_size);
588- printf(" %6ld.%-6ld %6ld %s %s%s\n",
589+ printf(" %6ld.%-6ld %6ld %s%s%s%s\n",
590 (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
591- datebuf, fname, linkbuf);
592+ mtimebuf, crtimebuf, fname, linkbuf);
593 }
594
595 static struct poptOption long_options[] = {
596 /* longName, shortName, argInfo, argPtr, value, descrip, argDesc */
597+ {"crtimes", 'N', POPT_ARG_NONE, &display_crtimes, 0, 0, 0},
598 #ifdef SUPPORT_XATTRS
599 {"fake-super", 'f', POPT_ARG_VAL, &am_root, -1, 0, 0 },
600 #endif
601@@ -197,6 +212,7 @@ static void tls_usage(int ret)
602 fprintf(F,"usage: " PROGRAM " [OPTIONS] FILE ...\n");
603 fprintf(F,"Trivial file listing program for portably checking rsync\n");
604 fprintf(F,"\nOptions:\n");
605+ fprintf(F," -N, --crtimes display create (newness) times\n");
606 #ifdef SUPPORT_XATTRS
607 fprintf(F," -f, --fake-super display attributes including fake-super xattrs\n");
608 #endif