| 1 | This patch handles the creation time on OS X by putting it into an |
| 2 | extended attribute. This is a modified version of a patch that was |
| 3 | provided by Wesley W. Terpstra. |
| 4 | |
| 5 | To use this patch, run these commands for a successful build: |
| 6 | |
| 7 | patch -p1 <patches/osx-create-time.diff |
| 8 | ./configure (optional if already run) |
| 9 | make |
| 10 | |
| 11 | --- old/lib/sysxattrs.c |
| 12 | +++ new/lib/sysxattrs.c |
| 13 | @@ -52,29 +52,118 @@ ssize_t sys_llistxattr(const char *path, |
| 14 | |
| 15 | #elif HAVE_OSX_XATTRS |
| 16 | |
| 17 | +#include <sys/attr.h> |
| 18 | + |
| 19 | +#define CRTIME_XATTR "com.apple.crtime96" |
| 20 | +#define IS_CRTIME(name) (*(name) == *CRTIME_XATTR && strcmp(name, CRTIME_XATTR) == 0) |
| 21 | +#define CRTIME_XATTR_LEN (8+4) |
| 22 | + |
| 23 | +struct CreationTime { |
| 24 | + unsigned long length; |
| 25 | + struct timespec crtime; |
| 26 | +}; |
| 27 | + |
| 28 | +static struct timespec *getCreationTime(const char *path) |
| 29 | +{ |
| 30 | + static struct CreationTime attrBuf; |
| 31 | + struct attrlist attrList; |
| 32 | + |
| 33 | + memset(&attrList, 0, sizeof attrList); |
| 34 | + attrList.bitmapcount = ATTR_BIT_MAP_COUNT; |
| 35 | + attrList.commonattr = ATTR_CMN_CRTIME; |
| 36 | + if (getattrlist(path, &attrList, &attrBuf, sizeof attrBuf, FSOPT_NOFOLLOW) < 0) |
| 37 | + return NULL; |
| 38 | + return &attrBuf.crtime; |
| 39 | +} |
| 40 | + |
| 41 | +static ssize_t get_crtime_xattr(const char *path, char *buf, size_t size) |
| 42 | +{ |
| 43 | + struct timespec *crtime_p; |
| 44 | + |
| 45 | + if (buf == NULL) |
| 46 | + return CRTIME_XATTR_LEN; |
| 47 | + if (size < CRTIME_XATTR_LEN) |
| 48 | + return -1; /* Doesn't happen with rsync code... */ |
| 49 | + |
| 50 | + if ((crtime_p = getCreationTime(path)) == NULL) |
| 51 | + return -1; |
| 52 | + |
| 53 | + SIVAL(buf, 0, crtime_p->tv_sec); |
| 54 | +#if SIZEOF_TIME_T > 4 |
| 55 | + SIVAL(buf, 4, crtime_p->tv_sec >> 32); |
| 56 | +#else |
| 57 | + SIVAL(buf, 4, 0); |
| 58 | +#endif |
| 59 | + SIVAL(buf, 8, crtime_p->tv_nsec); |
| 60 | + |
| 61 | + return CRTIME_XATTR_LEN; |
| 62 | +} |
| 63 | + |
| 64 | +static int set_crtime_xattr(const char *path, const char *buf, size_t size) |
| 65 | +{ |
| 66 | + struct attrlist attrList; |
| 67 | + struct timespec crtime; |
| 68 | + |
| 69 | + if (size != CRTIME_XATTR_LEN) |
| 70 | + return -1; |
| 71 | + |
| 72 | + crtime.tv_sec = IVAL(buf, 0); |
| 73 | +#if SIZEOF_TIME_T > 4 |
| 74 | + crtime.tv_sec += (time_t)IVAL(buf, 4) << 32; |
| 75 | +#endif |
| 76 | + crtime.tv_nsec = IVAL(buf, 8); |
| 77 | + |
| 78 | + memset(&attrList, 0, sizeof attrList); |
| 79 | + attrList.bitmapcount = ATTR_BIT_MAP_COUNT; |
| 80 | + attrList.commonattr = ATTR_CMN_CRTIME; |
| 81 | + return setattrlist(path, &attrList, &crtime, sizeof crtime, FSOPT_NOFOLLOW); |
| 82 | +} |
| 83 | + |
| 84 | ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size) |
| 85 | { |
| 86 | + if (IS_CRTIME(name)) |
| 87 | + return get_crtime_xattr(path, value, size); |
| 88 | return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW); |
| 89 | } |
| 90 | |
| 91 | ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size) |
| 92 | { |
| 93 | + /* XXX Figure out how to get creation time from an open filedes? */ |
| 94 | return fgetxattr(filedes, name, value, size, 0, 0); |
| 95 | } |
| 96 | |
| 97 | int sys_lsetxattr(const char *path, const char *name, const void *value, size_t size) |
| 98 | { |
| 99 | + if (IS_CRTIME(name)) |
| 100 | + return set_crtime_xattr(path, value, size); |
| 101 | return setxattr(path, name, value, size, 0, XATTR_NOFOLLOW); |
| 102 | } |
| 103 | |
| 104 | int sys_lremovexattr(const char *path, const char *name) |
| 105 | { |
| 106 | + /* Every file on hfs+ has this; you can't remove it... */ |
| 107 | + if (IS_CRTIME(name)) |
| 108 | + return 0; |
| 109 | return removexattr(path, name, XATTR_NOFOLLOW); |
| 110 | } |
| 111 | |
| 112 | ssize_t sys_llistxattr(const char *path, char *list, size_t size) |
| 113 | { |
| 114 | - return listxattr(path, list, size, XATTR_NOFOLLOW); |
| 115 | + ssize_t ret = listxattr(path, list, size, XATTR_NOFOLLOW); |
| 116 | + if (ret < 0) |
| 117 | + return ret; |
| 118 | + if (getCreationTime(path) != NULL) { |
| 119 | + ret += sizeof CRTIME_XATTR; |
| 120 | + if (list) { |
| 121 | + if ((size_t)ret > size) { |
| 122 | + errno = ERANGE; |
| 123 | + return -1; |
| 124 | + } |
| 125 | + memcpy(list + ret - sizeof CRTIME_XATTR, |
| 126 | + CRTIME_XATTR, sizeof CRTIME_XATTR); |
| 127 | + } |
| 128 | + } |
| 129 | + return ret; |
| 130 | } |
| 131 | |
| 132 | #elif HAVE_FREEBSD_XATTRS |