This patch handles the creation time on OS X by putting it into an extended attribute. This is a modified version of a patch that was provided by Wesley W. Terpstra. To use this patch, run these commands for a successful build: patch -p1 + +#define CRTIME_XATTR "com.apple.crtime96" +#define IS_CRTIME(name) (*(name) == *CRTIME_XATTR && strcmp(name, CRTIME_XATTR) == 0) +#define CRTIME_XATTR_LEN (8+4) + +struct CreationTime { + unsigned long length; + struct timespec crtime; +}; + +static struct timespec *getCreationTime(const char *path) +{ + static struct CreationTime attrBuf; + struct attrlist attrList; + + memset(&attrList, 0, sizeof attrList); + attrList.bitmapcount = ATTR_BIT_MAP_COUNT; + attrList.commonattr = ATTR_CMN_CRTIME; + if (getattrlist(path, &attrList, &attrBuf, sizeof attrBuf, FSOPT_NOFOLLOW) < 0) + return NULL; + return &attrBuf.crtime; +} + +static ssize_t get_crtime_xattr(const char *path, char *buf, size_t size) +{ + struct timespec *crtime_p; + + if (buf == NULL) + return CRTIME_XATTR_LEN; + if (size < CRTIME_XATTR_LEN) + return -1; /* Doesn't happen with rsync code... */ + + if ((crtime_p = getCreationTime(path)) == NULL) + return -1; + + SIVAL(buf, 0, crtime_p->tv_sec); +#if SIZEOF_TIME_T > 4 + SIVAL(buf, 4, crtime_p->tv_sec >> 32); +#else + SIVAL(buf, 4, 0); +#endif + SIVAL(buf, 8, crtime_p->tv_nsec); + + return CRTIME_XATTR_LEN; +} + +static int set_crtime_xattr(const char *path, const char *buf, size_t size) +{ + struct attrlist attrList; + struct timespec crtime; + + if (size != CRTIME_XATTR_LEN) + return -1; + + crtime.tv_sec = IVAL(buf, 0); +#if SIZEOF_TIME_T > 4 + crtime.tv_sec += (time_t)IVAL(buf, 4) << 32; +#endif + crtime.tv_nsec = IVAL(buf, 8); + + memset(&attrList, 0, sizeof attrList); + attrList.bitmapcount = ATTR_BIT_MAP_COUNT; + attrList.commonattr = ATTR_CMN_CRTIME; + return setattrlist(path, &attrList, &crtime, sizeof crtime, FSOPT_NOFOLLOW); +} + ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size) { + if (IS_CRTIME(name)) + return get_crtime_xattr(path, value, size); return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW); } ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size) { + /* XXX Figure out how to get creation time from an open filedes? */ return fgetxattr(filedes, name, value, size, 0, 0); } int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size) { + if (IS_CRTIME(name)) + return set_crtime_xattr(path, value, size); return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW); } int sys_lremovexattr(const char *path, const char *name) { + /* Every file on hfs+ has this; you can't remove it... */ + if (IS_CRTIME(name)) + return 0; return removexattr(path, name, XATTR_NOFOLLOW); } ssize_t sys_llistxattr(const char *path, char *list, size_t size) { - return listxattr(path, list, size, XATTR_NOFOLLOW); + ssize_t ret = listxattr(path, list, size, XATTR_NOFOLLOW); + if (ret < 0) + return ret; + if (getCreationTime(path) != NULL) { + ret += sizeof CRTIME_XATTR; + if (list) { + if ((size_t)ret > size) { + errno = ERANGE; + return -1; + } + memcpy(list + ret - sizeof CRTIME_XATTR, + CRTIME_XATTR, sizeof CRTIME_XATTR); + } + } + return ret; } #elif HAVE_FREEBSD_XATTRS