#endif
#include <TargetConditionals.h>
-#if !TARGET_OS_EMBEDDED
+#if !TARGET_OS_IPHONE
#include <quarantine.h>
#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,
off_t totalCopied;
int err;
char *xattr_name;
+ CopyOperationIntent_t copyIntent;
};
struct acl_entry {
(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)
}
#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) {
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;
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;
#define offsetof(type, member) ((size_t)(&((type *)0)->member))
-#define XATTR_MAXATTRLEN (32*1024)
+#define XATTR_MAXATTRLEN (16*1024*1024)
/*
#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)
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;
for (i = 0; i < count; i++)
{
- void * dataptr;
/*
* First we do some simple sanity checking.
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) {
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)) {
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;
/* 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);
}
}
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) {
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) {
}
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) {
}
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) {
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) {
}
exit:
if (buffer) free(buffer);
+ if (dataptr) free(dataptr);
return error;
}
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);
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];
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
} 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);
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);
#define COPYFILE_STATE_COPIED 8
#define COPYFILE_STATE_XATTRNAME 9
+
#define COPYFILE_DISABLE_VAR "COPYFILE_DISABLE"
/* flags for copyfile */
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 = "<group>"; };
+ 72B4C0F31676C47D00C13E05 /* copyfile_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = copyfile_private.h; sourceTree = "<group>"; };
+ 72EAA3AF16A72F4500833E98 /* xattr_properties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xattr_properties.h; sourceTree = "<group>"; };
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 = "<group>"; };
FCCE17C1135A658F002CEE6D /* copyfile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = copyfile.c; sourceTree = "<group>"; };
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 */,
buildActionMask = 2147483647;
files = (
FCCE17C4135A658F002CEE6D /* copyfile.h in Headers */,
+ 72B4C0F41676C47D00C13E05 /* copyfile_private.h in Headers */,
+ 72EAA3B016A72F4500833E98 /* xattr_properties.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
buildActionMask = 2147483647;
files = (
FCCE17C3135A658F002CEE6D /* copyfile.c in Sources */,
+ 72406E631676C3C80099568B /* xattr_properties.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
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",
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+
+#include <xattr_properties.h>
+
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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 <stdint.h>
+
+#include <sys/cdefs.h>
+
+
+__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 */
#!/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