From 11f767b3cfa8bba36faedd4e66d14c3809f8982d Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 7 May 2013 23:09:28 +0000 Subject: [PATCH] copyfile-103.tar.gz --- copyfile.c | 187 ++++++++++++------- copyfile.h | 1 + copyfile.xcodeproj/project.pbxproj | 52 +++++- copyfile_private.h | 33 ++++ xattr_properties.c | 288 +++++++++++++++++++++++++++++ xattr_properties.h | 126 +++++++++++++ xcodescripts/install_files.sh | 13 +- 7 files changed, 634 insertions(+), 66 deletions(-) create mode 100644 copyfile_private.h create mode 100644 xattr_properties.c create mode 100644 xattr_properties.h diff --git a/copyfile.c b/copyfile.c index a1861ea..8a13514 100644 --- a/copyfile.c +++ b/copyfile.c @@ -51,24 +51,28 @@ #endif #include -#if !TARGET_OS_EMBEDDED +#if !TARGET_OS_IPHONE #include #define XATTR_QUARANTINE_NAME qtn_xattr_name -#else /* TARGET_OS_EMBEDDED */ +#else /* TARGET_OS_IPHONE */ #define qtn_file_t void * #define QTN_SERIALIZED_DATA_MAX 0 static void * qtn_file_alloc(void) { return NULL; } static int qtn_file_init_with_fd(void *x, int y) { return -1; } +static int qtn_file_init_with_path(void *x, const char *path) { return -1; } +static int qtn_file_init_with_data(void *x, const void *data, size_t len) { return -1; } static void qtn_file_free(void *x) { return; } static int qtn_file_apply_to_fd(void *x, int y) { return 0; } static char *qtn_error(int x) { return NULL; } static int qtn_file_to_data(void *x, char *y, size_t z) { return -1; } static void *qtn_file_clone(void *x) { return NULL; } #define XATTR_QUARANTINE_NAME "figgledidiggledy" -#endif /* TARGET_OS_EMBEDDED */ +#endif /* TARGET_OS_IPHONE */ #include "copyfile.h" +#include "copyfile_private.h" +#include "xattr_properties.h" enum cfInternalFlags { cfDelayAce = 1 << 0, @@ -105,6 +109,7 @@ struct _copyfile_state off_t totalCopied; int err; char *xattr_name; + CopyOperationIntent_t copyIntent; }; struct acl_entry { @@ -1050,6 +1055,9 @@ int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_ (void)fcntl(s->src_fd, F_NOCACHE, 1); (void)fcntl(s->dst_fd, F_NOCACHE, 1); +#ifdef F_SINGLE_WRITER + (void)fcntl(s->dst_fd, F_SINGLE_WRITER, 1); +#endif ret = copyfile_internal(s, flags); if (ret == -1) @@ -2288,6 +2296,11 @@ static int copyfile_xattr(copyfile_state_t s) } #endif + // If we have a copy intention stated, and the EA is to be ignored, we ignore it + if (s->copyIntent + && _PreserveEA(name, s->copyIntent) == 0) + continue; + s->xattr_name = strdup(name); if (s->statuscb) { @@ -2396,6 +2409,11 @@ int copyfile_state_get(copyfile_state_t s, uint32_t flag, void *ret) case COPYFILE_STATE_XATTRNAME: *(char**)ret = s->xattr_name; break; +#endif +#ifdef COPYFILE_STATE_INTENT + case COPYFILE_STATE_INTENT: + *(CopyOperationIntent_t*)ret = s->copyIntent; + break; #endif default: errno = EINVAL; @@ -2467,6 +2485,11 @@ int copyfile_state_set(copyfile_state_t s, uint32_t flag, const void * thing) case COPYFILE_STATE_STATUS_CTX: s->ctx = (void*)thing; break; +#endif +#ifdef COPYFILE_STATE_INTENT + case COPYFILE_STATE_INTENT: + s->copyIntent = *(CopyOperationIntent_t*)thing; + break; #endif default: errno = EINVAL; @@ -2541,7 +2564,7 @@ int main(int c, char *v[]) #define offsetof(type, member) ((size_t)(&((type *)0)->member)) -#define XATTR_MAXATTRLEN (32*1024) +#define XATTR_MAXATTRLEN (16*1024*1024) /* @@ -2632,7 +2655,7 @@ int main(int c, char *v[]) #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */ /* Implementation Limits */ -#define ATTR_MAX_SIZE (128*1024) /* 128K maximum attribute data size */ +#define ATTR_MAX_SIZE (16*1024*1024) /* 16 megabyte maximum attribute data size */ #define ATTR_MAX_NAME_LEN 128 #define ATTR_MAX_HDR_SIZE (65536+18) @@ -2822,7 +2845,7 @@ static const u_int32_t emptyfinfo[8] = {0}; static int copyfile_unpack(copyfile_state_t s) { ssize_t bytes; - void * buffer, * endptr; + void * buffer, * endptr, * dataptr = NULL; apple_double_header_t *adhdr; ssize_t hdrsize; int error = 0; @@ -2936,7 +2959,6 @@ static int copyfile_unpack(copyfile_state_t s) for (i = 0; i < count; i++) { - void * dataptr; /* * First we do some simple sanity checking. @@ -3020,6 +3042,7 @@ static int copyfile_unpack(copyfile_state_t s) copyfile_debug(3, "extracting \"%s\" (%d bytes) at offset %u", entry->name, entry->length, entry->offset); +#if 0 dataptr = (char *)attrhdr + entry->offset; if (dataptr > endptr || dataptr < buffer) { @@ -3028,6 +3051,7 @@ static int copyfile_unpack(copyfile_state_t s) s->err = EINVAL; /* Invalid buffer */ goto exit; } + if (((char*)dataptr + entry->length) > (char*)endptr || (((char*)dataptr + entry->length) < (char*)buffer) || (entry->length > (size_t)hdrsize)) { @@ -3040,6 +3064,22 @@ static int copyfile_unpack(copyfile_state_t s) goto exit; } +#else + dataptr = malloc(entry->length); + if (dataptr == NULL) { + copyfile_debug(1, "no memory for %u bytes\n", entry->length); + error = -1; + s->err = ENOMEM; + goto exit; + } + if (pread(s->src_fd, dataptr, entry->length, entry->offset) != (ssize_t)entry->length) { + copyfile_debug(1, "failed to read %u bytes at offset %u\n", entry->length, entry->offset); + error = -1; + s->err = EINVAL; + goto exit; + } +#endif + if (strcmp((char*)entry->name, XATTR_QUARANTINE_NAME) == 0) { qtn_file_t tqinfo = NULL; @@ -3160,57 +3200,64 @@ acl_done: /* And, finally, everything else */ else { - if (s->statuscb) { - int rv; - s->xattr_name = strdup((char*)entry->name); - s->totalCopied = 0; - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); - if (s->xattr_name) { - free(s->xattr_name); - s->xattr_name = NULL; - } - if (rv == COPYFILE_QUIT) { - s->err = ECANCELED; - error = -1; - goto exit; - } - } - if (fsetxattr(s->dst_fd, (char *)entry->name, dataptr, entry->length, 0, 0) == -1) { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("error %d setting attribute %s", error, entry->name); + if (s->copyIntent || + _PreserveEA((char*)entry->name, s->copyIntent) == 1) { if (s->statuscb) { int rv; - s->xattr_name = strdup((char*)entry->name); - rv = (s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + s->totalCopied = 0; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); if (s->xattr_name) { free(s->xattr_name); s->xattr_name = NULL; } if (rv == COPYFILE_QUIT) { + s->err = ECANCELED; error = -1; goto exit; } - } else { - error = -1; - goto exit; - } - } else if (s->statuscb) { - int rv; - s->xattr_name = strdup((char*)entry->name); - s->totalCopied = entry->length; - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); - if (s->xattr_name) { - free(s->xattr_name); - s->xattr_name = NULL; } - if (rv == COPYFILE_QUIT) { - error = -1; - s->err = ECANCELED; - goto exit; + if (fsetxattr(s->dst_fd, (char *)entry->name, dataptr, entry->length, 0, 0) == -1) { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("error %d setting attribute %s", errno, entry->name); + if (s->statuscb) { + int rv; + + s->xattr_name = strdup((char*)entry->name); + rv = (s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + if (s->xattr_name) { + free(s->xattr_name); + s->xattr_name = NULL; + } + if (rv == COPYFILE_QUIT) { + error = -1; + goto exit; + } + } else { + error = -1; + goto exit; + } + } else if (s->statuscb) { + int rv; + s->xattr_name = strdup((char*)entry->name); + s->totalCopied = entry->length; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); + if (s->xattr_name) { + free(s->xattr_name); + s->xattr_name = NULL; + } + if (rv == COPYFILE_QUIT) { + error = -1; + s->err = ECANCELED; + goto exit; + } } } } + if (dataptr) { + free(dataptr); + dataptr = NULL; + } entry = ATTR_NEXT(entry); } } @@ -3249,7 +3296,7 @@ acl_done: if (error) { if (s->statuscb) { int rv; - s->xattr_name = XATTR_FINDERINFO_NAME; + s->xattr_name = (char *)XATTR_FINDERINFO_NAME; rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); s->xattr_name = NULL; if (rv == COPYFILE_QUIT) { @@ -3261,7 +3308,7 @@ acl_done: goto exit; } else if (s->statuscb) { int rv; - s->xattr_name = XATTR_FINDERINFO_NAME; + s->xattr_name = (char *)XATTR_FINDERINFO_NAME; rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); s->xattr_name = NULL; if (rv == COPYFILE_QUIT) { @@ -3323,7 +3370,7 @@ skip_fi: } if (s->statuscb) { int rv; - s->xattr_name = XATTR_RESOURCEFORK_NAME; + s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME; rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); s->xattr_name = NULL; if (rv == COPYFILE_QUIT) { @@ -3356,7 +3403,7 @@ skip_fi: } if (s->statuscb) { int rv; - s->xattr_name = XATTR_RESOURCEFORK_NAME; + s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME; rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); s->xattr_name = NULL; if (rv == COPYFILE_CONTINUE) { @@ -3369,7 +3416,7 @@ skip_fi: goto bad; } else if (s->statuscb) { int rv; - s->xattr_name = XATTR_RESOURCEFORK_NAME; + s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME; rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); s->xattr_name = NULL; if (rv == COPYFILE_QUIT) { @@ -3403,6 +3450,7 @@ bad: } exit: if (buffer) free(buffer); + if (dataptr) free(dataptr); return error; } @@ -3607,12 +3655,13 @@ static int copyfile_pack(copyfile_state_t s) int error = 0; int seenq = 0; // Have we seen any quarantine info already? - filehdr = (attr_header_t *) calloc(1, ATTR_MAX_SIZE); + filehdr = (attr_header_t *) calloc(1, ATTR_MAX_HDR_SIZE); + if (filehdr == NULL) { error = -1; goto exit; } else { - endfilehdr = (attr_header_t*)(((char*)filehdr) + ATTR_MAX_SIZE); + endfilehdr = (attr_header_t*)(((char*)filehdr) + ATTR_MAX_HDR_SIZE); } attrnamebuf = calloc(1, ATTR_MAX_HDR_SIZE); @@ -3706,6 +3755,17 @@ static int copyfile_pack(copyfile_state_t s) if (namelen > XATTR_MAXNAMELEN + 1) { namelen = XATTR_MAXNAMELEN + 1; } + if (s->copyIntent && + _PreserveEA(nameptr, s->copyIntent) == 0) { + // Skip it + size_t amt = endnamebuf - (nameptr + namelen); + memmove(nameptr, nameptr + namelen, amt); + endnamebuf -= namelen; + /* Set namelen to 0 so continue doesn't miss names */ + namelen = 0; + continue; + } + if (s->statuscb) { int rv; char eaname[namelen]; @@ -3937,6 +3997,7 @@ static int copyfile_pack(copyfile_state_t s) entry->offset = filehdr->data_start + filehdr->data_length; filehdr->data_length += (u_int32_t)datasize; +#if 0 /* * >>> WARNING <<< * This assumes that the data is fits in memory (not @@ -3949,6 +4010,11 @@ static int copyfile_pack(copyfile_state_t s) } else { bcopy(databuf, (char*)filehdr + entry->offset, datasize); } +#else + if (pwrite(s->dst_fd, databuf, datasize, entry->offset) != datasize) { + error = 1; + } +#endif free(databuf); copyfile_debug(3, "copied %ld bytes of \"%s\" data @ offset 0x%08x", datasize, nameptr, entry->offset); @@ -3958,24 +4024,21 @@ next: entry = (attr_entry_t *)((char *)entry + entrylen); } - if (filehdr->data_length > 0) - { - /* Now we know where the resource fork data starts. */ - filehdr->appledouble.entries[1].offset = (filehdr->data_start + filehdr->data_length); - - /* We also know the size of the "Finder Info entry. */ - filehdr->appledouble.entries[0].length = + /* Now we know where the resource fork data starts. */ + filehdr->appledouble.entries[1].offset = (filehdr->data_start + filehdr->data_length); + + /* We also know the size of the "Finder Info entry. */ + filehdr->appledouble.entries[0].length = filehdr->appledouble.entries[1].offset - filehdr->appledouble.entries[0].offset; - - filehdr->total_size = filehdr->appledouble.entries[1].offset; - } - + + filehdr->total_size = filehdr->appledouble.entries[1].offset; + /* Copy Resource Fork. */ if (hasrsrcfork && (error = copyfile_pack_rsrcfork(s, filehdr))) goto exit; /* Write the header to disk. */ - datasize = filehdr->appledouble.entries[1].offset; + datasize = filehdr->data_start; swap_adhdr(&filehdr->appledouble); swap_attrhdr(filehdr); diff --git a/copyfile.h b/copyfile.h index 4029d21..59c198a 100644 --- a/copyfile.h +++ b/copyfile.h @@ -77,6 +77,7 @@ typedef int (*copyfile_callback_t)(int, int, copyfile_state_t, const char *, con #define COPYFILE_STATE_COPIED 8 #define COPYFILE_STATE_XATTRNAME 9 + #define COPYFILE_DISABLE_VAR "COPYFILE_DISABLE" /* flags for copyfile */ diff --git a/copyfile.xcodeproj/project.pbxproj b/copyfile.xcodeproj/project.pbxproj index 181f09c..04b9858 100644 --- a/copyfile.xcodeproj/project.pbxproj +++ b/copyfile.xcodeproj/project.pbxproj @@ -7,11 +7,17 @@ objects = { /* Begin PBXBuildFile section */ + 72406E631676C3C80099568B /* xattr_properties.c in Sources */ = {isa = PBXBuildFile; fileRef = 72406E621676C3C80099568B /* xattr_properties.c */; }; + 72B4C0F41676C47D00C13E05 /* copyfile_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 72B4C0F31676C47D00C13E05 /* copyfile_private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 72EAA3B016A72F4500833E98 /* xattr_properties.h in Headers */ = {isa = PBXBuildFile; fileRef = 72EAA3AF16A72F4500833E98 /* xattr_properties.h */; settings = {ATTRIBUTES = (Private, ); }; }; FCCE17C3135A658F002CEE6D /* copyfile.c in Sources */ = {isa = PBXBuildFile; fileRef = FCCE17C1135A658F002CEE6D /* copyfile.c */; }; FCCE17C4135A658F002CEE6D /* copyfile.h in Headers */ = {isa = PBXBuildFile; fileRef = FCCE17C2135A658F002CEE6D /* copyfile.h */; settings = {ATTRIBUTES = (Public, ); }; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 72406E621676C3C80099568B /* xattr_properties.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xattr_properties.c; sourceTree = ""; }; + 72B4C0F31676C47D00C13E05 /* copyfile_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = copyfile_private.h; sourceTree = ""; }; + 72EAA3AF16A72F4500833E98 /* xattr_properties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xattr_properties.h; sourceTree = ""; }; FCCE17BB135A6444002CEE6D /* libcopyfile.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libcopyfile.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; FCCE17C0135A658F002CEE6D /* copyfile.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = copyfile.3; sourceTree = ""; }; FCCE17C1135A658F002CEE6D /* copyfile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = copyfile.c; sourceTree = ""; }; @@ -33,6 +39,9 @@ FCCE17AB135A5FFB002CEE6D = { isa = PBXGroup; children = ( + 72B4C0F31676C47D00C13E05 /* copyfile_private.h */, + 72EAA3AF16A72F4500833E98 /* xattr_properties.h */, + 72406E621676C3C80099568B /* xattr_properties.c */, FCCE17C0135A658F002CEE6D /* copyfile.3 */, FCCE17C1135A658F002CEE6D /* copyfile.c */, FCCE17C2135A658F002CEE6D /* copyfile.h */, @@ -57,6 +66,8 @@ buildActionMask = 2147483647; files = ( FCCE17C4135A658F002CEE6D /* copyfile.h in Headers */, + 72B4C0F41676C47D00C13E05 /* copyfile_private.h in Headers */, + 72EAA3B016A72F4500833E98 /* xattr_properties.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -129,6 +140,7 @@ buildActionMask = 2147483647; files = ( FCCE17C3135A658F002CEE6D /* copyfile.c in Sources */, + 72406E631676C3C80099568B /* xattr_properties.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -173,12 +185,48 @@ GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = "__DARWIN_NOW_CANCELABLE=1"; INSTALL_PATH = /usr/lib/system; + "INSTALL_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/lib/system"; + LINK_WITH_STANDARD_LIBRARIES = NO; OTHER_LDFLAGS = ( - "-umbrella", - System, + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld", + "-lcompiler_rt", + "-lsystem_kernel", + "-lsystem_malloc", + "-lsystem_c", + "-lsystem_blocks", + "-lquarantine", + "-lsystem_asl", + "-lsystem_info", ); + "OTHER_LDFLAGS[sdk=iphoneos*]" = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld", + "-lcompiler_rt", + "-lsystem_kernel", + "-lsystem_malloc", + "-lsystem_c", + "-lsystem_blocks", + "-lsystem_asl", + "-lsystem_info", + ); + "OTHER_LDFLAGS[sdk=iphonesimulator*]" = ( + "-Wl,-umbrella,System", + "-L/usr/lib/system", + "-ldyld_sim", + "-lcompiler_rt_sim", + "-lsystem_sim_c", + "-lsystem_sim_blocks", + "-lsystem_sim_info", + "-Wl,-upward-lSystem", + ); + "PRIVATE_HEADERS_FOLDER_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/local/include"; PRODUCT_NAME = "$(TARGET_NAME)"; + "PRODUCT_NAME[sdk=iphonesimulator*]" = copyfile_sim; PUBLIC_HEADERS_FOLDER_PATH = /usr/include; + "PUBLIC_HEADERS_FOLDER_PATH[sdk=iphonesimulator*]" = "$(SDKROOT)/usr/include"; WARNING_CFLAGS = ( "-Wall", "-Wextra", diff --git a/copyfile_private.h b/copyfile_private.h new file mode 100644 index 0000000..8f805d9 --- /dev/null +++ b/copyfile_private.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2013 Apple, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _COPYFILE_PRIVATE_H +# define _COPYFILE_PRIVATE_H + +/* + * Set (or get) the intent type; see xattr_properties.h for details. + * This command uses a pointer to CopyOperationIntent_t as the parameter. + */ +# define COPYFILE_STATE_INTENT 256 + +#endif /* _COPYFILE_PRIVATE_H */ diff --git a/xattr_properties.c b/xattr_properties.c new file mode 100644 index 0000000..78eccd0 --- /dev/null +++ b/xattr_properties.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2013 Apple, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Some default propeteries for EAs we know about internally. + */ +struct defaultList { + const char *eaName; + const char *propList; + int flags; // See below +}; + +#define propFlagsPrefix 0x0001 // The name is a prefix, so only look at that part + +static const struct defaultList +defaultPropertyTable[] = { + { "com.apple.quarantine", "PC", 0 }, // not public + { "com.apple.TextEncoding", "PC", 0 }, // Content-dependent, public + { "com.apple.metadata:", "P", propFlagsPrefix }, // Don't export, keep for copy & safe save + { "com.apple.security.", "N", propFlagsPrefix }, + { XATTR_RESOURCEFORK_NAME, "PC", 0 }, // Don't keep for safe save + { XATTR_FINDERINFO_NAME, "PC", 0 }, // Same as ResourceFork + { 0, 0, 0 }, +}; + +/* + * The property lists on an EA are set by having a suffix character, + * and then a list of characters. In general, we're choosing upper-case + * to indicate the property is set, and lower-case to indicate it's to be + * cleared. + */ +struct propertyListMapping { + char enable; // Character to enable + char disable; // Character to disable -- usually lower-case of enable + CopyOperationProperties_t value; +}; +static const struct propertyListMapping +PropertyListMapTable[] = { + { 'C', 'c', kCopyOperationPropertyContentDependent }, + { 'P', 'p', kCopyOperationPropertyNoExport }, + { 'N', 'n', kCopyOperationPropertyNeverPreserve }, + { 0, 0, 0 }, +}; + +/* + * Given a converted property list (that is, converted to the + * CopyOperationProperties_t type), and an intent, determine if + * it should be preserved or not. + * + * I've chosen to use a block instead of a simple mask on the belief + * that the question may be moderately complex. If it ends up not being + * so, then this can simply be turned into a mask of which bits to check + * as being exclusionary. + */ +static const struct divineIntent { + CopyOperationIntent_t intent; + int (^checker)(CopyOperationProperties_t); +} intentTable[] = { + { CopyOperationIntentCopy, ^(CopyOperationProperties_t props) { + if (props & kCopyOperationPropertyNeverPreserve) + return 0; + return 1; + } }, + { CopyOperationIntentSave, ^(CopyOperationProperties_t props) { + if (props & (kCopyOperationPropertyContentDependent | kCopyOperationPropertyNeverPreserve)) + return 0; + return 1; + } }, + { CopyOperationIntentShare, ^(CopyOperationProperties_t props) { + if ((props & (kCopyOperationPropertyNoExport | kCopyOperationPropertyNeverPreserve)) != 0) + return 0; + return 1; + } }, + { 0, 0 }, +}; + + +/* + * If an EA name is in the default list, find it, and return the property + * list string for it. + */ +static const char * +nameInDefaultList(const char *eaname) +{ + const struct defaultList *retval; + + for (retval = defaultPropertyTable; retval->eaName; retval++) { + if ((retval->flags & propFlagsPrefix) != 0 && + strncmp(retval->eaName, eaname, strlen(retval->eaName)) == 0) + return retval->propList; + if (strcmp(retval->eaName, eaname) == 0) + return retval->propList; + } + return NULL; +} + +/* + * Given an EA name, see if it has a property list in it, and + * return a pointer to it. All this is doing is looking for + * the delimiter, and returning the string after that. Returns + * NULL if the delimiter isn't found. Note that an empty string + * is a valid property list, as far as we're concerned. + */ +static const char * +findPropertyList(const char *eaname) +{ + const char *ptr = strrchr(eaname, '#'); + if (ptr) + return ptr+1; + return NULL; +} + +/* + * Convert a property list string (e.g., "pCd") into a + * CopyOperationProperties_t type. + */ +static CopyOperationProperties_t +stringToProperties(const char *proplist) +{ + CopyOperationProperties_t retval = 0; + const char *ptr; + + // A switch would be more efficient, but less generic. + for (ptr = proplist; *ptr; ptr++) { + const struct propertyListMapping *mapPtr; + for (mapPtr = PropertyListMapTable; mapPtr->enable; mapPtr++) { + if (*ptr == mapPtr->enable) { + retval |= mapPtr->value; + } else if (*ptr == mapPtr->disable) { + retval &= ~mapPtr->value; + } + } + } + return retval; +} + +/* + * Given an EA name (e.g., "com.apple.lfs.hfs.test"), and a + * CopyOperationProperties_t value (it's currently an integral value, so + * just a bitmask), cycle through the list of known properties, and return + * a string with the EA name, and the property list appended. E.g., we + * might return "com.apple.lfs.hfs.test#pD". + * + * The tricky part of this funciton is that it will not append any letters + * if the value is only the default properites. In that case, it will copy + * the EA name, and return that. + * + * It returns NULL if there was an error. The two errors right now are + * no memory (strdup failed), in which case it will set errno to ENOMEM; and + * the resulting EA name is longer than XATTR_MAXNAMELEN, in which case it + * sets errno to ENAMETOOLONG. + * + * (Note that it also uses ENAMETOOLONG if the buffer it's trying to set + * gets too large. I honestly can't see how that would happen, but it's there + * for sanity checking. That would require having more than 64 bits to use.) + */ +char * +_xattrNameWithProperties(const char *orig, CopyOperationProperties_t propList) +{ + char *retval = NULL; + char suffix[66] = { 0 }; // 66: uint64_t for property types, plus '#', plus NUL + char *cur = suffix; + const struct propertyListMapping *mapPtr; + + *cur++ = '#'; + for (mapPtr = PropertyListMapTable; mapPtr->enable; mapPtr++) { + if ((propList & mapPtr->value) != 0) { + *cur++ = mapPtr->enable; + } + if (cur >= (suffix + sizeof(suffix))) { + errno = ENAMETOOLONG; + return NULL; + } + + } + + + if (cur == suffix + 1) { + // No changes made + retval = strdup(orig); + if (retval == NULL) + errno = ENOMEM; + } else { + const char *defaultEntry = NULL; + if ((defaultEntry = nameInDefaultList(orig)) != NULL && + strcmp(defaultEntry, suffix + 1) == 0) { + // Just use the name passed in + retval = strdup(orig); + } else { + asprintf(&retval, "%s%s", orig, suffix); + } + if (retval == NULL) { + errno = ENOMEM; + } else { + if (strlen(retval) > XATTR_MAXNAMELEN) { + free(retval); + retval = NULL; + errno = ENAMETOOLONG; + } + } + } + return retval; +} + +CopyOperationProperties_t +_xattrPropertiesFromName(const char *eaname) +{ + CopyOperationProperties_t retval = 0; + const char *propList; + + propList = findPropertyList(eaname); + if (propList == NULL) { + propList = findPropertyList(eaname); + } + if (propList != NULL) { + retval = stringToProperties(propList); + } + + return retval; +} + +/* + * Indicate whether an EA should be preserved, when using the + * given intent. + * + * This returns 0 if it should not be preserved, and 1 if it should. + * + * It simply looks through the tables we have above, and compares the + * CopyOperationProperties_t for the EA with the intent. If the + * EA doesn't have any properties, and it's not on the default list, the + * default is to preserve it. + */ + +int +_PreserveEA(const char *eaname, CopyOperationIntent_t intent) +{ + const struct divineIntent *ip; + CopyOperationProperties_t props; + const char *propList; + + if ((propList = findPropertyList(eaname)) == NULL && + (propList = nameInDefaultList(eaname)) == NULL) + props = 0; + else + props = stringToProperties(propList); + + for (ip = intentTable; ip->intent; ip++) { + if (ip->intent == intent) { + return ip->checker(props); + } + } + + if ((props & kCopyOperationPropertyNeverPreserve) != 0) + return 0; // Special case, don't try to preserve this one + + return 1; // Default to preserving everything +} diff --git a/xattr_properties.h b/xattr_properties.h new file mode 100644 index 0000000..e6eb457 --- /dev/null +++ b/xattr_properties.h @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2013 Apple, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef _XATTR_PROPERTIES_H +#define _XATTR_PROPERTIES_H + +#include + +#include + + +__BEGIN_DECLS + +/* + * CopyOperationIntent_t is used to declare what the intent of the copy is. + * Not a bit-field (for now, at least). + * + * CopyOperationIntentCopy indicates that the EA is attached to an object + * that is simply being copied. E.g., cp src dst + * + * CopyOperationIntentSave indicates that the EA is attached to an object + * being saved; as in a "safe save," the destination is being replaced by + * the source, so the question is whether the EA should be applied to the + * destination, or generated anew. + * + * CopyOperationIntentShare indicates that the EA is attached to an object that + * is being given out to other people. For example, saving to a public folder, + * or attaching to an email message. + */ + +typedef enum { + CopyOperationIntentCopy = 1, + CopyOperationIntentSave, + CopyOperationIntentShare, +} CopyOperationIntent_t; + +typedef uint64_t CopyOperationProperties_t; + +/* + * Various properties used to determine how to handle the xattr during + * copying. The intent is that the default is reasonable for most xattrs. + */ + +/* + * kCopyOperationPropertyNoExport + * Declare that the extended property should not be exported; this is + * deliberately a bit vague, but this is used by CopyOperationIntentShare + * to indicate not to preserve the xattr. + */ +#define kCopyOperationPropertyNoExport ((CopyOperationProperties_t)0x0001) + +/* + * kCopyOperationPropertyContentDependent + * Declares the extended attribute to be tied to the contents of the file (or + * vice versa), such that it should be re-created when the contents of the + * file change. Examples might include cryptographic keys, checksums, saved + * position or search information, and text encoding. + * + * This property causes the EA to be preserved for copy and share, but not for + * safe save. (In a safe save, the EA exists on the original, and will not + * be copied to the new version.) + */ +#define kCopyOperationPropertyContentDependent ((CopyOperationProperties_t)0x0002) + +/* + * kCopyOperationPropertyNeverPreserve + * Declares that the extended attribute is never to be copied, for any + * intention type. + */ +#define kCopyOperationPropertyNeverPreserve ((CopyOperationProperties_t)0x0004) + +// Given a named extended attribute, and a copy intent, should the EA be preserved? +extern int _PreserveEA(const char *, CopyOperationIntent_t); + +/* + * Given an extended attribute name, and a set of properties, return an + * allocated C string with the name. This will return NULL on error; + * errno may be set to ENOMEM if the new name cannot be allocated, or + * ENAMETOOLONG if the new name is longer than the maximum for EAs (127 UTF8 + * characters). The caller must deallocate the return value otherwise. + * + * If no properties are set, it returns a copy of the EA name. + * + * If the EA name is in the internal list, and the properties are the same as + * defined there, then it will also return an unmodified copy of the EA name. + */ +extern char *_xattrNameWithProperties(const char *, CopyOperationProperties_t); + +/* + * Given an extended attribute name, which may or may not have properties encoded + * as a suffix, return just the name of the attribute. E.g., com.example.mine#P + * would return "com.example.mine". The return value will be NULL on error; + * errno will be set to ENOMEM if it cannot be allocated. The caller must deallocate + * the return value. + */ +extern char *_xattrNameWithoutProperties(const char *); + +/* + * Given an EA name, return the properties. If the name is in the internal list, + * those properties will be returned. Unknown property encodings are ignored. + */ +extern CopyOperationProperties_t _xattrPropertiesFromName(const char *); + +__END_DECLS + +#endif /* _XATTR_PROPERTIES_H */ diff --git a/xcodescripts/install_files.sh b/xcodescripts/install_files.sh index 7f2e58f..50174fb 100644 --- a/xcodescripts/install_files.sh +++ b/xcodescripts/install_files.sh @@ -1,9 +1,18 @@ #!/bin/sh set -e -x +# check if we're building for the simulator +if [ "${RC_ProjectName%_Sim}" != "${RC_ProjectName}" ] ; then + if [ -d ${DSTROOT}${SDKROOT}/usr/lib/system ] ; then + for lib in ${DSTROOT}${SDKROOT}/usr/lib/system/*.dylib ; do + install_name_tool -id "${lib#${DSTROOT}${SDKROOT}}" "${lib}" + done + fi + exit 0 +fi + # don't install files for installhdrs or simulator builds -if [ "$ACTION" == "installhdrs" -o \ - "${RC_ProjectName%_Sim}" != "$RC_ProjectName" ]; then +if [ "$ACTION" == "installhdrs" -o ] ; then exit 0 fi -- 2.47.2