* 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,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
- *
+ *
* @APPLE_LICENSE_HEADER_END@
*/
#include <membership.h>
#include <fts.h>
#include <libgen.h>
-
+#include <sys/clonefile.h>
#include <System/sys/content_protection.h>
#ifdef VOL_CAP_FMT_DECMPFS_COMPRESSION
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"
*/
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) \
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);
}
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
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;
#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
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) {
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;
}
/*
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;
}
rv = 0;
}
-skipit:
-stopit:
+ skipit:
+ stopit:
s->internal_flags &= ~COPYFILE_MNT_CPROTECT_MASK;
s->internal_flags |= (tstate->internal_flags & COPYFILE_MNT_CPROTECT_MASK);
*/
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;
}
/*
*/
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;
}
/*
*/
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;
}
/*
*/
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;
}
/*
*/
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;
}
/*
*/
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;
}
/*
*/
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;
}
/*
*/
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;
}
*/
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;
}
/*
*/
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;
}
/*
*/
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;
}
*/
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;
}
/*
*/
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;
}
/*
*/
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;
}
/*
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
}
#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
/*
/*
- 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.
+ */
/*
#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
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;
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 */
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;
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];
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 = {
#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)
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
}
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)
*/
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;
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;
}
/*
*/
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);
}