From 1f4df596dfa62fdf7a932b82879ec73d6570abc5 Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 28 Jun 2016 00:27:55 +0000 Subject: [PATCH] copyfile-138.tar.gz --- copyfile.3 | 29 + copyfile.c | 5531 +++++++++++++++++++++++++++------------------------- copyfile.h | 6 + 3 files changed, 2860 insertions(+), 2706 deletions(-) diff --git a/copyfile.3 b/copyfile.3 index e7e94e3..93ae95d 100644 --- a/copyfile.3 +++ b/copyfile.3 @@ -178,9 +178,21 @@ Unlink the file before starting. (This is only applicable for the .Fn copyfile function.) +.It Dv COPYFILE_CLONE_FORCE +Clone the file/directory instead. +This is a force flag i.e. if cloning fails, an error is returned. +This flag is equivalent to (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA). +Note that if cloning is successful, callbacks will not be invoked. +.It Dv COPYFILE_CLONE +Try to clone the file/directory instead. +This is a best try flag i.e. if cloning fails, fallback to copying the file. +This flag is equivalent to (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA). +Note that if cloning is successful, callbacks will not be invoked. .It Dv COPYFILE_NOFOLLOW This is a convenience macro, equivalent to .Dv (COPYFILE_NOFOLLOW_DST|COPYFILE_NOFOLLOW_SRC) . +.It Dv COPYFILE_RUN_IN_PLACE +If the src file has quarantine information, add the QTN_FLAG_DO_NOT_TRANSLOCATE flag to the quarantine information of the dst file. This allows a bundle to run in place instead of being translocated. .El .Pp The @@ -264,6 +276,11 @@ Get the number of data bytes copied so far. (Only valid for .Fn copyfile_state_get ; see below for more details about callbacks.) +If a +.Dv COPYFILE_CLONE +or +.Dv COPYFILE_CLONE_FORCE +operation successfully cloned the requested objects, then this value will be 0. The .Va dst parameter is a pointer to @@ -277,6 +294,18 @@ for (see below for details). This field cannot be set, and may be .Dv NULL . +.It Dv COPYFILE_STATE_WAS_CLONED +True if a +.Dv COPYFILE_CLONE +or +.Dv COPYFILE_CLONE_FORCE +operation successfully cloned the requested objects. +The +.Va dst +parameter is a pointer to +.Vt bool +(type +.Vt bool\ * ). .El .Sh Recursive Copies When given the diff --git a/copyfile.c b/copyfile.c index b60739e..ed3bfce 100644 --- a/copyfile.c +++ b/copyfile.c @@ -2,14 +2,14 @@ * Copyright (c) 2004-2010 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, @@ -17,7 +17,7 @@ * 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@ */ @@ -45,7 +45,7 @@ #include #include #include - +#include #include #ifdef VOL_CAP_FMT_DECMPFS_COMPRESSION @@ -69,7 +69,10 @@ 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; } +static uint32_t qtn_file_get_flags(void *x) { return 0; } +static int qtn_file_set_flags(void *x, uint32_t flags) { return 0; } #define XATTR_QUARANTINE_NAME "figgledidiggledy" +#define QTN_FLAG_DO_NOT_TRANSLOCATE 0 #endif /* TARGET_OS_IPHONE */ #include "copyfile.h" @@ -99,42 +102,44 @@ enum cfInternalFlags { */ struct _copyfile_state { - char *src; - char *dst; - int src_fd; - int dst_fd; - struct stat sb; - filesec_t fsec; - copyfile_flags_t flags; - unsigned int internal_flags; - void *stats; - uint32_t debug; - copyfile_callback_t statuscb; - void *ctx; - qtn_file_t qinfo; /* Quarantine information -- probably NULL */ - filesec_t original_fsec; - filesec_t permissive_fsec; - off_t totalCopied; - int err; - char *xattr_name; - xattr_operation_intent_t copyIntent; + char *src; + char *dst; + int src_fd; + int dst_fd; + struct stat sb; + filesec_t fsec; + copyfile_flags_t flags; + unsigned int internal_flags; + void *stats; + uint32_t debug; + copyfile_callback_t statuscb; + void *ctx; + qtn_file_t qinfo; /* Quarantine information -- probably NULL */ + filesec_t original_fsec; + filesec_t permissive_fsec; + off_t totalCopied; + int err; + char *xattr_name; + xattr_operation_intent_t copyIntent; + bool was_cloned; }; #define GET_PROT_CLASS(fd) fcntl((fd), F_GETPROTECTIONCLASS) #define SET_PROT_CLASS(fd, prot_class) fcntl((fd), F_SETPROTECTIONCLASS, (prot_class)) struct acl_entry { - u_int32_t ae_magic; + u_int32_t ae_magic; #define _ACL_ENTRY_MAGIC 0xac1ac101 - u_int32_t ae_tag; - guid_t ae_applicable; - u_int32_t ae_flags; - u_int32_t ae_perms; + u_int32_t ae_tag; + guid_t ae_applicable; + u_int32_t ae_flags; + u_int32_t ae_perms; }; -#define PACE(ace) do { \ - struct acl_entry *__t = (struct acl_entry*)(ace); \ - fprintf(stderr, "%s(%d): " #ace " = { flags = %#x, perms = %#x }\n", __FUNCTION__, __LINE__, __t->ae_flags, __t->ae_perms); \ +#define PACE(ace) \ + do { \ + struct acl_entry *__t = (struct acl_entry*)(ace); \ + fprintf(stderr, "%s(%d): " #ace " = { flags = %#x, perms = %#x }\n", __FUNCTION__, __LINE__, __t->ae_flags, __t->ae_perms); \ } while (0) #define PACL(ace) \ @@ -150,7 +155,7 @@ acl_compare_permset_np(acl_permset_t p1, acl_permset_t p2) ps1 = (struct pm*) p1; ps2 = (struct pm*) p2; - return ((ps1->ap_perms == ps2->ap_perms) ? 1 : 0); + return ((ps1->ap_perms == ps2->ap_perms) ? 1 : 0); } @@ -187,12 +192,12 @@ doesdecmpfs(int fd) { static int does_copy_protection(int fd) { - struct statfs sfs; + struct statfs sfs; - if (fstatfs(fd, &sfs) == -1) - return -1; + if (fstatfs(fd, &sfs) == -1) + return -1; - return ((sfs.f_flags & MNT_CPROTECT) == MNT_CPROTECT); + return ((sfs.f_flags & MNT_CPROTECT) == MNT_CPROTECT); } static void @@ -219,13 +224,13 @@ sort_xattrname_list(void *start, size_t length) goto done; #ifdef DEBUG -{ - char *curPtr = start; - while (curPtr < (char*)start + length) { - printf("%s\n", curPtr); - curPtr += strlen(curPtr) + 1; + { + char *curPtr = start; + while (curPtr < (char*)start + length) { + printf("%s\n", curPtr); + curPtr += strlen(curPtr) + 1; + } } -} #endif tmp = ptrs[indx++] = (char*)start; @@ -308,44 +313,44 @@ static int copyfile_quarantine(copyfile_state_t); #ifndef _COPYFILE_TEST # define copyfile_warn(str, ...) syslog(LOG_WARNING, str ": %m", ## __VA_ARGS__) # define copyfile_debug(d, str, ...) \ - do { \ - if (s && (d <= s->debug)) {\ - syslog(LOG_DEBUG, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \ - } \ - } while (0) + do { \ + if (s && (d <= s->debug)) {\ + syslog(LOG_DEBUG, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \ + } \ + } while (0) #else #define copyfile_warn(str, ...) \ - fprintf(stderr, "%s:%d:%s() " str ": %s\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__, (errno) ? strerror(errno) : "") + fprintf(stderr, "%s:%d:%s() " str ": %s\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__, (errno) ? strerror(errno) : "") # define copyfile_debug(d, str, ...) \ - do { \ - if (s && (d <= s->debug)) {\ - fprintf(stderr, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \ - } \ - } while(0) + do { \ + if (s && (d <= s->debug)) {\ + fprintf(stderr, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \ + } \ + } while(0) #endif static int copyfile_quarantine(copyfile_state_t s) { - int rv = 0; - if (s->qinfo == NULL) - { - int error; - s->qinfo = qtn_file_alloc(); + int rv = 0; if (s->qinfo == NULL) { - rv = -1; - goto done; - } - if ((error = qtn_file_init_with_fd(s->qinfo, s->src_fd)) != 0) - { - qtn_file_free(s->qinfo); - s->qinfo = NULL; - rv = -1; - goto done; + int error; + s->qinfo = qtn_file_alloc(); + if (s->qinfo == NULL) + { + rv = -1; + goto done; + } + if ((error = qtn_file_init_with_fd(s->qinfo, s->src_fd)) != 0) + { + qtn_file_free(s->qinfo); + s->qinfo = NULL; + rv = -1; + goto done; + } } - } done: - return rv; + return rv; } static int @@ -512,17 +517,17 @@ noacl: static void reset_security(copyfile_state_t s) { - /* If we haven't reset the file security information - * (COPYFILE_SECURITY is not set in flags) - * restore back the permissions the file had originally - * - * One of the reasons this seems so complicated is that - * it is partially at odds with copyfile_security(). - * - * Simplisticly, we are simply trying to make sure we - * only copy what was requested, and that we don't stomp - * on what wasn't requested. - */ + /* If we haven't reset the file security information + * (COPYFILE_SECURITY is not set in flags) + * restore back the permissions the file had originally + * + * One of the reasons this seems so complicated is that + * it is partially at odds with copyfile_security(). + * + * Simplisticly, we are simply trying to make sure we + * only copy what was requested, and that we don't stomp + * on what wasn't requested. + */ #ifdef COPYFILE_RECURSIVE if (s->dst_fd > -1) { @@ -532,44 +537,44 @@ reset_security(copyfile_state_t s) fstat(s->src_fd, &sbuf); else fstat(s->dst_fd, &sbuf); - + if (!(s->internal_flags & cfDelayAce)) remove_uberace(s->dst_fd, &sbuf); } #else - if (s->permissive_fsec && (s->flags & COPYFILE_SECURITY) != COPYFILE_SECURITY) { - if (s->flags & COPYFILE_ACL) { - /* Just need to reset the BSD information -- mode, owner, group */ - (void)fchown(s->dst_fd, s->dst_sb.st_uid, s->dst_sb.st_gid); - (void)fchmod(s->dst_fd, s->dst_sb.st_mode); - } else { - /* - * flags is either COPYFILE_STAT, or neither; if it's - * neither, then we restore both ACL and POSIX permissions; - * if it's STAT, however, then we only want to restore the - * ACL (which may be empty). We do that by removing the - * POSIX information from the filesec object. - */ - if (s->flags & COPYFILE_STAT) { - copyfile_unset_posix_fsec(s->original_fsec); + if (s->permissive_fsec && (s->flags & COPYFILE_SECURITY) != COPYFILE_SECURITY) { + if (s->flags & COPYFILE_ACL) { + /* Just need to reset the BSD information -- mode, owner, group */ + (void)fchown(s->dst_fd, s->dst_sb.st_uid, s->dst_sb.st_gid); + (void)fchmod(s->dst_fd, s->dst_sb.st_mode); + } else { + /* + * flags is either COPYFILE_STAT, or neither; if it's + * neither, then we restore both ACL and POSIX permissions; + * if it's STAT, however, then we only want to restore the + * ACL (which may be empty). We do that by removing the + * POSIX information from the filesec object. + */ + if (s->flags & COPYFILE_STAT) { + copyfile_unset_posix_fsec(s->original_fsec); + } + if (fchmodx_np(s->dst_fd, s->original_fsec) < 0 && errno != ENOTSUP) + copyfile_warn("restoring security information"); } - if (fchmodx_np(s->dst_fd, s->original_fsec) < 0 && errno != ENOTSUP) - copyfile_warn("restoring security information"); } - } - if (s->permissive_fsec) { - filesec_free(s->permissive_fsec); - s->permissive_fsec = NULL; - } + if (s->permissive_fsec) { + filesec_free(s->permissive_fsec); + s->permissive_fsec = NULL; + } - if (s->original_fsec) { - filesec_free(s->original_fsec); - s->original_fsec = NULL; - } + if (s->original_fsec) { + filesec_free(s->original_fsec); + s->original_fsec = NULL; + } #endif - return; + return; } /* @@ -751,41 +756,41 @@ copytree(copyfile_state_t s) break; } switch (ftsent->fts_info) { - case FTS_D: - tstate->internal_flags |= cfDelayAce; - cmd = COPYFILE_RECURSE_DIR; - break; - case FTS_SL: - case FTS_SLNONE: - case FTS_DEFAULT: - case FTS_F: - cmd = COPYFILE_RECURSE_FILE; - break; - case FTS_DP: - cmd = COPYFILE_RECURSE_DIR_CLEANUP; - break; - case FTS_DNR: - case FTS_ERR: - case FTS_NS: - case FTS_NSOK: - default: - errno = ftsent->fts_errno; - if (status) { - rv = (*status)(COPYFILE_RECURSE_ERROR, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx); - if (rv == COPYFILE_SKIP || rv == COPYFILE_CONTINUE) { - errno = 0; - goto skipit; - } - if (rv == COPYFILE_QUIT) { + case FTS_D: + tstate->internal_flags |= cfDelayAce; + cmd = COPYFILE_RECURSE_DIR; + break; + case FTS_SL: + case FTS_SLNONE: + case FTS_DEFAULT: + case FTS_F: + cmd = COPYFILE_RECURSE_FILE; + break; + case FTS_DP: + cmd = COPYFILE_RECURSE_DIR_CLEANUP; + break; + case FTS_DNR: + case FTS_ERR: + case FTS_NS: + case FTS_NSOK: + default: + errno = ftsent->fts_errno; + if (status) { + rv = (*status)(COPYFILE_RECURSE_ERROR, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx); + if (rv == COPYFILE_SKIP || rv == COPYFILE_CONTINUE) { + errno = 0; + goto skipit; + } + if (rv == COPYFILE_QUIT) { + retval = -1; + goto stopit; + } + } else { retval = -1; goto stopit; } - } else { - retval = -1; - goto stopit; - } - case FTS_DOT: - goto skipit; + case FTS_DOT: + goto skipit; } @@ -871,8 +876,8 @@ copytree(copyfile_state_t s) rv = 0; } -skipit: -stopit: + skipit: + stopit: s->internal_flags &= ~COPYFILE_MNT_CPROTECT_MASK; s->internal_flags |= (tstate->internal_flags & COPYFILE_MNT_CPROTECT_MASK); @@ -897,75 +902,146 @@ done: */ int fcopyfile(int src_fd, int dst_fd, copyfile_state_t state, copyfile_flags_t flags) { - int ret = 0; - copyfile_state_t s = state; - struct stat dst_sb; + int ret = 0; + copyfile_state_t s = state; + struct stat dst_sb; - if (src_fd < 0 || dst_fd < 0) - { - errno = EINVAL; - return -1; - } + if (src_fd < 0 || dst_fd < 0) + { + errno = EINVAL; + return -1; + } - if (copyfile_preamble(&s, flags) < 0) - return -1; + if (copyfile_preamble(&s, flags) < 0) + return -1; + + copyfile_debug(2, "set src_fd <- %d", src_fd); + if (s->src_fd == -2 && src_fd > -1) + { + s->src_fd = src_fd; + if (fstatx_np(s->src_fd, &s->sb, s->fsec) != 0) + { + if (errno == ENOTSUP || errno == EPERM) + fstat(s->src_fd, &s->sb); + else + { + copyfile_warn("fstatx_np on src fd %d", s->src_fd); + return -1; + } + } + } + + /* prevent copying on unsupported types */ + switch (s->sb.st_mode & S_IFMT) + { + case S_IFLNK: + case S_IFDIR: + case S_IFREG: + break; + default: + errno = ENOTSUP; + return -1; + } + + copyfile_debug(2, "set dst_fd <- %d", dst_fd); + if (s->dst_fd == -2 && dst_fd > -1) + s->dst_fd = dst_fd; + + (void)fstat(s->dst_fd, &dst_sb); + (void)fchmod(s->dst_fd, (dst_sb.st_mode & ~S_IFMT) | (S_IRUSR | S_IWUSR)); + + (void)copyfile_quarantine(s); + + ret = copyfile_internal(s, flags); + + if (ret >= 0 && !(s->flags & COPYFILE_STAT)) + { + (void)fchmod(s->dst_fd, dst_sb.st_mode & ~S_IFMT); + } + + if (s->err) { + errno = s->err; + s->err = 0; + } + if (state == NULL) { + int t = errno; + copyfile_state_free(s); + errno = t; + } + + return ret; + +} + +/* + * This routine implements the clonefileat functionality + * for copyfile. There are 2 kinds of clone flags, namely + * 1. COPYFILE_CLONE_FORCE which is a 'force' clone flag. + * 2. COPYFILE_CLONE which is a 'best try' flag. + * In both cases, we inherit the flags provided + * to copyfile call and clone the file. + * Both these flags are equivalent to + * (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA) + * With force clone flag set, we return failure if cloning fails, + * however, in case of best try flag, we fallback to the copy method. + */ + +static int copyfile_clone(const char *src, const char *dst, copyfile_state_t state, copyfile_flags_t flags) +{ + int ret = 0; + int cloneFlags = 0; + struct stat src_sb; - copyfile_debug(2, "set src_fd <- %d", src_fd); - if (s->src_fd == -2 && src_fd > -1) - { - s->src_fd = src_fd; - if (fstatx_np(s->src_fd, &s->sb, s->fsec) != 0) + if (lstat(src, &src_sb) != 0) { - if (errno == ENOTSUP || errno == EPERM) - fstat(s->src_fd, &s->sb); - else - { - copyfile_warn("fstatx_np on src fd %d", s->src_fd); + errno = EINVAL; return -1; - } - } - } - - /* prevent copying on unsupported types */ - switch (s->sb.st_mode & S_IFMT) - { - case S_IFLNK: - case S_IFDIR: - case S_IFREG: - break; - default: - errno = ENOTSUP; - return -1; - } - - copyfile_debug(2, "set dst_fd <- %d", dst_fd); - if (s->dst_fd == -2 && dst_fd > -1) - s->dst_fd = dst_fd; - - (void)fstat(s->dst_fd, &dst_sb); - (void)fchmod(s->dst_fd, (dst_sb.st_mode & ~S_IFMT) | (S_IRUSR | S_IWUSR)); - - (void)copyfile_quarantine(s); - - ret = copyfile_internal(s, flags); - - if (ret >= 0 && !(s->flags & COPYFILE_STAT)) - { - (void)fchmod(s->dst_fd, dst_sb.st_mode & ~S_IFMT); - } - - if (s->err) { - errno = s->err; - s->err = 0; - } - if (state == NULL) { - int t = errno; - copyfile_state_free(s); - errno = t; - } - - return ret; + } + + if (COPYFILE_NOFOLLOW & flags) + { + cloneFlags = CLONE_NOFOLLOW; + } + /* + * Support only for files and symbolic links. + * TODO:Remove this check when support for directories is added. + */ + if (S_ISREG(src_sb.st_mode) || S_ISLNK(src_sb.st_mode)) + { + /* + * COPYFILE_UNLINK tells us to try removing the destination + * before we create it. We don't care if the file doesn't + * exist, so we ignore ENOENT. + */ + if (flags & COPYFILE_UNLINK) + { + if (remove(dst) < 0 && errno != ENOENT) + { + return -1; + } + } + ret = clonefileat(AT_FDCWD, src, AT_FDCWD, dst, cloneFlags); + if (ret == 0) { + /* + * We could also report the size of the single + * object that was cloned. However, that's a lot + * more difficult when we eventually support + * cloning directories. It seems reasonable to NOT + * report any bytes being "copied" in this scenario, + * and let the caller figure out how they want to + * deal. + */ + if (state != NULL) + state->was_cloned = true; + } + } + else + { + errno = EINVAL; + ret = -1; + } + return ret; } /* @@ -976,155 +1052,169 @@ int fcopyfile(int src_fd, int dst_fd, copyfile_state_t state, copyfile_flags_t f */ int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_flags_t flags) { - int ret = 0; - int createdst = 0; - copyfile_state_t s = state; - struct stat dst_sb; - - if (src == NULL && dst == NULL) - { - errno = EINVAL; - return -1; - } + int ret = 0; + int createdst = 0; + copyfile_state_t s = state; + struct stat dst_sb; - if (copyfile_preamble(&s, flags) < 0) - { - return -1; - } + if (src == NULL && dst == NULL) + { + errno = EINVAL; + return -1; + } -/* - * This macro is... well, it's not the worst thing you can do with cpp, not - * by a long shot. Essentially, we are setting the filename (src or dst) - * in the state structure; since the structure may not have been cleared out - * before being used again, we do some of the cleanup here: if the given - * filename (e.g., src) is set, and state->src is not equal to that, then - * we need to check to see if the file descriptor had been opened, and if so, - * close it. After that, we set state->src to be a copy of the given filename, - * releasing the old copy if necessary. - */ -#define COPYFILE_SET_FNAME(NAME, S) \ - do { \ - if (NAME != NULL) { \ - if (S->NAME != NULL && strncmp(NAME, S->NAME, MAXPATHLEN)) { \ - copyfile_debug(2, "replacing string %s (%s) -> (%s)", #NAME, NAME, S->NAME);\ - if (S->NAME##_fd != -2 && S->NAME##_fd > -1) { \ - copyfile_debug(4, "closing %s fd: %d", #NAME, S->NAME##_fd); \ - close(S->NAME##_fd); \ - S->NAME##_fd = -2; \ - } \ - } \ - if (S->NAME) { \ - free(S->NAME); \ - S->NAME = NULL; \ - } \ - if ((NAME) && (S->NAME = strdup(NAME)) == NULL) \ - return -1; \ - } \ - } while (0) - - COPYFILE_SET_FNAME(src, s); - COPYFILE_SET_FNAME(dst, s); - - if (s->flags & COPYFILE_RECURSIVE) { - ret = copytree(s); - goto exit; - } - - /* - * Get a copy of the source file's security settings - */ - if (s->original_fsec) { - filesec_free(s->original_fsec); - s->original_fsec = NULL; - } - if ((s->original_fsec = filesec_init()) == NULL) - goto error_exit; - - if ((s->flags & COPYFILE_NOFOLLOW_DST) && lstat(s->dst, &dst_sb) == 0 && - ((dst_sb.st_mode & S_IFMT) == S_IFLNK)) { - if (s->permissive_fsec) - free(s->permissive_fsec); - s->permissive_fsec = NULL; - } else if(statx_np(s->dst, &dst_sb, s->original_fsec) == 0) - { - /* - * copyfile_fix_perms() will make a copy of the permission set, - * and insert at the beginning an ACE that ensures we can write - * to the file and set attributes. - */ - - if((s->permissive_fsec = copyfile_fix_perms(s, &s->original_fsec)) != NULL) + if (flags & (COPYFILE_CLONE_FORCE | COPYFILE_CLONE)) { - /* - * Set the permissions for the destination to our copy. - * We should get ENOTSUP from any filesystem that simply - * doesn't support it. - */ - if (chmodx_np(s->dst, s->permissive_fsec) < 0 && errno != ENOTSUP) - { - copyfile_warn("setting security information"); - filesec_free(s->permissive_fsec); + ret = copyfile_clone(src, dst, state, flags); + if ((ret == 0) || (flags & COPYFILE_CLONE_FORCE)) + { + return ret; + } + // cloning failed. Inherit clonefile flags required for + // falling back to copyfile. + flags = flags | (COPYFILE_EXCL | COPYFILE_ACL | + COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA); + flags = flags & (~COPYFILE_CLONE); + } + ret = 0; + if (copyfile_preamble(&s, flags) < 0) + { + return -1; + } + + /* + * This macro is... well, it's not the worst thing you can do with cpp, not + * by a long shot. Essentially, we are setting the filename (src or dst) + * in the state structure; since the structure may not have been cleared out + * before being used again, we do some of the cleanup here: if the given + * filename (e.g., src) is set, and state->src is not equal to that, then + * we need to check to see if the file descriptor had been opened, and if so, + * close it. After that, we set state->src to be a copy of the given filename, + * releasing the old copy if necessary. + */ +#define COPYFILE_SET_FNAME(NAME, S) \ + do { \ + if (NAME != NULL) { \ + if (S->NAME != NULL && strncmp(NAME, S->NAME, MAXPATHLEN)) { \ + copyfile_debug(2, "replacing string %s (%s) -> (%s)", #NAME, NAME, S->NAME);\ + if (S->NAME##_fd != -2 && S->NAME##_fd > -1) { \ + copyfile_debug(4, "closing %s fd: %d", #NAME, S->NAME##_fd); \ + close(S->NAME##_fd); \ + S->NAME##_fd = -2; \ + } \ + } \ + if (S->NAME) { \ + free(S->NAME); \ + S->NAME = NULL; \ + } \ + if ((NAME) && (S->NAME = strdup(NAME)) == NULL) \ + return -1; \ + } \ + } while (0) + + COPYFILE_SET_FNAME(src, s); + COPYFILE_SET_FNAME(dst, s); + + if (s->flags & COPYFILE_RECURSIVE) { + ret = copytree(s); + goto exit; + } + + /* + * Get a copy of the source file's security settings + */ + if (s->original_fsec) { + filesec_free(s->original_fsec); + s->original_fsec = NULL; + } + if ((s->original_fsec = filesec_init()) == NULL) + goto error_exit; + + if ((s->flags & COPYFILE_NOFOLLOW_DST) && lstat(s->dst, &dst_sb) == 0 && + ((dst_sb.st_mode & S_IFMT) == S_IFLNK)) { + if (s->permissive_fsec) + free(s->permissive_fsec); s->permissive_fsec = NULL; - } - } - } else if (errno == ENOENT) { - createdst = 1; - } - - /* - * If COPYFILE_CHECK is set in flags, then all we are going to do - * is see what kinds of things WOULD have been copied (see - * copyfile_check() below). We return that value. - */ - if (COPYFILE_CHECK & flags) - { - ret = copyfile_check(s); - goto exit; - } else if ((ret = copyfile_open(s)) < 0) - goto error_exit; + } else if(statx_np(s->dst, &dst_sb, s->original_fsec) == 0) + { + /* + * copyfile_fix_perms() will make a copy of the permission set, + * and insert at the beginning an ACE that ensures we can write + * to the file and set attributes. + */ + + if((s->permissive_fsec = copyfile_fix_perms(s, &s->original_fsec)) != NULL) + { + /* + * Set the permissions for the destination to our copy. + * We should get ENOTSUP from any filesystem that simply + * doesn't support it. + */ + if (chmodx_np(s->dst, s->permissive_fsec) < 0 && errno != ENOTSUP) + { + copyfile_warn("setting security information"); + filesec_free(s->permissive_fsec); + s->permissive_fsec = NULL; + } + } + } else if (errno == ENOENT) { + createdst = 1; + } + + /* + * If COPYFILE_CHECK is set in flags, then all we are going to do + * is see what kinds of things WOULD have been copied (see + * copyfile_check() below). We return that value. + */ + if (COPYFILE_CHECK & flags) + { + ret = copyfile_check(s); + goto exit; + } else if ((ret = copyfile_open(s)) < 0) + goto error_exit; - (void)fcntl(s->src_fd, F_NOCACHE, 1); - (void)fcntl(s->dst_fd, F_NOCACHE, 1); + (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); + (void)fcntl(s->dst_fd, F_SINGLE_WRITER, 1); #endif - ret = copyfile_internal(s, flags); - if (ret == -1) - goto error_exit; + ret = copyfile_internal(s, flags); + if (ret == -1) + goto error_exit; #ifdef COPYFILE_RECURSIVE - if (!(flags & COPYFILE_STAT)) { - if (!createdst) - { - /* Just need to reset the BSD information -- mode, owner, group */ - (void)fchown(s->dst_fd, dst_sb.st_uid, dst_sb.st_gid); - (void)fchmod(s->dst_fd, dst_sb.st_mode); + if (!(flags & COPYFILE_STAT)) { + if (!createdst) + { + /* Just need to reset the BSD information -- mode, owner, group */ + (void)fchown(s->dst_fd, dst_sb.st_uid, dst_sb.st_gid); + (void)fchmod(s->dst_fd, dst_sb.st_mode); + } } - } #endif - reset_security(s); + reset_security(s); - if (s->src && (flags & COPYFILE_MOVE)) - (void)remove(s->src); + if (s->src && (flags & COPYFILE_MOVE)) + (void)remove(s->src); exit: - if (state == NULL) { - int t = errno; - copyfile_state_free(s); - errno = t; - } + if (state == NULL) { + int t = errno; + copyfile_state_free(s); + errno = t; + } - return ret; + return ret; error_exit: - ret = -1; - if (s->err) { - errno = s->err; - s->err = 0; - } - goto exit; + ret = -1; + if (s->err) { + errno = s->err; + s->err = 0; + } + goto exit; } /* @@ -1134,43 +1224,43 @@ error_exit: */ static int copyfile_preamble(copyfile_state_t *state, copyfile_flags_t flags) { - copyfile_state_t s; + copyfile_state_t s; - if (*state == NULL) - { - if ((*state = copyfile_state_alloc()) == NULL) - return -1; - } + if (*state == NULL) + { + if ((*state = copyfile_state_alloc()) == NULL) + return -1; + } - s = *state; + s = *state; - if (COPYFILE_DEBUG & flags) - { - char *e; - if ((e = getenv(COPYFILE_DEBUG_VAR))) + if (COPYFILE_DEBUG & flags) { - errno = 0; - s->debug = (uint32_t)strtol(e, NULL, 0); + char *e; + if ((e = getenv(COPYFILE_DEBUG_VAR))) + { + errno = 0; + s->debug = (uint32_t)strtol(e, NULL, 0); - /* clamp s->debug to 1 if the environment variable is not parsable */ - if (s->debug == 0 && errno != 0) - s->debug = 1; + /* clamp s->debug to 1 if the environment variable is not parsable */ + if (s->debug == 0 && errno != 0) + s->debug = 1; + } + copyfile_debug(2, "debug value set to: %d", s->debug); } - copyfile_debug(2, "debug value set to: %d", s->debug); - } #if 0 - /* Temporarily disabled */ - if (getenv(COPYFILE_DISABLE_VAR) != NULL) - { - copyfile_debug(1, "copyfile disabled"); - return 2; - } + /* Temporarily disabled */ + if (getenv(COPYFILE_DISABLE_VAR) != NULL) + { + copyfile_debug(1, "copyfile disabled"); + return 2; + } #endif - copyfile_debug(2, "setting flags: %d", s->flags); - s->flags = flags; + copyfile_debug(2, "setting flags: %d", s->flags); + s->flags = flags; - return 0; + return 0; } /* @@ -1180,190 +1270,216 @@ static int copyfile_preamble(copyfile_state_t *state, copyfile_flags_t flags) */ static int copyfile_internal(copyfile_state_t s, copyfile_flags_t flags) { - int ret = 0; + int ret = 0; - if (s->dst_fd < 0 || s->src_fd < 0) - { - copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)", s->src_fd, s->dst_fd); - s->err = EINVAL; - return -1; - } - - /* - * COPYFILE_PACK causes us to create an Apple Double version of the - * source file, and puts it into the destination file. See - * copyfile_pack() below for all the gory details. - */ - if (COPYFILE_PACK & flags) - { - if ((ret = copyfile_pack(s)) < 0) + if (s->dst_fd < 0 || s->src_fd < 0) { - if (s->dst) unlink(s->dst); - goto exit; + copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)", s->src_fd, s->dst_fd); + s->err = EINVAL; + return -1; } - goto exit; - } - - /* - * COPYFILE_UNPACK is the undoing of COPYFILE_PACK, obviously. - * The goal there is to take an Apple Double file, and turn it - * into a normal file (with data fork, resource fork, modes, - * extended attributes, ACLs, etc.). - */ - if (COPYFILE_UNPACK & flags) - { - if ((ret = copyfile_unpack(s)) < 0) - goto error_exit; - goto exit; - } - - /* - * If we have quarantine info set, we attempt - * to apply it to dst_fd. We don't care if - * it fails, not yet anyway. - */ - if (s->qinfo) { - int qr = qtn_file_apply_to_fd(s->qinfo, s->dst_fd); - if (qr != 0) { - if (s->statuscb) { - int rv; - s->xattr_name = (char*)XATTR_QUARANTINE_NAME; - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); - s->xattr_name = NULL; - if (rv == COPYFILE_QUIT) { - s->err = errno = (qr < 0 ? ENOTSUP : qr); - ret = -1; - goto exit; - } - } else { - s->err = errno = (qr < 0 ? ENOTSUP : qr); - ret = -1; + /* + * COPYFILE_PACK causes us to create an Apple Double version of the + * source file, and puts it into the destination file. See + * copyfile_pack() below for all the gory details. + */ + if (COPYFILE_PACK & flags) + { + if ((ret = copyfile_pack(s)) < 0) + { + if (s->dst) unlink(s->dst); goto exit; } + goto exit; } - } - - /* - * COPYFILE_XATTR tells us to copy the extended attributes; - * this is seperate from the extended security (aka ACLs), - * however. If we succeed in this, we continue to the next - * stage; if we fail, we return with an error value. Note - * that we fail if the errno is ENOTSUP, but we don't print - * a warning in that case. - */ - if (COPYFILE_XATTR & flags) - { - if ((ret = copyfile_xattr(s)) < 0) - { - if (errno != ENOTSUP && errno != EPERM) - copyfile_warn("error processing extended attributes"); - goto exit; - } - } - - /* - * Simialr to above, this tells us whether or not to copy - * the non-meta data portion of the file. We attempt to - * remove (via unlink) the destination file if we fail. - */ - if (COPYFILE_DATA & flags) - { - if ((ret = copyfile_data(s)) < 0) - { - copyfile_warn("error processing data"); - if (s->dst && unlink(s->dst)) - copyfile_warn("%s: remove", s->src ? s->src : "(null src)"); - goto exit; - } - } - - /* - * COPYFILE_SECURITY requests that we copy the security, both - * extended and mundane (that is, ACLs and POSIX). - */ - if (COPYFILE_SECURITY & flags) - { - if ((ret = copyfile_security(s)) < 0) - { - copyfile_warn("error processing security information"); - goto exit; - } - } - if (COPYFILE_STAT & flags) - { - if ((ret = copyfile_stat(s)) < 0) + /* + * COPYFILE_UNPACK is the undoing of COPYFILE_PACK, obviously. + * The goal there is to take an Apple Double file, and turn it + * into a normal file (with data fork, resource fork, modes, + * extended attributes, ACLs, etc.). + */ + if (COPYFILE_UNPACK & flags) { - copyfile_warn("error processing POSIX information"); - goto exit; + if ((ret = copyfile_unpack(s)) < 0) + goto error_exit; + goto exit; } - } - -exit: - return ret; -error_exit: - ret = -1; - goto exit; -} -/* - * A publicly-visible routine, copyfile_state_alloc() sets up the state variable. - */ -copyfile_state_t copyfile_state_alloc(void) -{ - copyfile_state_t s = (copyfile_state_t) calloc(1, sizeof(struct _copyfile_state)); - if (s != NULL) - { - s->src_fd = -2; - s->dst_fd = -2; - if (s->fsec) { - filesec_free(s->fsec); - s->fsec = NULL; - } - s->fsec = filesec_init(); - } else - errno = ENOMEM; + /* + * If we have quarantine info set, we attempt + * to apply it to dst_fd. We don't care if + * it fails, not yet anyway. + */ + if (s->qinfo) + { + int qr; + uint32_t q_flags; - return s; -} + /* + * If COPYFILE_RUN_IN_PLACE is set, we need to add + * QTN_FLAG_DO_NOT_TRANSLOCATE to the qinfo flags. + * + * On iOS, qtn_file_get_flags & qtn_file_set_flags + * don't modify anything, always return 0, per static + * defines at top of this file, though we should never + * get here in that case as qinfo will always be NULL. + */ + if (COPYFILE_RUN_IN_PLACE & flags) + { + q_flags = 0; -/* - * copyfile_state_free() returns the memory allocated to the state structure. - * It also closes the file descriptors, if they've been opened. - */ -int copyfile_state_free(copyfile_state_t s) -{ - if (s != NULL) - { - if (s->fsec) - filesec_free(s->fsec); + q_flags = qtn_file_get_flags(s->qinfo); + q_flags |= QTN_FLAG_DO_NOT_TRANSLOCATE; - if (s->original_fsec) - filesec_free(s->original_fsec); + if (qtn_file_set_flags(s->qinfo, q_flags) != 0) { + s->err = errno = EINVAL; + goto error_exit; + } + } - if (s->permissive_fsec) - filesec_free(s->permissive_fsec); + qr = qtn_file_apply_to_fd(s->qinfo, s->dst_fd); + if (qr != 0) { + if (s->statuscb) { + int rv; - if (s->qinfo) - qtn_file_free(s->qinfo); + s->xattr_name = (char*)XATTR_QUARANTINE_NAME; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) { + s->err = errno = (qr < 0 ? ENOTSUP : qr); + goto error_exit; + } + } else { + s->err = errno = (qr < 0 ? ENOTSUP : qr); + goto error_exit; + } + } + } - if (copyfile_close(s) < 0) + /* + * COPYFILE_XATTR tells us to copy the extended attributes; + * this is seperate from the extended security (aka ACLs), + * however. If we succeed in this, we continue to the next + * stage; if we fail, we return with an error value. Note + * that we fail if the errno is ENOTSUP, but we don't print + * a warning in that case. + */ + if (COPYFILE_XATTR & flags) + { + if ((ret = copyfile_xattr(s)) < 0) + { + if (errno != ENOTSUP && errno != EPERM) + copyfile_warn("error processing extended attributes"); + goto exit; + } + } + + /* + * Simialr to above, this tells us whether or not to copy + * the non-meta data portion of the file. We attempt to + * remove (via unlink) the destination file if we fail. + */ + if (COPYFILE_DATA & flags) + { + if ((ret = copyfile_data(s)) < 0) + { + copyfile_warn("error processing data"); + if (s->dst && unlink(s->dst)) + copyfile_warn("%s: remove", s->src ? s->src : "(null src)"); + goto exit; + } + } + + /* + * COPYFILE_SECURITY requests that we copy the security, both + * extended and mundane (that is, ACLs and POSIX). + */ + if (COPYFILE_SECURITY & flags) + { + if ((ret = copyfile_security(s)) < 0) + { + copyfile_warn("error processing security information"); + goto exit; + } + } + + if (COPYFILE_STAT & flags) + { + if ((ret = copyfile_stat(s)) < 0) + { + copyfile_warn("error processing POSIX information"); + goto exit; + } + } + +exit: + return ret; + +error_exit: + ret = -1; + goto exit; +} + +/* + * A publicly-visible routine, copyfile_state_alloc() sets up the state variable. + */ +copyfile_state_t copyfile_state_alloc(void) +{ + copyfile_state_t s = (copyfile_state_t) calloc(1, sizeof(struct _copyfile_state)); + + if (s != NULL) + { + s->src_fd = -2; + s->dst_fd = -2; + if (s->fsec) { + filesec_free(s->fsec); + s->fsec = NULL; + } + s->fsec = filesec_init(); + } else + errno = ENOMEM; + + return s; +} + +/* + * copyfile_state_free() returns the memory allocated to the state structure. + * It also closes the file descriptors, if they've been opened. + */ +int copyfile_state_free(copyfile_state_t s) +{ + if (s != NULL) { - copyfile_warn("error closing files"); - return -1; - } - if (s->xattr_name) - free(s->xattr_name); - if (s->dst) - free(s->dst); - if (s->src) - free(s->src); - free(s); - } - return 0; + if (s->fsec) + filesec_free(s->fsec); + + if (s->original_fsec) + filesec_free(s->original_fsec); + + if (s->permissive_fsec) + filesec_free(s->permissive_fsec); + + if (s->qinfo) + qtn_file_free(s->qinfo); + + if (copyfile_close(s) < 0) + { + copyfile_warn("error closing files"); + return -1; + } + if (s->xattr_name) + free(s->xattr_name); + if (s->dst) + free(s->dst); + if (s->src) + free(s->src); + free(s); + } + return 0; } /* @@ -1372,15 +1488,15 @@ int copyfile_state_free(copyfile_state_t s) */ static int copyfile_close(copyfile_state_t s) { - if (s->src && s->src_fd >= 0) - close(s->src_fd); + if (s->src && s->src_fd >= 0) + close(s->src_fd); - if (s->dst && s->dst_fd >= 0) { - if (close(s->dst_fd)) - return -1; - } + if (s->dst && s->dst_fd >= 0) { + if (close(s->dst_fd)) + return -1; + } - return 0; + return 0; } /* @@ -1393,86 +1509,86 @@ static int copyfile_close(copyfile_state_t s) */ static filesec_t copyfile_fix_perms(copyfile_state_t s __unused, filesec_t *fsec) { - filesec_t ret_fsec = NULL; - mode_t mode; - acl_t acl = NULL; + filesec_t ret_fsec = NULL; + mode_t mode; + acl_t acl = NULL; - if ((ret_fsec = filesec_dup(*fsec)) == NULL) - goto error_exit; + if ((ret_fsec = filesec_dup(*fsec)) == NULL) + goto error_exit; - if (filesec_get_property(ret_fsec, FILESEC_ACL, &acl) == 0) - { + if (filesec_get_property(ret_fsec, FILESEC_ACL, &acl) == 0) + { #ifdef COPYFILE_RECURSIVE - if (add_uberace(&acl)) - goto error_exit; + if (add_uberace(&acl)) + goto error_exit; #else - acl_entry_t entry; - acl_permset_t permset; - uuid_t qual; + acl_entry_t entry; + acl_permset_t permset; + uuid_t qual; - if (mbr_uid_to_uuid(getuid(), qual) != 0) - goto error_exit; + if (mbr_uid_to_uuid(getuid(), qual) != 0) + goto error_exit; - /* - * First, we create an entry, and give it the special name - * of ACL_FIRST_ENTRY, thus guaranteeing it will be first. - * After that, we clear out all the permissions in it, and - * add three permissions: WRITE_DATA, WRITE_ATTRIBUTES, and - * WRITE_EXTATTRIBUTES. We put these into an ACE that allows - * the functionality, and put this into the ACL. - */ - if (acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY) == -1) - goto error_exit; - if (acl_get_permset(entry, &permset) == -1) - goto error_exit; - if (acl_clear_perms(permset) == -1) - goto error_exit; - if (acl_add_perm(permset, ACL_WRITE_DATA) == -1) - goto error_exit; - if (acl_add_perm(permset, ACL_WRITE_ATTRIBUTES) == -1) - goto error_exit; - if (acl_add_perm(permset, ACL_WRITE_EXTATTRIBUTES) == -1) - goto error_exit; - if (acl_set_tag_type(entry, ACL_EXTENDED_ALLOW) == -1) - goto error_exit; - - if(acl_set_permset(entry, permset) == -1) - goto error_exit; - if(acl_set_qualifier(entry, qual) == -1) - goto error_exit; + /* + * First, we create an entry, and give it the special name + * of ACL_FIRST_ENTRY, thus guaranteeing it will be first. + * After that, we clear out all the permissions in it, and + * add three permissions: WRITE_DATA, WRITE_ATTRIBUTES, and + * WRITE_EXTATTRIBUTES. We put these into an ACE that allows + * the functionality, and put this into the ACL. + */ + if (acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY) == -1) + goto error_exit; + if (acl_get_permset(entry, &permset) == -1) + goto error_exit; + if (acl_clear_perms(permset) == -1) + goto error_exit; + if (acl_add_perm(permset, ACL_WRITE_DATA) == -1) + goto error_exit; + if (acl_add_perm(permset, ACL_WRITE_ATTRIBUTES) == -1) + goto error_exit; + if (acl_add_perm(permset, ACL_WRITE_EXTATTRIBUTES) == -1) + goto error_exit; + if (acl_set_tag_type(entry, ACL_EXTENDED_ALLOW) == -1) + goto error_exit; + + if(acl_set_permset(entry, permset) == -1) + goto error_exit; + if(acl_set_qualifier(entry, qual) == -1) + goto error_exit; #endif - if (filesec_set_property(ret_fsec, FILESEC_ACL, &acl) != 0) - goto error_exit; - } - - /* - * This is for the normal, mundane, POSIX permission model. - * We make sure that we can write to the file. - */ - if (filesec_get_property(ret_fsec, FILESEC_MODE, &mode) == 0) - { - if ((mode & (S_IWUSR | S_IRUSR)) != (S_IWUSR | S_IRUSR)) + if (filesec_set_property(ret_fsec, FILESEC_ACL, &acl) != 0) + goto error_exit; + } + + /* + * This is for the normal, mundane, POSIX permission model. + * We make sure that we can write to the file. + */ + if (filesec_get_property(ret_fsec, FILESEC_MODE, &mode) == 0) { - mode |= S_IWUSR|S_IRUSR; - if (filesec_set_property(ret_fsec, FILESEC_MODE, &mode) != 0) - goto error_exit; + if ((mode & (S_IWUSR | S_IRUSR)) != (S_IWUSR | S_IRUSR)) + { + mode |= S_IWUSR|S_IRUSR; + if (filesec_set_property(ret_fsec, FILESEC_MODE, &mode) != 0) + goto error_exit; + } } - } exit: - if (acl) - acl_free(acl); + if (acl) + acl_free(acl); - return ret_fsec; + return ret_fsec; error_exit: - if (ret_fsec) - { - filesec_free(ret_fsec); - ret_fsec = NULL; - } - goto exit; + if (ret_fsec) + { + filesec_free(ret_fsec); + ret_fsec = NULL; + } + goto exit; } /* @@ -1494,23 +1610,23 @@ copyfile_unset_posix_fsec(filesec_t fsec) */ static int copyfile_unset_acl(copyfile_state_t s) { - int ret = 0; - if (filesec_set_property(s->fsec, FILESEC_ACL, NULL) == -1) - { - copyfile_debug(5, "unsetting acl attribute on %s", s->dst ? s->dst : "(null dst)"); - ++ret; - } - if (filesec_set_property(s->fsec, FILESEC_UUID, NULL) == -1) - { - copyfile_debug(5, "unsetting uuid attribute on %s", s->dst ? s->dst : "(null dst)"); - ++ret; - } - if (filesec_set_property(s->fsec, FILESEC_GRPUUID, NULL) == -1) - { - copyfile_debug(5, "unsetting group uuid attribute on %s", s->dst ? s->dst : "(null dst)"); - ++ret; - } - return ret; + int ret = 0; + if (filesec_set_property(s->fsec, FILESEC_ACL, NULL) == -1) + { + copyfile_debug(5, "unsetting acl attribute on %s", s->dst ? s->dst : "(null dst)"); + ++ret; + } + if (filesec_set_property(s->fsec, FILESEC_UUID, NULL) == -1) + { + copyfile_debug(5, "unsetting uuid attribute on %s", s->dst ? s->dst : "(null dst)"); + ++ret; + } + if (filesec_set_property(s->fsec, FILESEC_GRPUUID, NULL) == -1) + { + copyfile_debug(5, "unsetting group uuid attribute on %s", s->dst ? s->dst : "(null dst)"); + ++ret; + } + return ret; } /* @@ -1521,264 +1637,264 @@ static int copyfile_unset_acl(copyfile_state_t s) */ static int copyfile_open(copyfile_state_t s) { - int oflags = O_EXCL | O_CREAT | O_WRONLY; - int islnk = 0, isdir = 0, isreg = 0; - int osrc = 0, dsrc = 0; - int prot_class = PROTECTION_CLASS_DEFAULT; - int set_cprot_explicit = 0; - int error = 0; - - if (s->src && s->src_fd == -2) - { - if ((COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np) - (s->src, &s->sb, s->fsec)) + int oflags = O_EXCL | O_CREAT | O_WRONLY; + int islnk = 0, isdir = 0, isreg = 0; + int osrc = 0, dsrc = 0; + int prot_class = PROTECTION_CLASS_DEFAULT; + int set_cprot_explicit = 0; + int error = 0; + + if (s->src && s->src_fd == -2) { - copyfile_warn("stat on %s", s->src); - return -1; - } - - /* prevent copying on unsupported types */ - switch (s->sb.st_mode & S_IFMT) - { - case S_IFLNK: - islnk = 1; - if ((size_t)s->sb.st_size > SIZE_T_MAX) { - s->err = ENOMEM; /* too big for us to copy */ + if ((COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np) + (s->src, &s->sb, s->fsec)) + { + copyfile_warn("stat on %s", s->src); return -1; } - osrc = O_SYMLINK; - break; - case S_IFDIR: - isdir = 1; - break; - case S_IFREG: - isreg = 1; - break; - default: - if (!(strcmp(s->src, "/dev/null") == 0 && (s->flags & COPYFILE_METADATA))) { - s->err = ENOTSUP; - return -1; + + /* prevent copying on unsupported types */ + switch (s->sb.st_mode & S_IFMT) + { + case S_IFLNK: + islnk = 1; + if ((size_t)s->sb.st_size > SIZE_T_MAX) { + s->err = ENOMEM; /* too big for us to copy */ + return -1; + } + osrc = O_SYMLINK; + break; + case S_IFDIR: + isdir = 1; + break; + case S_IFREG: + isreg = 1; + break; + default: + if (!(strcmp(s->src, "/dev/null") == 0 && (s->flags & COPYFILE_METADATA))) { + s->err = ENOTSUP; + return -1; + } } - } - /* - * If we're packing, then we are actually - * creating a file, no matter what the source - * was. - */ - if (s->flags & COPYFILE_PACK) { /* - * O_SYMLINK and O_NOFOLLOW are not compatible options: - * if the file is a symlink, and O_NOFOLLOW is specified, - * open will return ELOOP, whether or not O_SYMLINK is set. - * However, we know whether or not it was a symlink from - * the stat above (although there is a potentiaal for a race - * condition here, but it will err on the side of returning - * ELOOP from open). + * If we're packing, then we are actually + * creating a file, no matter what the source + * was. */ - if (!islnk) - osrc = (s->flags & COPYFILE_NOFOLLOW_SRC) ? O_NOFOLLOW : 0; - isdir = islnk = 0; - } - - if ((s->src_fd = open(s->src, O_RDONLY | osrc , 0)) < 0) - { - copyfile_warn("open on %s", s->src); - return -1; - } - copyfile_debug(2, "open successful on source (%s)", s->src); - - (void)copyfile_quarantine(s); - } - - if (s->dst && s->dst_fd == -2) - { - /* - * COPYFILE_UNLINK tells us to try removing the destination - * before we create it. We don't care if the file doesn't - * exist, so we ignore ENOENT. - */ - if (COPYFILE_UNLINK & s->flags) - { - if (remove(s->dst) < 0 && errno != ENOENT) - { - copyfile_warn("%s: remove", s->dst); - return -1; - } - } - - if (s->flags & COPYFILE_NOFOLLOW_DST) { - struct stat st; - - dsrc = O_NOFOLLOW; - if (lstat(s->dst, &st) != -1) { - if ((st.st_mode & S_IFMT) == S_IFLNK) - dsrc = O_SYMLINK; + if (s->flags & COPYFILE_PACK) { + /* + * O_SYMLINK and O_NOFOLLOW are not compatible options: + * if the file is a symlink, and O_NOFOLLOW is specified, + * open will return ELOOP, whether or not O_SYMLINK is set. + * However, we know whether or not it was a symlink from + * the stat above (although there is a potentiaal for a race + * condition here, but it will err on the side of returning + * ELOOP from open). + */ + if (!islnk) + osrc = (s->flags & COPYFILE_NOFOLLOW_SRC) ? O_NOFOLLOW : 0; + isdir = islnk = 0; } - } - if (!(s->internal_flags & cfSrcProtSupportValid)) - { - if ((error = does_copy_protection(s->src_fd)) > 0) - { - s->internal_flags |= cfSrcSupportsCProtect; - } - else if (error < 0) + if ((s->src_fd = open(s->src, O_RDONLY | osrc , 0)) < 0) { - copyfile_warn("does_copy_protection failed on (%s) with error <%d>", s->src, errno); + copyfile_warn("open on %s", s->src); return -1; } - s->internal_flags |= cfSrcProtSupportValid; + copyfile_debug(2, "open successful on source (%s)", s->src); + + (void)copyfile_quarantine(s); } - /* copy protection is only valid for regular files and directories. */ - if ((isreg || isdir) && (s->internal_flags & cfSrcSupportsCProtect)) + if (s->dst && s->dst_fd == -2) { - prot_class = GET_PROT_CLASS(s->src_fd); - if (prot_class < 0) + /* + * COPYFILE_UNLINK tells us to try removing the destination + * before we create it. We don't care if the file doesn't + * exist, so we ignore ENOENT. + */ + if (COPYFILE_UNLINK & s->flags) { - copyfile_warn("GET_PROT_CLASS failed on (%s) with error <%d>", s->src, errno); - return -1; + if (remove(s->dst) < 0 && errno != ENOENT) + { + copyfile_warn("%s: remove", s->dst); + return -1; + } } - } - if (islnk) { - size_t sz = (size_t)s->sb.st_size + 1; - char *bp; + if (s->flags & COPYFILE_NOFOLLOW_DST) { + struct stat st; - bp = calloc(1, sz); - if (bp == NULL) { - copyfile_warn("cannot allocate %zd bytes", sz); - return -1; - } - if (readlink(s->src, bp, sz-1) == -1) { - copyfile_warn("cannot readlink %s", s->src); - free(bp); - return -1; + dsrc = O_NOFOLLOW; + if (lstat(s->dst, &st) != -1) { + if ((st.st_mode & S_IFMT) == S_IFLNK) + dsrc = O_SYMLINK; + } } - if (symlink(bp, s->dst) == -1) { - if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) { - copyfile_warn("Cannot make symlink %s", s->dst); - free(bp); + + if (!(s->internal_flags & cfSrcProtSupportValid)) + { + if ((error = does_copy_protection(s->src_fd)) > 0) + { + s->internal_flags |= cfSrcSupportsCProtect; + } + else if (error < 0) + { + copyfile_warn("does_copy_protection failed on (%s) with error <%d>", s->src, errno); return -1; } + s->internal_flags |= cfSrcProtSupportValid; } - free(bp); - s->dst_fd = open(s->dst, O_RDONLY | O_SYMLINK); - if (s->dst_fd == -1) { - copyfile_warn("Cannot open symlink %s for reading", s->dst); - return -1; - } - } else if (isdir) { - mode_t mode; - mode = (s->sb.st_mode & ~S_IFMT) | S_IRWXU; - if (mkdir(s->dst, mode) == -1) { - if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) { - copyfile_warn("Cannot make directory %s", s->dst); + /* copy protection is only valid for regular files and directories. */ + if ((isreg || isdir) && (s->internal_flags & cfSrcSupportsCProtect)) + { + prot_class = GET_PROT_CLASS(s->src_fd); + if (prot_class < 0) + { + copyfile_warn("GET_PROT_CLASS failed on (%s) with error <%d>", s->src, errno); return -1; } } - s->dst_fd = open(s->dst, O_RDONLY | dsrc); - if (s->dst_fd == -1) { - copyfile_warn("Cannot open directory %s for reading", s->dst); - return -1; - } - set_cprot_explicit = 1; - } else while((s->dst_fd = open_dprotected_np(s->dst, oflags | dsrc, prot_class, 0, s->sb.st_mode | S_IWUSR)) < 0) - { - /* - * We set S_IWUSR because fsetxattr does not -- at the time this comment - * was written -- allow one to set an extended attribute on a file descriptor - * for a read-only file, even if the file descriptor is opened for writing. - * This will only matter if the file does not already exist. - */ - switch(errno) - { - case EEXIST: - copyfile_debug(3, "open failed, retrying (%s)", s->dst); - if (s->flags & COPYFILE_EXCL) - break; - oflags = oflags & ~O_CREAT; - /* if O_CREAT isn't set in open_dprotected_np, it won't set protection class. - * Set the flag here so we know to do it later. - */ - set_cprot_explicit = 1; - if (s->flags & (COPYFILE_PACK | COPYFILE_DATA)) - { - copyfile_debug(4, "truncating existing file (%s)", s->dst); - oflags |= O_TRUNC; - } - continue; - case EACCES: - if(chmod(s->dst, (s->sb.st_mode | S_IWUSR) & ~S_IFMT) == 0) - continue; - else { - /* - * If we're trying to write to a directory to which we don't - * have access, the create above would have failed, but chmod - * here would have given us ENOENT. But the real error is - * still one of access, so we change the errno we're reporting. - * This could cause confusion with a race condition. - */ - if (errno == ENOENT) - errno = EACCES; - break; - } - case EISDIR: - copyfile_debug(3, "open failed because it is a directory (%s)", s->dst); - if (((s->flags & COPYFILE_EXCL) || - (!isdir && (s->flags & COPYFILE_DATA))) - && !(s->flags & COPYFILE_UNPACK)) - break; - oflags = (oflags & ~(O_WRONLY|O_CREAT|O_TRUNC)) | O_RDONLY; - continue; - } - copyfile_warn("open on %s", s->dst); - return -1; - } - copyfile_debug(2, "open successful on destination (%s)", s->dst); + if (islnk) { + size_t sz = (size_t)s->sb.st_size + 1; + char *bp; - if (s->internal_flags & cfSrcSupportsCProtect) - { - if (!(s->internal_flags & cfDstProtSupportValid)) - { - if ((error = does_copy_protection(s->dst_fd)) > 0) - { - s->internal_flags |= cfDstSupportsCProtect; + bp = calloc(1, sz); + if (bp == NULL) { + copyfile_warn("cannot allocate %zd bytes", sz); + return -1; } - else if (error < 0) - { - copyfile_warn("does_copy_protection failed on (%s) with error <%d>", s->dst, errno); + if (readlink(s->src, bp, sz-1) == -1) { + copyfile_warn("cannot readlink %s", s->src); + free(bp); + return -1; + } + if (symlink(bp, s->dst) == -1) { + if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) { + copyfile_warn("Cannot make symlink %s", s->dst); + free(bp); + return -1; + } + } + free(bp); + s->dst_fd = open(s->dst, O_RDONLY | O_SYMLINK); + if (s->dst_fd == -1) { + copyfile_warn("Cannot open symlink %s for reading", s->dst); + return -1; + } + } else if (isdir) { + mode_t mode; + mode = (s->sb.st_mode & ~S_IFMT) | S_IRWXU; + + if (mkdir(s->dst, mode) == -1) { + if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) { + copyfile_warn("Cannot make directory %s", s->dst); + return -1; + } + } + s->dst_fd = open(s->dst, O_RDONLY | dsrc); + if (s->dst_fd == -1) { + copyfile_warn("Cannot open directory %s for reading", s->dst); return -1; } - s->internal_flags |= cfDstProtSupportValid; + set_cprot_explicit = 1; + } else while((s->dst_fd = open_dprotected_np(s->dst, oflags | dsrc, prot_class, 0, s->sb.st_mode | S_IWUSR)) < 0) + { + /* + * We set S_IWUSR because fsetxattr does not -- at the time this comment + * was written -- allow one to set an extended attribute on a file descriptor + * for a read-only file, even if the file descriptor is opened for writing. + * This will only matter if the file does not already exist. + */ + switch(errno) + { + case EEXIST: + copyfile_debug(3, "open failed, retrying (%s)", s->dst); + if (s->flags & COPYFILE_EXCL) + break; + oflags = oflags & ~O_CREAT; + /* if O_CREAT isn't set in open_dprotected_np, it won't set protection class. + * Set the flag here so we know to do it later. + */ + set_cprot_explicit = 1; + if (s->flags & (COPYFILE_PACK | COPYFILE_DATA)) + { + copyfile_debug(4, "truncating existing file (%s)", s->dst); + oflags |= O_TRUNC; + } + continue; + case EACCES: + if(chmod(s->dst, (s->sb.st_mode | S_IWUSR) & ~S_IFMT) == 0) + continue; + else { + /* + * If we're trying to write to a directory to which we don't + * have access, the create above would have failed, but chmod + * here would have given us ENOENT. But the real error is + * still one of access, so we change the errno we're reporting. + * This could cause confusion with a race condition. + */ + + if (errno == ENOENT) + errno = EACCES; + break; + } + case EISDIR: + copyfile_debug(3, "open failed because it is a directory (%s)", s->dst); + if (((s->flags & COPYFILE_EXCL) || + (!isdir && (s->flags & COPYFILE_DATA))) + && !(s->flags & COPYFILE_UNPACK)) + break; + oflags = (oflags & ~(O_WRONLY|O_CREAT|O_TRUNC)) | O_RDONLY; + continue; + } + copyfile_warn("open on %s", s->dst); + return -1; } + copyfile_debug(2, "open successful on destination (%s)", s->dst); - if ((isreg || isdir) - && set_cprot_explicit - && (s->internal_flags & cfDstSupportsCProtect)) + if (s->internal_flags & cfSrcSupportsCProtect) { - /* Protection class is set in open_dprotected_np for regular files that aren't truncated. - * We set the protection class here for truncated files and directories. - */ - if (SET_PROT_CLASS(s->dst_fd, prot_class) != 0) + if (!(s->internal_flags & cfDstProtSupportValid)) { - copyfile_warn("SET_PROT_CLASS failed on (%s) with error <%d>", s->dst, errno); - return -1; + if ((error = does_copy_protection(s->dst_fd)) > 0) + { + s->internal_flags |= cfDstSupportsCProtect; + } + else if (error < 0) + { + copyfile_warn("does_copy_protection failed on (%s) with error <%d>", s->dst, errno); + return -1; + } + s->internal_flags |= cfDstProtSupportValid; + } + + if ((isreg || isdir) + && set_cprot_explicit + && (s->internal_flags & cfDstSupportsCProtect)) + { + /* Protection class is set in open_dprotected_np for regular files that aren't truncated. + * We set the protection class here for truncated files and directories. + */ + if (SET_PROT_CLASS(s->dst_fd, prot_class) != 0) + { + copyfile_warn("SET_PROT_CLASS failed on (%s) with error <%d>", s->dst, errno); + return -1; + } } } } - } - if (s->dst_fd < 0 || s->src_fd < 0) - { - copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)", - s->src_fd, s->dst_fd); - s->err = EINVAL; - return -1; - } - return 0; + if (s->dst_fd < 0 || s->src_fd < 0) + { + copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)", + s->src_fd, s->dst_fd); + s->err = EINVAL; + return -1; + } + return 0; } @@ -1792,82 +1908,82 @@ static int copyfile_open(copyfile_state_t s) */ static copyfile_flags_t copyfile_check(copyfile_state_t s) { - acl_t acl = NULL; - copyfile_flags_t ret = 0; - int nofollow = (s->flags & COPYFILE_NOFOLLOW_SRC); - qtn_file_t qinfo; - - if (!s->src) - { - s->err = EINVAL; - return -1; - } + acl_t acl = NULL; + copyfile_flags_t ret = 0; + int nofollow = (s->flags & COPYFILE_NOFOLLOW_SRC); + qtn_file_t qinfo; - /* check EAs */ - if (COPYFILE_XATTR & s->flags) - if (listxattr(s->src, 0, 0, nofollow ? XATTR_NOFOLLOW : 0) > 0) + if (!s->src) { - ret |= COPYFILE_XATTR; + s->err = EINVAL; + return -1; } - if (COPYFILE_ACL & s->flags) - { - (COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np) - (s->src, &s->sb, s->fsec); + /* check EAs */ + if (COPYFILE_XATTR & s->flags) + if (listxattr(s->src, 0, 0, nofollow ? XATTR_NOFOLLOW : 0) > 0) + { + ret |= COPYFILE_XATTR; + } - if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) == 0) - ret |= COPYFILE_ACL; - } + if (COPYFILE_ACL & s->flags) + { + (COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np) + (s->src, &s->sb, s->fsec); - copyfile_debug(2, "check result: %d (%s)", ret, s->src); + if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) == 0) + ret |= COPYFILE_ACL; + } - if (acl) - acl_free(acl); + copyfile_debug(2, "check result: %d (%s)", ret, s->src); - if (s->qinfo) { - /* If the state has had quarantine info set already, we use that */ - ret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL); - } else { - qinfo = qtn_file_alloc(); - /* - * For quarantine information, we need to see if the source file - * has any. Since it may be a symlink, however, and we may, or - * not be following, *and* there's no qtn* routine which can optionally - * follow or not follow a symlink, we need to instead work around - * this limitation. - */ - if (qinfo) { - int fd; - int qret = 0; - struct stat sbuf; + if (acl) + acl_free(acl); + if (s->qinfo) { + /* If the state has had quarantine info set already, we use that */ + ret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL); + } else { + qinfo = qtn_file_alloc(); /* - * If we care about not following symlinks, *and* the file exists - * (which is to say, lstat doesn't return an error), *and* the file - * is a symlink, then we open it up (with O_SYMLINK), and use - * qtn_file_init_with_fd(); if none of that is true, however, then - * we can simply use qtn_file_init_with_path(). + * For quarantine information, we need to see if the source file + * has any. Since it may be a symlink, however, and we may, or + * not be following, *and* there's no qtn* routine which can optionally + * follow or not follow a symlink, we need to instead work around + * this limitation. */ - if (nofollow - && lstat(s->src, &sbuf) == 0 + if (qinfo) { + int fd; + int qret = 0; + struct stat sbuf; + + /* + * If we care about not following symlinks, *and* the file exists + * (which is to say, lstat doesn't return an error), *and* the file + * is a symlink, then we open it up (with O_SYMLINK), and use + * qtn_file_init_with_fd(); if none of that is true, however, then + * we can simply use qtn_file_init_with_path(). + */ + if (nofollow + && lstat(s->src, &sbuf) == 0 && ((sbuf.st_mode & S_IFMT) == S_IFLNK)) { - fd = open(s->src, O_RDONLY | O_SYMLINK); - if (fd != -1) { - if (!qtn_file_init_with_fd(qinfo, fd)) { + fd = open(s->src, O_RDONLY | O_SYMLINK); + if (fd != -1) { + if (!qtn_file_init_with_fd(qinfo, fd)) { + qret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL); + } + close(fd); + } + } else { + if (!qtn_file_init_with_path(qinfo, s->src)) { qret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL); } - close(fd); - } - } else { - if (!qtn_file_init_with_path(qinfo, s->src)) { - qret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL); } + qtn_file_free(qinfo); + ret |= qret; } - qtn_file_free(qinfo); - ret |= qret; } - } - return ret; + return ret; } /* @@ -1878,140 +1994,140 @@ static copyfile_flags_t copyfile_check(copyfile_state_t s) */ static int copyfile_data(copyfile_state_t s) { - size_t blen; - char *bp = 0; - ssize_t nread; - int ret = 0; - size_t iBlocksize = 0; - size_t oBlocksize = 0; - const size_t onegig = 1 << 30; - struct statfs sfs; - copyfile_callback_t status = s->statuscb; - - /* Unless it's a normal file, we don't copy. For now, anyway */ - if ((s->sb.st_mode & S_IFMT) != S_IFREG) - return 0; + size_t blen; + char *bp = 0; + ssize_t nread; + int ret = 0; + size_t iBlocksize = 0; + size_t oBlocksize = 0; + const size_t onegig = 1 << 30; + struct statfs sfs; + copyfile_callback_t status = s->statuscb; + + /* Unless it's a normal file, we don't copy. For now, anyway */ + if ((s->sb.st_mode & S_IFMT) != S_IFREG) + return 0; #ifdef VOL_CAP_FMT_DECMPFS_COMPRESSION - if (s->internal_flags & cfSawDecmpEA) { - if (s->sb.st_flags & UF_COMPRESSED) { - if ((s->flags & COPYFILE_STAT) == 0) { - if (fchflags(s->dst_fd, UF_COMPRESSED) == 0) { - goto exit; + if (s->internal_flags & cfSawDecmpEA) { + if (s->sb.st_flags & UF_COMPRESSED) { + if ((s->flags & COPYFILE_STAT) == 0) { + if (fchflags(s->dst_fd, UF_COMPRESSED) == 0) { + goto exit; + } + } } - } } - } #endif - - if (fstatfs(s->src_fd, &sfs) == -1) { - iBlocksize = s->sb.st_blksize; - } else { - iBlocksize = sfs.f_iosize; - } - - /* Work-around for 6453525, limit blocksize to 1G */ - if (iBlocksize > onegig) { - iBlocksize = onegig; - } - - if ((bp = malloc(iBlocksize)) == NULL) - return -1; - if (fstatfs(s->dst_fd, &sfs) == -1 || sfs.f_iosize == 0) { - oBlocksize = iBlocksize; - } else { - oBlocksize = sfs.f_iosize; - if (oBlocksize > onegig) - oBlocksize = onegig; - } + if (fstatfs(s->src_fd, &sfs) == -1) { + iBlocksize = s->sb.st_blksize; + } else { + iBlocksize = sfs.f_iosize; + } - blen = iBlocksize; + /* Work-around for 6453525, limit blocksize to 1G */ + if (iBlocksize > onegig) { + iBlocksize = onegig; + } - s->totalCopied = 0; -/* If supported, do preallocation for Xsan / HFS volumes */ -#ifdef F_PREALLOCATE - { - fstore_t fst; - - fst.fst_flags = 0; - fst.fst_posmode = F_PEOFPOSMODE; - fst.fst_offset = 0; - fst.fst_length = s->sb.st_size; - /* Ignore errors; this is merely advisory. */ - (void)fcntl(s->dst_fd, F_PREALLOCATE, &fst); - } -#endif + if ((bp = malloc(iBlocksize)) == NULL) + return -1; - while ((nread = read(s->src_fd, bp, blen)) > 0) - { - ssize_t nwritten; - size_t left = nread; - void *ptr = bp; - int loop = 0; - - while (left > 0) { - nwritten = write(s->dst_fd, ptr, MIN(left, oBlocksize)); - switch (nwritten) { - case 0: - if (++loop > 5) { - copyfile_warn("writing to output %d times resulted in 0 bytes written", loop); - ret = -1; - s->err = EAGAIN; - goto exit; - } - break; - case -1: - copyfile_warn("writing to output file got error"); - if (status) { - int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_ERR, s, s->src, s->dst, s->ctx); - if (rv == COPYFILE_SKIP) { // Skip the data copy - ret = 0; + if (fstatfs(s->dst_fd, &sfs) == -1 || sfs.f_iosize == 0) { + oBlocksize = iBlocksize; + } else { + oBlocksize = sfs.f_iosize; + if (oBlocksize > onegig) + oBlocksize = onegig; + } + + blen = iBlocksize; + + s->totalCopied = 0; + /* If supported, do preallocation for Xsan / HFS volumes */ +#ifdef F_PREALLOCATE + { + fstore_t fst; + + fst.fst_flags = 0; + fst.fst_posmode = F_PEOFPOSMODE; + fst.fst_offset = 0; + fst.fst_length = s->sb.st_size; + /* Ignore errors; this is merely advisory. */ + (void)fcntl(s->dst_fd, F_PREALLOCATE, &fst); + } +#endif + + while ((nread = read(s->src_fd, bp, blen)) > 0) + { + ssize_t nwritten; + size_t left = nread; + void *ptr = bp; + int loop = 0; + + while (left > 0) { + nwritten = write(s->dst_fd, ptr, MIN(left, oBlocksize)); + switch (nwritten) { + case 0: + if (++loop > 5) { + copyfile_warn("writing to output %d times resulted in 0 bytes written", loop); + ret = -1; + s->err = EAGAIN; + goto exit; + } + break; + case -1: + copyfile_warn("writing to output file got error"); + if (status) { + int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + if (rv == COPYFILE_SKIP) { // Skip the data copy + ret = 0; + goto exit; + } + if (rv == COPYFILE_CONTINUE) { // Retry the write + errno = 0; + continue; + } + } + ret = -1; goto exit; - } - if (rv == COPYFILE_CONTINUE) { // Retry the write - errno = 0; - continue; - } + default: + left -= nwritten; + ptr = ((char*)ptr) + nwritten; + loop = 0; + break; } - ret = -1; - goto exit; - default: - left -= nwritten; - ptr = ((char*)ptr) + nwritten; - loop = 0; - break; - } - s->totalCopied += nwritten; - if (status) { - int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); - if (rv == COPYFILE_QUIT) { - ret = -1; s->err = errno = ECANCELED; - goto exit; + s->totalCopied += nwritten; + if (status) { + int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); + if (rv == COPYFILE_QUIT) { + ret = -1; s->err = errno = ECANCELED; + goto exit; + } } } } - } - if (nread < 0) - { - copyfile_warn("reading from %s", s->src ? s->src : "(null src)"); - ret = -1; - goto exit; - } + if (nread < 0) + { + copyfile_warn("reading from %s", s->src ? s->src : "(null src)"); + ret = -1; + goto exit; + } - if (ftruncate(s->dst_fd, s->totalCopied) < 0) - { - ret = -1; - goto exit; - } + if (ftruncate(s->dst_fd, s->totalCopied) < 0) + { + ret = -1; + goto exit; + } exit: - if (ret == -1) - { - s->err = errno; - } - free(bp); - return ret; + if (ret == -1) + { + s->err = errno; + } + free(bp); + return ret; } /* @@ -2022,174 +2138,174 @@ exit: */ static int copyfile_security(copyfile_state_t s) { - int copied = 0; - struct stat sb; - acl_t acl_src = NULL, acl_tmp = NULL, acl_dst = NULL; - int ret = 0; - filesec_t tmp_fsec = NULL; - filesec_t fsec_dst = filesec_init(); - - if (fsec_dst == NULL) - return -1; + int copied = 0; + struct stat sb; + acl_t acl_src = NULL, acl_tmp = NULL, acl_dst = NULL; + int ret = 0; + filesec_t tmp_fsec = NULL; + filesec_t fsec_dst = filesec_init(); + + if (fsec_dst == NULL) + return -1; - if (COPYFILE_ACL & s->flags) - { - if (filesec_get_property(s->fsec, FILESEC_ACL, &acl_src)) + if (COPYFILE_ACL & s->flags) { - if (errno == ENOENT) - acl_src = NULL; - else - goto error_exit; - } + if (filesec_get_property(s->fsec, FILESEC_ACL, &acl_src)) + { + if (errno == ENOENT) + acl_src = NULL; + else + goto error_exit; + } -/* grab the destination acl - cannot assume it's empty due to inheritance -*/ - if(fstatx_np(s->dst_fd, &sb, fsec_dst)) - goto error_exit; + /* grab the destination acl + cannot assume it's empty due to inheritance + */ + if(fstatx_np(s->dst_fd, &sb, fsec_dst)) + goto error_exit; - if (filesec_get_property(fsec_dst, FILESEC_ACL, &acl_dst)) - { - if (errno == ENOENT) - acl_dst = NULL; - else - goto error_exit; - } + if (filesec_get_property(fsec_dst, FILESEC_ACL, &acl_dst)) + { + if (errno == ENOENT) + acl_dst = NULL; + else + goto error_exit; + } - if (acl_src == NULL && acl_dst == NULL) - goto no_acl; + if (acl_src == NULL && acl_dst == NULL) + goto no_acl; - acl_tmp = acl_init(4); - if (acl_tmp == NULL) - goto error_exit; + acl_tmp = acl_init(4); + if (acl_tmp == NULL) + goto error_exit; - if (acl_src) { - acl_entry_t ace = NULL; - acl_entry_t tmp = NULL; - for (copied = 0; - acl_get_entry(acl_src, - ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, - &ace) == 0;) - { - acl_flagset_t flags = { 0 }; - acl_get_flagset_np(ace, &flags); - if (!acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) - { - if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1) - goto error_exit; - - if ((ret = acl_copy_entry(tmp, ace)) == -1) - goto error_exit; - - copyfile_debug(2, "copied acl entry from %s to %s", - s->src ? s->src : "(null src)", - s->dst ? s->dst : "(null tmp)"); - copied++; - } + if (acl_src) { + acl_entry_t ace = NULL; + acl_entry_t tmp = NULL; + for (copied = 0; + acl_get_entry(acl_src, + ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, + &ace) == 0;) + { + acl_flagset_t flags = { 0 }; + acl_get_flagset_np(ace, &flags); + if (!acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) + { + if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1) + goto error_exit; + + if ((ret = acl_copy_entry(tmp, ace)) == -1) + goto error_exit; + + copyfile_debug(2, "copied acl entry from %s to %s", + s->src ? s->src : "(null src)", + s->dst ? s->dst : "(null tmp)"); + copied++; + } + } } - } - if (acl_dst) { - acl_entry_t ace = NULL; - acl_entry_t tmp = NULL; - acl_flagset_t flags = { 0 }; - for (copied = 0;acl_get_entry(acl_dst, - ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, - &ace) == 0;) + if (acl_dst) { + acl_entry_t ace = NULL; + acl_entry_t tmp = NULL; + acl_flagset_t flags = { 0 }; + for (copied = 0;acl_get_entry(acl_dst, + ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, + &ace) == 0;) + { + acl_get_flagset_np(ace, &flags); + if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) + { + if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1) + goto error_exit; + + if ((ret = acl_copy_entry(tmp, ace)) == -1) + goto error_exit; + + copyfile_debug(2, "copied acl entry from %s to %s", + s->src ? s->src : "(null dst)", + s->dst ? s->dst : "(null tmp)"); + copied++; + } + } + } + if (!filesec_set_property(s->fsec, FILESEC_ACL, &acl_tmp)) { - acl_get_flagset_np(ace, &flags); - if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) - { - if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1) - goto error_exit; - - if ((ret = acl_copy_entry(tmp, ace)) == -1) - goto error_exit; - - copyfile_debug(2, "copied acl entry from %s to %s", - s->src ? s->src : "(null dst)", - s->dst ? s->dst : "(null tmp)"); - copied++; - } + copyfile_debug(3, "altered acl"); } } - if (!filesec_set_property(s->fsec, FILESEC_ACL, &acl_tmp)) - { - copyfile_debug(3, "altered acl"); - } - } no_acl: - /* - * The following code is attempting to ensure that only the requested - * security information gets copied over to the destination file. - * We essentially have four cases: COPYFILE_ACL, COPYFILE_STAT, - * COPYFILE_(STAT|ACL), and none (in which case, we wouldn't be in - * this function). - * - * If we have both flags, we copy everything; if we have ACL but not STAT, - * we remove the POSIX information from the filesec object, and apply the - * ACL; if we have STAT but not ACL, then we just use fchmod(), and ignore - * the extended version. - */ - tmp_fsec = filesec_dup(s->fsec); - if (tmp_fsec == NULL) { - goto error_exit; - } - - switch (COPYFILE_SECURITY & s->flags) { - case COPYFILE_ACL: - copyfile_unset_posix_fsec(tmp_fsec); - /* FALLTHROUGH */ - case COPYFILE_ACL | COPYFILE_STAT: - if (fchmodx_np(s->dst_fd, tmp_fsec) < 0) { - acl_t acl = NULL; - /* - * The call could have failed for a number of reasons, since - * it does a number of things: it changes the mode of the file, - * sets the owner and group, and applies an ACL (if one exists). - * The typical failure is going to be trying to set the group of - * the destination file to match the source file, when the process - * doesn't have permission to put files in that group. We try to - * work around this by breaking the steps out and doing them - * discretely. We don't care if the fchown fails, but we do care - * if the mode or ACL can't be set. For historical reasons, we - * simply log those failures, however. - * - * Big warning here: we may NOT have COPYFILE_STAT set, since - * we fell-through from COPYFILE_ACL. So check for the fchmod. - */ + /* + * The following code is attempting to ensure that only the requested + * security information gets copied over to the destination file. + * We essentially have four cases: COPYFILE_ACL, COPYFILE_STAT, + * COPYFILE_(STAT|ACL), and none (in which case, we wouldn't be in + * this function). + * + * If we have both flags, we copy everything; if we have ACL but not STAT, + * we remove the POSIX information from the filesec object, and apply the + * ACL; if we have STAT but not ACL, then we just use fchmod(), and ignore + * the extended version. + */ + tmp_fsec = filesec_dup(s->fsec); + if (tmp_fsec == NULL) { + goto error_exit; + } + + switch (COPYFILE_SECURITY & s->flags) { + case COPYFILE_ACL: + copyfile_unset_posix_fsec(tmp_fsec); + /* FALLTHROUGH */ + case COPYFILE_ACL | COPYFILE_STAT: + if (fchmodx_np(s->dst_fd, tmp_fsec) < 0) { + acl_t acl = NULL; + /* + * The call could have failed for a number of reasons, since + * it does a number of things: it changes the mode of the file, + * sets the owner and group, and applies an ACL (if one exists). + * The typical failure is going to be trying to set the group of + * the destination file to match the source file, when the process + * doesn't have permission to put files in that group. We try to + * work around this by breaking the steps out and doing them + * discretely. We don't care if the fchown fails, but we do care + * if the mode or ACL can't be set. For historical reasons, we + * simply log those failures, however. + * + * Big warning here: we may NOT have COPYFILE_STAT set, since + * we fell-through from COPYFILE_ACL. So check for the fchmod. + */ #define NS(x) ((x) ? (x) : "(null string)") - if ((s->flags & COPYFILE_STAT) && - fchmod(s->dst_fd, s->sb.st_mode) == -1) { - copyfile_warn("could not change mode of destination file %s to match source file %s", NS(s->dst), NS(s->src)); - } - (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid); - if (filesec_get_property(tmp_fsec, FILESEC_ACL, &acl) == 0) { - if (acl_set_fd(s->dst_fd, acl) == -1) { - copyfile_warn("could not apply acl to destination file %s from source file %s", NS(s->dst), NS(s->src)); + if ((s->flags & COPYFILE_STAT) && + fchmod(s->dst_fd, s->sb.st_mode) == -1) { + copyfile_warn("could not change mode of destination file %s to match source file %s", NS(s->dst), NS(s->src)); + } + (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid); + if (filesec_get_property(tmp_fsec, FILESEC_ACL, &acl) == 0) { + if (acl_set_fd(s->dst_fd, acl) == -1) { + copyfile_warn("could not apply acl to destination file %s from source file %s", NS(s->dst), NS(s->src)); + } + acl_free(acl); + } } - acl_free(acl); - } - } #undef NS - break; - case COPYFILE_STAT: - (void)fchmod(s->dst_fd, s->sb.st_mode); - break; - } - filesec_free(tmp_fsec); + break; + case COPYFILE_STAT: + (void)fchmod(s->dst_fd, s->sb.st_mode); + break; + } + filesec_free(tmp_fsec); exit: - filesec_free(fsec_dst); - if (acl_src) acl_free(acl_src); - if (acl_dst) acl_free(acl_dst); - if (acl_tmp) acl_free(acl_tmp); + filesec_free(fsec_dst); + if (acl_src) acl_free(acl_src); + if (acl_dst) acl_free(acl_dst); + if (acl_tmp) acl_free(acl_tmp); - return ret; + return ret; error_exit: - ret = -1; -goto exit; + ret = -1; + goto exit; } @@ -2199,42 +2315,42 @@ goto exit; */ static int copyfile_stat(copyfile_state_t s) { - struct timeval tval[2]; - unsigned int added_flags = 0, dst_flags = 0; - struct stat dst_sb; - - /* - * NFS doesn't support chflags; ignore errors as a result, since - * we don't return failure for this. - */ - if (s->internal_flags & cfMakeFileInvisible) - added_flags |= UF_HIDDEN; - - /* - * We need to check if SF_RESTRICTED was set on the destination - * by the kernel. If it was, don't drop it. - */ - if (fstat(s->dst_fd, &dst_sb)) - return -1; - if (dst_sb.st_flags & SF_RESTRICTED) - added_flags |= SF_RESTRICTED; + struct timeval tval[2]; + unsigned int added_flags = 0, dst_flags = 0; + struct stat dst_sb; + + /* + * NFS doesn't support chflags; ignore errors as a result, since + * we don't return failure for this. + */ + if (s->internal_flags & cfMakeFileInvisible) + added_flags |= UF_HIDDEN; + + /* + * We need to check if SF_RESTRICTED was set on the destination + * by the kernel. If it was, don't drop it. + */ + if (fstat(s->dst_fd, &dst_sb)) + return -1; + if (dst_sb.st_flags & SF_RESTRICTED) + added_flags |= SF_RESTRICTED; - /* Copy file flags, masking out any we don't want to preserve */ - dst_flags = (s->sb.st_flags & ~COPYFILE_OMIT_FLAGS) | added_flags; - (void)fchflags(s->dst_fd, dst_flags); + /* Copy file flags, masking out any we don't want to preserve */ + dst_flags = (s->sb.st_flags & ~COPYFILE_OMIT_FLAGS) | added_flags; + (void)fchflags(s->dst_fd, dst_flags); - /* If this fails, we don't care */ - (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid); + /* If this fails, we don't care */ + (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid); - /* This may have already been done in copyfile_security() */ - (void)fchmod(s->dst_fd, s->sb.st_mode & ~S_IFMT); + /* This may have already been done in copyfile_security() */ + (void)fchmod(s->dst_fd, s->sb.st_mode & ~S_IFMT); - tval[0].tv_sec = s->sb.st_atime; - tval[1].tv_sec = s->sb.st_mtime; - tval[0].tv_usec = tval[1].tv_usec = 0; - (void)futimes(s->dst_fd, tval); + tval[0].tv_sec = s->sb.st_atime; + tval[1].tv_sec = s->sb.st_mtime; + tval[0].tv_usec = tval[1].tv_usec = 0; + (void)futimes(s->dst_fd, tval); - return 0; + return 0; } /* @@ -2249,264 +2365,264 @@ static int copyfile_stat(copyfile_state_t s) */ static int copyfile_xattr(copyfile_state_t s) { - char *name; - char *namebuf, *end; - ssize_t xa_size; - void *xa_dataptr; - ssize_t bufsize = 4096; - ssize_t asize; - ssize_t nsize; - int ret = 0; - int look_for_decmpea = 0; - - /* delete EAs on destination */ - if ((nsize = flistxattr(s->dst_fd, 0, 0, 0)) > 0) - { - if ((namebuf = (char *) malloc(nsize)) == NULL) - return -1; - else - nsize = flistxattr(s->dst_fd, namebuf, nsize, 0); - - if (nsize > 0) { - /* - * With this, end points to the last byte of the allocated buffer - * This *should* be NUL, from flistxattr, but if it's not, we can - * set it anyway -- it'll result in a truncated name, which then - * shouldn't match when we get them later. - */ - end = namebuf + nsize - 1; - if (*end != 0) - *end = 0; - for (name = namebuf; name <= end; name += strlen(name) + 1) { - /* If the quarantine information shows up as an EA, we skip over it */ - if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0) { - continue; + char *name; + char *namebuf, *end; + ssize_t xa_size; + void *xa_dataptr; + ssize_t bufsize = 4096; + ssize_t asize; + ssize_t nsize; + int ret = 0; + int look_for_decmpea = 0; + + /* delete EAs on destination */ + if ((nsize = flistxattr(s->dst_fd, 0, 0, 0)) > 0) + { + if ((namebuf = (char *) malloc(nsize)) == NULL) + return -1; + else + nsize = flistxattr(s->dst_fd, namebuf, nsize, 0); + + if (nsize > 0) { + /* + * With this, end points to the last byte of the allocated buffer + * This *should* be NUL, from flistxattr, but if it's not, we can + * set it anyway -- it'll result in a truncated name, which then + * shouldn't match when we get them later. + */ + end = namebuf + nsize - 1; + if (*end != 0) + *end = 0; + for (name = namebuf; name <= end; name += strlen(name) + 1) { + /* If the quarantine information shows up as an EA, we skip over it */ + if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0) { + continue; + } + fremovexattr(s->dst_fd, name,0); + } + } + free(namebuf); + } else + if (nsize < 0) + { + if (errno == ENOTSUP || errno == EPERM) + return 0; + else + return -1; } - fremovexattr(s->dst_fd, name,0); - } - } - free(namebuf); - } else - if (nsize < 0) - { - if (errno == ENOTSUP || errno == EPERM) - return 0; - else - return -1; - } #ifdef DECMPFS_XATTR_NAME - if ((s->flags & COPYFILE_DATA) && - (s->sb.st_flags & UF_COMPRESSED) && - doesdecmpfs(s->src_fd) && - doesdecmpfs(s->dst_fd)) { - look_for_decmpea = XATTR_SHOWCOMPRESSION; - } + if ((s->flags & COPYFILE_DATA) && + (s->sb.st_flags & UF_COMPRESSED) && + doesdecmpfs(s->src_fd) && + doesdecmpfs(s->dst_fd)) { + look_for_decmpea = XATTR_SHOWCOMPRESSION; + } #endif - /* get name list of EAs on source */ - if ((nsize = flistxattr(s->src_fd, 0, 0, look_for_decmpea)) < 0) - { - if (errno == ENOTSUP || errno == EPERM) - return 0; - else - return -1; - } else - if (nsize == 0) - return 0; + /* get name list of EAs on source */ + if ((nsize = flistxattr(s->src_fd, 0, 0, look_for_decmpea)) < 0) + { + if (errno == ENOTSUP || errno == EPERM) + return 0; + else + return -1; + } else + if (nsize == 0) + return 0; - if ((namebuf = (char *) malloc(nsize)) == NULL) - return -1; - else - nsize = flistxattr(s->src_fd, namebuf, nsize, look_for_decmpea); - - if (nsize <= 0) { - free(namebuf); - return (int)nsize; - } - - /* - * With this, end points to the last byte of the allocated buffer - * This *should* be NUL, from flistxattr, but if it's not, we can - * set it anyway -- it'll result in a truncated name, which then - * shouldn't match when we get them later. - */ - end = namebuf + nsize - 1; - if (*end != 0) - *end = 0; - - if ((xa_dataptr = (void *) malloc(bufsize)) == NULL) { - free(namebuf); - return -1; - } + if ((namebuf = (char *) malloc(nsize)) == NULL) + return -1; + else + nsize = flistxattr(s->src_fd, namebuf, nsize, look_for_decmpea); - for (name = namebuf; name <= end; name += strlen(name) + 1) - { - if (s->xattr_name) { - free(s->xattr_name); - s->xattr_name = NULL; + if (nsize <= 0) { + free(namebuf); + return (int)nsize; } - /* If the quarantine information shows up as an EA, we skip over it */ - if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0) - continue; + /* + * With this, end points to the last byte of the allocated buffer + * This *should* be NUL, from flistxattr, but if it's not, we can + * set it anyway -- it'll result in a truncated name, which then + * shouldn't match when we get them later. + */ + end = namebuf + nsize - 1; + if (*end != 0) + *end = 0; - if ((xa_size = fgetxattr(s->src_fd, name, 0, 0, 0, look_for_decmpea)) < 0) - { - continue; + if ((xa_dataptr = (void *) malloc(bufsize)) == NULL) { + free(namebuf); + return -1; } - if (xa_size > bufsize) + for (name = namebuf; name <= end; name += strlen(name) + 1) { - void *tdptr = xa_dataptr; - bufsize = xa_size; - if ((xa_dataptr = - (void *) realloc((void *) xa_dataptr, bufsize)) == NULL) - { - free(tdptr); - ret = -1; - continue; - } - } + if (s->xattr_name) { + free(s->xattr_name); + s->xattr_name = NULL; + } - if ((asize = fgetxattr(s->src_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea)) < 0) - { - continue; - } + /* If the quarantine information shows up as an EA, we skip over it */ + if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0) + continue; - if (xa_size != asize) - xa_size = asize; + if ((xa_size = fgetxattr(s->src_fd, name, 0, 0, 0, look_for_decmpea)) < 0) + { + continue; + } -#ifdef DECMPFS_XATTR_NAME - if (strncmp(name, DECMPFS_XATTR_NAME, end-name) == 0) - { - decmpfs_disk_header *hdr = xa_dataptr; - - /* - * If the EA has the decmpfs name, but is too - * small, or doesn't have the right magic number, - * or isn't the right type, we'll just skip it. - * This means it won't end up in the destination - * file, and data copy will happen normally. - */ - if ((size_t)xa_size < sizeof(decmpfs_disk_header)) { - continue; - } - if (OSSwapLittleToHostInt32(hdr->compression_magic) != DECMPFS_MAGIC) { - continue; - } - /* - * From AppleFSCompression documentation: - * "It is incumbent on the aware copy engine to identify - * the type of compression being used, and to perform an - * unaware copy of any file it does not recognize." - * - * Compression Types are defined in: - * "AppleFSCompression/Common/compressorCommon.h" - * - * Unfortunately, they don't provide a way to dynamically - * determine what possible compression_type values exist, - * so we have to update this every time a new compression_type - * is added. Types 7->10 were added in 10.10, Types 11 & 12 - * were added in 10.11. - * - * Ubiquity faulting file compression type 0x80000001 are - * deprecated as of Yosemite, per rdar://17714998 don't copy the - * decmpfs xattr on these files, zero byte files are safer - * than a fault nobody knows how to handle. - */ - switch (OSSwapLittleToHostInt32(hdr->compression_type)) { - case 3: /* zlib-compressed data in xattr */ - case 4: /* 64k chunked zlib-compressed data in resource fork */ - - case 7: /* LZVN-compressed data in xattr */ - case 8: /* 64k chunked LZVN-compressed data in resource fork */ - - case 9: /* uncompressed data in xattr (similar to but not identical to CMP_Type1) */ - case 10: /* 64k chunked uncompressed data in resource fork */ - - case 11: /* LZFSE-compressed data in xattr */ - case 12: /* 64k chunked LZFSE-compressed data in resource fork */ - - /* valid compression type, we want to copy. */ - break; - - case 5: /* specifies de-dup within the generation store. Don't copy decmpfs xattr. */ - copyfile_debug(3, "compression_type <5> on attribute com.apple.decmpfs for src file %s is not copied.", - s->src ? s->src : "(null string)"); + if (xa_size > bufsize) + { + void *tdptr = xa_dataptr; + bufsize = xa_size; + if ((xa_dataptr = + (void *) realloc((void *) xa_dataptr, bufsize)) == NULL) + { + free(tdptr); + ret = -1; + continue; + } + } + + if ((asize = fgetxattr(s->src_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea)) < 0) + { continue; + } - case 6: /* unused */ - case 0x80000001: /* faulting files are deprecated, don't copy decmpfs xattr */ - default: - copyfile_warn("Invalid compression_type <%d> on attribute %s for src file %s", + if (xa_size != asize) + xa_size = asize; + +#ifdef DECMPFS_XATTR_NAME + if (strncmp(name, DECMPFS_XATTR_NAME, end-name) == 0) + { + decmpfs_disk_header *hdr = xa_dataptr; + + /* + * If the EA has the decmpfs name, but is too + * small, or doesn't have the right magic number, + * or isn't the right type, we'll just skip it. + * This means it won't end up in the destination + * file, and data copy will happen normally. + */ + if ((size_t)xa_size < sizeof(decmpfs_disk_header)) { + continue; + } + if (OSSwapLittleToHostInt32(hdr->compression_magic) != DECMPFS_MAGIC) { + continue; + } + /* + * From AppleFSCompression documentation: + * "It is incumbent on the aware copy engine to identify + * the type of compression being used, and to perform an + * unaware copy of any file it does not recognize." + * + * Compression Types are defined in: + * "AppleFSCompression/Common/compressorCommon.h" + * + * Unfortunately, they don't provide a way to dynamically + * determine what possible compression_type values exist, + * so we have to update this every time a new compression_type + * is added. Types 7->10 were added in 10.10, Types 11 & 12 + * were added in 10.11. + * + * Ubiquity faulting file compression type 0x80000001 are + * deprecated as of Yosemite, per rdar://17714998 don't copy the + * decmpfs xattr on these files, zero byte files are safer + * than a fault nobody knows how to handle. + */ + switch (OSSwapLittleToHostInt32(hdr->compression_type)) { + case 3: /* zlib-compressed data in xattr */ + case 4: /* 64k chunked zlib-compressed data in resource fork */ + + case 7: /* LZVN-compressed data in xattr */ + case 8: /* 64k chunked LZVN-compressed data in resource fork */ + + case 9: /* uncompressed data in xattr (similar to but not identical to CMP_Type1) */ + case 10: /* 64k chunked uncompressed data in resource fork */ + + case 11: /* LZFSE-compressed data in xattr */ + case 12: /* 64k chunked LZFSE-compressed data in resource fork */ + + /* valid compression type, we want to copy. */ + break; + + case 5: /* specifies de-dup within the generation store. Don't copy decmpfs xattr. */ + copyfile_debug(3, "compression_type <5> on attribute com.apple.decmpfs for src file %s is not copied.", + s->src ? s->src : "(null string)"); + continue; + + case 6: /* unused */ + case 0x80000001: /* faulting files are deprecated, don't copy decmpfs xattr */ + default: + copyfile_warn("Invalid compression_type <%d> on attribute %s for src file %s", OSSwapLittleToHostInt32(hdr->compression_type), name, s->src ? s->src : "(null string)"); - continue; - } - s->internal_flags |= cfSawDecmpEA; - } + continue; + } + s->internal_flags |= cfSawDecmpEA; + } #endif - // If we have a copy intention stated, and the EA is to be ignored, we ignore it - if (s->copyIntent - && xattr_preserve_for_intent(name, s->copyIntent) == 0) - continue; - - s->xattr_name = strdup(name); - - if (s->statuscb) { - int rv; - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); - if (rv == COPYFILE_QUIT) { - s->err = ECANCELED; - goto out; - } else if (rv == COPYFILE_SKIP) { + // If we have a copy intention stated, and the EA is to be ignored, we ignore it + if (s->copyIntent + && xattr_preserve_for_intent(name, s->copyIntent) == 0) continue; + + s->xattr_name = strdup(name); + + if (s->statuscb) { + int rv; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); + if (rv == COPYFILE_QUIT) { + s->err = ECANCELED; + goto out; + } else if (rv == COPYFILE_SKIP) { + continue; + } } - } - if (fsetxattr(s->dst_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea) < 0) - { - if (s->statuscb) - { - int rv; - if (s->xattr_name == NULL) - s->xattr_name = strdup(name); - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); - if (rv == COPYFILE_QUIT) + if (fsetxattr(s->dst_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea) < 0) { - s->err = ECANCELED; - ret = -1; - goto out; + if (s->statuscb) + { + int rv; + if (s->xattr_name == NULL) + s->xattr_name = strdup(name); + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + if (rv == COPYFILE_QUIT) + { + s->err = ECANCELED; + ret = -1; + goto out; + } + } + else + { + ret = -1; + copyfile_warn("could not set attributes %s on destination file descriptor: %s", name, strerror(errno)); + continue; + } } - } - else - { - ret = -1; - copyfile_warn("could not set attributes %s on destination file descriptor: %s", name, strerror(errno)); - continue; - } - } - if (s->statuscb) { - int rv; - if (s->xattr_name == NULL) - s->xattr_name = strdup(name); + if (s->statuscb) { + int rv; + if (s->xattr_name == NULL) + s->xattr_name = strdup(name); - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); - if (rv == COPYFILE_QUIT) { - s->err = ECANCELED; - goto out; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); + if (rv == COPYFILE_QUIT) { + s->err = ECANCELED; + goto out; + } } } - } out: - if (namebuf) - free(namebuf); - free((void *) xa_dataptr); - if (s->xattr_name) { - free(s->xattr_name); - s->xattr_name = NULL; - } - return ret; + if (namebuf) + free(namebuf); + free((void *) xa_dataptr); + if (s->xattr_name) { + free(s->xattr_name); + s->xattr_name = NULL; + } + return ret; } /* @@ -2514,64 +2630,67 @@ out: */ int copyfile_state_get(copyfile_state_t s, uint32_t flag, void *ret) { - if (ret == NULL) - { - errno = EFAULT; - return -1; - } - - switch(flag) - { - case COPYFILE_STATE_SRC_FD: - *(int*)ret = s->src_fd; - break; - case COPYFILE_STATE_DST_FD: - *(int*)ret = s->dst_fd; - break; - case COPYFILE_STATE_SRC_FILENAME: - *(char**)ret = s->src; - break; - case COPYFILE_STATE_DST_FILENAME: - *(char**)ret = s->dst; - break; - case COPYFILE_STATE_QUARANTINE: - *(qtn_file_t*)ret = s->qinfo; - break; + if (ret == NULL) + { + errno = EFAULT; + return -1; + } + + switch(flag) + { + case COPYFILE_STATE_SRC_FD: + *(int*)ret = s->src_fd; + break; + case COPYFILE_STATE_DST_FD: + *(int*)ret = s->dst_fd; + break; + case COPYFILE_STATE_SRC_FILENAME: + *(char**)ret = s->src; + break; + case COPYFILE_STATE_DST_FILENAME: + *(char**)ret = s->dst; + break; + case COPYFILE_STATE_QUARANTINE: + *(qtn_file_t*)ret = s->qinfo; + break; #if 0 - case COPYFILE_STATE_STATS: - ret = s->stats.global; - break; - case COPYFILE_STATE_PROGRESS_CB: - ret = s->callbacks.progress; - break; + case COPYFILE_STATE_STATS: + ret = s->stats.global; + break; + case COPYFILE_STATE_PROGRESS_CB: + ret = s->callbacks.progress; + break; #endif #ifdef COPYFILE_STATE_STATUS_CB - case COPYFILE_STATE_STATUS_CB: - *(copyfile_callback_t*)ret = s->statuscb; - break; - case COPYFILE_STATE_STATUS_CTX: - *(void**)ret = s->ctx; - break; - case COPYFILE_STATE_COPIED: - *(off_t*)ret = s->totalCopied; - break; + case COPYFILE_STATE_STATUS_CB: + *(copyfile_callback_t*)ret = s->statuscb; + break; + case COPYFILE_STATE_STATUS_CTX: + *(void**)ret = s->ctx; + break; + case COPYFILE_STATE_COPIED: + *(off_t*)ret = s->totalCopied; + break; #endif #ifdef COPYFILE_STATE_XATTRNAME - case COPYFILE_STATE_XATTRNAME: - *(char**)ret = s->xattr_name; - break; + case COPYFILE_STATE_XATTRNAME: + *(char**)ret = s->xattr_name; + break; #endif #ifdef COPYFILE_STATE_INTENT - case COPYFILE_STATE_INTENT: - *(xattr_operation_intent_t*)ret = s->copyIntent; - break; + case COPYFILE_STATE_INTENT: + *(xattr_operation_intent_t*)ret = s->copyIntent; + break; #endif - default: - errno = EINVAL; - ret = NULL; - return -1; - } - return 0; + case COPYFILE_STATE_WAS_CLONED: + *(bool *)ret = s->was_cloned; + break; + default: + errno = EINVAL; + ret = NULL; + return -1; + } + return 0; } /* @@ -2581,72 +2700,72 @@ int copyfile_state_get(copyfile_state_t s, uint32_t flag, void *ret) int copyfile_state_set(copyfile_state_t s, uint32_t flag, const void * thing) { #define copyfile_set_string(DST, SRC) \ - do { \ - if (SRC != NULL) { \ - DST = strdup((char *)SRC); \ - } else { \ - if (DST != NULL) { \ - free(DST); \ - } \ - DST = NULL; \ - } \ - } while (0) - - if (thing == NULL) - { - errno = EFAULT; - return -1; - } - - switch(flag) - { - case COPYFILE_STATE_SRC_FD: - s->src_fd = *(int*)thing; - break; - case COPYFILE_STATE_DST_FD: - s->dst_fd = *(int*)thing; - break; - case COPYFILE_STATE_SRC_FILENAME: - copyfile_set_string(s->src, thing); - break; - case COPYFILE_STATE_DST_FILENAME: - copyfile_set_string(s->dst, thing); - break; - case COPYFILE_STATE_QUARANTINE: - if (s->qinfo) - { - qtn_file_free(s->qinfo); - s->qinfo = NULL; - } - if (*(qtn_file_t*)thing) - s->qinfo = qtn_file_clone(*(qtn_file_t*)thing); - break; -#if 0 - case COPYFILE_STATE_STATS: - s->stats.global = thing; - break; - case COPYFILE_STATE_PROGRESS_CB: - s->callbacks.progress = thing; - break; -#endif -#ifdef COPYFILE_STATE_STATUS_CB - case COPYFILE_STATE_STATUS_CB: - s->statuscb = (copyfile_callback_t)thing; - break; - case COPYFILE_STATE_STATUS_CTX: - s->ctx = (void*)thing; - break; -#endif + do { \ + if (SRC != NULL) { \ + DST = strdup((char *)SRC); \ + } else { \ + if (DST != NULL) { \ + free(DST); \ + } \ + DST = NULL; \ + } \ + } while (0) + + if (thing == NULL) + { + errno = EFAULT; + return -1; + } + + switch(flag) + { + case COPYFILE_STATE_SRC_FD: + s->src_fd = *(int*)thing; + break; + case COPYFILE_STATE_DST_FD: + s->dst_fd = *(int*)thing; + break; + case COPYFILE_STATE_SRC_FILENAME: + copyfile_set_string(s->src, thing); + break; + case COPYFILE_STATE_DST_FILENAME: + copyfile_set_string(s->dst, thing); + break; + case COPYFILE_STATE_QUARANTINE: + if (s->qinfo) + { + qtn_file_free(s->qinfo); + s->qinfo = NULL; + } + if (*(qtn_file_t*)thing) + s->qinfo = qtn_file_clone(*(qtn_file_t*)thing); + break; +#if 0 + case COPYFILE_STATE_STATS: + s->stats.global = thing; + break; + case COPYFILE_STATE_PROGRESS_CB: + s->callbacks.progress = thing; + break; +#endif +#ifdef COPYFILE_STATE_STATUS_CB + case COPYFILE_STATE_STATUS_CB: + s->statuscb = (copyfile_callback_t)thing; + break; + case COPYFILE_STATE_STATUS_CTX: + s->ctx = (void*)thing; + break; +#endif #ifdef COPYFILE_STATE_INTENT - case COPYFILE_STATE_INTENT: - s->copyIntent = *(xattr_operation_intent_t*)thing; - break; + case COPYFILE_STATE_INTENT: + s->copyIntent = *(xattr_operation_intent_t*)thing; + break; #endif - default: - errno = EINVAL; - return -1; - } - return 0; + default: + errno = EINVAL; + return -1; + } + return 0; #undef copyfile_set_string } @@ -2659,49 +2778,49 @@ int copyfile_state_set(copyfile_state_t s, uint32_t flag, const void * thing) #define COPYFILE_OPTION(x) { #x, COPYFILE_ ## x }, struct {char *s; int v;} opts[] = { - COPYFILE_OPTION(ACL) - COPYFILE_OPTION(STAT) - COPYFILE_OPTION(XATTR) - COPYFILE_OPTION(DATA) - COPYFILE_OPTION(SECURITY) - COPYFILE_OPTION(METADATA) - COPYFILE_OPTION(ALL) - COPYFILE_OPTION(NOFOLLOW_SRC) - COPYFILE_OPTION(NOFOLLOW_DST) - COPYFILE_OPTION(NOFOLLOW) - COPYFILE_OPTION(EXCL) - COPYFILE_OPTION(MOVE) - COPYFILE_OPTION(UNLINK) - COPYFILE_OPTION(PACK) - COPYFILE_OPTION(UNPACK) - COPYFILE_OPTION(CHECK) - COPYFILE_OPTION(VERBOSE) - COPYFILE_OPTION(DEBUG) - {NULL, 0} + COPYFILE_OPTION(ACL) + COPYFILE_OPTION(STAT) + COPYFILE_OPTION(XATTR) + COPYFILE_OPTION(DATA) + COPYFILE_OPTION(SECURITY) + COPYFILE_OPTION(METADATA) + COPYFILE_OPTION(ALL) + COPYFILE_OPTION(NOFOLLOW_SRC) + COPYFILE_OPTION(NOFOLLOW_DST) + COPYFILE_OPTION(NOFOLLOW) + COPYFILE_OPTION(EXCL) + COPYFILE_OPTION(MOVE) + COPYFILE_OPTION(UNLINK) + COPYFILE_OPTION(PACK) + COPYFILE_OPTION(UNPACK) + COPYFILE_OPTION(CHECK) + COPYFILE_OPTION(VERBOSE) + COPYFILE_OPTION(DEBUG) + {NULL, 0} }; int main(int c, char *v[]) { - int i; - int flags = 0; + int i; + int flags = 0; - if (c < 3) - errx(1, "insufficient arguments"); + if (c < 3) + errx(1, "insufficient arguments"); - while(c-- > 3) - { - for (i = 0; opts[i].s != NULL; ++i) + while(c-- > 3) { - if (strcasecmp(opts[i].s, v[c]) == 0) - { - printf("option %d: %s <- %d\n", c, opts[i].s, opts[i].v); - flags |= opts[i].v; - break; - } + for (i = 0; opts[i].s != NULL; ++i) + { + if (strcasecmp(opts[i].s, v[c]) == 0) + { + printf("option %d: %s <- %d\n", c, opts[i].s, opts[i].v); + flags |= opts[i].v; + break; + } + } } - } - return copyfile(v[1], v[2], NULL, flags); + return copyfile(v[1], v[2], NULL, flags); } #endif /* @@ -2719,51 +2838,51 @@ int main(int c, char *v[]) /* - Typical "._" AppleDouble Header File layout: - ------------------------------------------------------------ - MAGIC 0x00051607 - VERSION 0x00020000 - FILLER 0 - COUNT 2 - .-- AD ENTRY[0] Finder Info Entry (must be first) - .--+-- AD ENTRY[1] Resource Fork Entry (must be last) - | '-> FINDER INFO - | ///////////// Fixed Size Data (32 bytes) - | EXT ATTR HDR - | ///////////// - | ATTR ENTRY[0] --. - | ATTR ENTRY[1] --+--. - | ATTR ENTRY[2] --+--+--. - | ... | | | - | ATTR ENTRY[N] --+--+--+--. - | ATTR DATA 0 <-' | | | - | //////////// | | | - | ATTR DATA 1 <----' | | - | ///////////// | | - | ATTR DATA 2 <-------' | - | ///////////// | - | ... | - | ATTR DATA N <----------' - | ///////////// - | Attribute Free Space - | - '----> RESOURCE FORK - ///////////// Variable Sized Data - ///////////// - ///////////// - ///////////// - ///////////// - ///////////// - ... - ///////////// - - ------------------------------------------------------------ - - NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are - stored as part of the Finder Info. The length in the Finder - Info AppleDouble entry includes the length of the extended - attribute header, attribute entries, and attribute data. -*/ + Typical "._" AppleDouble Header File layout: + ------------------------------------------------------------ + MAGIC 0x00051607 + VERSION 0x00020000 + FILLER 0 + COUNT 2 + .-- AD ENTRY[0] Finder Info Entry (must be first) + .--+-- AD ENTRY[1] Resource Fork Entry (must be last) + | '-> FINDER INFO + | ///////////// Fixed Size Data (32 bytes) + | EXT ATTR HDR + | ///////////// + | ATTR ENTRY[0] --. + | ATTR ENTRY[1] --+--. + | ATTR ENTRY[2] --+--+--. + | ... | | | + | ATTR ENTRY[N] --+--+--+--. + | ATTR DATA 0 <-' | | | + | //////////// | | | + | ATTR DATA 1 <----' | | + | ///////////// | | + | ATTR DATA 2 <-------' | + | ///////////// | + | ... | + | ATTR DATA N <----------' + | ///////////// + | Attribute Free Space + | + '----> RESOURCE FORK + ///////////// Variable Sized Data + ///////////// + ///////////// + ///////////// + ///////////// + ///////////// + ... + ///////////// + + ------------------------------------------------------------ + + NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are + stored as part of the Finder Info. The length in the Finder + Info AppleDouble entry includes the length of the extended + attribute header, attribute entries, and attribute data. + */ /* @@ -2796,7 +2915,7 @@ int main(int c, char *v[]) #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */ #define AD_AFPNAME 13 /* Short name on AFP server */ #define AD_AFPINFO 14 /* AFP file info, attrib., etc */ -#define AD_AFPDIRID 15 /* AFP directory ID */ +#define AD_AFPDIRID 15 /* AFP directory ID */ #define AD_ATTRIBUTES AD_FINDERINFO @@ -2821,7 +2940,7 @@ int main(int c, char *v[]) typedef struct apple_double_entry { - u_int32_t type; /* entry type: see list, 0 invalid */ + u_int32_t type; /* entry type: see list, 0 invalid */ u_int32_t offset; /* entry data offset from the beginning of the file. */ u_int32_t length; /* entry data length in bytes. */ } __attribute__((aligned(2), packed)) apple_double_entry_t; @@ -2830,9 +2949,9 @@ typedef struct apple_double_entry typedef struct apple_double_header { u_int32_t magic; /* == ADH_MAGIC */ - u_int32_t version; /* format version: 2 = 0x00020000 */ + u_int32_t version; /* format version: 2 = 0x00020000 */ u_int32_t filler[4]; - u_int16_t numEntries; /* number of entries which follow */ + u_int16_t numEntries; /* number of entries which follow */ apple_double_entry_t entries[2]; /* 'finfo' & 'rsrc' always exist */ u_int8_t finfo[FINDERINFOSIZE]; /* Must start with Finder Info (32 bytes) */ u_int8_t pad[2]; /* get better alignment inside attr_header */ @@ -2845,7 +2964,7 @@ typedef struct attr_entry u_int32_t offset; /* file offset to data */ u_int32_t length; /* size of attribute data */ u_int16_t flags; - u_int8_t namelen; /* length of name including NULL termination char */ + u_int8_t namelen; /* length of name including NULL termination char */ u_int8_t name[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */ } __attribute__((aligned(2), packed)) attr_entry_t; @@ -2857,7 +2976,7 @@ typedef struct attr_header apple_double_header_t appledouble; u_int32_t magic; /* == ATTR_HDR_MAGIC */ u_int32_t debug_tag; /* for debugging == file id of owning file */ - u_int32_t total_size; /* total size of attribute header + entries + data */ + u_int32_t total_size; /* total size of attribute header + entries + data */ u_int32_t data_start; /* file offset to attribute data area */ u_int32_t data_length; /* length of attribute data area */ u_int32_t reserved[3]; @@ -2874,20 +2993,20 @@ typedef struct rsrcfork_header { u_int32_t fh_MapLength; u_int8_t systemData[112]; u_int8_t appData[128]; - u_int32_t mh_DataOffset; + u_int32_t mh_DataOffset; u_int32_t mh_MapOffset; - u_int32_t mh_DataLength; + u_int32_t mh_DataLength; u_int32_t mh_MapLength; u_int32_t mh_Next; u_int16_t mh_RefNum; u_int8_t mh_Attr; - u_int8_t mh_InMemoryAttr; + u_int8_t mh_InMemoryAttr; u_int16_t mh_Types; u_int16_t mh_Names; u_int16_t typeCount; } __attribute__((aligned(2), packed)) rsrcfork_header_t; #define RF_FIRST_RESOURCE 256 -#define RF_NULL_MAP_LENGTH 30 +#define RF_NULL_MAP_LENGTH 30 #define RF_EMPTY_TAG "This resource fork intentionally left blank " static const rsrcfork_header_t empty_rsrcfork_header = { @@ -2917,15 +3036,15 @@ static const rsrcfork_header_t empty_rsrcfork_header = { #define ATTR_ALIGN 3L /* Use four-byte alignment */ #define ATTR_ENTRY_LENGTH(namelen) \ - ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN)) + ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN)) #define ATTR_NEXT(ae) \ - (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen)) + (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen)) #define XATTR_SECURITY_NAME "com.apple.acl.text" /* - * Endian swap Apple Double header + * Endian swap Apple Double header */ static void swap_adhdr(apple_double_header_t *adh) @@ -2958,11 +3077,11 @@ static void swap_attrhdr_entry(attr_entry_t *ae) { #if BYTE_ORDER == LITTLE_ENDIAN - ae->offset = SWAP32 (ae->offset); - ae->length = SWAP32 (ae->length); - ae->flags = SWAP16 (ae->flags); + ae->offset = SWAP32 (ae->offset); + ae->length = SWAP32 (ae->length); + ae->flags = SWAP16 (ae->flags); #else - (void)ae; + (void)ae; #endif } @@ -2974,30 +3093,30 @@ static void swap_attrhdr_entries(attr_header_t *ah) { #if BYTE_ORDER == LITTLE_ENDIAN - int i; - int count; - attr_entry_t *entry; - attr_entry_t *next; - - /* If we're in copyfile_pack, num_args is native endian, - * if we're in _unpack, num_args is big endian. Use - * the magic number to test for endianess. - */ - count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs); - - entry = (attr_entry_t *)(&ah[1]); - for (i = 0; i < count; i++) { - next = ATTR_NEXT(entry); - swap_attrhdr_entry(entry); - entry = next; - } + int i; + int count; + attr_entry_t *entry; + attr_entry_t *next; + + /* If we're in copyfile_pack, num_args is native endian, + * if we're in _unpack, num_args is big endian. Use + * the magic number to test for endianess. + */ + count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs); + + entry = (attr_entry_t *)(&ah[1]); + for (i = 0; i < count; i++) { + next = ATTR_NEXT(entry); + swap_attrhdr_entry(entry); + entry = next; + } #else - (void)ah; + (void)ah; #endif } /* - * Endian swap extended attributes header + * Endian swap extended attributes header */ static void swap_attrhdr(attr_header_t *ah) @@ -3024,468 +3143,480 @@ static const u_int32_t emptyfinfo[8] = {0}; */ static int copyfile_unpack(copyfile_state_t s) { - ssize_t bytes; - void * buffer, * endptr, * dataptr = NULL; - apple_double_header_t *adhdr; - ssize_t hdrsize; - int error = 0; - - if (s->sb.st_size < ATTR_MAX_HDR_SIZE) - hdrsize = (ssize_t)s->sb.st_size; - else - hdrsize = ATTR_MAX_HDR_SIZE; - - buffer = calloc(1, hdrsize); - if (buffer == NULL) { - copyfile_debug(1, "copyfile_unpack: calloc(1, %zu) returned NULL", hdrsize); - error = -1; - goto exit; - } else - endptr = (char*)buffer + hdrsize; - - bytes = pread(s->src_fd, buffer, hdrsize, 0); - - if (bytes < 0) - { - copyfile_debug(1, "pread returned: %zd", bytes); - error = -1; - goto exit; - } - if (bytes < hdrsize) - { - copyfile_debug(1, - "pread couldn't read entire header: %d of %d", - (int)bytes, (int)s->sb.st_size); - error = -1; - goto exit; - } - adhdr = (apple_double_header_t *)buffer; - - /* - * Check for Apple Double file. - */ - if ((size_t)bytes < sizeof(apple_double_header_t) - 2 || - SWAP32(adhdr->magic) != ADH_MAGIC || - SWAP32(adhdr->version) != ADH_VERSION || - SWAP16(adhdr->numEntries) != 2 || - SWAP32(adhdr->entries[0].type) != AD_FINDERINFO) - { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("Not a valid Apple Double header"); - error = -1; - goto exit; - } - swap_adhdr(adhdr); + ssize_t bytes; + void * buffer, * endptr, * dataptr = NULL; + apple_double_header_t *adhdr; + ssize_t hdrsize; + int error = 0; + + if (s->sb.st_size < ATTR_MAX_HDR_SIZE) + hdrsize = (ssize_t)s->sb.st_size; + else + hdrsize = ATTR_MAX_HDR_SIZE; - /* - * Remove any extended attributes on the target. - */ + buffer = calloc(1, hdrsize); + if (buffer == NULL) { + copyfile_debug(1, "copyfile_unpack: calloc(1, %zu) returned NULL", hdrsize); + error = -1; + goto exit; + } else + endptr = (char*)buffer + hdrsize; - if ((bytes = flistxattr(s->dst_fd, 0, 0, 0)) > 0) - { - char *namebuf, *name; + bytes = pread(s->src_fd, buffer, hdrsize, 0); - if ((namebuf = (char*) malloc(bytes)) == NULL) + if (bytes < 0) { - s->err = ENOMEM; - goto exit; - } - bytes = flistxattr(s->dst_fd, namebuf, bytes, 0); - - if (bytes > 0) - for (name = namebuf; name < namebuf + bytes; name += strlen(name) + 1) - (void)fremovexattr(s->dst_fd, name, 0); - - free(namebuf); - } - else if (bytes < 0) - { - if (errno != ENOTSUP && errno != EPERM) - goto exit; - } - - /* - * Extract the extended attributes. - * - * >>> WARNING <<< - * This assumes that the data is already in memory (not - * the case when there are lots of attributes or one of - * the attributes is very large. - */ - if (adhdr->entries[0].length > FINDERINFOSIZE) - { - attr_header_t *attrhdr; - attr_entry_t *entry; - int count; - int i; - - if ((size_t)hdrsize < sizeof(attr_header_t)) { - copyfile_warn("bad attribute header: %zu < %zu", hdrsize, sizeof(attr_header_t)); + copyfile_debug(1, "pread returned: %zd", bytes); error = -1; goto exit; } - - attrhdr = (attr_header_t *)buffer; - swap_attrhdr(attrhdr); - if (attrhdr->magic != ATTR_HDR_MAGIC) + if (bytes < hdrsize) { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("bad attribute header"); - error = -1; - goto exit; + copyfile_debug(1, + "pread couldn't read entire header: %d of %d", + (int)bytes, (int)s->sb.st_size); + error = -1; + goto exit; } - count = attrhdr->num_attrs; - entry = (attr_entry_t *)&attrhdr[1]; + adhdr = (apple_double_header_t *)buffer; - for (i = 0; i < count; i++) + /* + * Check for Apple Double file. + */ + if ((size_t)bytes < sizeof(apple_double_header_t) - 2 || + SWAP32(adhdr->magic) != ADH_MAGIC || + SWAP32(adhdr->version) != ADH_VERSION || + SWAP16(adhdr->numEntries) != 2 || + SWAP32(adhdr->entries[0].type) != AD_FINDERINFO) { - /* - * First we do some simple sanity checking. - * +) See if entry is within the buffer's range; - * - * +) Check the attribute name length; if it's longer than the - * maximum, we truncate it down. (We could error out as well; - * I'm not sure which is the better way to go here.) - * - * +) If, given the name length, it goes beyond the end of - * the buffer, error out. - * - * +) If the last byte isn't a NUL, make it a NUL. (Since we - * truncated the name length above, we truncate the name here.) - * - * +) If entry->offset is so large that it causes dataptr to - * go beyond the end of the buffer -- or, worse, so large that - * it wraps around! -- we error out. - * - * +) If entry->length would cause the entry to go beyond the - * end of the buffer (or, worse, wrap around to before it), - * *or* if the length is larger than the hdrsize, we error out. - * (An explanation of that: what we're checking for there is - * the small range of values such that offset+length would cause - * it to go beyond endptr, and then wrap around past buffer. We - * care about this because we are passing entry->length down to - * fgetxattr() below, and an erroneously large value could cause - * problems there. By making sure that it's less than hdrsize, - * which has already been sanity-checked above, we're safe. - * That may mean that the check against < buffer is unnecessary.) - */ - if ((void*)entry >= endptr || (void*)entry < buffer) { if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("Incomplete or corrupt attribute entry"); + copyfile_warn("Not a valid Apple Double header"); error = -1; - s->err = EINVAL; goto exit; - } + } + swap_adhdr(adhdr); - if (((char*)entry + sizeof(*entry)) > (char*)endptr) { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("Incomplete or corrupt attribute entry"); - error = -1; - s->err = EINVAL; - goto exit; - } - - /* - * Endian swap the entry we're looking at. Previously - * we did this swap as part of swap_attrhdr, but that - * allowed a maliciously constructed file to overrun - * our allocation. Instead do the swap after we've verified - * the entry struct is within the buffer's range. - */ - swap_attrhdr_entry(entry); - - if (entry->namelen < 2) { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("Corrupt attribute entry (only %d bytes)", entry->namelen); - error = -1; - s->err = EINVAL; - goto exit; - } + /* + * Remove any extended attributes on the target. + */ - if (entry->namelen > XATTR_MAXNAMELEN + 1) { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("Corrupt attribute entry (name length is %d bytes)", entry->namelen); - error = -1; - s->err = EINVAL; - goto exit; - } + if ((bytes = flistxattr(s->dst_fd, 0, 0, 0)) > 0) + { + char *namebuf, *name; - if ((void*)(entry->name + entry->namelen) > endptr) { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("Incomplete or corrupt attribute entry"); - error = -1; - s->err = EINVAL; - goto exit; - } + if ((namebuf = (char*) malloc(bytes)) == NULL) + { + s->err = ENOMEM; + goto exit; + } + bytes = flistxattr(s->dst_fd, namebuf, bytes, 0); - /* Because namelen includes the NUL, we check one byte back */ - if (entry->name[entry->namelen-1] != 0) { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("Corrupt attribute entry (name is not NUL-terminated)"); - error = -1; - s->err = EINVAL; - goto exit; - } + if (bytes > 0) + for (name = namebuf; name < namebuf + bytes; name += strlen(name) + 1) + (void)fremovexattr(s->dst_fd, name, 0); + + free(namebuf); + } + else if (bytes < 0) + { + if (errno != ENOTSUP && errno != EPERM) + goto exit; + } + + /* + * Extract the extended attributes. + * + * >>> WARNING <<< + * This assumes that the data is already in memory (not + * the case when there are lots of attributes or one of + * the attributes is very large. + */ + if (adhdr->entries[0].length > FINDERINFOSIZE) + { + attr_header_t *attrhdr; + attr_entry_t *entry; + int count; + int i; + + if ((size_t)hdrsize < sizeof(attr_header_t)) { + copyfile_warn("bad attribute header: %zu < %zu", hdrsize, sizeof(attr_header_t)); + error = -1; + goto exit; + } + + attrhdr = (attr_header_t *)buffer; + swap_attrhdr(attrhdr); + if (attrhdr->magic != ATTR_HDR_MAGIC) + { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("bad attribute header"); + error = -1; + goto exit; + } + count = attrhdr->num_attrs; + entry = (attr_entry_t *)&attrhdr[1]; + + for (i = 0; i < count; i++) + { + /* + * First we do some simple sanity checking. + * +) See if entry is within the buffer's range; + * + * +) Check the attribute name length; if it's longer than the + * maximum, we truncate it down. (We could error out as well; + * I'm not sure which is the better way to go here.) + * + * +) If, given the name length, it goes beyond the end of + * the buffer, error out. + * + * +) If the last byte isn't a NUL, make it a NUL. (Since we + * truncated the name length above, we truncate the name here.) + * + * +) If entry->offset is so large that it causes dataptr to + * go beyond the end of the buffer -- or, worse, so large that + * it wraps around! -- we error out. + * + * +) If entry->length would cause the entry to go beyond the + * end of the buffer (or, worse, wrap around to before it), + * *or* if the length is larger than the hdrsize, we error out. + * (An explanation of that: what we're checking for there is + * the small range of values such that offset+length would cause + * it to go beyond endptr, and then wrap around past buffer. We + * care about this because we are passing entry->length down to + * fgetxattr() below, and an erroneously large value could cause + * problems there. By making sure that it's less than hdrsize, + * which has already been sanity-checked above, we're safe. + * That may mean that the check against < buffer is unnecessary.) + */ + if ((void*)entry >= endptr || (void*)entry < buffer) { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("Incomplete or corrupt attribute entry"); + error = -1; + s->err = EINVAL; + goto exit; + } + + if (((char*)entry + sizeof(*entry)) > (char*)endptr) { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("Incomplete or corrupt attribute entry"); + error = -1; + s->err = EINVAL; + goto exit; + } + + /* + * Endian swap the entry we're looking at. Previously + * we did this swap as part of swap_attrhdr, but that + * allowed a maliciously constructed file to overrun + * our allocation. Instead do the swap after we've verified + * the entry struct is within the buffer's range. + */ + swap_attrhdr_entry(entry); + + if (entry->namelen < 2) { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("Corrupt attribute entry (only %d bytes)", entry->namelen); + error = -1; + s->err = EINVAL; + goto exit; + } + + if (entry->namelen > XATTR_MAXNAMELEN + 1) { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("Corrupt attribute entry (name length is %d bytes)", entry->namelen); + error = -1; + s->err = EINVAL; + goto exit; + } + + if ((void*)(entry->name + entry->namelen) > endptr) { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("Incomplete or corrupt attribute entry"); + error = -1; + s->err = EINVAL; + goto exit; + } + + /* Because namelen includes the NUL, we check one byte back */ + if (entry->name[entry->namelen-1] != 0) { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("Corrupt attribute entry (name is not NUL-terminated)"); + error = -1; + s->err = EINVAL; + goto exit; + } - copyfile_debug(3, "extracting \"%s\" (%d bytes) at offset %u", - entry->name, entry->length, entry->offset); + copyfile_debug(3, "extracting \"%s\" (%d bytes) at offset %u", + entry->name, entry->length, entry->offset); #if 0 - dataptr = (char *)attrhdr + entry->offset; + dataptr = (char *)attrhdr + entry->offset; - if (dataptr > endptr || dataptr < buffer) { - copyfile_debug(1, "Entry %d overflows: offset = %u", i, entry->offset); - error = -1; - s->err = EINVAL; /* Invalid buffer */ - goto exit; - } + if (dataptr > endptr || dataptr < buffer) { + copyfile_debug(1, "Entry %d overflows: offset = %u", i, entry->offset); + error = -1; + 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)) { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("Incomplete or corrupt attribute entry"); - copyfile_debug(1, "Entry %d length overflows: offset = %u, length = %u", - i, entry->offset, entry->length); - error = -1; - 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)) { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("Incomplete or corrupt attribute entry"); + copyfile_debug(1, "Entry %d length overflows: offset = %u, length = %u", + i, entry->offset, entry->length); + error = -1; + s->err = EINVAL; /* Invalid buffer */ + 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; + 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 (s->qinfo == NULL) - { - tqinfo = qtn_file_alloc(); - if (tqinfo) - { - int x; - if ((x = qtn_file_init_with_data(tqinfo, dataptr, entry->length)) != 0) + if (strcmp((char*)entry->name, XATTR_QUARANTINE_NAME) == 0) { - copyfile_warn("qtn_file_init_with_data failed: %s", qtn_error(x)); - qtn_file_free(tqinfo); - tqinfo = NULL; + qtn_file_t tqinfo = NULL; + + if (s->qinfo == NULL) + { + tqinfo = qtn_file_alloc(); + if (tqinfo) + { + int x; + if ((x = qtn_file_init_with_data(tqinfo, dataptr, entry->length)) != 0) + { + copyfile_warn("qtn_file_init_with_data failed: %s", qtn_error(x)); + qtn_file_free(tqinfo); + tqinfo = NULL; + } + } + } + else + { + tqinfo = s->qinfo; + } + if (tqinfo) + { + int x; + x = qtn_file_apply_to_fd(tqinfo, s->dst_fd); + if (x != 0) { + copyfile_warn("qtn_file_apply_to_fd failed: %s", qtn_error(x)); + if (s->statuscb) { + int rv; + s->xattr_name = (char*)XATTR_QUARANTINE_NAME; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) { + error = s->err = x < 0 ? ENOTSUP : errno; + goto exit; + } + } else { + error = s->err = x < 0 ? ENOTSUP : errno; + goto exit; + } + } + } + if (tqinfo && !s->qinfo) + { + qtn_file_free(tqinfo); + } } - } - } - else - { - tqinfo = s->qinfo; - } - if (tqinfo) - { - int x; - x = qtn_file_apply_to_fd(tqinfo, s->dst_fd); - if (x != 0) { - copyfile_warn("qtn_file_apply_to_fd failed: %s", qtn_error(x)); - if (s->statuscb) { - int rv; - s->xattr_name = (char*)XATTR_QUARANTINE_NAME; - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); - s->xattr_name = NULL; - if (rv == COPYFILE_QUIT) { - error = s->err = x < 0 ? ENOTSUP : errno; + /* Look for ACL data */ + else if (strcmp((char*)entry->name, XATTR_SECURITY_NAME) == 0) + { + acl_t acl; + struct stat sb; + int retry = 1; + char *tcp = dataptr; + + if (entry->length == 0) { + /* Not sure how we got here, but we had one case + * where it was 0. In a normal EA, we can have a 0-byte + * payload. That means nothing in this case, so we'll + * simply skip the EA. + */ + error = 0; + goto acl_done; + } + /* + * acl_from_text() requires a NUL-terminated string. The ACL EA, + * however, may not be NUL-terminated. So in that case, we need to + * copy it to a +1 sized buffer, to ensure it's got a terminated string. + */ + if (tcp[entry->length - 1] != 0) { + char *tmpstr = malloc(entry->length + 1); + if (tmpstr == NULL) { + error = -1; goto exit; } + strlcpy(tmpstr, tcp, entry->length + 1); + acl = acl_from_text(tmpstr); + free(tmpstr); } else { - error = s->err = x < 0 ? ENOTSUP : errno; - goto exit; + acl = acl_from_text(tcp); } - } - } - if (tqinfo && !s->qinfo) - { - qtn_file_free(tqinfo); - } - } - /* Look for ACL data */ - else if (strcmp((char*)entry->name, XATTR_SECURITY_NAME) == 0) - { - acl_t acl; - struct stat sb; - int retry = 1; - char *tcp = dataptr; - - if (entry->length == 0) { - /* Not sure how we got here, but we had one case - * where it was 0. In a normal EA, we can have a 0-byte - * payload. That means nothing in this case, so we'll - * simply skip the EA. - */ - error = 0; - goto acl_done; - } - /* - * acl_from_text() requires a NUL-terminated string. The ACL EA, - * however, may not be NUL-terminated. So in that case, we need to - * copy it to a +1 sized buffer, to ensure it's got a terminated string. - */ - if (tcp[entry->length - 1] != 0) { - char *tmpstr = malloc(entry->length + 1); - if (tmpstr == NULL) { - error = -1; - goto exit; - } - strlcpy(tmpstr, tcp, entry->length + 1); - acl = acl_from_text(tmpstr); - free(tmpstr); - } else { - acl = acl_from_text(tcp); - } - if (acl != NULL) - { - filesec_t fsec_tmp; + if (acl != NULL) + { + filesec_t fsec_tmp; - if ((fsec_tmp = filesec_init()) == NULL) - error = -1; - else if((error = fstatx_np(s->dst_fd, &sb, fsec_tmp)) < 0) - error = -1; - else if (filesec_set_property(fsec_tmp, FILESEC_ACL, &acl) < 0) - error = -1; - else { - while (fchmodx_np(s->dst_fd, fsec_tmp) < 0) - { + if ((fsec_tmp = filesec_init()) == NULL) + error = -1; + else if((error = fstatx_np(s->dst_fd, &sb, fsec_tmp)) < 0) + error = -1; + else if (filesec_set_property(fsec_tmp, FILESEC_ACL, &acl) < 0) + error = -1; + else { + while (fchmodx_np(s->dst_fd, fsec_tmp) < 0) + { if (errno == ENOTSUP) - { - if (retry && !copyfile_unset_acl(s)) - { - retry = 0; - continue; - } - } + { + if (retry && !copyfile_unset_acl(s)) + { + retry = 0; + continue; + } + } copyfile_warn("setting security information"); error = -1; break; + } + } + acl_free(acl); + filesec_free(fsec_tmp); + + acl_done: + if (error == -1) + goto exit; + } + } + /* And, finally, everything else */ + else + { + if (s->copyIntent || + xattr_preserve_for_intent((char*)entry->name, s->copyIntent) == 1) { + 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", 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; } - } - acl_free(acl); - filesec_free(fsec_tmp); + entry = ATTR_NEXT(entry); + } + } -acl_done: - if (error == -1) - goto exit; + /* + * Extract the Finder Info. + */ + if (adhdr->entries[0].offset > (hdrsize - sizeof(emptyfinfo))) { + error = -1; + goto exit; + } + + if (bcmp((u_int8_t*)buffer + adhdr->entries[0].offset, emptyfinfo, sizeof(emptyfinfo)) != 0) + { + uint16_t *fFlags; + uint8_t *newFinfo; + enum { kFinderInvisibleMask = 1 << 14 }; + + newFinfo = (u_int8_t*)buffer + adhdr->entries[0].offset; + fFlags = (uint16_t*)&newFinfo[8]; + copyfile_debug(3, " extracting \"%s\" (32 bytes)", XATTR_FINDERINFO_NAME); + if (s->statuscb) { + int rv; + s->xattr_name = (char*)XATTR_FINDERINFO_NAME; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) { + error = -1; + s->err = ECANCELED; + goto exit; + } else if (rv == COPYFILE_SKIP) { + goto skip_fi; + } } - } - /* And, finally, everything else */ - else - { - if (s->copyIntent || - xattr_preserve_for_intent((char*)entry->name, s->copyIntent) == 1) { + error = fsetxattr(s->dst_fd, XATTR_FINDERINFO_NAME, (u_int8_t*)buffer + adhdr->entries[0].offset, sizeof(emptyfinfo), 0, 0); + if (error) { 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; - } + 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) { - s->err = ECANCELED; 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); - } - } - - /* - * Extract the Finder Info. - */ - if (adhdr->entries[0].offset > (hdrsize - sizeof(emptyfinfo))) { - error = -1; - goto exit; - } - - if (bcmp((u_int8_t*)buffer + adhdr->entries[0].offset, emptyfinfo, sizeof(emptyfinfo)) != 0) - { - uint16_t *fFlags; - uint8_t *newFinfo; - enum { kFinderInvisibleMask = 1 << 14 }; - - newFinfo = (u_int8_t*)buffer + adhdr->entries[0].offset; - fFlags = (uint16_t*)&newFinfo[8]; - copyfile_debug(3, " extracting \"%s\" (32 bytes)", XATTR_FINDERINFO_NAME); - if (s->statuscb) { - int rv; - s->xattr_name = (char*)XATTR_FINDERINFO_NAME; - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); - s->xattr_name = NULL; - if (rv == COPYFILE_QUIT) { - error = -1; - s->err = ECANCELED; goto exit; - } else if (rv == COPYFILE_SKIP) { - goto skip_fi; - } - } - error = fsetxattr(s->dst_fd, XATTR_FINDERINFO_NAME, (u_int8_t*)buffer + adhdr->entries[0].offset, sizeof(emptyfinfo), 0, 0); - if (error) { - if (s->statuscb) { + } else if (s->statuscb) { int rv; s->xattr_name = (char *)XATTR_FINDERINFO_NAME; - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); s->xattr_name = NULL; if (rv == COPYFILE_QUIT) { error = -1; @@ -3493,335 +3624,323 @@ acl_done: goto exit; } } - goto exit; - } else if (s->statuscb) { - int rv; - 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) { - error = -1; - s->err = ECANCELED; - goto exit; - } + if (SWAP16(*fFlags) & kFinderInvisibleMask) + s->internal_flags |= cfMakeFileInvisible; } - if (SWAP16(*fFlags) & kFinderInvisibleMask) - s->internal_flags |= cfMakeFileInvisible; - } skip_fi: - /* - * Extract the Resource Fork. - */ - if (adhdr->entries[1].type == AD_RESOURCE && - adhdr->entries[1].length > 0) - { - void * rsrcforkdata = NULL; - size_t length; - off_t offset; - struct stat sb; - struct timeval tval[2]; - - length = adhdr->entries[1].length; - offset = adhdr->entries[1].offset; - rsrcforkdata = malloc(length); + /* + * Extract the Resource Fork. + */ + if (adhdr->entries[1].type == AD_RESOURCE && + adhdr->entries[1].length > 0) + { + void * rsrcforkdata = NULL; + size_t length; + off_t offset; + struct stat sb; + struct timeval tval[2]; - if (rsrcforkdata == NULL) { - copyfile_debug(1, "could not allocate %zu bytes for rsrcforkdata", - length); - error = -1; - goto bad; - } + length = adhdr->entries[1].length; + offset = adhdr->entries[1].offset; + rsrcforkdata = malloc(length); - if (fstat(s->dst_fd, &sb) < 0) - { - copyfile_debug(1, "couldn't stat destination file"); - error = -1; - goto bad; - } + if (rsrcforkdata == NULL) { + copyfile_debug(1, "could not allocate %zu bytes for rsrcforkdata", + length); + error = -1; + goto bad; + } - bytes = pread(s->src_fd, rsrcforkdata, length, offset); - if (bytes < (ssize_t)length) - { - if (bytes == -1) - { - copyfile_debug(1, "couldn't read resource fork"); - } - else - { - copyfile_debug(1, - "couldn't read resource fork (only read %d bytes of %d)", - (int)bytes, (int)length); - } - error = -1; - goto bad; - } - if (s->statuscb) { - int rv; - 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 (fstat(s->dst_fd, &sb) < 0) + { + copyfile_debug(1, "couldn't stat destination file"); error = -1; - s->err = ECANCELED; - if (rsrcforkdata) - free(rsrcforkdata); - goto exit; - } else if (rv == COPYFILE_SKIP) { goto bad; } - } - error = fsetxattr(s->dst_fd, XATTR_RESOURCEFORK_NAME, rsrcforkdata, bytes, 0, 0); - if (error) - { - /* - * For filesystems that do not natively support named attributes, - * the kernel creates an AppleDouble file that -- for compatabilty - * reasons -- has a resource fork containing nothing but a rsrcfork_header_t - * structure that says there are no resources. So, if fsetxattr has - * failed, and the resource fork is that empty structure, *and* the - * target file is a directory, then we do nothing with it. - */ - if ((bytes == sizeof(rsrcfork_header_t)) && - ((sb.st_mode & S_IFMT) == S_IFDIR) && - (memcmp(rsrcforkdata, &empty_rsrcfork_header, bytes) == 0)) { - copyfile_debug(2, "not setting empty resource fork on directory"); - error = errno = 0; - goto bad; - } - if (s->statuscb) { - int rv; - 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) { - error = errno = 0; + + bytes = pread(s->src_fd, rsrcforkdata, length, offset); + if (bytes < (ssize_t)length) + { + if (bytes == -1) + { + copyfile_debug(1, "couldn't read resource fork"); + } + else + { + copyfile_debug(1, + "couldn't read resource fork (only read %d bytes of %d)", + (int)bytes, (int)length); + } + error = -1; goto bad; } - } - copyfile_debug(1, "error %d setting resource fork attribute", error); - error = -1; - goto bad; - } else if (s->statuscb) { - int rv; - 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) { + if (s->statuscb) { + int rv; + 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) { + error = -1; + s->err = ECANCELED; + if (rsrcforkdata) + free(rsrcforkdata); + goto exit; + } else if (rv == COPYFILE_SKIP) { + goto bad; + } + } + error = fsetxattr(s->dst_fd, XATTR_RESOURCEFORK_NAME, rsrcforkdata, bytes, 0, 0); + if (error) + { + /* + * For filesystems that do not natively support named attributes, + * the kernel creates an AppleDouble file that -- for compatabilty + * reasons -- has a resource fork containing nothing but a rsrcfork_header_t + * structure that says there are no resources. So, if fsetxattr has + * failed, and the resource fork is that empty structure, *and* the + * target file is a directory, then we do nothing with it. + */ + if ((bytes == sizeof(rsrcfork_header_t)) && + ((sb.st_mode & S_IFMT) == S_IFDIR) && + (memcmp(rsrcforkdata, &empty_rsrcfork_header, bytes) == 0)) { + copyfile_debug(2, "not setting empty resource fork on directory"); + error = errno = 0; + goto bad; + } + if (s->statuscb) { + int rv; + 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) { + error = errno = 0; + goto bad; + } + } + copyfile_debug(1, "error %d setting resource fork attribute", error); error = -1; - s->err = ECANCELED; - if (rsrcforkdata) - free(rsrcforkdata); - goto exit; + goto bad; + } else if (s->statuscb) { + int rv; + 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) { + error = -1; + s->err = ECANCELED; + if (rsrcforkdata) + free(rsrcforkdata); + goto exit; + } + } + copyfile_debug(3, "extracting \"%s\" (%d bytes)", + XATTR_RESOURCEFORK_NAME, (int)length); + + if (!(s->flags & COPYFILE_STAT)) + { + tval[0].tv_sec = sb.st_atime; + tval[1].tv_sec = sb.st_mtime; + tval[0].tv_usec = tval[1].tv_usec = 0; + + if (futimes(s->dst_fd, tval)) + copyfile_warn("%s: set times", s->dst ? s->dst : "(null dst)"); } + bad: + if (rsrcforkdata) + free(rsrcforkdata); } - copyfile_debug(3, "extracting \"%s\" (%d bytes)", - XATTR_RESOURCEFORK_NAME, (int)length); - if (!(s->flags & COPYFILE_STAT)) + if (COPYFILE_STAT & s->flags) { - tval[0].tv_sec = sb.st_atime; - tval[1].tv_sec = sb.st_mtime; - tval[0].tv_usec = tval[1].tv_usec = 0; - - if (futimes(s->dst_fd, tval)) - copyfile_warn("%s: set times", s->dst ? s->dst : "(null dst)"); - } -bad: - if (rsrcforkdata) - free(rsrcforkdata); - } - - if (COPYFILE_STAT & s->flags) - { - error = copyfile_stat(s); - } + error = copyfile_stat(s); + } exit: - if (buffer) free(buffer); - if (dataptr) free(dataptr); - return error; + if (buffer) free(buffer); + if (dataptr) free(dataptr); + return error; } static int copyfile_pack_quarantine(copyfile_state_t s, void **buf, ssize_t *len) { - int ret = 0; - char qbuf[QTN_SERIALIZED_DATA_MAX]; - size_t qlen = sizeof(qbuf); + int ret = 0; + char qbuf[QTN_SERIALIZED_DATA_MAX]; + size_t qlen = sizeof(qbuf); - if (s->qinfo == NULL) - { - ret = -1; - goto done; - } + if (s->qinfo == NULL) + { + ret = -1; + goto done; + } - if (qtn_file_to_data(s->qinfo, qbuf, &qlen) != 0) - { - ret = -1; - goto done; - } - - *buf = malloc(qlen); - if (*buf) - { - memcpy(*buf, qbuf, qlen); - *len = qlen; - } + if (qtn_file_to_data(s->qinfo, qbuf, &qlen) != 0) + { + ret = -1; + goto done; + } + + *buf = malloc(qlen); + if (*buf) + { + memcpy(*buf, qbuf, qlen); + *len = qlen; + } done: - return ret; + return ret; } static int copyfile_pack_acl(copyfile_state_t s, void **buf, ssize_t *len) { - int ret = 0; - acl_t acl = NULL; - char *acl_text; + int ret = 0; + acl_t acl = NULL; + char *acl_text; - if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) < 0) - { - if (errno != ENOENT) + if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) < 0) { - ret = -1; - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("getting acl"); + if (errno != ENOENT) + { + ret = -1; + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("getting acl"); + } + *len = 0; + goto exit; } - *len = 0; - goto exit; - } - if ((acl_text = acl_to_text(acl, len)) != NULL) - { - /* - * acl_to_text() doesn't include the NUL at the endo - * in it's count (*len). It does, however, promise to - * return a valid C string, so we need to up the count - * by 1. - */ - *len = *len + 1; - *buf = malloc(*len); - if (*buf) - memcpy(*buf, acl_text, *len); - else - *len = 0; - acl_free(acl_text); - } - copyfile_debug(2, "copied acl (%ld) %p", *len, *buf); + if ((acl_text = acl_to_text(acl, len)) != NULL) + { + /* + * acl_to_text() doesn't include the NUL at the endo + * in it's count (*len). It does, however, promise to + * return a valid C string, so we need to up the count + * by 1. + */ + *len = *len + 1; + *buf = malloc(*len); + if (*buf) + memcpy(*buf, acl_text, *len); + else + *len = 0; + acl_free(acl_text); + } + copyfile_debug(2, "copied acl (%ld) %p", *len, *buf); exit: - if (acl) - acl_free(acl); - return ret; + if (acl) + acl_free(acl); + return ret; } static int copyfile_pack_rsrcfork(copyfile_state_t s, attr_header_t *filehdr) { - ssize_t datasize; - char *databuf = NULL; - int ret = 0; + ssize_t datasize; + char *databuf = NULL; + int ret = 0; -/* - * XXX - * do COPYFILE_COPY_XATTR here; no need to - * the work if we want to skip. - */ + /* + * XXX + * do COPYFILE_COPY_XATTR here; no need to + * the work if we want to skip. + */ - if (s->statuscb) - { - int rv; + if (s->statuscb) + { + int rv; - s->xattr_name = (char*)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_SKIP) { - ret = 0; - goto done; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_SKIP) { + ret = 0; + goto done; + } + if (rv == COPYFILE_QUIT) { + ret = -1; + s->err = ECANCELED; + goto done; + } } - if (rv == COPYFILE_QUIT) { + /* Get the resource fork size */ + if ((datasize = fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, NULL, 0, 0, 0)) < 0) + { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("skipping attr \"%s\" due to error %d", XATTR_RESOURCEFORK_NAME, errno); + return -1; + } + + if (datasize > INT_MAX) { + s->err = EINVAL; ret = -1; - s->err = ECANCELED; goto done; } - } - /* Get the resource fork size */ - if ((datasize = fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, NULL, 0, 0, 0)) < 0) - { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("skipping attr \"%s\" due to error %d", XATTR_RESOURCEFORK_NAME, errno); - return -1; - } - if (datasize > INT_MAX) { - s->err = EINVAL; - ret = -1; - goto done; - } + if (s->statuscb) { + int rv; + s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME; - if (s->statuscb) { - int rv; - s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME; + s->totalCopied = 0; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) { + s->err = ECANCELED; + ret = -1; + goto done; + } + } + if ((databuf = malloc(datasize)) == NULL) + { + copyfile_warn("malloc"); + ret = -1; + goto done; + } - s->totalCopied = 0; - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); - s->xattr_name = NULL; - if (rv == COPYFILE_QUIT) { - s->err = ECANCELED; + if (fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, databuf, datasize, 0, 0) != datasize) + { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("couldn't read entire resource fork"); ret = -1; goto done; } - } - if ((databuf = malloc(datasize)) == NULL) - { - copyfile_warn("malloc"); - ret = -1; - goto done; - } - if (fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, databuf, datasize, 0, 0) != datasize) - { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("couldn't read entire resource fork"); - ret = -1; - goto done; - } - - /* Write the resource fork to disk. */ - if (pwrite(s->dst_fd, databuf, datasize, filehdr->appledouble.entries[1].offset) != datasize) - { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("couldn't write resource fork"); - } - if (s->statuscb) - { - int rv; - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); - if (rv == COPYFILE_QUIT) { - ret = -1; - goto done; + /* Write the resource fork to disk. */ + if (pwrite(s->dst_fd, databuf, datasize, filehdr->appledouble.entries[1].offset) != datasize) + { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("couldn't write resource fork"); } - } - copyfile_debug(3, "copied %zd bytes of \"%s\" data @ offset 0x%08x", - datasize, XATTR_RESOURCEFORK_NAME, filehdr->appledouble.entries[1].offset); - filehdr->appledouble.entries[1].length = (u_int32_t)datasize; + if (s->statuscb) + { + int rv; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx); + if (rv == COPYFILE_QUIT) { + ret = -1; + goto done; + } + } + copyfile_debug(3, "copied %zd bytes of \"%s\" data @ offset 0x%08x", + datasize, XATTR_RESOURCEFORK_NAME, filehdr->appledouble.entries[1].offset); + filehdr->appledouble.entries[1].length = (u_int32_t)datasize; done: - if (ret == -1 && s->statuscb) - { - int rv; - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); - if (rv == COPYFILE_CONTINUE) - ret = 0; - } - if (s->xattr_name) { - s->xattr_name = NULL; - } - if (databuf) - free(databuf); + if (ret == -1 && s->statuscb) + { + int rv; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); + if (rv == COPYFILE_CONTINUE) + ret = 0; + } + if (s->xattr_name) { + s->xattr_name = NULL; + } + if (databuf) + free(databuf); -/* - * XXX - * Do status callback here - * If ret == -1, then error callback - */ - return ret; + /* + * XXX + * Do status callback here + * If ret == -1, then error callback + */ + return ret; } /* @@ -3829,422 +3948,422 @@ done: */ static int copyfile_pack(copyfile_state_t s) { - char *attrnamebuf = NULL, *endnamebuf; - void *databuf = NULL; - attr_header_t *filehdr, *endfilehdr; - attr_entry_t *entry; - ssize_t listsize = 0; - char *nameptr; - size_t namelen; - size_t entrylen; - ssize_t datasize; - size_t offset = 0; - int hasrsrcfork = 0; - int error = 0; - int seenq = 0; // Have we seen any quarantine info already? - - 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_HDR_SIZE); - } - - attrnamebuf = calloc(1, ATTR_MAX_HDR_SIZE); - if (attrnamebuf == NULL) { - error = -1; - goto exit; - } else { - endnamebuf = ((char*)attrnamebuf) + ATTR_MAX_HDR_SIZE; - } - - /* - * Fill in the Apple Double Header defaults. - */ - filehdr->appledouble.magic = ADH_MAGIC; - filehdr->appledouble.version = ADH_VERSION; - filehdr->appledouble.numEntries = 2; - filehdr->appledouble.entries[0].type = AD_FINDERINFO; - filehdr->appledouble.entries[0].offset = (u_int32_t)offsetof(apple_double_header_t, finfo); - filehdr->appledouble.entries[0].length = FINDERINFOSIZE; - filehdr->appledouble.entries[1].type = AD_RESOURCE; - filehdr->appledouble.entries[1].offset = (u_int32_t)offsetof(apple_double_header_t, pad); - filehdr->appledouble.entries[1].length = 0; - bcopy(ADH_MACOSX, filehdr->appledouble.filler, sizeof(filehdr->appledouble.filler)); - - /* - * Fill in the initial Attribute Header. - */ - filehdr->magic = ATTR_HDR_MAGIC; - filehdr->debug_tag = 0; - filehdr->data_start = (u_int32_t)sizeof(attr_header_t); - - /* - * Collect the attribute names. - */ - entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t)); - - /* - * Test if there are acls to copy - */ - if (COPYFILE_ACL & s->flags) - { - acl_t temp_acl = NULL; - if (filesec_get_property(s->fsec, FILESEC_ACL, &temp_acl) < 0) - { - copyfile_debug(2, "no acl entries found (errno = %d)", errno); - } else - { - offset = strlen(XATTR_SECURITY_NAME) + 1; - strcpy(attrnamebuf, XATTR_SECURITY_NAME); - endnamebuf = attrnamebuf + offset; - } - if (temp_acl) - acl_free(temp_acl); - } - - if (COPYFILE_XATTR & s->flags) - { - ssize_t left = ATTR_MAX_HDR_SIZE - offset; - if ((listsize = flistxattr(s->src_fd, attrnamebuf + offset, left, 0)) <= 0) - { - copyfile_debug(2, "no extended attributes found (%d)", errno); - } - if (listsize > left) - { - copyfile_debug(1, "extended attribute list too long"); - listsize = left; + char *attrnamebuf = NULL, *endnamebuf; + void *databuf = NULL; + attr_header_t *filehdr, *endfilehdr; + attr_entry_t *entry; + ssize_t listsize = 0; + char *nameptr; + size_t namelen; + size_t entrylen; + ssize_t datasize; + size_t offset = 0; + int hasrsrcfork = 0; + int error = 0; + int seenq = 0; // Have we seen any quarantine info already? + + 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_HDR_SIZE); } - endnamebuf = attrnamebuf + offset + (listsize > 0 ? listsize : 0); - if (endnamebuf > (attrnamebuf + ATTR_MAX_HDR_SIZE)) { + attrnamebuf = calloc(1, ATTR_MAX_HDR_SIZE); + if (attrnamebuf == NULL) { error = -1; goto exit; + } else { + endnamebuf = ((char*)attrnamebuf) + ATTR_MAX_HDR_SIZE; } - if (listsize > 0) - sort_xattrname_list(attrnamebuf, endnamebuf - attrnamebuf); + /* + * Fill in the Apple Double Header defaults. + */ + filehdr->appledouble.magic = ADH_MAGIC; + filehdr->appledouble.version = ADH_VERSION; + filehdr->appledouble.numEntries = 2; + filehdr->appledouble.entries[0].type = AD_FINDERINFO; + filehdr->appledouble.entries[0].offset = (u_int32_t)offsetof(apple_double_header_t, finfo); + filehdr->appledouble.entries[0].length = FINDERINFOSIZE; + filehdr->appledouble.entries[1].type = AD_RESOURCE; + filehdr->appledouble.entries[1].offset = (u_int32_t)offsetof(apple_double_header_t, pad); + filehdr->appledouble.entries[1].length = 0; + bcopy(ADH_MACOSX, filehdr->appledouble.filler, sizeof(filehdr->appledouble.filler)); + + /* + * Fill in the initial Attribute Header. + */ + filehdr->magic = ATTR_HDR_MAGIC; + filehdr->debug_tag = 0; + filehdr->data_start = (u_int32_t)sizeof(attr_header_t); + + /* + * Collect the attribute names. + */ + entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t)); - for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen) + /* + * Test if there are acls to copy + */ + if (COPYFILE_ACL & s->flags) { - namelen = strlen(nameptr) + 1; - /* Skip over FinderInfo or Resource Fork names */ - if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0 || - strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0) { - continue; - } - if (strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0) { - seenq = 1; - } - - /* The system should prevent this from happening, but... */ - if (namelen > XATTR_MAXNAMELEN + 1) { - namelen = XATTR_MAXNAMELEN + 1; - } - if (s->copyIntent && - xattr_preserve_for_intent(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]; - bcopy(nameptr, eaname, namelen); - eaname[namelen - 1] = 0; // Just to be sure! - s->xattr_name = eaname; - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); - s->xattr_name = NULL; - if (rv == COPYFILE_QUIT) { - error = -1; - s->err = ECANCELED; - goto exit; - } else if (rv == COPYFILE_SKIP) { - 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; + acl_t temp_acl = NULL; + if (filesec_get_property(s->fsec, FILESEC_ACL, &temp_acl) < 0) + { + copyfile_debug(2, "no acl entries found (errno = %d)", errno); + } else + { + offset = strlen(XATTR_SECURITY_NAME) + 1; + strcpy(attrnamebuf, XATTR_SECURITY_NAME); + endnamebuf = attrnamebuf + offset; } - } - entry->namelen = namelen; - entry->flags = 0; - if (nameptr + namelen > endnamebuf) { - error = -1; - goto exit; - } - - bcopy(nameptr, &entry->name[0], namelen); - copyfile_debug(2, "copied name [%s]", entry->name); - - entrylen = ATTR_ENTRY_LENGTH(namelen); - entry = (attr_entry_t *)(((char *)entry) + entrylen); - - if ((void*)entry >= (void*)endfilehdr) { - error = -1; - goto exit; - } - - /* Update the attributes header. */ - filehdr->num_attrs++; - filehdr->data_start += (u_int32_t)entrylen; - } - } - - /* - * If we have any quarantine data, we always pack it. - * But if we've already got it in the EA list, don't put it in again. - */ - if (s->qinfo && !seenq) - { - ssize_t left = ATTR_MAX_HDR_SIZE - offset; - /* strlcpy returns number of bytes copied, but we need offset to point to the next byte */ - offset += strlcpy(attrnamebuf + offset, XATTR_QUARANTINE_NAME, left) + 1; - } - - seenq = 0; - /* - * Collect the attribute data. - */ - entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t)); - - for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen + 1) - { - namelen = strlen(nameptr); - - if (strcmp(nameptr, XATTR_SECURITY_NAME) == 0) - copyfile_pack_acl(s, &databuf, &datasize); - else if (s->qinfo && strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0) - { - copyfile_pack_quarantine(s, &databuf, &datasize); + if (temp_acl) + acl_free(temp_acl); } - /* Check for Finder Info. */ - else if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0) + + if (COPYFILE_XATTR & s->flags) { - if (s->statuscb) - { - int rv; - s->xattr_name = (char*)XATTR_FINDERINFO_NAME; - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); - s->xattr_name = NULL; - if (rv == COPYFILE_QUIT) + ssize_t left = ATTR_MAX_HDR_SIZE - offset; + if ((listsize = flistxattr(s->src_fd, attrnamebuf + offset, left, 0)) <= 0) { - s->xattr_name = NULL; - s->err = ECANCELED; - error = -1; - goto exit; + copyfile_debug(2, "no extended attributes found (%d)", errno); } - else if (rv == COPYFILE_SKIP) + if (listsize > left) { - s->xattr_name = NULL; - continue; + copyfile_debug(1, "extended attribute list too long"); + listsize = left; } - s->totalCopied = 0; - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); - s->xattr_name = NULL; - if (rv == COPYFILE_QUIT) - { - s->err = ECANCELED; - error = -1; - goto exit; + + endnamebuf = attrnamebuf + offset + (listsize > 0 ? listsize : 0); + if (endnamebuf > (attrnamebuf + ATTR_MAX_HDR_SIZE)) { + error = -1; + goto exit; } - } - datasize = fgetxattr(s->src_fd, nameptr, (u_int8_t*)filehdr + filehdr->appledouble.entries[0].offset, 32, 0, 0); - if (datasize < 0) - { - if (s->statuscb) { - int rv; - s->xattr_name = strdup(nameptr); - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx); - if (s->xattr_name) { - free(s->xattr_name); + + if (listsize > 0) + sort_xattrname_list(attrnamebuf, endnamebuf - attrnamebuf); + + for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen) + { + namelen = strlen(nameptr) + 1; + /* Skip over FinderInfo or Resource Fork names */ + if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0 || + strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0) { + continue; + } + if (strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0) { + seenq = 1; + } + + /* The system should prevent this from happening, but... */ + if (namelen > XATTR_MAXNAMELEN + 1) { + namelen = XATTR_MAXNAMELEN + 1; + } + if (s->copyIntent && + xattr_preserve_for_intent(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]; + bcopy(nameptr, eaname, namelen); + eaname[namelen - 1] = 0; // Just to be sure! + s->xattr_name = eaname; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) { + error = -1; + s->err = ECANCELED; + goto exit; + } else if (rv == COPYFILE_SKIP) { + 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 (rv == COPYFILE_QUIT) { + entry->namelen = namelen; + entry->flags = 0; + if (nameptr + namelen > endnamebuf) { error = -1; goto exit; } - } - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno); - } else if (datasize != 32) - { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("unexpected size (%ld) for \"%s\"", datasize, nameptr); - } else - { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn(" copied 32 bytes of \"%s\" data @ offset 0x%08x", - XATTR_FINDERINFO_NAME, filehdr->appledouble.entries[0].offset); - if (s->statuscb) { - int rv; - s->xattr_name = strdup(nameptr); - 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) { + + bcopy(nameptr, &entry->name[0], namelen); + copyfile_debug(2, "copied name [%s]", entry->name); + + entrylen = ATTR_ENTRY_LENGTH(namelen); + entry = (attr_entry_t *)(((char *)entry) + entrylen); + + if ((void*)entry >= (void*)endfilehdr) { error = -1; goto exit; } - } - } - continue; /* finder info doesn't have an attribute entry */ + + /* Update the attributes header. */ + filehdr->num_attrs++; + filehdr->data_start += (u_int32_t)entrylen; + } } - /* Check for Resource Fork. */ - else if (strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0) + + /* + * If we have any quarantine data, we always pack it. + * But if we've already got it in the EA list, don't put it in again. + */ + if (s->qinfo && !seenq) { - hasrsrcfork = 1; - continue; - } else + ssize_t left = ATTR_MAX_HDR_SIZE - offset; + /* strlcpy returns number of bytes copied, but we need offset to point to the next byte */ + offset += strlcpy(attrnamebuf + offset, XATTR_QUARANTINE_NAME, left) + 1; + } + + seenq = 0; + /* + * Collect the attribute data. + */ + entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t)); + + for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen + 1) { - /* Just a normal attribute. */ - if (s->statuscb) - { - int rv; - s->xattr_name = strdup(nameptr); - s->totalCopied = 0; - rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); - if (s->xattr_name) { - free(s->xattr_name); - s->xattr_name = NULL; - } - /* - * Due to the nature of the packed file, we can't skip at this point. - */ - if (rv == COPYFILE_QUIT) + namelen = strlen(nameptr); + + if (strcmp(nameptr, XATTR_SECURITY_NAME) == 0) + copyfile_pack_acl(s, &databuf, &datasize); + else if (s->qinfo && strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0) { - s->err = ECANCELED; - error = -1; - goto exit; + copyfile_pack_quarantine(s, &databuf, &datasize); } - } - datasize = fgetxattr(s->src_fd, nameptr, NULL, 0, 0, 0); - if (datasize == 0) - goto next; - if (datasize < 0) - { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno); - if (s->statuscb) + /* Check for Finder Info. */ + else if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0) { - int rv; - s->xattr_name = strdup(nameptr); - 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) - { - s->err = ECANCELED; - error = -1; - goto exit; - } - } - goto next; - } - if (datasize > XATTR_MAXATTRLEN) - { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("skipping attr \"%s\" (too big)", nameptr); - goto next; - } - databuf = malloc(datasize); - if (databuf == NULL) { - error = -1; - continue; - } - datasize = fgetxattr(s->src_fd, nameptr, databuf, datasize, 0, 0); - if (s->statuscb) { - int rv; - s->xattr_name = strdup(nameptr); - 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 (s->statuscb) + { + int rv; + s->xattr_name = (char*)XATTR_FINDERINFO_NAME; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) + { + s->xattr_name = NULL; + s->err = ECANCELED; + error = -1; + goto exit; + } + else if (rv == COPYFILE_SKIP) + { + s->xattr_name = NULL; + continue; + } + s->totalCopied = 0; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); + s->xattr_name = NULL; + if (rv == COPYFILE_QUIT) + { + s->err = ECANCELED; + error = -1; + goto exit; + } + } + datasize = fgetxattr(s->src_fd, nameptr, (u_int8_t*)filehdr + filehdr->appledouble.entries[0].offset, 32, 0, 0); + if (datasize < 0) + { + if (s->statuscb) { + int rv; + s->xattr_name = strdup(nameptr); + 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; + } + } + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno); + } else if (datasize != 32) + { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("unexpected size (%ld) for \"%s\"", datasize, nameptr); + } else + { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn(" copied 32 bytes of \"%s\" data @ offset 0x%08x", + XATTR_FINDERINFO_NAME, filehdr->appledouble.entries[0].offset); + if (s->statuscb) { + int rv; + s->xattr_name = strdup(nameptr); + 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; + goto exit; + } + } + } + continue; /* finder info doesn't have an attribute entry */ } - if (rv == COPYFILE_QUIT) { - s->err = ECANCELED; - error = -1; - goto exit; + /* Check for Resource Fork. */ + else if (strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0) + { + hasrsrcfork = 1; + continue; + } else + { + /* Just a normal attribute. */ + if (s->statuscb) + { + int rv; + s->xattr_name = strdup(nameptr); + s->totalCopied = 0; + rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx); + if (s->xattr_name) { + free(s->xattr_name); + s->xattr_name = NULL; + } + /* + * Due to the nature of the packed file, we can't skip at this point. + */ + if (rv == COPYFILE_QUIT) + { + s->err = ECANCELED; + error = -1; + goto exit; + } + } + datasize = fgetxattr(s->src_fd, nameptr, NULL, 0, 0, 0); + if (datasize == 0) + goto next; + if (datasize < 0) + { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno); + if (s->statuscb) + { + int rv; + s->xattr_name = strdup(nameptr); + 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) + { + s->err = ECANCELED; + error = -1; + goto exit; + } + } + goto next; + } + if (datasize > XATTR_MAXATTRLEN) + { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("skipping attr \"%s\" (too big)", nameptr); + goto next; + } + databuf = malloc(datasize); + if (databuf == NULL) { + error = -1; + continue; + } + datasize = fgetxattr(s->src_fd, nameptr, databuf, datasize, 0, 0); + if (s->statuscb) { + int rv; + s->xattr_name = strdup(nameptr); + 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) { + s->err = ECANCELED; + error = -1; + goto exit; + } + } } - } - } - entry->length = (u_int32_t)datasize; - entry->offset = filehdr->data_start + filehdr->data_length; + entry->length = (u_int32_t)datasize; + entry->offset = filehdr->data_start + filehdr->data_length; - filehdr->data_length += (u_int32_t)datasize; + filehdr->data_length += (u_int32_t)datasize; #if 0 - /* - * >>> WARNING <<< - * This assumes that the data is fits in memory (not - * the case when there are lots of attributes or one of - * the attributes is very large. - */ - if (entry->offset > ATTR_MAX_SIZE || - (entry->offset + datasize > ATTR_MAX_SIZE)) { - error = 1; - } else { - bcopy(databuf, (char*)filehdr + entry->offset, datasize); - } + /* + * >>> WARNING <<< + * This assumes that the data is fits in memory (not + * the case when there are lots of attributes or one of + * the attributes is very large. + */ + if (entry->offset > ATTR_MAX_SIZE || + (entry->offset + datasize > ATTR_MAX_SIZE)) { + error = 1; + } else { + bcopy(databuf, (char*)filehdr + entry->offset, datasize); + } #else - if (pwrite(s->dst_fd, databuf, datasize, entry->offset) != datasize) { - error = 1; - } + 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); -next: - /* bump to next entry */ - entrylen = ATTR_ENTRY_LENGTH(entry->namelen); - entry = (attr_entry_t *)((char *)entry + entrylen); - } - - /* 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; - - /* Copy Resource Fork. */ - if (hasrsrcfork && (error = copyfile_pack_rsrcfork(s, filehdr))) - goto exit; + free(databuf); - /* Write the header to disk. */ - datasize = filehdr->data_start; + copyfile_debug(3, "copied %ld bytes of \"%s\" data @ offset 0x%08x", datasize, nameptr, entry->offset); + next: + /* bump to next entry */ + entrylen = ATTR_ENTRY_LENGTH(entry->namelen); + entry = (attr_entry_t *)((char *)entry + entrylen); + } - swap_adhdr(&filehdr->appledouble); - swap_attrhdr(filehdr); - swap_attrhdr_entries(filehdr); + /* Now we know where the resource fork data starts. */ + filehdr->appledouble.entries[1].offset = (filehdr->data_start + filehdr->data_length); - if (pwrite(s->dst_fd, filehdr, datasize, 0) != datasize) - { - if (COPYFILE_VERBOSE & s->flags) - copyfile_warn("couldn't write file header"); - error = -1; - goto exit; - } + /* 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; + + /* Copy Resource Fork. */ + if (hasrsrcfork && (error = copyfile_pack_rsrcfork(s, filehdr))) + goto exit; + + /* Write the header to disk. */ + datasize = filehdr->data_start; + + swap_adhdr(&filehdr->appledouble); + swap_attrhdr(filehdr); + swap_attrhdr_entries(filehdr); + + if (pwrite(s->dst_fd, filehdr, datasize, 0) != datasize) + { + if (COPYFILE_VERBOSE & s->flags) + copyfile_warn("couldn't write file header"); + error = -1; + goto exit; + } exit: - if (filehdr) free(filehdr); - if (attrnamebuf) free(attrnamebuf); + if (filehdr) free(filehdr); + if (attrnamebuf) free(attrnamebuf); - if (error) - return error; - else - return copyfile_stat(s); + if (error) + return error; + else + return copyfile_stat(s); } diff --git a/copyfile.h b/copyfile.h index 91dfe87..e071fef 100644 --- a/copyfile.h +++ b/copyfile.h @@ -73,6 +73,7 @@ typedef int (*copyfile_callback_t)(int, int, copyfile_state_t, const char *, con #define COPYFILE_STATE_STATUS_CTX 7 #define COPYFILE_STATE_COPIED 8 #define COPYFILE_STATE_XATTRNAME 9 +#define COPYFILE_STATE_WAS_CLONED 10 #define COPYFILE_DISABLE_VAR "COPYFILE_DISABLE" @@ -100,6 +101,11 @@ typedef int (*copyfile_callback_t)(int, int, copyfile_state_t, const char *, con #define COPYFILE_PACK (1<<22) #define COPYFILE_UNPACK (1<<23) +#define COPYFILE_CLONE (1<<24) +#define COPYFILE_CLONE_FORCE (1<<25) + +#define COPYFILE_RUN_IN_PLACE (1<<26) + #define COPYFILE_VERBOSE (1<<30) #define COPYFILE_RECURSE_ERROR 0 -- 2.47.2