+ copyfile_debug(1, "returning: %d errno %d\n", retval, errno);
+ return retval;
+}
+
+/*
+ * fcopyfile() is used to copy a source file descriptor to a destination file
+ * descriptor. This allows an application to figure out how it wants to open
+ * the files (doing various security checks, perhaps), and then just pass in
+ * the file descriptors.
+ */
+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;
+
+ if (src_fd < 0 || dst_fd < 0)
+ {
+ errno = EINVAL;
+ 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;
+ }
+ if (ret >= 0) {
+ errno = 0;
+ }
+
+ 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(copyfile_state_t state)
+{
+ int ret = 0;
+ // Since we don't allow cloning of directories, we must also forbid
+ // cloning the target of symlinks (since that may be a directory).
+ int cloneFlags = CLONE_NOFOLLOW;
+ struct stat src_sb;
+
+ if (lstat(state->src, &src_sb) != 0)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * 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 (state->flags & COPYFILE_UNLINK)
+ {
+ if (remove(state->dst) < 0 && errno != ENOENT)
+ {
+ return -1;
+ }
+ }
+ ret = clonefileat(AT_FDCWD, state->src, AT_FDCWD, state->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.
+ */
+ state->was_cloned = true;
+
+ /*
+ * COPYFILE_MOVE tells us to attempt removing
+ * the source file after the copy, and to
+ * ignore any errors returned by remove(3).
+ */
+ if (state->flags & COPYFILE_MOVE) {
+ (void)remove(state->src);
+ }
+ }
+ }
+ else
+ {
+ errno = EINVAL;
+ ret = -1;
+ }
+ return ret;
+}
+
+/*
+ * the original copyfile() routine; this copies a source file to a destination
+ * file. Note that because we need to set the names in the state variable, this
+ * is not just the same as opening the two files, and then calling fcopyfile().
+ * Oh, if only life were that simple!
+ */
+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;
+ }
+
+ 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;
+ }
+
+ if (s->flags & (COPYFILE_CLONE_FORCE | COPYFILE_CLONE))
+ {
+ ret = copyfile_clone(s);
+ if (ret == 0) {
+ goto exit;
+ } else if (s->flags & COPYFILE_CLONE_FORCE) {
+ goto error_exit;
+ }
+ // cloning failed. Inherit clonefile flags required for
+ // falling back to copyfile.
+ s->flags |= (COPYFILE_ACL | COPYFILE_EXCL | COPYFILE_NOFOLLOW_SRC |
+ COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA);
+
+ s->flags &= ~COPYFILE_CLONE;
+ flags = s->flags;
+ ret = 0;
+ }
+
+ /*
+ * 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)
+ {
+ /*
+ * 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);
+#ifdef F_SINGLE_WRITER
+ (void)fcntl(s->dst_fd, F_SINGLE_WRITER, 1);
+#endif
+
+ 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);
+ }
+ }
+#endif
+
+ reset_security(s);
+
+ if (s->src && (flags & COPYFILE_MOVE))
+ (void)remove(s->src);
+
+exit:
+ if (ret >= 0) {
+ errno = 0;
+ }
+ copyfile_debug(5, "returning %d errno %d\n", ret, errno);
+
+ if (state == NULL) {
+ int t = errno;
+ copyfile_state_free(s);
+ errno = t;
+ }
+ return ret;
+
+error_exit:
+ ret = -1;
+ if (s && s->err) {
+ errno = s->err;
+ s->err = 0;
+ }
+ goto exit;
+}
+
+/*
+ * Shared prelude to the {f,}copyfile(). This initializes the
+ * state variable, if necessary, and also checks for both debugging
+ * and disabling environment variables.
+ */
+static int copyfile_preamble(copyfile_state_t *state, copyfile_flags_t flags)
+{
+ copyfile_state_t s;
+
+ if (*state == NULL)
+ {
+ if ((*state = copyfile_state_alloc()) == NULL)
+ return -1;
+ }
+
+ s = *state;
+
+ if (COPYFILE_DEBUG & flags)
+ {
+ 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;
+ }
+ 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;
+ }
+#endif
+ copyfile_debug(2, "setting flags: %d", s->flags);
+ s->flags = flags;
+
+ return 0;
+}
+
+/*
+ * The guts of {f,}copyfile().
+ * This looks through the flags in a particular order, and calls the
+ * associated functions.
+ */
+static int copyfile_internal(copyfile_state_t s, copyfile_flags_t flags)
+{
+ 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) unlink(s->dst);
+ goto exit;
+ }
+ 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;
+ uint32_t q_flags;
+
+ /*
+ * 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;
+
+ q_flags = qtn_file_get_flags(s->qinfo);
+ q_flags |= QTN_FLAG_DO_NOT_TRANSLOCATE;
+
+ if (qtn_file_set_flags(s->qinfo, q_flags) != 0) {
+ s->err = errno = EINVAL;
+ goto error_exit;
+ }
+ }
+
+ 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);
+ goto error_exit;
+ }
+ } else {
+ s->err = errno = (qr < 0 ? ENOTSUP : qr);
+ goto error_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;
+ }
+ }
+
+ /*
+ * Similar 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|COPYFILE_DATA_SPARSE) & 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)
+ {
+ 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;
+}
+
+/*
+ * Should we worry if we can't close the source? NFS says we
+ * should, but it's pretty late for us at this point.
+ */
+static int copyfile_close(copyfile_state_t s)
+{
+ 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;
+ }
+
+ return 0;