···11.\"
22.\" Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
33.\"
44-.Dd April 27, 2006
44+.Dd July 22, 2019
55.Dt COPYFILE 3
66.Os
77.Sh NAME
···178178file before starting. (This is only applicable for the
179179.Fn copyfile
180180function.)
181181+.It Dv COPYFILE_CLONE_FORCE
182182+Clone the file instead.
183183+This is a force flag i.e. if cloning fails, an error is returned.
184184+This flag is equivalent to (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA
185185+| COPYFILE_NOFOLLOW_SRC).
186186+Note that if cloning is successful, progress callbacks will not be invoked.
187187+Note also that there is no support for cloning directories: if a directory is provided as the source,
188188+an error will be returned. Since this flag implies COPYFILE_NOFOLLOW_SRC, symbolic links themselves will
189189+be cloned instead of their targets.
190190+(This is only applicable for the
191191+.Fn copyfile
192192+function.)
193193+.It Dv COPYFILE_CLONE
194194+Try to clone the file instead.
195195+This is a best try flag i.e. if cloning fails, fallback to copying the file.
196196+This flag is equivalent to (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA
197197+| COPYFILE_NOFOLLOW_SRC).
198198+Note that if cloning is successful, progress callbacks will not be invoked.
199199+Note also that there is no support for cloning directories: if a directory is provided as the source and
200200+COPYFILE_CLONE_FORCE is not passed, this will instead copy the directory. Since this flag implies COPYFILE_NOFOLLOW_SRC,
201201+symbolic links themselves will be cloned instead of their targets. Recursive copying however is
202202+supported, see below for more information.
203203+(This is only applicable for the
204204+.Fn copyfile
205205+function.)
206206+.It Dv COPYFILE_DATA_SPARSE
207207+Copy a file sparsely.
208208+This requires that the source and destination file systems support sparse files with hole sizes
209209+at least as large as their block sizes.
210210+This also requires that the source file is sparse, and for
211211+.Fn fcopyfile
212212+the source file descriptor's offset be a multiple of the minimum hole size.
213213+If COPYFILE_DATA is also specified, this will fall back to a full copy
214214+if sparse copying cannot be performed for any reason; otherwise, an error is returned.
181215.It Dv COPYFILE_NOFOLLOW
182216This is a convenience macro, equivalent to
183183-.Dv (COPYFILE_NOFOLLOW_DST|COPYFILE_NOFOLLOW_SRC) .
217217+.Dv (COPYFILE_NOFOLLOW_DST | COPYFILE_NOFOLLOW_SRC) .
218218+.It Dv COPYFILE_RUN_IN_PLACE
219219+If the src file has quarantine information, add the QTN_FLAG_DO_NOT_TRANSLOCATE flag to the quarantine information of the dst file. This allows a bundle to run in place instead of being translocated.
220220+.It Dv COPYFILE_PRESERVE_DST_TRACKED
221221+Preserve the UF_TRACKED flag at
222222+.Va to
223223+when copying metadata, regardless of whether
224224+.Va from
225225+has it set. This flag is used in conjunction with COPYFILE_STAT, or COPYFILE_CLONE (for its fallback case).
184226.El
227227+.Pp
228228+Copying files into a directory is supported. If
229229+.Va to
230230+is a directory,
231231+.Va from
232232+will be copied into
233233+.Va to
234234+(if
235235+.Va from
236236+is a directory,
237237+copying its contents requires use of the COPYFILE_RECURSIVE parameter,
238238+which is documented below).
185239.Pp
186240The
187241.Fn copyfile_state_get
···264318(Only valid for
265319.Fn copyfile_state_get ;
266320see below for more details about callbacks.)
321321+If a
322322+.Dv COPYFILE_CLONE
323323+or
324324+.Dv COPYFILE_CLONE_FORCE
325325+operation successfully cloned the requested objects, then this value will be 0.
267326The
268327.Va dst
269328parameter is a pointer to
···277336(see below for details). This field cannot be set,
278337and may be
279338.Dv NULL .
339339+.It Dv COPYFILE_STATE_WAS_CLONED
340340+True if a
341341+.Dv COPYFILE_CLONE
342342+or
343343+.Dv COPYFILE_CLONE_FORCE
344344+operation successfully cloned the requested objects.
345345+The
346346+.Va dst
347347+parameter is a pointer to
348348+.Vt bool
349349+(type
350350+.Vt bool\ * ).
280351.El
281352.Sh Recursive Copies
282353When given the
···399470.Dv errno
400471value will be set appropriately.
401472.Pp
473473+Note that recursive cloning is also supported with the
474474+.Dv COPYFILE_CLONE
475475+flag (but not the
476476+.Dv COPYFILE_CLONE_FORCE
477477+flag). A recursive clone operation invokes
478478+.Fn copyfile
479479+with
480480+.Dv COPYFILE_CLONE
481481+on every entry found in the source file-system object. Because
482482+.Fn copyfile
483483+does not allow the cloning of directories, a recursive clone will
484484+instead copy any directory it finds (while cloning its contents).
485485+As symbolic links may point to directories, they are not followed
486486+during recursive clones even if the source is a symbolic link.
487487+Additionally, because the
488488+.Dv COPYFILE_CLONE
489489+flag implies the
490490+.Dv COPYFILE_EXCL
491491+flag, recursive clones require a nonexistent destination.
492492+.Pp
402493The
403494.Dv COPYFILE_PACK ,
404495.Dv COPYFILE_UNPACK ,
···407498.Dv COPYFILE_UNLINK
408499flags are not used during a recursive copy, and will result
409500in an error being returned.
501501+.Pp
502502+Note that if the source path ends in a
503503+.Va /
504504+its contents are copied rather than the directory itself (like cp(1)).
505505+The behavior of a recursive copy on a directory hierarchy also depends
506506+on the contents of the destination. If the destination is a directory,
507507+the source directory (or its contents, if the source path ends in a
508508+.Va /
509509+) will be copied into it. If the destination exists but is not a
510510+directory, and the source is a non-empty directory, the copy will fail;
511511+the exact error set depends on the flags provided to
512512+.Fn copyfile
513513+initially.
410514.Sh Progress Callback
411515In addition to the recursive callbacks described above,
412516.Fn copyfile
···485589.Dv NULL
486590in the case of
487591.Fn fcopyfile .
592592+.Pp
593593+Note that progress callbacks are not invoked when a clone is requested
594594+(e.g.
595595+.Dv COPYFILE_CLONE )
596596+unless the clone cannot be performed and a copy is performed instead.
488597.Sh RETURN VALUES
489598Except when given the
490599.Dv COPYFILE_CHECK
···537646or
538647.Va to
539648parameter to
540540-.Fn copyfile
649649+.Fn fcopyfile
541650was a negative number.
542651.It Bq Er ENOMEM
543652A memory allocation failed.
544653.It Bq Er ENOTSUP
545654The source file was not a directory, symbolic link, or regular file.
655655+.It Bq Er ENOTSUP
656656+COPYFILE_CLONE_FORCE was specified and file cloning is not supported.
657657+.It Bq Er ENOTSUP
658658+COPYFILE_DATA_SPARSE was specified, sparse copying is not supported,
659659+and COPYFILE_DATA was not specified.
546660.It Bq Er ECANCELED
547661The copy was cancelled by callback.
662662+.It Bq Er EEXIST
663663+The
664664+.Va to
665665+parameter to
666666+.Fn copyfile
667667+already existed and was passed in with
668668+.Dv COPYFILE_EXCL .
669669+.It Bq Er ENOENT
670670+The
671671+.Va from
672672+parameter to
673673+.Fn copyfile
674674+did not exist.
675675+.It Bq Er EACCES
676676+Search permission is denied for a component of the path prefix for
677677+the
678678+.Va from
679679+or
680680+.Va to
681681+parameters.
682682+.It Bq Er EACCES
683683+Write permission is denied for a component of the path prefix for the
684684+.Va to
685685+parameter.
548686.El
687687+.Pp
549688In addition, both functions may set
550689.Dv errno
551690via an underlying library or system call.
+3236-2580
src/copyfile/copyfile.c
···11-// Modified by Lubos Dolezel for Darling
21/*
33- * Copyright (c) 2004-2010 Apple, Inc. All rights reserved.
22+ * Copyright (c) 2004-2019 Apple, Inc. All rights reserved.
43 *
54 * @APPLE_LICENSE_HEADER_START@
66- *
55+ *
76 * This file contains Original Code and/or Modifications of Original Code
87 * as defined in and that are subject to the Apple Public Source License
98 * Version 2.0 (the 'License'). You may not use this file except in
109 * compliance with the License. Please obtain a copy of the License at
1110 * http://www.opensource.apple.com/apsl/ and read it before using this
1211 * file.
1313- *
1212+ *
1413 * The Original Code and all software distributed under the License are
1514 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
1615 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
···1817 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
1918 * Please see the License for the specific language governing rights and
2019 * limitations under the License.
2121- *
2020+ *
2221 * @APPLE_LICENSE_HEADER_END@
2322 */
2423···4645#include <membership.h>
4746#include <fts.h>
4847#include <libgen.h>
4848+#include <sys/clonefile.h>
4949+#include <System/sys/content_protection.h>
49505051#ifdef VOL_CAP_FMT_DECMPFS_COMPRESSION
5152# include <Kernel/sys/decmpfs.h>
5253#endif
53545455#include <TargetConditionals.h>
5555-#if !TARGET_OS_IPHONE && !defined(DARLING)
5656+#if !TARGET_OS_IPHONE
5657#include <quarantine.h>
57585859#define XATTR_QUARANTINE_NAME qtn_xattr_name
···6061#define qtn_file_t void *
6162#define QTN_SERIALIZED_DATA_MAX 0
6263static void * qtn_file_alloc(void) { return NULL; }
6363-static int qtn_file_init_with_fd(void *x, int y) { return -1; }
6464-static int qtn_file_init_with_path(void *x, const char *path) { return -1; }
6565-static int qtn_file_init_with_data(void *x, const void *data, size_t len) { return -1; }
6666-static void qtn_file_free(void *x) { return; }
6767-static int qtn_file_apply_to_fd(void *x, int y) { return 0; }
6868-static char *qtn_error(int x) { return NULL; }
6969-static int qtn_file_to_data(void *x, char *y, size_t z) { return -1; }
7070-static void *qtn_file_clone(void *x) { return NULL; }
6464+static int qtn_file_init_with_fd(__unused void *x, __unused int y) { return -1; }
6565+static int qtn_file_init_with_path(__unused void *x, __unused const char *path) { return -1; }
6666+static int qtn_file_init_with_data(__unused void *x, __unused const void *data, __unused size_t len) { return -1; }
6767+static void qtn_file_free(__unused void *x) { return; }
6868+static int qtn_file_apply_to_fd(__unused void *x, __unused int y) { return 0; }
6969+static char *qtn_error(__unused int x) { return NULL; }
7070+static int qtn_file_to_data(__unused void *x, __unused char *y, __unused size_t *z) { return -1; }
7171+static void *qtn_file_clone(__unused void *x) { return NULL; }
7272+static uint32_t qtn_file_get_flags(__unused void *x) { return 0; }
7373+static int qtn_file_set_flags(__unused void *x, __unused uint32_t flags) { return 0; }
7174#define XATTR_QUARANTINE_NAME "figgledidiggledy"
7575+#define QTN_FLAG_DO_NOT_TRANSLOCATE 0
7276#endif /* TARGET_OS_IPHONE */
73777478#include "copyfile.h"
7579#include "copyfile_private.h"
7680#include "xattr_flags.h"
77817878-#ifdef DARLING
7979-# define xattr_preserve_for_intent(x,y) (0)
8080-#endif
8282+#define XATTR_ROOT_INSTALLED_NAME "com.apple.root.installed"
81838284enum cfInternalFlags {
8383- cfDelayAce = 1 << 0,
8484- cfMakeFileInvisible = 1 << 1,
8585- cfSawDecmpEA = 1 << 2,
8585+ cfDelayAce = 1 << 0, /* set if ACE shouldn't be set until post-order traversal */
8686+ cfMakeFileInvisible = 1 << 1, /* set if kFinderInvisibleMask is on src */
8787+ cfSawDecmpEA = 1 << 2, /* set if we've seen a com.apple.decmpfs xattr */
8888+ cfSrcProtSupportValid = 1 << 3, /* set if cfSrcSupportsCProtect is valid */
8989+ cfSrcSupportsCProtect = 1 << 4, /* set if src supports MNT_CPROTECT */
9090+ cfDstProtSupportValid = 1 << 5, /* set if cfDstSupportsCProtect is valid */
9191+ cfDstSupportsCProtect = 1 << 6, /* set if dst supports MNT_CPROTECT */
8692};
87939494+#define COPYFILE_MNT_CPROTECT_MASK (cfSrcProtSupportValid | cfSrcSupportsCProtect | cfDstProtSupportValid | cfDstSupportsCProtect)
9595+8896/*
8997 * The state structure keeps track of
9098 * the source filename, the destination filename, their
9191- * associated file-descriptors, the stat infomration for the
9999+ * associated file-descriptors, the stat information for the
92100 * source file, the security information for the source file,
93101 * the flags passed in for the copy, a pointer to place statistics
94102 * (not currently implemented), debug flags, and a pointer to callbacks
···96104 */
97105struct _copyfile_state
98106{
9999- char *src;
100100- char *dst;
101101- int src_fd;
102102- int dst_fd;
103103- struct stat sb;
104104- filesec_t fsec;
105105- copyfile_flags_t flags;
106106- unsigned int internal_flags;
107107- void *stats;
108108- uint32_t debug;
109109- copyfile_callback_t statuscb;
110110- void *ctx;
111111- qtn_file_t qinfo; /* Quarantine information -- probably NULL */
112112- filesec_t original_fsec;
113113- filesec_t permissive_fsec;
114114- off_t totalCopied;
115115- int err;
116116- char *xattr_name;
117117- xattr_operation_intent_t copyIntent;
107107+ char *src;
108108+ char *dst;
109109+ int src_fd;
110110+ int dst_fd;
111111+ struct stat sb;
112112+ filesec_t fsec;
113113+ copyfile_flags_t flags;
114114+ unsigned int internal_flags;
115115+ void *stats;
116116+ uint32_t debug;
117117+ copyfile_callback_t statuscb;
118118+ void *ctx;
119119+ qtn_file_t qinfo; /* Quarantine information -- probably NULL */
120120+ filesec_t original_fsec;
121121+ filesec_t permissive_fsec;
122122+ off_t totalCopied;
123123+ int err;
124124+ char *xattr_name;
125125+ xattr_operation_intent_t copyIntent;
126126+ bool was_cloned;
118127};
119128129129+#define GET_PROT_CLASS(fd) fcntl((fd), F_GETPROTECTIONCLASS)
130130+#define SET_PROT_CLASS(fd, prot_class) fcntl((fd), F_SETPROTECTIONCLASS, (prot_class))
131131+120132struct acl_entry {
121121- u_int32_t ae_magic;
133133+ u_int32_t ae_magic;
122134#define _ACL_ENTRY_MAGIC 0xac1ac101
123123- u_int32_t ae_tag;
124124- guid_t ae_applicable;
125125- u_int32_t ae_flags;
126126- u_int32_t ae_perms;
135135+ u_int32_t ae_tag;
136136+ guid_t ae_applicable;
137137+ u_int32_t ae_flags;
138138+ u_int32_t ae_perms;
127139};
128140129129-#define PACE(ace) do { \
130130- struct acl_entry *__t = (struct acl_entry*)(ace); \
131131- fprintf(stderr, "%s(%d): " #ace " = { flags = %#x, perms = %#x }\n", __FUNCTION__, __LINE__, __t->ae_flags, __t->ae_perms); \
141141+#define PACE(ace) \
142142+ do { \
143143+ struct acl_entry *__t = (struct acl_entry*)(ace); \
144144+ fprintf(stderr, "%s(%d): " #ace " = { flags = %#x, perms = %#x }\n", __FUNCTION__, __LINE__, __t->ae_flags, __t->ae_perms); \
132145 } while (0)
133146134147#define PACL(ace) \
···144157 ps1 = (struct pm*) p1;
145158 ps2 = (struct pm*) p2;
146159147147- return ((ps1->ap_perms == ps2->ap_perms) ? 1 : 0);
160160+ return ((ps1->ap_perms == ps2->ap_perms) ? 1 : 0);
148161}
149162150163···178191 return 0;
179192}
180193194194+static int
195195+does_copy_protection(int fd)
196196+{
197197+ struct statfs sfs;
198198+199199+ if (fstatfs(fd, &sfs) == -1)
200200+ return -1;
201201+202202+ return ((sfs.f_flags & MNT_CPROTECT) == MNT_CPROTECT);
203203+}
181204182205static void
183206sort_xattrname_list(void *start, size_t length)
···203226 goto done;
204227205228#ifdef DEBUG
206206-{
207207- char *curPtr = start;
208208- while (curPtr < (char*)start + length) {
209209- printf("%s\n", curPtr);
210210- curPtr += strlen(curPtr) + 1;
229229+ {
230230+ char *curPtr = start;
231231+ while (curPtr < (char*)start + length) {
232232+ printf("%s\n", curPtr);
233233+ curPtr += strlen(curPtr) + 1;
234234+ }
211235 }
212212-}
213236#endif
214237215238 tmp = ptrs[indx++] = (char*)start;
216239217217- while (tmp = memchr(tmp, 0, ((char*)start + length) - tmp)) {
240240+ while ((tmp = memchr(tmp, 0, ((char*)start + length) - tmp))) {
218241 if (indx == nel) {
219242 nel += 10;
220243 ptrs = realloc(ptrs, sizeof(char**) * nel);
···289312#define COPYFILE_DEBUG (1<<31)
290313#define COPYFILE_DEBUG_VAR "COPYFILE_DEBUG"
291314315315+// These macros preserve the value of errno.
292316#ifndef _COPYFILE_TEST
293293-# define copyfile_warn(str, ...) syslog(LOG_WARNING, str ": %m", ## __VA_ARGS__)
317317+# define copyfile_warn(str, ...) \
318318+ do { \
319319+ errno_t _errsv = errno; \
320320+ syslog(LOG_WARNING, str ": %m", ## __VA_ARGS__); \
321321+ errno = _errsv; \
322322+ } while (0)
294323# define copyfile_debug(d, str, ...) \
295295- do { \
296296- if (s && (d <= s->debug)) {\
297297- syslog(LOG_DEBUG, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \
298298- } \
299299- } while (0)
324324+ do { \
325325+ if (s && (d <= s->debug)) {\
326326+ errno_t _errsv = errno; \
327327+ syslog(LOG_DEBUG, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \
328328+ errno = _errsv; \
329329+ } \
330330+ } while (0)
300331#else
301332#define copyfile_warn(str, ...) \
302302- fprintf(stderr, "%s:%d:%s() " str ": %s\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__, (errno) ? strerror(errno) : "")
333333+ do { \
334334+ errno_t _errsv = errno; \
335335+ fprintf(stderr, "%s:%d:%s() " str ": %s\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__, (errno) ? strerror(errno) : ""); \
336336+ errno = _errsv; \
337337+ } while (0)
303338# define copyfile_debug(d, str, ...) \
304304- do { \
305305- if (s && (d <= s->debug)) {\
306306- fprintf(stderr, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \
307307- } \
308308- } while(0)
339339+ do { \
340340+ if (s && (d <= s->debug)) {\
341341+ errno_t _errsv = errno; \
342342+ fprintf(stderr, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \
343343+ errno = _errsv; \
344344+ } \
345345+ } while(0)
309346#endif
310347311311-#ifndef DARLING
312348static int copyfile_quarantine(copyfile_state_t s)
313349{
314314- int rv = 0;
315315- if (s->qinfo == NULL)
316316- {
317317- int error;
318318- s->qinfo = qtn_file_alloc();
350350+ int rv = 0;
319351 if (s->qinfo == NULL)
320352 {
321321- rv = -1;
322322- goto done;
353353+ int error;
354354+ s->qinfo = qtn_file_alloc();
355355+ if (s->qinfo == NULL)
356356+ {
357357+ rv = -1;
358358+ goto done;
359359+ }
360360+ if ((error = qtn_file_init_with_fd(s->qinfo, s->src_fd)) != 0)
361361+ {
362362+ qtn_file_free(s->qinfo);
363363+ s->qinfo = NULL;
364364+ rv = -1;
365365+ goto done;
366366+ }
323367 }
324324- if ((error = qtn_file_init_with_fd(s->qinfo, s->src_fd)) != 0)
325325- {
326326- qtn_file_free(s->qinfo);
327327- s->qinfo = NULL;
328328- rv = -1;
329329- goto done;
330330- }
331331- }
332368done:
333333- return rv;
369369+ return rv;
334370}
335335-#else
336336-static int copyfile_quarantine(copyfile_state_t s) { return 0; }
337337-#endif
338371339372static int
340373add_uberace(acl_t *acl)
···500533static void
501534reset_security(copyfile_state_t s)
502535{
503503- /* If we haven't reset the file security information
504504- * (COPYFILE_SECURITY is not set in flags)
505505- * restore back the permissions the file had originally
506506- *
507507- * One of the reasons this seems so complicated is that
508508- * it is partially at odds with copyfile_security().
509509- *
510510- * Simplisticly, we are simply trying to make sure we
511511- * only copy what was requested, and that we don't stomp
512512- * on what wasn't requested.
513513- */
536536+ /* If we haven't reset the file security information
537537+ * (COPYFILE_SECURITY is not set in flags)
538538+ * restore back the permissions the file had originally
539539+ *
540540+ * One of the reasons this seems so complicated is that
541541+ * it is partially at odds with copyfile_security().
542542+ *
543543+ * Simplisticly, we are simply trying to make sure we
544544+ * only copy what was requested, and that we don't stomp
545545+ * on what wasn't requested.
546546+ */
514547515548#ifdef COPYFILE_RECURSIVE
516549 if (s->dst_fd > -1) {
···520553 fstat(s->src_fd, &sbuf);
521554 else
522555 fstat(s->dst_fd, &sbuf);
523523-556556+524557 if (!(s->internal_flags & cfDelayAce))
525558 remove_uberace(s->dst_fd, &sbuf);
526559 }
527560#else
528528- if (s->permissive_fsec && (s->flags & COPYFILE_SECURITY) != COPYFILE_SECURITY) {
529529- if (s->flags & COPYFILE_ACL) {
530530- /* Just need to reset the BSD information -- mode, owner, group */
531531- (void)fchown(s->dst_fd, s->dst_sb.st_uid, s->dst_sb.st_gid);
532532- (void)fchmod(s->dst_fd, s->dst_sb.st_mode);
533533- } else {
534534- /*
535535- * flags is either COPYFILE_STAT, or neither; if it's
536536- * neither, then we restore both ACL and POSIX permissions;
537537- * if it's STAT, however, then we only want to restore the
538538- * ACL (which may be empty). We do that by removing the
539539- * POSIX information from the filesec object.
540540- */
541541- if (s->flags & COPYFILE_STAT) {
542542- copyfile_unset_posix_fsec(s->original_fsec);
561561+ if (s->permissive_fsec && (s->flags & COPYFILE_SECURITY) != COPYFILE_SECURITY) {
562562+ if (s->flags & COPYFILE_ACL) {
563563+ /* Just need to reset the BSD information -- mode, owner, group */
564564+ (void)fchown(s->dst_fd, s->dst_sb.st_uid, s->dst_sb.st_gid);
565565+ (void)fchmod(s->dst_fd, s->dst_sb.st_mode);
566566+ } else {
567567+ /*
568568+ * flags is either COPYFILE_STAT, or neither; if it's
569569+ * neither, then we restore both ACL and POSIX permissions;
570570+ * if it's STAT, however, then we only want to restore the
571571+ * ACL (which may be empty). We do that by removing the
572572+ * POSIX information from the filesec object.
573573+ */
574574+ if (s->flags & COPYFILE_STAT) {
575575+ copyfile_unset_posix_fsec(s->original_fsec);
576576+ }
577577+ if (fchmodx_np(s->dst_fd, s->original_fsec) < 0 && errno != ENOTSUP)
578578+ copyfile_warn("restoring security information");
543579 }
544544- if (fchmodx_np(s->dst_fd, s->original_fsec) < 0 && errno != ENOTSUP)
545545- copyfile_warn("restoring security information");
546580 }
547547- }
548581549549- if (s->permissive_fsec) {
550550- filesec_free(s->permissive_fsec);
551551- s->permissive_fsec = NULL;
552552- }
582582+ if (s->permissive_fsec) {
583583+ filesec_free(s->permissive_fsec);
584584+ s->permissive_fsec = NULL;
585585+ }
553586554554- if (s->original_fsec) {
555555- filesec_free(s->original_fsec);
556556- s->original_fsec = NULL;
557557- }
587587+ if (s->original_fsec) {
588588+ filesec_free(s->original_fsec);
589589+ s->original_fsec = NULL;
590590+ }
558591#endif
559592560560- return;
593593+ return;
561594}
562595563596/*
···575608 *
576609 * copytree() is called from copyfile() -- but copytree() itself then calls
577610 * copyfile() to copy each individual object.
611611+ *
612612+ * If COPYFILE_CLONE is passed, copytree() will clone (instead of copy)
613613+ * regular files and symbolic links found in each directory.
614614+ * Directories will still be copied normally.
578615 *
579616 * XXX - no effort is made to handle overlapping hierarchies at the moment.
580617 *
···601638 const char *paths[2] = { 0 };
602639 unsigned int flags = 0;
603640 int fts_flags = FTS_NOCHDIR;
641641+ dev_t last_dev = s->sb.st_dev;
604642605643 if (s == NULL) {
606644 errno = EINVAL;
607645 retval = -1;
608646 goto done;
609647 }
610610- if (s->flags & (COPYFILE_MOVE | COPYFILE_UNLINK | COPYFILE_CHECK | COPYFILE_PACK | COPYFILE_UNPACK)) {
648648+ if (s->flags & (COPYFILE_MOVE | COPYFILE_UNLINK | COPYFILE_CHECK | COPYFILE_PACK | COPYFILE_UNPACK | COPYFILE_CLONE_FORCE)) {
611649 errno = EINVAL;
612650 retval = -1;
613651 goto done;
614652 }
615653616616- flags = s->flags & (COPYFILE_ALL | COPYFILE_NOFOLLOW | COPYFILE_VERBOSE);
654654+ flags = s->flags & (COPYFILE_ALL | COPYFILE_NOFOLLOW | COPYFILE_VERBOSE | COPYFILE_EXCL | COPYFILE_CLONE | COPYFILE_DATA_SPARSE);
617655618656 paths[0] = src = s->src;
619657 dst = s->dst;
···683721 * 6) src is a directory, dst does not exist
684722 *
685723 * (1) copies src to dst/basename(src).
686686- * (2) fails if COPYFILE_EXCLUSIVE is set, otherwise copies src to dst.
724724+ * (2) fails if COPYFILE_EXCL is set, otherwise copies src to dst.
687725 * (3) and (6) copy src to the name dst.
688726 * (4) copies the contents of src to the contents of dst.
689727 * (5) is an error.
···705743 offset = strlen(src);
706744 }
707745708708- if (s->flags | COPYFILE_NOFOLLOW_SRC)
709709- fts_flags |= FTS_PHYSICAL;
710710- else
711711- fts_flags |= FTS_LOGICAL;
746746+ // COPYFILE_RECURSIVE is always done physically: see 11717978.
747747+ fts_flags |= FTS_PHYSICAL;
748748+ if (!(s->flags & (COPYFILE_NOFOLLOW_SRC|COPYFILE_CLONE))) {
749749+ // Follow 'src', even if it's a symlink, unless instructed not to
750750+ // or we're cloning, where we never follow symlinks.
751751+ fts_flags |= FTS_COMFOLLOW;
752752+ }
712753713754 fts = fts_open((char * const *)paths, fts_flags, NULL);
714755···725766 }
726767 tstate->statuscb = s->statuscb;
727768 tstate->ctx = s->ctx;
769769+ if (last_dev == ftsent->fts_dev) {
770770+ tstate->internal_flags |= (s->internal_flags & COPYFILE_MNT_CPROTECT_MASK);
771771+ } else {
772772+ last_dev = ftsent->fts_dev;
773773+ }
728774 asprintf(&dstfile, "%s%s%s", dst, dstpathsep, ftsent->fts_path + offset);
729775 if (dstfile == NULL) {
730776 copyfile_state_free(tstate);
···733779 break;
734780 }
735781 switch (ftsent->fts_info) {
736736- case FTS_D:
737737- tstate->internal_flags |= cfDelayAce;
738738- cmd = COPYFILE_RECURSE_DIR;
739739- break;
740740- case FTS_SL:
741741- case FTS_SLNONE:
742742- case FTS_DEFAULT:
743743- case FTS_F:
744744- cmd = COPYFILE_RECURSE_FILE;
745745- break;
746746- case FTS_DP:
747747- cmd = COPYFILE_RECURSE_DIR_CLEANUP;
748748- break;
749749- case FTS_DNR:
750750- case FTS_ERR:
751751- case FTS_NS:
752752- case FTS_NSOK:
753753- default:
754754- errno = ftsent->fts_errno;
755755- if (status) {
756756- rv = (*status)(COPYFILE_RECURSE_ERROR, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx);
757757- if (rv == COPYFILE_SKIP || rv == COPYFILE_CONTINUE) {
758758- errno = 0;
759759- goto skipit;
760760- }
761761- if (rv == COPYFILE_QUIT) {
782782+ case FTS_D:
783783+ tstate->internal_flags |= cfDelayAce;
784784+ cmd = COPYFILE_RECURSE_DIR;
785785+ break;
786786+ case FTS_SL:
787787+ case FTS_SLNONE:
788788+ case FTS_DEFAULT:
789789+ case FTS_F:
790790+ cmd = COPYFILE_RECURSE_FILE;
791791+ break;
792792+ case FTS_DP:
793793+ cmd = COPYFILE_RECURSE_DIR_CLEANUP;
794794+ break;
795795+ case FTS_DNR:
796796+ case FTS_ERR:
797797+ case FTS_NS:
798798+ case FTS_NSOK:
799799+ default:
800800+ errno = ftsent->fts_errno;
801801+ if (status) {
802802+ rv = (*status)(COPYFILE_RECURSE_ERROR, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx);
803803+ if (rv == COPYFILE_SKIP || rv == COPYFILE_CONTINUE) {
804804+ errno = 0;
805805+ goto skipit;
806806+ }
807807+ if (rv == COPYFILE_QUIT) {
808808+ retval = -1;
809809+ goto stopit;
810810+ }
811811+ } else {
762812 retval = -1;
763813 goto stopit;
764814 }
765765- } else {
766766- retval = -1;
767767- goto stopit;
768768- }
769769- case FTS_DOT:
770770- goto skipit;
815815+ case FTS_DOT:
816816+ goto skipit;
771817772818 }
773819···790836 goto stopit;
791837 }
792838 }
839839+ // Since we don't support cloning directories this code depends on copyfile()
840840+ // falling back to a regular directory copy.
793841 int tmp_flags = (cmd == COPYFILE_RECURSE_DIR) ? (flags & ~COPYFILE_STAT) : flags;
794842 rv = copyfile(ftsent->fts_path, dstfile, tstate, tmp_flags);
795843 if (rv < 0) {
···853901854902 rv = 0;
855903 }
856856-skipit:
857857-stopit:
904904+ skipit:
905905+ stopit:
906906+ s->internal_flags &= ~COPYFILE_MNT_CPROTECT_MASK;
907907+ s->internal_flags |= (tstate->internal_flags & COPYFILE_MNT_CPROTECT_MASK);
908908+858909 copyfile_state_free(tstate);
859910 free(dstfile);
860911 if (retval == -1)
···865916 if (fts)
866917 fts_close(fts);
867918919919+ copyfile_debug(1, "returning: %d errno %d\n", retval, errno);
868920 return retval;
869921}
870922···876928 */
877929int fcopyfile(int src_fd, int dst_fd, copyfile_state_t state, copyfile_flags_t flags)
878930{
879879- int ret = 0;
880880- copyfile_state_t s = state;
881881- struct stat dst_sb;
931931+ int ret = 0;
932932+ copyfile_state_t s = state;
933933+ struct stat dst_sb;
882934883883- if (src_fd < 0 || dst_fd < 0)
884884- {
885885- errno = EINVAL;
886886- return -1;
887887- }
935935+ if (src_fd < 0 || dst_fd < 0)
936936+ {
937937+ errno = EINVAL;
938938+ return -1;
939939+ }
888940889889- if (copyfile_preamble(&s, flags) < 0)
890890- return -1;
941941+ if (copyfile_preamble(&s, flags) < 0)
942942+ return -1;
891943892892- copyfile_debug(2, "set src_fd <- %d", src_fd);
893893- if (s->src_fd == -2 && src_fd > -1)
894894- {
895895- s->src_fd = src_fd;
896896- if (fstatx_np(s->src_fd, &s->sb, s->fsec) != 0)
944944+ copyfile_debug(2, "set src_fd <- %d", src_fd);
945945+ if (s->src_fd == -2 && src_fd > -1)
897946 {
898898- if (errno == ENOTSUP || errno == EPERM)
899899- fstat(s->src_fd, &s->sb);
900900- else
901901- {
902902- copyfile_warn("fstatx_np on src fd %d", s->src_fd);
903903- return -1;
904904- }
947947+ s->src_fd = src_fd;
948948+ if (fstatx_np(s->src_fd, &s->sb, s->fsec) != 0)
949949+ {
950950+ if (errno == ENOTSUP || errno == EPERM)
951951+ fstat(s->src_fd, &s->sb);
952952+ else
953953+ {
954954+ copyfile_warn("fstatx_np on src fd %d", s->src_fd);
955955+ return -1;
956956+ }
957957+ }
905958 }
906906- }
907959908908- /* prevent copying on unsupported types */
909909- switch (s->sb.st_mode & S_IFMT)
910910- {
911911- case S_IFLNK:
912912- case S_IFDIR:
913913- case S_IFREG:
914914- break;
915915- default:
916916- errno = ENOTSUP;
917917- return -1;
918918- }
960960+ /* prevent copying on unsupported types */
961961+ switch (s->sb.st_mode & S_IFMT)
962962+ {
963963+ case S_IFLNK:
964964+ case S_IFDIR:
965965+ case S_IFREG:
966966+ break;
967967+ default:
968968+ errno = ENOTSUP;
969969+ return -1;
970970+ }
919971920920- copyfile_debug(2, "set dst_fd <- %d", dst_fd);
921921- if (s->dst_fd == -2 && dst_fd > -1)
922922- s->dst_fd = dst_fd;
972972+ copyfile_debug(2, "set dst_fd <- %d", dst_fd);
973973+ if (s->dst_fd == -2 && dst_fd > -1)
974974+ s->dst_fd = dst_fd;
975975+976976+ (void)fstat(s->dst_fd, &dst_sb);
977977+ (void)fchmod(s->dst_fd, (dst_sb.st_mode & ~S_IFMT) | (S_IRUSR | S_IWUSR));
978978+979979+ (void)copyfile_quarantine(s);
923980924924- (void)fstat(s->dst_fd, &dst_sb);
925925- (void)fchmod(s->dst_fd, (dst_sb.st_mode & ~S_IFMT) | (S_IRUSR | S_IWUSR));
981981+ ret = copyfile_internal(s, flags);
926982927927- (void)copyfile_quarantine(s);
983983+ if (ret >= 0 && !(s->flags & COPYFILE_STAT))
984984+ {
985985+ (void)fchmod(s->dst_fd, dst_sb.st_mode & ~S_IFMT);
986986+ }
928987929929- ret = copyfile_internal(s, flags);
988988+ if (s->err) {
989989+ errno = s->err;
990990+ s->err = 0;
991991+ }
992992+ if (state == NULL) {
993993+ int t = errno;
994994+ copyfile_state_free(s);
995995+ errno = t;
996996+ }
997997+ if (ret >= 0) {
998998+ errno = 0;
999999+ }
10001000+10011001+ return ret;
10021002+}
10031003+10041004+/*
10051005+ * This routine implements the clonefileat functionality
10061006+ * for copyfile. There are 2 kinds of clone flags, namely
10071007+ * 1. COPYFILE_CLONE_FORCE which is a 'force' clone flag.
10081008+ * 2. COPYFILE_CLONE which is a 'best try' flag.
10091009+ * In both cases, we inherit the flags provided
10101010+ * to copyfile call and clone the file.
10111011+ * Both these flags are equivalent to
10121012+ * (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA)
10131013+ * With force clone flag set, we return failure if cloning fails,
10141014+ * however, in case of best try flag, we fallback to the copy method.
10151015+ */
9301016931931- if (ret >= 0 && !(s->flags & COPYFILE_STAT))
932932- {
933933- (void)fchmod(s->dst_fd, dst_sb.st_mode & ~S_IFMT);
934934- }
10171017+static int copyfile_clone(copyfile_state_t state)
10181018+{
10191019+ int ret = 0;
10201020+ // Since we don't allow cloning of directories, we must also forbid
10211021+ // cloning the target of symlinks (since that may be a directory).
10221022+ int cloneFlags = CLONE_NOFOLLOW;
10231023+ struct stat src_sb;
9351024936936- if (s->err) {
937937- errno = s->err;
938938- s->err = 0;
939939- }
940940- if (state == NULL) {
941941- int t = errno;
942942- copyfile_state_free(s);
943943- errno = t;
944944- }
10251025+ if (lstat(state->src, &src_sb) != 0)
10261026+ {
10271027+ errno = EINVAL;
10281028+ return -1;
10291029+ }
9451030946946- return ret;
10311031+ /*
10321032+ * Support only for files and symbolic links.
10331033+ * TODO:Remove this check when support for directories is added.
10341034+ */
10351035+ if (S_ISREG(src_sb.st_mode) || S_ISLNK(src_sb.st_mode))
10361036+ {
10371037+ /*
10381038+ * COPYFILE_UNLINK tells us to try removing the destination
10391039+ * before we create it. We don't care if the file doesn't
10401040+ * exist, so we ignore ENOENT.
10411041+ */
10421042+ if (state->flags & COPYFILE_UNLINK)
10431043+ {
10441044+ if (remove(state->dst) < 0 && errno != ENOENT)
10451045+ {
10461046+ return -1;
10471047+ }
10481048+ }
10491049+ ret = clonefileat(AT_FDCWD, state->src, AT_FDCWD, state->dst, cloneFlags);
10501050+ if (ret == 0) {
10511051+ /*
10521052+ * We could also report the size of the single
10531053+ * object that was cloned. However, that's a lot
10541054+ * more difficult when we eventually support
10551055+ * cloning directories. It seems reasonable to NOT
10561056+ * report any bytes being "copied" in this scenario,
10571057+ * and let the caller figure out how they want to
10581058+ * deal.
10591059+ */
10601060+ state->was_cloned = true;
947106110621062+ /*
10631063+ * COPYFILE_MOVE tells us to attempt removing
10641064+ * the source file after the copy, and to
10651065+ * ignore any errors returned by remove(3).
10661066+ */
10671067+ if (state->flags & COPYFILE_MOVE) {
10681068+ (void)remove(state->src);
10691069+ }
10701070+ }
10711071+ }
10721072+ else
10731073+ {
10741074+ errno = EINVAL;
10751075+ ret = -1;
10761076+ }
10771077+ return ret;
9481078}
94910799501080/*
···9551085 */
9561086int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_flags_t flags)
9571087{
958958- int ret = 0;
959959- int createdst = 0;
960960- copyfile_state_t s = state;
961961- struct stat dst_sb;
10881088+ int ret = 0;
10891089+ int createdst = 0;
10901090+ copyfile_state_t s = state;
10911091+ struct stat dst_sb;
9621092963963- if (src == NULL && dst == NULL)
964964- {
965965- errno = EINVAL;
966966- return -1;
967967- }
10931093+ if (src == NULL && dst == NULL)
10941094+ {
10951095+ errno = EINVAL;
10961096+ return -1;
10971097+ }
9681098969969- if (copyfile_preamble(&s, flags) < 0)
970970- {
971971- return -1;
972972- }
10991099+ if (copyfile_preamble(&s, flags) < 0)
11001100+ {
11011101+ return -1;
11021102+ }
9731103974974-/*
975975- * This macro is... well, it's not the worst thing you can do with cpp, not
976976- * by a long shot. Essentially, we are setting the filename (src or dst)
977977- * in the state structure; since the structure may not have been cleared out
978978- * before being used again, we do some of the cleanup here: if the given
979979- * filename (e.g., src) is set, and state->src is not equal to that, then
980980- * we need to check to see if the file descriptor had been opened, and if so,
981981- * close it. After that, we set state->src to be a copy of the given filename,
982982- * releasing the old copy if necessary.
983983- */
11041104+ /*
11051105+ * This macro is... well, it's not the worst thing you can do with cpp, not
11061106+ * by a long shot. Essentially, we are setting the filename (src or dst)
11071107+ * in the state structure; since the structure may not have been cleared out
11081108+ * before being used again, we do some of the cleanup here: if the given
11091109+ * filename (e.g., src) is set, and state->src is not equal to that, then
11101110+ * we need to check to see if the file descriptor had been opened, and if so,
11111111+ * close it. After that, we set state->src to be a copy of the given filename,
11121112+ * releasing the old copy if necessary.
11131113+ */
9841114#define COPYFILE_SET_FNAME(NAME, S) \
985985- do { \
986986- if (NAME != NULL) { \
987987- if (S->NAME != NULL && strncmp(NAME, S->NAME, MAXPATHLEN)) { \
988988- copyfile_debug(2, "replacing string %s (%s) -> (%s)", #NAME, NAME, S->NAME);\
989989- if (S->NAME##_fd != -2 && S->NAME##_fd > -1) { \
990990- copyfile_debug(4, "closing %s fd: %d", #NAME, S->NAME##_fd); \
991991- close(S->NAME##_fd); \
992992- S->NAME##_fd = -2; \
993993- } \
994994- } \
995995- if (S->NAME) { \
996996- free(S->NAME); \
997997- S->NAME = NULL; \
998998- } \
999999- if ((NAME) && (S->NAME = strdup(NAME)) == NULL) \
10001000- return -1; \
10011001- } \
10021002- } while (0)
11151115+ do { \
11161116+ if (NAME != NULL) { \
11171117+ if (S->NAME != NULL && strncmp(NAME, S->NAME, MAXPATHLEN)) { \
11181118+ copyfile_debug(2, "replacing string %s (%s) -> (%s)", #NAME, NAME, S->NAME);\
11191119+ if (S->NAME##_fd != -2 && S->NAME##_fd > -1) { \
11201120+ copyfile_debug(4, "closing %s fd: %d", #NAME, S->NAME##_fd); \
11211121+ close(S->NAME##_fd); \
11221122+ S->NAME##_fd = -2; \
11231123+ } \
11241124+ } \
11251125+ if (S->NAME) { \
11261126+ free(S->NAME); \
11271127+ S->NAME = NULL; \
11281128+ } \
11291129+ if ((NAME) && (S->NAME = strdup(NAME)) == NULL) \
11301130+ return -1; \
11311131+ } \
11321132+ } while (0)
1003113310041004- COPYFILE_SET_FNAME(src, s);
10051005- COPYFILE_SET_FNAME(dst, s);
11341134+ COPYFILE_SET_FNAME(src, s);
11351135+ COPYFILE_SET_FNAME(dst, s);
1006113610071007- if (s->flags & COPYFILE_RECURSIVE) {
10081008- ret = copytree(s);
10091009- goto exit;
10101010- }
11371137+ if (s->flags & COPYFILE_RECURSIVE) {
11381138+ ret = copytree(s);
11391139+ goto exit;
11401140+ }
1011114110121012- /*
10131013- * Get a copy of the source file's security settings
10141014- */
10151015- if (s->original_fsec) {
10161016- filesec_free(s->original_fsec);
10171017- s->original_fsec = NULL;
10181018- }
10191019- if ((s->original_fsec = filesec_init()) == NULL)
10201020- goto error_exit;
11421142+ if (s->flags & (COPYFILE_CLONE_FORCE | COPYFILE_CLONE))
11431143+ {
11441144+ ret = copyfile_clone(s);
11451145+ if (ret == 0) {
11461146+ goto exit;
11471147+ } else if (s->flags & COPYFILE_CLONE_FORCE) {
11481148+ goto error_exit;
11491149+ }
11501150+ // cloning failed. Inherit clonefile flags required for
11511151+ // falling back to copyfile.
11521152+ s->flags |= (COPYFILE_ACL | COPYFILE_EXCL | COPYFILE_NOFOLLOW_SRC |
11531153+ COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA);
1021115410221022- if ((s->flags & COPYFILE_NOFOLLOW_DST) && lstat(s->dst, &dst_sb) == 0 &&
10231023- ((dst_sb.st_mode & S_IFMT) == S_IFLNK)) {
10241024- if (s->permissive_fsec)
10251025- free(s->permissive_fsec);
10261026- s->permissive_fsec = NULL;
10271027- } else if(statx_np(s->dst, &dst_sb, s->original_fsec) == 0)
10281028- {
10291029- /*
10301030- * copyfile_fix_perms() will make a copy of the permission set,
10311031- * and insert at the beginning an ACE that ensures we can write
10321032- * to the file and set attributes.
10331033- */
11551155+ s->flags &= ~COPYFILE_CLONE;
11561156+ flags = s->flags;
11571157+ ret = 0;
11581158+ }
1034115910351035- if((s->permissive_fsec = copyfile_fix_perms(s, &s->original_fsec)) != NULL)
10361036- {
10371037- /*
10381038- * Set the permissions for the destination to our copy.
10391039- * We should get ENOTSUP from any filesystem that simply
10401040- * doesn't support it.
10411041- */
10421042- if (chmodx_np(s->dst, s->permissive_fsec) < 0 && errno != ENOTSUP)
10431043- {
10441044- copyfile_warn("setting security information");
10451045- filesec_free(s->permissive_fsec);
11601160+ /*
11611161+ * Get a copy of the source file's security settings
11621162+ */
11631163+ if (s->original_fsec) {
11641164+ filesec_free(s->original_fsec);
11651165+ s->original_fsec = NULL;
11661166+ }
11671167+ if ((s->original_fsec = filesec_init()) == NULL)
11681168+ goto error_exit;
11691169+11701170+ if ((s->flags & COPYFILE_NOFOLLOW_DST) && lstat(s->dst, &dst_sb) == 0 &&
11711171+ ((dst_sb.st_mode & S_IFMT) == S_IFLNK)) {
11721172+ if (s->permissive_fsec)
11731173+ free(s->permissive_fsec);
10461174 s->permissive_fsec = NULL;
10471047- }
11751175+ } else if(statx_np(s->dst, &dst_sb, s->original_fsec) == 0)
11761176+ {
11771177+ /*
11781178+ * copyfile_fix_perms() will make a copy of the permission set,
11791179+ * and insert at the beginning an ACE that ensures we can write
11801180+ * to the file and set attributes.
11811181+ */
11821182+11831183+ if((s->permissive_fsec = copyfile_fix_perms(s, &s->original_fsec)) != NULL)
11841184+ {
11851185+ /*
11861186+ * Set the permissions for the destination to our copy.
11871187+ * We should get ENOTSUP from any filesystem that simply
11881188+ * doesn't support it.
11891189+ */
11901190+ if (chmodx_np(s->dst, s->permissive_fsec) < 0 && errno != ENOTSUP)
11911191+ {
11921192+ copyfile_warn("setting security information");
11931193+ filesec_free(s->permissive_fsec);
11941194+ s->permissive_fsec = NULL;
11951195+ }
11961196+ }
11971197+ } else if (errno == ENOENT) {
11981198+ createdst = 1;
10481199 }
10491049- } else if (errno == ENOENT) {
10501050- createdst = 1;
10511051- }
1052120010531053- /*
10541054- * If COPYFILE_CHECK is set in flags, then all we are going to do
10551055- * is see what kinds of things WOULD have been copied (see
10561056- * copyfile_check() below). We return that value.
10571057- */
10581058- if (COPYFILE_CHECK & flags)
10591059- {
10601060- ret = copyfile_check(s);
10611061- goto exit;
10621062- } else if ((ret = copyfile_open(s)) < 0)
10631063- goto error_exit;
12011201+ /*
12021202+ * If COPYFILE_CHECK is set in flags, then all we are going to do
12031203+ * is see what kinds of things WOULD have been copied (see
12041204+ * copyfile_check() below). We return that value.
12051205+ */
12061206+ if (COPYFILE_CHECK & flags)
12071207+ {
12081208+ ret = copyfile_check(s);
12091209+ goto exit;
12101210+ } else if ((ret = copyfile_open(s)) < 0)
12111211+ goto error_exit;
1064121210651065- (void)fcntl(s->src_fd, F_NOCACHE, 1);
10661066- (void)fcntl(s->dst_fd, F_NOCACHE, 1);
12131213+ (void)fcntl(s->src_fd, F_NOCACHE, 1);
12141214+ (void)fcntl(s->dst_fd, F_NOCACHE, 1);
10671215#ifdef F_SINGLE_WRITER
10681068- (void)fcntl(s->dst_fd, F_SINGLE_WRITER, 1);
12161216+ (void)fcntl(s->dst_fd, F_SINGLE_WRITER, 1);
10691217#endif
1070121810711071- ret = copyfile_internal(s, flags);
10721072- if (ret == -1)
10731073- goto error_exit;
12191219+ ret = copyfile_internal(s, flags);
12201220+ if (ret == -1)
12211221+ goto error_exit;
1074122210751223#ifdef COPYFILE_RECURSIVE
10761076- if (!(flags & COPYFILE_STAT)) {
10771077- if (!createdst)
10781078- {
10791079- /* Just need to reset the BSD information -- mode, owner, group */
10801080- (void)fchown(s->dst_fd, dst_sb.st_uid, dst_sb.st_gid);
10811081- (void)fchmod(s->dst_fd, dst_sb.st_mode);
12241224+ if (!(flags & COPYFILE_STAT)) {
12251225+ if (!createdst)
12261226+ {
12271227+ /* Just need to reset the BSD information -- mode, owner, group */
12281228+ (void)fchown(s->dst_fd, dst_sb.st_uid, dst_sb.st_gid);
12291229+ (void)fchmod(s->dst_fd, dst_sb.st_mode);
12301230+ }
10821231 }
10831083- }
10841232#endif
1085123310861086- reset_security(s);
12341234+ reset_security(s);
1087123510881088- if (s->src && (flags & COPYFILE_MOVE))
10891089- (void)remove(s->src);
12361236+ if (s->src && (flags & COPYFILE_MOVE))
12371237+ (void)remove(s->src);
1090123810911239exit:
10921092- if (state == NULL) {
10931093- int t = errno;
10941094- copyfile_state_free(s);
10951095- errno = t;
10961096- }
12401240+ if (ret >= 0) {
12411241+ errno = 0;
12421242+ }
12431243+ copyfile_debug(5, "returning %d errno %d\n", ret, errno);
1097124410981098- return ret;
12451245+ if (state == NULL) {
12461246+ int t = errno;
12471247+ copyfile_state_free(s);
12481248+ errno = t;
12491249+ }
12501250+ return ret;
1099125111001252error_exit:
11011101- ret = -1;
11021102- if (s->err) {
11031103- errno = s->err;
11041104- s->err = 0;
11051105- }
11061106- goto exit;
12531253+ ret = -1;
12541254+ if (s && s->err) {
12551255+ errno = s->err;
12561256+ s->err = 0;
12571257+ }
12581258+ goto exit;
11071259}
1108126011091261/*
···11131265 */
11141266static int copyfile_preamble(copyfile_state_t *state, copyfile_flags_t flags)
11151267{
11161116- copyfile_state_t s;
12681268+ copyfile_state_t s;
1117126911181118- if (*state == NULL)
11191119- {
11201120- if ((*state = copyfile_state_alloc()) == NULL)
11211121- return -1;
11221122- }
12701270+ if (*state == NULL)
12711271+ {
12721272+ if ((*state = copyfile_state_alloc()) == NULL)
12731273+ return -1;
12741274+ }
1123127511241124- s = *state;
12761276+ s = *state;
1125127711261126- if (COPYFILE_DEBUG & flags)
11271127- {
11281128- char *e;
11291129- if ((e = getenv(COPYFILE_DEBUG_VAR)))
12781278+ if (COPYFILE_DEBUG & flags)
11301279 {
11311131- errno = 0;
11321132- s->debug = (uint32_t)strtol(e, NULL, 0);
12801280+ char *e;
12811281+ if ((e = getenv(COPYFILE_DEBUG_VAR)))
12821282+ {
12831283+ errno = 0;
12841284+ s->debug = (uint32_t)strtol(e, NULL, 0);
1133128511341134- /* clamp s->debug to 1 if the environment variable is not parsable */
11351135- if (s->debug == 0 && errno != 0)
11361136- s->debug = 1;
12861286+ /* clamp s->debug to 1 if the environment variable is not parsable */
12871287+ if (s->debug == 0 && errno != 0)
12881288+ s->debug = 1;
12891289+ }
12901290+ copyfile_debug(2, "debug value set to: %d", s->debug);
11371291 }
11381138- copyfile_debug(2, "debug value set to: %d", s->debug);
11391139- }
1140129211411293#if 0
11421142- /* Temporarily disabled */
11431143- if (getenv(COPYFILE_DISABLE_VAR) != NULL)
11441144- {
11451145- copyfile_debug(1, "copyfile disabled");
11461146- return 2;
11471147- }
12941294+ /* Temporarily disabled */
12951295+ if (getenv(COPYFILE_DISABLE_VAR) != NULL)
12961296+ {
12971297+ copyfile_debug(1, "copyfile disabled");
12981298+ return 2;
12991299+ }
11481300#endif
11491149- copyfile_debug(2, "setting flags: %d", s->flags);
11501150- s->flags = flags;
13011301+ copyfile_debug(2, "setting flags: %d", s->flags);
13021302+ s->flags = flags;
1151130311521152- return 0;
13041304+ return 0;
11531305}
1154130611551307/*
···11591311 */
11601312static int copyfile_internal(copyfile_state_t s, copyfile_flags_t flags)
11611313{
11621162- int ret = 0;
13141314+ int ret = 0;
1163131511641164- if (s->dst_fd < 0 || s->src_fd < 0)
11651165- {
11661166- copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)", s->src_fd, s->dst_fd);
11671167- s->err = EINVAL;
11681168- return -1;
11691169- }
13161316+ if (s->dst_fd < 0 || s->src_fd < 0)
13171317+ {
13181318+ copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)", s->src_fd, s->dst_fd);
13191319+ s->err = EINVAL;
13201320+ return -1;
13211321+ }
1170132211711171- /*
11721172- * COPYFILE_PACK causes us to create an Apple Double version of the
11731173- * source file, and puts it into the destination file. See
11741174- * copyfile_pack() below for all the gory details.
11751175- */
11761176- if (COPYFILE_PACK & flags)
11771177- {
11781178- if ((ret = copyfile_pack(s)) < 0)
13231323+ /*
13241324+ * COPYFILE_PACK causes us to create an Apple Double version of the
13251325+ * source file, and puts it into the destination file. See
13261326+ * copyfile_pack() below for all the gory details.
13271327+ */
13281328+ if (COPYFILE_PACK & flags)
11791329 {
11801180- if (s->dst) unlink(s->dst);
11811181- goto exit;
13301330+ if ((ret = copyfile_pack(s)) < 0)
13311331+ {
13321332+ if (s->dst) unlink(s->dst);
13331333+ goto exit;
13341334+ }
13351335+ goto exit;
11821336 }
11831183- goto exit;
11841184- }
13371337+13381338+ /*
13391339+ * COPYFILE_UNPACK is the undoing of COPYFILE_PACK, obviously.
13401340+ * The goal there is to take an Apple Double file, and turn it
13411341+ * into a normal file (with data fork, resource fork, modes,
13421342+ * extended attributes, ACLs, etc.).
13431343+ */
13441344+ if (COPYFILE_UNPACK & flags)
13451345+ {
13461346+ if ((ret = copyfile_unpack(s)) < 0)
13471347+ goto error_exit;
13481348+ goto exit;
13491349+ }
13501350+13511351+13521352+13531353+ /*
13541354+ * If we have quarantine info set, we attempt
13551355+ * to apply it to dst_fd. We don't care if
13561356+ * it fails, not yet anyway.
13571357+ */
13581358+ if (s->qinfo)
13591359+ {
13601360+ int qr;
13611361+ uint32_t q_flags;
1185136211861186- /*
11871187- * COPYFILE_UNPACK is the undoing of COPYFILE_PACK, obviously.
11881188- * The goal there is to take an Apple Double file, and turn it
11891189- * into a normal file (with data fork, resource fork, modes,
11901190- * extended attributes, ACLs, etc.).
11911191- */
11921192- if (COPYFILE_UNPACK & flags)
11931193- {
11941194- if ((ret = copyfile_unpack(s)) < 0)
11951195- goto error_exit;
11961196- goto exit;
11971197- }
13631363+ /*
13641364+ * If COPYFILE_RUN_IN_PLACE is set, we need to add
13651365+ * QTN_FLAG_DO_NOT_TRANSLOCATE to the qinfo flags.
13661366+ *
13671367+ * On iOS, qtn_file_get_flags & qtn_file_set_flags
13681368+ * don't modify anything, always return 0, per static
13691369+ * defines at top of this file, though we should never
13701370+ * get here in that case as qinfo will always be NULL.
13711371+ */
13721372+ if (COPYFILE_RUN_IN_PLACE & flags)
13731373+ {
13741374+ q_flags = 0;
1198137511991199- /*
12001200- * If we have quarantine info set, we attempt
12011201- * to apply it to dst_fd. We don't care if
12021202- * it fails, not yet anyway.
12031203- */
12041204- if (s->qinfo) {
12051205- int qr = qtn_file_apply_to_fd(s->qinfo, s->dst_fd);
12061206- if (qr != 0) {
12071207- if (s->statuscb) {
12081208- int rv;
13761376+ q_flags = qtn_file_get_flags(s->qinfo);
13771377+ q_flags |= QTN_FLAG_DO_NOT_TRANSLOCATE;
1209137812101210- s->xattr_name = (char*)XATTR_QUARANTINE_NAME;
12111211- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
12121212- s->xattr_name = NULL;
12131213- if (rv == COPYFILE_QUIT) {
13791379+ if (qtn_file_set_flags(s->qinfo, q_flags) != 0) {
13801380+ s->err = errno = EINVAL;
13811381+ goto error_exit;
13821382+ }
13831383+ }
13841384+13851385+ qr = qtn_file_apply_to_fd(s->qinfo, s->dst_fd);
13861386+ if (qr != 0) {
13871387+ if (s->statuscb) {
13881388+ int rv;
13891389+13901390+ s->xattr_name = (char*)XATTR_QUARANTINE_NAME;
13911391+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
13921392+ s->xattr_name = NULL;
13931393+ if (rv == COPYFILE_QUIT) {
13941394+ s->err = errno = (qr < 0 ? ENOTSUP : qr);
13951395+ goto error_exit;
13961396+ }
13971397+ } else {
12141398 s->err = errno = (qr < 0 ? ENOTSUP : qr);
12151215- ret = -1;
12161216- goto exit;
13991399+ goto error_exit;
12171400 }
12181218- } else {
12191219- s->err = errno = (qr < 0 ? ENOTSUP : qr);
12201220- ret = -1;
12211221- goto exit;
12221401 }
12231402 }
12241224- }
1225140312261226- /*
12271227- * COPYFILE_XATTR tells us to copy the extended attributes;
12281228- * this is seperate from the extended security (aka ACLs),
12291229- * however. If we succeed in this, we continue to the next
12301230- * stage; if we fail, we return with an error value. Note
12311231- * that we fail if the errno is ENOTSUP, but we don't print
12321232- * a warning in that case.
12331233- */
12341234- if (COPYFILE_XATTR & flags)
12351235- {
12361236- if ((ret = copyfile_xattr(s)) < 0)
14041404+ /*
14051405+ * COPYFILE_XATTR tells us to copy the extended attributes;
14061406+ * this is seperate from the extended security (aka ACLs),
14071407+ * however. If we succeed in this, we continue to the next
14081408+ * stage; if we fail, we return with an error value. Note
14091409+ * that we fail if the errno is ENOTSUP, but we don't print
14101410+ * a warning in that case.
14111411+ */
14121412+ if (COPYFILE_XATTR & flags)
12371413 {
12381238- if (errno != ENOTSUP && errno != EPERM)
12391239- copyfile_warn("error processing extended attributes");
12401240- goto exit;
14141414+ if ((ret = copyfile_xattr(s)) < 0)
14151415+ {
14161416+ if (errno != ENOTSUP && errno != EPERM)
14171417+ copyfile_warn("error processing extended attributes");
14181418+ goto exit;
14191419+ }
12411420 }
12421242- }
1243142112441244- /*
12451245- * Simialr to above, this tells us whether or not to copy
12461246- * the non-meta data portion of the file. We attempt to
12471247- * remove (via unlink) the destination file if we fail.
12481248- */
12491249- if (COPYFILE_DATA & flags)
12501250- {
12511251- if ((ret = copyfile_data(s)) < 0)
14221422+ /*
14231423+ * Similar to above, this tells us whether or not to copy
14241424+ * the non-meta data portion of the file. We attempt to
14251425+ * remove (via unlink) the destination file if we fail.
14261426+ */
14271427+ if ((COPYFILE_DATA|COPYFILE_DATA_SPARSE) & flags)
12521428 {
12531253- copyfile_warn("error processing data");
12541254- if (s->dst && unlink(s->dst))
12551255- copyfile_warn("%s: remove", s->src ? s->src : "(null src)");
12561256- goto exit;
14291429+ if ((ret = copyfile_data(s)) < 0)
14301430+ {
14311431+ copyfile_warn("error processing data");
14321432+ if (s->dst && unlink(s->dst))
14331433+ copyfile_warn("%s: remove", s->src ? s->src : "(null src)");
14341434+ goto exit;
14351435+ }
12571436 }
12581258- }
1259143712601260- /*
12611261- * COPYFILE_SECURITY requests that we copy the security, both
12621262- * extended and mundane (that is, ACLs and POSIX).
12631263- */
12641264- if (COPYFILE_SECURITY & flags)
12651265- {
12661266- if ((ret = copyfile_security(s)) < 0)
14381438+ /*
14391439+ * COPYFILE_SECURITY requests that we copy the security, both
14401440+ * extended and mundane (that is, ACLs and POSIX).
14411441+ */
14421442+ if (COPYFILE_SECURITY & flags)
12671443 {
12681268- copyfile_warn("error processing security information");
12691269- goto exit;
14441444+ if ((ret = copyfile_security(s)) < 0)
14451445+ {
14461446+ copyfile_warn("error processing security information");
14471447+ goto exit;
14481448+ }
12701449 }
12711271- }
1272145012731273- if (COPYFILE_STAT & flags)
12741274- {
12751275- if ((ret = copyfile_stat(s)) < 0)
14511451+ if (COPYFILE_STAT & flags)
12761452 {
12771277- copyfile_warn("error processing POSIX information");
12781278- goto exit;
14531453+ if ((ret = copyfile_stat(s)) < 0)
14541454+ {
14551455+ copyfile_warn("error processing POSIX information");
14561456+ goto exit;
14571457+ }
12791458 }
12801280- }
1281145912821460exit:
12831283- return ret;
14611461+ return ret;
1284146212851463error_exit:
12861286- ret = -1;
12871287- goto exit;
14641464+ ret = -1;
14651465+ goto exit;
12881466}
1289146712901468/*
···12921470 */
12931471copyfile_state_t copyfile_state_alloc(void)
12941472{
12951295- copyfile_state_t s = (copyfile_state_t) calloc(1, sizeof(struct _copyfile_state));
14731473+ copyfile_state_t s = (copyfile_state_t) calloc(1, sizeof(struct _copyfile_state));
1296147412971297- if (s != NULL)
12981298- {
12991299- s->src_fd = -2;
13001300- s->dst_fd = -2;
13011301- if (s->fsec) {
13021302- filesec_free(s->fsec);
13031303- s->fsec = NULL;
13041304- }
13051305- s->fsec = filesec_init();
13061306- } else
13071307- errno = ENOMEM;
14751475+ if (s != NULL)
14761476+ {
14771477+ s->src_fd = -2;
14781478+ s->dst_fd = -2;
14791479+ if (s->fsec) {
14801480+ filesec_free(s->fsec);
14811481+ s->fsec = NULL;
14821482+ }
14831483+ s->fsec = filesec_init();
14841484+ } else
14851485+ errno = ENOMEM;
1308148613091309- return s;
14871487+ return s;
13101488}
1311148913121490/*
···13151493 */
13161494int copyfile_state_free(copyfile_state_t s)
13171495{
13181318- if (s != NULL)
13191319- {
13201320- if (s->fsec)
13211321- filesec_free(s->fsec);
14961496+ if (s != NULL)
14971497+ {
14981498+ if (s->fsec)
14991499+ filesec_free(s->fsec);
1322150013231323- if (s->original_fsec)
13241324- filesec_free(s->original_fsec);
15011501+ if (s->original_fsec)
15021502+ filesec_free(s->original_fsec);
1325150313261326- if (s->permissive_fsec)
13271327- filesec_free(s->permissive_fsec);
15041504+ if (s->permissive_fsec)
15051505+ filesec_free(s->permissive_fsec);
1328150613291329- if (s->qinfo)
13301330- qtn_file_free(s->qinfo);
15071507+ if (s->qinfo)
15081508+ qtn_file_free(s->qinfo);
1331150913321332- if (copyfile_close(s) < 0)
13331333- {
13341334- copyfile_warn("error closing files");
13351335- return -1;
15101510+ if (copyfile_close(s) < 0)
15111511+ {
15121512+ copyfile_warn("error closing files");
15131513+ return -1;
15141514+ }
15151515+ if (s->xattr_name)
15161516+ free(s->xattr_name);
15171517+ if (s->dst)
15181518+ free(s->dst);
15191519+ if (s->src)
15201520+ free(s->src);
15211521+ free(s);
13361522 }
13371337- if (s->xattr_name)
13381338- free(s->xattr_name);
13391339- if (s->dst)
13401340- free(s->dst);
13411341- if (s->src)
13421342- free(s->src);
13431343- free(s);
13441344- }
13451345- return 0;
15231523+ return 0;
13461524}
1347152513481526/*
···13511529 */
13521530static int copyfile_close(copyfile_state_t s)
13531531{
13541354- if (s->src && s->src_fd >= 0)
13551355- close(s->src_fd);
15321532+ if (s->src && s->src_fd >= 0)
15331533+ close(s->src_fd);
1356153413571357- if (s->dst && s->dst_fd >= 0) {
13581358- if (close(s->dst_fd))
13591359- return -1;
13601360- }
15351535+ if (s->dst && s->dst_fd >= 0) {
15361536+ if (close(s->dst_fd))
15371537+ return -1;
15381538+ }
1361153913621362- return 0;
15401540+ return 0;
13631541}
1364154213651543/*
···13721550 */
13731551static filesec_t copyfile_fix_perms(copyfile_state_t s __unused, filesec_t *fsec)
13741552{
13751375- filesec_t ret_fsec = NULL;
13761376- mode_t mode;
13771377- acl_t acl = NULL;
15531553+ filesec_t ret_fsec = NULL;
15541554+ mode_t mode;
15551555+ acl_t acl = NULL;
1378155613791379- if ((ret_fsec = filesec_dup(*fsec)) == NULL)
13801380- goto error_exit;
15571557+ if ((ret_fsec = filesec_dup(*fsec)) == NULL)
15581558+ goto error_exit;
1381155913821382- if (filesec_get_property(ret_fsec, FILESEC_ACL, &acl) == 0)
13831383- {
15601560+ if (filesec_get_property(ret_fsec, FILESEC_ACL, &acl) == 0)
15611561+ {
13841562#ifdef COPYFILE_RECURSIVE
13851385- if (add_uberace(&acl))
13861386- goto error_exit;
15631563+ if (add_uberace(&acl))
15641564+ goto error_exit;
13871565#else
13881388- acl_entry_t entry;
13891389- acl_permset_t permset;
13901390- uuid_t qual;
15661566+ acl_entry_t entry;
15671567+ acl_permset_t permset;
15681568+ uuid_t qual;
1391156913921392- if (mbr_uid_to_uuid(getuid(), qual) != 0)
13931393- goto error_exit;
15701570+ if (mbr_uid_to_uuid(getuid(), qual) != 0)
15711571+ goto error_exit;
1394157213951395- /*
13961396- * First, we create an entry, and give it the special name
13971397- * of ACL_FIRST_ENTRY, thus guaranteeing it will be first.
13981398- * After that, we clear out all the permissions in it, and
13991399- * add three permissions: WRITE_DATA, WRITE_ATTRIBUTES, and
14001400- * WRITE_EXTATTRIBUTES. We put these into an ACE that allows
14011401- * the functionality, and put this into the ACL.
14021402- */
14031403- if (acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY) == -1)
14041404- goto error_exit;
14051405- if (acl_get_permset(entry, &permset) == -1)
14061406- goto error_exit;
14071407- if (acl_clear_perms(permset) == -1)
14081408- goto error_exit;
14091409- if (acl_add_perm(permset, ACL_WRITE_DATA) == -1)
14101410- goto error_exit;
14111411- if (acl_add_perm(permset, ACL_WRITE_ATTRIBUTES) == -1)
14121412- goto error_exit;
14131413- if (acl_add_perm(permset, ACL_WRITE_EXTATTRIBUTES) == -1)
14141414- goto error_exit;
14151415- if (acl_set_tag_type(entry, ACL_EXTENDED_ALLOW) == -1)
14161416- goto error_exit;
15731573+ /*
15741574+ * First, we create an entry, and give it the special name
15751575+ * of ACL_FIRST_ENTRY, thus guaranteeing it will be first.
15761576+ * After that, we clear out all the permissions in it, and
15771577+ * add three permissions: WRITE_DATA, WRITE_ATTRIBUTES, and
15781578+ * WRITE_EXTATTRIBUTES. We put these into an ACE that allows
15791579+ * the functionality, and put this into the ACL.
15801580+ */
15811581+ if (acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY) == -1)
15821582+ goto error_exit;
15831583+ if (acl_get_permset(entry, &permset) == -1)
15841584+ goto error_exit;
15851585+ if (acl_clear_perms(permset) == -1)
15861586+ goto error_exit;
15871587+ if (acl_add_perm(permset, ACL_WRITE_DATA) == -1)
15881588+ goto error_exit;
15891589+ if (acl_add_perm(permset, ACL_WRITE_ATTRIBUTES) == -1)
15901590+ goto error_exit;
15911591+ if (acl_add_perm(permset, ACL_WRITE_EXTATTRIBUTES) == -1)
15921592+ goto error_exit;
15931593+ if (acl_set_tag_type(entry, ACL_EXTENDED_ALLOW) == -1)
15941594+ goto error_exit;
1417159514181418- if(acl_set_permset(entry, permset) == -1)
14191419- goto error_exit;
14201420- if(acl_set_qualifier(entry, qual) == -1)
14211421- goto error_exit;
15961596+ if(acl_set_permset(entry, permset) == -1)
15971597+ goto error_exit;
15981598+ if(acl_set_qualifier(entry, qual) == -1)
15991599+ goto error_exit;
14221600#endif
1423160114241424- if (filesec_set_property(ret_fsec, FILESEC_ACL, &acl) != 0)
14251425- goto error_exit;
14261426- }
16021602+ if (filesec_set_property(ret_fsec, FILESEC_ACL, &acl) != 0)
16031603+ goto error_exit;
16041604+ }
1427160514281428- /*
14291429- * This is for the normal, mundane, POSIX permission model.
14301430- * We make sure that we can write to the file.
14311431- */
14321432- if (filesec_get_property(ret_fsec, FILESEC_MODE, &mode) == 0)
14331433- {
14341434- if ((mode & (S_IWUSR | S_IRUSR)) != (S_IWUSR | S_IRUSR))
16061606+ /*
16071607+ * This is for the normal, mundane, POSIX permission model.
16081608+ * We make sure that we can write to the file.
16091609+ */
16101610+ if (filesec_get_property(ret_fsec, FILESEC_MODE, &mode) == 0)
14351611 {
14361436- mode |= S_IWUSR|S_IRUSR;
14371437- if (filesec_set_property(ret_fsec, FILESEC_MODE, &mode) != 0)
14381438- goto error_exit;
16121612+ if ((mode & (S_IWUSR | S_IRUSR)) != (S_IWUSR | S_IRUSR))
16131613+ {
16141614+ mode |= S_IWUSR|S_IRUSR;
16151615+ if (filesec_set_property(ret_fsec, FILESEC_MODE, &mode) != 0)
16161616+ goto error_exit;
16171617+ }
14391618 }
14401440- }
1441161914421620exit:
14431443- if (acl)
14441444- acl_free(acl);
16211621+ if (acl)
16221622+ acl_free(acl);
1445162314461446- return ret_fsec;
16241624+ return ret_fsec;
1447162514481626error_exit:
14491449- if (ret_fsec)
14501450- {
14511451- filesec_free(ret_fsec);
14521452- ret_fsec = NULL;
14531453- }
14541454- goto exit;
16271627+ if (ret_fsec)
16281628+ {
16291629+ filesec_free(ret_fsec);
16301630+ ret_fsec = NULL;
16311631+ }
16321632+ goto exit;
14551633}
1456163414571635/*
···14731651 */
14741652static int copyfile_unset_acl(copyfile_state_t s)
14751653{
14761476- int ret = 0;
14771477- if (filesec_set_property(s->fsec, FILESEC_ACL, NULL) == -1)
14781478- {
14791479- copyfile_debug(5, "unsetting acl attribute on %s", s->dst ? s->dst : "(null dst)");
14801480- ++ret;
14811481- }
14821482- if (filesec_set_property(s->fsec, FILESEC_UUID, NULL) == -1)
14831483- {
14841484- copyfile_debug(5, "unsetting uuid attribute on %s", s->dst ? s->dst : "(null dst)");
14851485- ++ret;
14861486- }
14871487- if (filesec_set_property(s->fsec, FILESEC_GRPUUID, NULL) == -1)
14881488- {
14891489- copyfile_debug(5, "unsetting group uuid attribute on %s", s->dst ? s->dst : "(null dst)");
14901490- ++ret;
14911491- }
14921492- return ret;
16541654+ int ret = 0;
16551655+ if (filesec_set_property(s->fsec, FILESEC_ACL, NULL) == -1)
16561656+ {
16571657+ copyfile_debug(5, "unsetting acl attribute on %s", s->dst ? s->dst : "(null dst)");
16581658+ ++ret;
16591659+ }
16601660+ if (filesec_set_property(s->fsec, FILESEC_UUID, NULL) == -1)
16611661+ {
16621662+ copyfile_debug(5, "unsetting uuid attribute on %s", s->dst ? s->dst : "(null dst)");
16631663+ ++ret;
16641664+ }
16651665+ if (filesec_set_property(s->fsec, FILESEC_GRPUUID, NULL) == -1)
16661666+ {
16671667+ copyfile_debug(5, "unsetting group uuid attribute on %s", s->dst ? s->dst : "(null dst)");
16681668+ ++ret;
16691669+ }
16701670+ return ret;
14931671}
1494167214951673/*
···15001678 */
15011679static int copyfile_open(copyfile_state_t s)
15021680{
15031503- int oflags = O_EXCL | O_CREAT | O_WRONLY;
15041504- int islnk = 0, isdir = 0;
15051505- int osrc = 0, dsrc = 0;
16811681+ int oflags = O_EXCL | O_CREAT | O_WRONLY;
16821682+ int islnk = 0, isdir = 0, isreg = 0;
16831683+ int osrc = 0, dsrc = 0;
16841684+ int prot_class = PROTECTION_CLASS_DEFAULT;
16851685+ int set_cprot_explicit = 0;
16861686+ int error = 0;
1506168715071507- if (s->src && s->src_fd == -2)
15081508- {
15091509- if ((COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np)
15101510- (s->src, &s->sb, s->fsec))
16881688+ if (s->src && s->src_fd == -2)
15111689 {
15121512- copyfile_warn("stat on %s", s->src);
15131513- return -1;
15141514- }
15151515-15161516- /* prevent copying on unsupported types */
15171517- switch (s->sb.st_mode & S_IFMT)
15181518- {
15191519- case S_IFLNK:
15201520- islnk = 1;
15211521- if ((size_t)s->sb.st_size > SIZE_T_MAX) {
15221522- s->err = ENOMEM; /* too big for us to copy */
16901690+ if ((COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np)
16911691+ (s->src, &s->sb, s->fsec))
16921692+ {
16931693+ copyfile_warn("stat on %s", s->src);
15231694 return -1;
15241695 }
15251525- osrc = O_SYMLINK;
15261526- break;
15271527- case S_IFDIR:
15281528- isdir = 1;
15291529- break;
15301530- case S_IFREG:
15311531- break;
15321532- default:
15331533- if (!(strcmp(s->src, "/dev/null") == 0 && (s->flags & COPYFILE_METADATA))) {
15341534- s->err = ENOTSUP;
15351535- return -1;
16961696+16971697+ /* prevent copying on unsupported types */
16981698+ switch (s->sb.st_mode & S_IFMT)
16991699+ {
17001700+ case S_IFLNK:
17011701+ islnk = 1;
17021702+ if ((size_t)s->sb.st_size > SIZE_T_MAX) {
17031703+ s->err = ENOMEM; /* too big for us to copy */
17041704+ return -1;
17051705+ }
17061706+ osrc = O_SYMLINK;
17071707+ break;
17081708+ case S_IFDIR:
17091709+ isdir = 1;
17101710+ break;
17111711+ case S_IFREG:
17121712+ isreg = 1;
17131713+ break;
17141714+ default:
17151715+ if (!(strcmp(s->src, "/dev/null") == 0 && (s->flags & COPYFILE_METADATA))) {
17161716+ s->err = ENOTSUP;
17171717+ return -1;
17181718+ }
15361719 }
15371537- }
15381538- /*
15391539- * If we're packing, then we are actually
15401540- * creating a file, no matter what the source
15411541- * was.
15421542- */
15431543- if (s->flags & COPYFILE_PACK) {
15441720 /*
15451545- * O_SYMLINK and O_NOFOLLOW are not compatible options:
15461546- * if the file is a symlink, and O_NOFOLLOW is specified,
15471547- * open will return ELOOP, whether or not O_SYMLINK is set.
15481548- * However, we know whether or not it was a symlink from
15491549- * the stat above (although there is a potentiaal for a race
15501550- * condition here, but it will err on the side of returning
15511551- * ELOOP from open).
17211721+ * If we're packing, then we are actually
17221722+ * creating a file, no matter what the source
17231723+ * was.
15521724 */
15531553- if (!islnk)
15541554- osrc = (s->flags & COPYFILE_NOFOLLOW_SRC) ? O_NOFOLLOW : 0;
15551555- isdir = islnk = 0;
15561556- }
17251725+ if (s->flags & COPYFILE_PACK) {
17261726+ /*
17271727+ * O_SYMLINK and O_NOFOLLOW are not compatible options:
17281728+ * if the file is a symlink, and O_NOFOLLOW is specified,
17291729+ * open will return ELOOP, whether or not O_SYMLINK is set.
17301730+ * However, we know whether or not it was a symlink from
17311731+ * the stat above (although there is a potentiaal for a race
17321732+ * condition here, but it will err on the side of returning
17331733+ * ELOOP from open).
17341734+ */
17351735+ if (!islnk)
17361736+ osrc = (s->flags & COPYFILE_NOFOLLOW_SRC) ? O_NOFOLLOW : 0;
17371737+ isdir = islnk = 0;
17381738+ }
1557173915581558- if ((s->src_fd = open(s->src, O_RDONLY | osrc , 0)) < 0)
15591559- {
15601560- copyfile_warn("open on %s", s->src);
15611561- return -1;
15621562- } else
15631563- copyfile_debug(2, "open successful on source (%s)", s->src);
17401740+ if ((s->src_fd = open(s->src, O_RDONLY | osrc , 0)) < 0)
17411741+ {
17421742+ copyfile_warn("open on %s", s->src);
17431743+ return -1;
17441744+ }
17451745+ copyfile_debug(2, "open successful on source (%s)", s->src);
1564174615651565- (void)copyfile_quarantine(s);
15661566- }
17471747+ (void)copyfile_quarantine(s);
17481748+ }
1567174915681568- if (s->dst && s->dst_fd == -2)
15691569- {
15701570- /*
15711571- * COPYFILE_UNLINK tells us to try removing the destination
15721572- * before we create it. We don't care if the file doesn't
15731573- * exist, so we ignore ENOENT.
15741574- */
15751575- if (COPYFILE_UNLINK & s->flags)
17501750+ if (s->dst && s->dst_fd == -2)
15761751 {
15771577- if (remove(s->dst) < 0 && errno != ENOENT)
15781578- {
15791579- copyfile_warn("%s: remove", s->dst);
15801580- return -1;
15811581- }
15821582- }
17521752+ /*
17531753+ * COPYFILE_UNLINK tells us to try removing the destination
17541754+ * before we create it. We don't care if the file doesn't
17551755+ * exist, so we ignore ENOENT.
17561756+ */
17571757+ if (COPYFILE_UNLINK & s->flags)
17581758+ {
17591759+ if (remove(s->dst) < 0 && errno != ENOENT)
17601760+ {
17611761+ copyfile_warn("%s: remove", s->dst);
17621762+ return -1;
17631763+ }
17641764+ }
1583176515841584- if (s->flags & COPYFILE_NOFOLLOW_DST) {
15851585- struct stat st;
17661766+ if (s->flags & COPYFILE_NOFOLLOW_DST) {
17671767+ struct stat st;
1586176815871587- dsrc = O_NOFOLLOW;
15881588- if (lstat(s->dst, &st) != -1) {
15891589- if ((st.st_mode & S_IFMT) == S_IFLNK)
15901590- dsrc = O_SYMLINK;
17691769+ dsrc = O_NOFOLLOW;
17701770+ if (lstat(s->dst, &st) != -1) {
17711771+ if ((st.st_mode & S_IFMT) == S_IFLNK)
17721772+ dsrc = O_SYMLINK;
17731773+ }
15911774 }
15921592- }
1593177515941594- if (islnk) {
15951595- size_t sz = (size_t)s->sb.st_size + 1;
15961596- char *bp;
15971597-15981598- bp = calloc(1, sz);
15991599- if (bp == NULL) {
16001600- copyfile_warn("cannot allocate %zd bytes", sz);
16011601- return -1;
17761776+ if (!(s->internal_flags & cfSrcProtSupportValid))
17771777+ {
17781778+ if ((error = does_copy_protection(s->src_fd)) > 0)
17791779+ {
17801780+ s->internal_flags |= cfSrcSupportsCProtect;
17811781+ }
17821782+ else if (error < 0)
17831783+ {
17841784+ copyfile_warn("does_copy_protection failed on (%s) with error <%d>", s->src, errno);
17851785+ return -1;
17861786+ }
17871787+ s->internal_flags |= cfSrcProtSupportValid;
16021788 }
16031603- if (readlink(s->src, bp, sz-1) == -1) {
16041604- copyfile_warn("cannot readlink %s", s->src);
16051605- free(bp);
16061606- return -1;
17891789+17901790+ /* copy protection is only valid for regular files and directories. */
17911791+ if ((isreg || isdir) && (s->internal_flags & cfSrcSupportsCProtect))
17921792+ {
17931793+ prot_class = GET_PROT_CLASS(s->src_fd);
17941794+ if (prot_class < 0)
17951795+ {
17961796+ copyfile_warn("GET_PROT_CLASS failed on (%s) with error <%d>", s->src, errno);
17971797+ return -1;
17981798+ }
16071799 }
16081608- if (symlink(bp, s->dst) == -1) {
16091609- if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) {
16101610- copyfile_warn("Cannot make symlink %s", s->dst);
18001800+18011801+ if (islnk) {
18021802+ size_t sz = (size_t)s->sb.st_size + 1;
18031803+ char *bp;
18041804+18051805+ bp = calloc(1, sz);
18061806+ if (bp == NULL) {
18071807+ copyfile_warn("cannot allocate %zd bytes", sz);
18081808+ return -1;
18091809+ }
18101810+ if (readlink(s->src, bp, sz-1) == -1) {
18111811+ copyfile_warn("cannot readlink %s", s->src);
16111812 free(bp);
16121813 return -1;
16131814 }
16141614- }
16151615- free(bp);
16161616- s->dst_fd = open(s->dst, O_RDONLY | O_SYMLINK);
16171617- if (s->dst_fd == -1) {
16181618- copyfile_warn("Cannot open symlink %s for reading", s->dst);
16191619- return -1;
16201620- }
16211621- } else if (isdir) {
16221622- mode_t mode;
16231623- mode = (s->sb.st_mode & ~S_IFMT) | S_IRWXU;
18151815+ if (symlink(bp, s->dst) == -1) {
18161816+ if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) {
18171817+ copyfile_warn("Cannot make symlink %s", s->dst);
18181818+ free(bp);
18191819+ return -1;
18201820+ }
18211821+ }
18221822+ free(bp);
18231823+ s->dst_fd = open(s->dst, O_RDONLY | O_SYMLINK);
18241824+ if (s->dst_fd == -1) {
18251825+ copyfile_warn("Cannot open symlink %s for reading", s->dst);
18261826+ return -1;
18271827+ }
18281828+ } else if (isdir) {
18291829+ mode_t mode;
18301830+ mode = (s->sb.st_mode & ~S_IFMT) | S_IRWXU;
1624183116251625- if (mkdir(s->dst, mode) == -1) {
16261626- if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) {
16271627- copyfile_warn("Cannot make directory %s", s->dst);
18321832+ if (mkdir(s->dst, mode) == -1) {
18331833+ if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) {
18341834+ copyfile_warn("Cannot make directory %s", s->dst);
18351835+ return -1;
18361836+ }
18371837+ }
18381838+ s->dst_fd = open(s->dst, O_RDONLY | dsrc);
18391839+ if (s->dst_fd == -1) {
18401840+ copyfile_warn("Cannot open directory %s for reading", s->dst);
16281841 return -1;
16291842 }
16301630- }
16311631- s->dst_fd = open(s->dst, O_RDONLY | dsrc);
16321632- if (s->dst_fd == -1) {
16331633- copyfile_warn("Cannot open directory %s for reading", s->dst);
16341634- return -1;
16351635- }
16361636- } else while((s->dst_fd = open(s->dst, oflags | dsrc, s->sb.st_mode | S_IWUSR)) < 0)
16371637- {
16381638- /*
16391639- * We set S_IWUSR because fsetxattr does not -- at the time this comment
16401640- * was written -- allow one to set an extended attribute on a file descriptor
16411641- * for a read-only file, even if the file descriptor is opened for writing.
16421642- * This will only matter if the file does not already exist.
16431643- */
16441644- switch(errno)
16451645- {
16461646- case EEXIST:
16471647- copyfile_debug(3, "open failed, retrying (%s)", s->dst);
16481648- if (s->flags & COPYFILE_EXCL)
16491649- break;
16501650- oflags = oflags & ~O_CREAT;
16511651- if (s->flags & (COPYFILE_PACK | COPYFILE_DATA))
16521652- {
16531653- copyfile_debug(4, "truncating existing file (%s)", s->dst);
16541654- oflags |= O_TRUNC;
16551655- }
16561656- continue;
16571657- case EACCES:
16581658- if(chmod(s->dst, (s->sb.st_mode | S_IWUSR) & ~S_IFMT) == 0)
16591659- continue;
16601660- else {
18431843+ set_cprot_explicit = 1;
18441844+ } else while((s->dst_fd = open_dprotected_np(s->dst, oflags | dsrc, prot_class, 0, s->sb.st_mode | S_IWUSR)) < 0)
18451845+ {
16611846 /*
16621662- * If we're trying to write to a directory to which we don't
16631663- * have access, the create above would have failed, but chmod
16641664- * here would have given us ENOENT. But the real error is
16651665- * still one of access, so we change the errno we're reporting.
16661666- * This could cause confusion with a race condition.
18471847+ * We set S_IWUSR because fsetxattr does not -- at the time this comment
18481848+ * was written -- allow one to set an extended attribute on a file descriptor
18491849+ * for a read-only file, even if the file descriptor is opened for writing.
18501850+ * This will only matter if the file does not already exist.
16671851 */
18521852+ switch(errno)
18531853+ {
18541854+ case EEXIST:
18551855+ copyfile_debug(3, "open failed, retrying (%s)", s->dst);
18561856+ if (s->flags & COPYFILE_EXCL)
18571857+ break;
18581858+ oflags = oflags & ~O_CREAT;
18591859+ /* if O_CREAT isn't set in open_dprotected_np, it won't set protection class.
18601860+ * Set the flag here so we know to do it later.
18611861+ */
18621862+ set_cprot_explicit = 1;
18631863+ if (s->flags & (COPYFILE_PACK | COPYFILE_DATA))
18641864+ {
18651865+ copyfile_debug(4, "truncating existing file (%s)", s->dst);
18661866+ oflags |= O_TRUNC;
18671867+ }
18681868+ continue;
18691869+ case EACCES:
18701870+ if(chmod(s->dst, (s->sb.st_mode | S_IWUSR) & ~S_IFMT) == 0)
18711871+ continue;
18721872+ else {
18731873+ /*
18741874+ * If we're trying to write to a directory to which we don't
18751875+ * have access, the create above would have failed, but chmod
18761876+ * here would have given us ENOENT. But the real error is
18771877+ * still one of access, so we change the errno we're reporting.
18781878+ * This could cause confusion with a race condition.
18791879+ */
1668188016691669- if (errno == ENOENT)
16701670- errno = EACCES;
16711671- break;
16721672- }
16731673- case EISDIR:
16741674- copyfile_debug(3, "open failed because it is a directory (%s)", s->dst);
16751675- if (((s->flags & COPYFILE_EXCL) ||
16761676- (!isdir && (s->flags & COPYFILE_DATA)))
16771677- && !(s->flags & COPYFILE_UNPACK))
16781678- break;
16791679- oflags = (oflags & ~(O_WRONLY|O_CREAT|O_TRUNC)) | O_RDONLY;
16801680- continue;
16811681- }
16821682- copyfile_warn("open on %s", s->dst);
16831683- return -1;
18811881+ if (errno == ENOENT)
18821882+ errno = EACCES;
18831883+ break;
18841884+ }
18851885+ case EISDIR:
18861886+ copyfile_debug(3, "open failed because it is a directory (%s)", s->dst);
18871887+ if (((s->flags & COPYFILE_EXCL) ||
18881888+ (!isdir && (s->flags & COPYFILE_DATA)))
18891889+ && !(s->flags & COPYFILE_UNPACK))
18901890+ break;
18911891+ oflags = (oflags & ~(O_WRONLY|O_CREAT|O_TRUNC)) | O_RDONLY;
18921892+ continue;
18931893+ }
18941894+ copyfile_warn("open on %s", s->dst);
18951895+ return -1;
18961896+ }
18971897+ copyfile_debug(2, "open successful on destination (%s)", s->dst);
18981898+18991899+ if (s->internal_flags & cfSrcSupportsCProtect)
19001900+ {
19011901+ if (!(s->internal_flags & cfDstProtSupportValid))
19021902+ {
19031903+ if ((error = does_copy_protection(s->dst_fd)) > 0)
19041904+ {
19051905+ s->internal_flags |= cfDstSupportsCProtect;
19061906+ }
19071907+ else if (error < 0)
19081908+ {
19091909+ copyfile_warn("does_copy_protection failed on (%s) with error <%d>", s->dst, errno);
19101910+ return -1;
19111911+ }
19121912+ s->internal_flags |= cfDstProtSupportValid;
19131913+ }
19141914+19151915+ if ((isreg || isdir)
19161916+ && set_cprot_explicit
19171917+ && (s->internal_flags & cfDstSupportsCProtect))
19181918+ {
19191919+ /* Protection class is set in open_dprotected_np for regular files that aren't truncated.
19201920+ * We set the protection class here for truncated files and directories.
19211921+ */
19221922+ if (SET_PROT_CLASS(s->dst_fd, prot_class) != 0)
19231923+ {
19241924+ copyfile_warn("SET_PROT_CLASS failed on (%s) with error <%d>", s->dst, errno);
19251925+ return -1;
19261926+ }
19271927+ }
19281928+ }
16841929 }
16851685- copyfile_debug(2, "open successful on destination (%s)", s->dst);
16861686- }
1687193016881688- if (s->dst_fd < 0 || s->src_fd < 0)
16891689- {
16901690- copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)",
16911691- s->src_fd, s->dst_fd);
16921692- s->err = EINVAL;
16931693- return -1;
16941694- }
16951695- return 0;
19311931+ if (s->dst_fd < 0 || s->src_fd < 0)
19321932+ {
19331933+ copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)",
19341934+ s->src_fd, s->dst_fd);
19351935+ s->err = EINVAL;
19361936+ return -1;
19371937+ }
19381938+ return 0;
16961939}
1697194016981941···17061949 */
17071950static copyfile_flags_t copyfile_check(copyfile_state_t s)
17081951{
17091709- acl_t acl = NULL;
17101710- copyfile_flags_t ret = 0;
17111711- int nofollow = (s->flags & COPYFILE_NOFOLLOW_SRC);
17121712- qtn_file_t qinfo;
19521952+ acl_t acl = NULL;
19531953+ copyfile_flags_t ret = 0;
19541954+ int nofollow = (s->flags & COPYFILE_NOFOLLOW_SRC);
19551955+ qtn_file_t qinfo;
1713195617141714- if (!s->src)
17151715- {
17161716- s->err = EINVAL;
17171717- return -1;
17181718- }
17191719-17201720- /* check EAs */
17211721- if (COPYFILE_XATTR & s->flags)
17221722- if (listxattr(s->src, 0, 0, nofollow ? XATTR_NOFOLLOW : 0) > 0)
19571957+ if (!s->src)
17231958 {
17241724- ret |= COPYFILE_XATTR;
19591959+ s->err = EINVAL;
19601960+ return -1;
17251961 }
1726196217271727- if (COPYFILE_ACL & s->flags)
17281728- {
17291729- (COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np)
19631963+ /* check EAs */
19641964+ if (COPYFILE_XATTR & s->flags)
19651965+ if (listxattr(s->src, 0, 0, nofollow ? XATTR_NOFOLLOW : 0) > 0)
19661966+ {
19671967+ ret |= COPYFILE_XATTR;
19681968+ }
19691969+19701970+ if (COPYFILE_ACL & s->flags)
19711971+ {
19721972+ (COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np)
17301973 (s->src, &s->sb, s->fsec);
1731197417321732- if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) == 0)
17331733- ret |= COPYFILE_ACL;
17341734- }
19751975+ if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) == 0)
19761976+ ret |= COPYFILE_ACL;
19771977+ }
1735197817361736- copyfile_debug(2, "check result: %d (%s)", ret, s->src);
19791979+ copyfile_debug(2, "check result: %d (%s)", ret, s->src);
1737198017381738- if (acl)
17391739- acl_free(acl);
19811981+ if (acl)
19821982+ acl_free(acl);
1740198317411741- if (s->qinfo) {
17421742- /* If the state has had quarantine info set already, we use that */
17431743- ret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL);
17441744- } else {
17451745- qinfo = qtn_file_alloc();
17461746- /*
17471747- * For quarantine information, we need to see if the source file
17481748- * has any. Since it may be a symlink, however, and we may, or
17491749- * not be following, *and* there's no qtn* routine which can optionally
17501750- * follow or not follow a symlink, we need to instead work around
17511751- * this limitation.
17521752- */
17531753- if (qinfo) {
17541754- int fd;
17551755- int qret = 0;
17561756- struct stat sbuf;
17571757-19841984+ if (s->qinfo) {
19851985+ /* If the state has had quarantine info set already, we use that */
19861986+ ret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL);
19871987+ } else {
19881988+ qinfo = qtn_file_alloc();
17581989 /*
17591759- * If we care about not following symlinks, *and* the file exists
17601760- * (which is to say, lstat doesn't return an error), *and* the file
17611761- * is a symlink, then we open it up (with O_SYMLINK), and use
17621762- * qtn_file_init_with_fd(); if none of that is true, however, then
17631763- * we can simply use qtn_file_init_with_path().
19901990+ * For quarantine information, we need to see if the source file
19911991+ * has any. Since it may be a symlink, however, and we may, or
19921992+ * not be following, *and* there's no qtn* routine which can optionally
19931993+ * follow or not follow a symlink, we need to instead work around
19941994+ * this limitation.
17641995 */
17651765- if (nofollow
17661766- && lstat(s->src, &sbuf) == 0
19961996+ if (qinfo) {
19971997+ int fd;
19981998+ int qret = 0;
19991999+ struct stat sbuf;
20002000+20012001+ /*
20022002+ * If we care about not following symlinks, *and* the file exists
20032003+ * (which is to say, lstat doesn't return an error), *and* the file
20042004+ * is a symlink, then we open it up (with O_SYMLINK), and use
20052005+ * qtn_file_init_with_fd(); if none of that is true, however, then
20062006+ * we can simply use qtn_file_init_with_path().
20072007+ */
20082008+ if (nofollow
20092009+ && lstat(s->src, &sbuf) == 0
17672010 && ((sbuf.st_mode & S_IFMT) == S_IFLNK)) {
17681768- fd = open(s->src, O_RDONLY | O_SYMLINK);
17691769- if (fd != -1) {
17701770- if (!qtn_file_init_with_fd(qinfo, fd)) {
20112011+ fd = open(s->src, O_RDONLY | O_SYMLINK);
20122012+ if (fd != -1) {
20132013+ if (!qtn_file_init_with_fd(qinfo, fd)) {
20142014+ qret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL);
20152015+ }
20162016+ close(fd);
20172017+ }
20182018+ } else {
20192019+ if (!qtn_file_init_with_path(qinfo, s->src)) {
17712020 qret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL);
17722021 }
17731773- close(fd);
20222022+ }
20232023+ qtn_file_free(qinfo);
20242024+ ret |= qret;
20252025+ }
20262026+ }
20272027+ return ret;
20282028+}
20292029+20302030+/*
20312031+ * Attempt to copy the data section of a file sparsely.
20322032+ * Requires that the source and destination file systems support sparse files.
20332033+ * Also requires that the source file descriptor's offset is a multiple of the smaller of the
20342034+ * source and destination file systems' block size.
20352035+ * In practice, this means that we refuse to perform copies that are only partially sparse.
20362036+ * Returns 0 if the source sparse file was copied, -1 on an unrecoverable error that
20372037+ * callers should propagate, and ENOTSUP where this routine refuses to copy the source file.
20382038+ * In this final case, callers are free to attempt a full copy.
20392039+ */
20402040+static int copyfile_data_sparse(copyfile_state_t s, size_t input_blk_size, size_t output_blk_size)
20412041+{
20422042+ int src_fd = s->src_fd, dst_fd = s->dst_fd, rc = 0;
20432043+ off_t src_start, dst_start, src_size = s->sb.st_size;
20442044+ off_t first_hole_offset, next_hole_offset, current_src_offset, next_src_offset;
20452045+ ssize_t nread;
20462046+ size_t iosize = MIN(input_blk_size, output_blk_size);
20472047+ copyfile_callback_t status = s->statuscb;
20482048+ char *bp = NULL;
20492049+ bool use_punchhole = true;
20502050+ errno = 0;
20512051+20522052+ // Sanity checks.
20532053+ if (!(s->flags & COPYFILE_DATA_SPARSE)) {
20542054+ // Don't attempt this unless the right flags are passed.
20552055+ return ENOTSUP;
20562056+ } else if (src_size <= 0) {
20572057+ // The file size of our source is invalid; there's nothing to copy.
20582058+ errno = EINVAL;
20592059+ goto error_exit;
20602060+ }
20612061+20622062+ // Since a major underlying filesystem requires that holes are block-aligned,
20632063+ // we only punch holes if we can guarantee that all holes from the source can
20642064+ // be holes in the destination, which requires that the source filesystem's block size
20652065+ // be an integral multiple of the destination filesystem's block size.
20662066+ if (input_blk_size % output_blk_size != 0) {
20672067+ use_punchhole = false;
20682068+ }
20692069+20702070+ // Get the starting src/dest file descriptor offsets.
20712071+ src_start = lseek(src_fd, 0, SEEK_CUR);
20722072+ dst_start = lseek(dst_fd, 0, SEEK_CUR);
20732073+ if (src_start < 0 || src_start >= src_size || dst_start < 0) {
20742074+ /*
20752075+ * Invalid starting source/destination offset:
20762076+ * Either < 0 which is plainly invalid (lseek may have failed),
20772077+ * or > EOF which means that the copy operation is undefined,
20782078+ * as by definition there is no data past EOF.
20792079+ */
20802080+ if (errno == 0) {
20812081+ errno = EINVAL;
20822082+ }
20832083+ copyfile_warn("Invalid file descriptor offset, cannot perform a sparse copy");
20842084+ goto error_exit;
20852085+ } else if (src_start != (off_t) roundup(src_start, iosize) ||
20862086+ dst_start != (off_t) roundup(dst_start, iosize)) {
20872087+ // If the starting offset isn't a multiple of the iosize, we can't do an entire sparse copy.
20882088+ // Fall back to copyfile_data(), which will perform a full copy from the starting position.
20892089+ return ENOTSUP;
20902090+ }
20912091+20922092+ // Make sure that there is at least one hole in this [part of the] file.
20932093+ first_hole_offset = lseek(src_fd, src_start, SEEK_HOLE);
20942094+ if (first_hole_offset == -1 || first_hole_offset == src_size) {
20952095+ /*
20962096+ * Either an error occurred, the src starting position is EOF, or there are no
20972097+ * holes in this [portion of the] source file. Regardless, we rewind the source file
20982098+ * and return ENOTSUP so copyfile_data() can attempt a full copy.
20992099+ */
21002100+ if (lseek(src_fd, src_start, SEEK_SET) == -1) {
21012101+ goto error_exit;
21022102+ }
21032103+ return ENOTSUP;
21042104+ }
21052105+21062106+ // We are ready to begin copying.
21072107+ // First, truncate the destination file to zero out any existing contents.
21082108+ // Then, truncate it again to its eventual size.
21092109+ if (ftruncate(dst_fd, dst_start) == -1) {
21102110+ copyfile_warn("Could not zero destination file before copy");
21112111+ goto error_exit;
21122112+ } else if (ftruncate(dst_fd, dst_start + src_size - src_start) == -1) {
21132113+ copyfile_warn("Could not set destination file size before copy");
21142114+ goto error_exit;
21152115+ }
21162116+21172117+ // Set the source's offset to the first data section.
21182118+ current_src_offset = lseek(src_fd, src_start, SEEK_DATA);
21192119+ if (current_src_offset == -1) {
21202120+ if (errno == ENXIO) {
21212121+ // There are no more data sections in the file, so there's nothing to copy.
21222122+ goto set_total_copied;
21232123+ }
21242124+ goto error_exit;
21252125+ }
21262126+21272127+ // Now, current_src_offset points at the start of src's first data region.
21282128+ // Update dst_fd to point to the same offset (respecting its start).
21292129+ if (lseek(dst_fd, dst_start + current_src_offset - src_start, SEEK_SET) == -1) {
21302130+ copyfile_warn("failed to set dst to first data section");
21312131+ goto error_exit;
21322132+ }
21332133+21342134+ // Allocate a temporary buffer to copy data sections into.
21352135+ bp = malloc(iosize);
21362136+ if (bp == NULL) {
21372137+ copyfile_warn("No memory for copy buffer");
21382138+ goto error_exit;
21392139+ }
21402140+21412141+ /*
21422142+ * Performing a sparse copy:
21432143+ * While our source fd points to a data section (and is < EOF), read iosize bytes in.
21442144+ * Then, write those bytes to the dest fd, using the same iosize.
21452145+ * Finally, update our source and dest fds to point to the next data section.
21462146+ */
21472147+ while ((nread = read(src_fd, bp, iosize)) > 0) {
21482148+ ssize_t nwritten;
21492149+ size_t left = nread;
21502150+ void *ptr = bp;
21512151+ int loop = 0;
21522152+21532153+ while (left > 0) {
21542154+ nwritten = write(dst_fd, ptr, left);
21552155+ switch (nwritten) {
21562156+ case 0:
21572157+ if (++loop > 5) {
21582158+ copyfile_warn("writing to output %d times resulted in 0 bytes written", loop);
21592159+ errno = EAGAIN;
21602160+ goto error_exit;
21612161+ }
21622162+ break;
21632163+ case -1:
21642164+ copyfile_warn("writing to output file failed");
21652165+ if (status) {
21662166+ int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
21672167+ if (rv == COPYFILE_SKIP) { // Skip the data copy
21682168+ errno = 0;
21692169+ goto exit;
21702170+ } else if (rv == COPYFILE_CONTINUE) { // Retry the write
21712171+ errno = 0;
21722172+ continue;
21732173+ }
21742174+ }
21752175+ // If we get here, we either have no callback or it didn't tell us to continue.
21762176+ goto error_exit;
21772177+ break;
21782178+ default:
21792179+ left -= nwritten;
21802180+ ptr = ((char*)ptr) + nwritten;
21812181+ loop = 0;
21822182+ break;
21832183+ }
21842184+ s->totalCopied += nwritten;
21852185+ if (status) {
21862186+ int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
21872187+ if (rv == COPYFILE_QUIT) {
21882188+ errno = ECANCELED;
21892189+ goto error_exit;
21902190+ }
21912191+ }
21922192+ }
21932193+ current_src_offset += nread;
21942194+21952195+ // Find the next area of src_fd to copy.
21962196+ // Since data sections can be any length, we need see if current_src_offset points
21972197+ // at a hole.
21982198+ // If we get ENXIO, we're done copying (the last part of the file was a data section).
21992199+ // If this is not a hole, we do not need to alter current_src_offset yet.
22002200+ // If this is a hole, then we need to look for the next data section.
22012201+ next_hole_offset = lseek(src_fd, current_src_offset, SEEK_HOLE);
22022202+ if (next_hole_offset == -1) {
22032203+ if (errno == ENXIO) {
22042204+ break; // We're done copying data sections.
22052205+ }
22062206+ copyfile_warn("unable to find next hole in file during copy");
22072207+ goto error_exit;
22082208+ } else if (next_hole_offset != current_src_offset) {
22092209+ // Keep copying this data section (we must rewind src_fd to current_src_offset).
22102210+ if (lseek(src_fd, current_src_offset, SEEK_SET) == -1) {
22112211+ goto error_exit;
22122212+ }
22132213+ continue;
22142214+ }
22152215+22162216+ // If we get here, we need to find the next data section to copy.
22172217+ next_src_offset = lseek(src_fd, current_src_offset, SEEK_DATA);
22182218+ if (next_src_offset == -1) {
22192219+ if (errno == ENXIO) {
22202220+ // There are no more data sections in this file, so we're done with the copy.
22212221+ break;
22222222+ }
22232223+22242224+ copyfile_warn("unable to advance src to next data section");
22252225+ goto error_exit;
22262226+ }
22272227+22282228+ // Advance the dst_fd to match (taking into account where it started).
22292229+ if (lseek(dst_fd, dst_start + (next_src_offset - src_start), SEEK_SET) == -1) {
22302230+ copyfile_warn("unable to advance dst to next data section");
22312231+ goto error_exit;
22322232+ }
22332233+22342234+ current_src_offset = next_src_offset;
22352235+ }
22362236+ if (nread < 0) {
22372237+ copyfile_warn("error %d reading from %s", errno, s->src ? s->src : "(null src)");
22382238+ goto error_exit;
22392239+ }
22402240+22412241+ // Punch holes where possible if needed.
22422242+ if (use_punchhole) {
22432243+ struct fpunchhole punchhole_args;
22442244+ off_t hole_start = first_hole_offset, hole_end;
22452245+ bool trailing_hole = true;
22462246+22472247+ // First, reset the source and destination file descriptors.
22482248+ if (lseek(src_fd, src_start, SEEK_SET) == -1 || lseek(dst_fd, dst_start, SEEK_SET) == -1) {
22492249+ copyfile_warn("unable to reset file descriptors to punch holes");
22502250+ // We have still copied the data, so there's no need to return an error here.
22512251+ goto set_total_copied;
22522252+ }
22532253+22542254+ // Now, find holes in the source (first_hole_offset already points to a source hole),
22552255+ // determining their length by the presence of a data section.
22562256+ while ((hole_end = lseek(src_fd, hole_start + (off_t) iosize, SEEK_DATA)) != -1) {
22572257+ memset(&punchhole_args, 0, sizeof(punchhole_args));
22582258+22592259+ // Fix up the offset and length for the destination file.
22602260+ punchhole_args.fp_offset = hole_start - src_start + dst_start;
22612261+ punchhole_args.fp_length = hole_end - hole_start;
22622262+ if (fcntl(dst_fd, F_PUNCHHOLE, &punchhole_args) == -1) {
22632263+ copyfile_warn("unable to punch hole in destination file, offset %lld length %lld",
22642264+ hole_start - src_start + dst_start, hole_end - hole_start);
22652265+ goto set_total_copied;
17742266 }
17751775- } else {
17761776- if (!qtn_file_init_with_path(qinfo, s->src)) {
17771777- qret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL);
22672267+22682268+ // Now, find the start of the next hole.
22692269+ hole_start = lseek(src_fd, hole_end, SEEK_HOLE);
22702270+ if (hole_start == -1 || hole_start == src_size) {
22712271+ // No more holes (or lseek failed), so break.
22722272+ trailing_hole = false;
22732273+ break;
17782274 }
17792275 }
17801780- qtn_file_free(qinfo);
17811781- ret |= qret;
22762276+22772277+ if ((hole_end == -1 || hole_start == -1) && errno != ENXIO) {
22782278+ // A call to lseek() failed. Hole punching is best effort, so exit.
22792279+ copyfile_warn("lseek during hole punching failed");
22802280+ goto set_total_copied;
22812281+ }
22822282+22832283+ // We will still have a trailing hole to punch if the last lseek(SEEK_HOLE) succeeded.
22842284+ if (trailing_hole) {
22852285+ // Since we can only punch iosize-aligned holes, we must make sure the last hole
22862286+ // is iosize-aligned. Unfortunately, no good truncate macros are in scope here,
22872287+ // so we must round down the end of the trailing hole to an iosize boundary ourselves.
22882288+ hole_end = (src_size % iosize == 0) ? src_size : roundup(src_size, iosize) - iosize;
22892289+22902290+ memset(&punchhole_args, 0, sizeof(punchhole_args));
22912291+ punchhole_args.fp_offset = hole_start - src_start + dst_start;
22922292+ punchhole_args.fp_length = hole_end - hole_start;
22932293+ if (fcntl(dst_fd, F_PUNCHHOLE, &punchhole_args) == -1) {
22942294+ copyfile_warn("unable to punch trailing hole in destination file, offset %lld",
22952295+ hole_start - src_start + dst_start);
22962296+ goto set_total_copied;
22972297+ }
22982298+ }
22992299+ }
23002300+23012301+set_total_copied:
23022302+ // Since we don't know in advance how many bytes we're copying, we advance this number
23032303+ // as we copy, but to match copyfile_data() we set it here to the amount of bytes that would
23042304+ // have been transferred in a full copy.
23052305+ s->totalCopied = src_size - src_start;
23062306+23072307+exit:
23082308+ if (bp) {
23092309+ free(bp);
23102310+ bp = NULL;
17822311 }
17831783- }
17841784- return ret;
23122312+23132313+ return rc;
23142314+23152315+error_exit:
23162316+ s->err = errno;
23172317+ rc = -1;
23182318+ goto exit;
17852319}
1786232017872321/*
···17922326 */
17932327static int copyfile_data(copyfile_state_t s)
17942328{
17951795- size_t blen;
17961796- char *bp = 0;
17971797- ssize_t nread;
17981798- int ret = 0;
17991799- size_t iBlocksize = 0;
18001800- size_t oBlocksize = 0;
18011801- const size_t onegig = 1 << 30;
18021802- struct statfs sfs;
18031803- copyfile_callback_t status = s->statuscb;
23292329+ size_t blen;
23302330+ char *bp = 0;
23312331+ ssize_t nread;
23322332+ int ret = 0;
23332333+ size_t iBlocksize = 0, iMinblocksize = 0;
23342334+ size_t oBlocksize = 0, oMinblocksize = 0; // If 0, we don't support sparse copying.
23352335+ const size_t blocksize_limit = 1 << 30; // 1 GiB
23362336+ struct statfs sfs;
23372337+ copyfile_callback_t status = s->statuscb;
1804233818051805- /* Unless it's a normal file, we don't copy. For now, anyway */
18061806- if ((s->sb.st_mode & S_IFMT) != S_IFREG)
18071807- return 0;
23392339+ /* Unless it's a normal file, we don't copy. For now, anyway */
23402340+ if ((s->sb.st_mode & S_IFMT) != S_IFREG)
23412341+ return 0;
1808234218092343#ifdef VOL_CAP_FMT_DECMPFS_COMPRESSION
18101810- if (s->internal_flags & cfSawDecmpEA) {
18111811- if (s->sb.st_flags & UF_COMPRESSED) {
18121812- if ((s->flags & COPYFILE_STAT) == 0) {
18131813- if (fchflags(s->dst_fd, UF_COMPRESSED) == 0) {
18141814- goto exit;
23442344+ if (s->internal_flags & cfSawDecmpEA) {
23452345+ if (s->sb.st_flags & UF_COMPRESSED) {
23462346+ if ((s->flags & COPYFILE_STAT) == 0) {
23472347+ if (fchflags(s->dst_fd, UF_COMPRESSED) == 0) {
23482348+ goto exit;
23492349+ }
23502350+ }
18152351 }
18161816- }
18172352 }
18181818- }
18192353#endif
18201820-18211821- if (fstatfs(s->src_fd, &sfs) == -1) {
18221822- iBlocksize = s->sb.st_blksize;
18231823- } else {
18241824- iBlocksize = sfs.f_iosize;
18251825- }
23542354+23552355+ // Calculate the input and output block sizes.
23562356+ // Our output block size can be no greater than our input block size.
23572357+ if (fstatfs(s->src_fd, &sfs) == -1) {
23582358+ iBlocksize = s->sb.st_blksize;
23592359+ } else {
23602360+ iBlocksize = sfs.f_iosize;
23612361+ iMinblocksize = sfs.f_bsize;
23622362+ }
23632363+23642364+ if (fstatfs(s->dst_fd, &sfs) == -1) {
23652365+ oBlocksize = iBlocksize;
23662366+ } else {
23672367+ oBlocksize = (sfs.f_iosize == 0) ? iBlocksize : MIN((size_t) sfs.f_iosize, iBlocksize);
23682368+ oMinblocksize = sfs.f_bsize;
23692369+ }
23702370+23712371+ // 6453525 and 34848916 require us to limit our blocksize to resonable values.
23722372+ if ((size_t) s->sb.st_size < iBlocksize && iMinblocksize > 0) {
23732373+ copyfile_debug(3, "rounding up block size from fsize: %lld to multiple of %zu\n", s->sb.st_size, iMinblocksize);
23742374+ iBlocksize = roundup((size_t) s->sb.st_size, iMinblocksize);
23752375+ oBlocksize = MIN(oBlocksize, iBlocksize);
23762376+ }
23772377+23782378+ if (iBlocksize > blocksize_limit) {
23792379+ iBlocksize = blocksize_limit;
23802380+ oBlocksize = MIN(oBlocksize, iBlocksize);
23812381+ }
1826238218271827- /* Work-around for 6453525, limit blocksize to 1G */
18281828- if (iBlocksize > onegig) {
18291829- iBlocksize = onegig;
18301830- }
23832383+ copyfile_debug(3, "input block size: %zu output block size: %zu\n", iBlocksize, oBlocksize);
1831238418321832- if ((bp = malloc(iBlocksize)) == NULL)
18331833- return -1;
23852385+ s->totalCopied = 0;
1834238618351835- if (fstatfs(s->dst_fd, &sfs) == -1 || sfs.f_iosize == 0) {
18361836- oBlocksize = iBlocksize;
18371837- } else {
18381838- oBlocksize = sfs.f_iosize;
18391839- if (oBlocksize > onegig)
18401840- oBlocksize = onegig;
18411841- }
23872387+ // If requested, attempt a sparse copy.
23882388+ if (s->flags & COPYFILE_DATA_SPARSE) {
23892389+ // Check if the source & destination volumes both support sparse files.
23902390+ long min_hole_size = MIN(fpathconf(s->src_fd, _PC_MIN_HOLE_SIZE),
23912391+ fpathconf(s->dst_fd, _PC_MIN_HOLE_SIZE));
1842239218431843- blen = iBlocksize;
23932393+ // If holes are supported on both the source and dest volumes, make sure our min_hole_size
23942394+ // is reasonable: if it's smaller than the source/dest block size,
23952395+ // our copy performance will suffer (and we may not create sparse files).
23962396+ if (iMinblocksize > 0 && oMinblocksize > 0 && (size_t) min_hole_size >= iMinblocksize
23972397+ && (size_t) min_hole_size >= oMinblocksize) {
23982398+ // Do the copy.
23992399+ ret = copyfile_data_sparse(s, iMinblocksize, oMinblocksize);
1844240018451845- s->totalCopied = 0;
18461846-/* If supported, do preallocation for Xsan / HFS volumes */
24012401+ // If we returned an error, exit gracefully.
24022402+ // If sparse copying is not supported, we try full copying if allowed by our caller.
24032403+ if (ret == 0) {
24042404+ goto exit;
24052405+ } else if (ret != ENOTSUP) {
24062406+ goto exit;
24072407+ }
24082408+ ret = 0;
24092409+ }
24102410+24112411+ // Make sure we're allowed to perform non-sparse copying.
24122412+ if (!(s->flags & COPYFILE_DATA)) {
24132413+ ret = -1;
24142414+ errno = ENOTSUP;
24152415+ goto exit;
24162416+ }
24172417+ }
24182418+24192419+ if ((bp = malloc(iBlocksize)) == NULL)
24202420+ return -1;
24212421+24222422+ blen = iBlocksize;
24232423+24242424+ /* If supported, do preallocation for Xsan / HFS / apfs volumes */
18472425#ifdef F_PREALLOCATE
18481848- {
18491849- fstore_t fst;
24262426+ {
24272427+ off_t dst_bytes_allocated = 0;
24282428+ struct stat dst_sb;
24292429+24302430+ if (fstat(s->dst_fd, &dst_sb) == 0) {
24312431+ // The destination may already have
24322432+ // preallocated space we can use.
24332433+ dst_bytes_allocated = dst_sb.st_blocks * S_BLKSIZE;
24342434+ }
24352435+24362436+ if (dst_bytes_allocated < s->sb.st_size) {
24372437+ fstore_t fst;
24382438+24392439+ fst.fst_flags = 0;
24402440+ fst.fst_posmode = F_PEOFPOSMODE;
24412441+ fst.fst_offset = 0;
24422442+ fst.fst_length = s->sb.st_size - dst_bytes_allocated;
1850244318511851- fst.fst_flags = 0;
18521852- fst.fst_posmode = F_PEOFPOSMODE;
18531853- fst.fst_offset = 0;
18541854- fst.fst_length = s->sb.st_size;
18551855- /* Ignore errors; this is merely advisory. */
18561856- (void)fcntl(s->dst_fd, F_PREALLOCATE, &fst);
18571857- }
24442444+ copyfile_debug(3, "preallocating %lld bytes on destination", fst.fst_length);
24452445+ /* Ignore errors; this is merely advisory. */
24462446+ (void)fcntl(s->dst_fd, F_PREALLOCATE, &fst);
24472447+ }
24482448+ }
18582449#endif
1859245018601860- while ((nread = read(s->src_fd, bp, blen)) > 0)
18611861- {
18621862- ssize_t nwritten;
18631863- size_t left = nread;
18641864- void *ptr = bp;
18651865- int loop = 0;
24512451+ while ((nread = read(s->src_fd, bp, blen)) > 0)
24522452+ {
24532453+ ssize_t nwritten;
24542454+ size_t left = nread;
24552455+ void *ptr = bp;
24562456+ int loop = 0;
1866245718671867- while (left > 0) {
18681868- nwritten = write(s->dst_fd, ptr, MIN(left, oBlocksize));
18691869- switch (nwritten) {
18701870- case 0:
18711871- if (++loop > 5) {
18721872- copyfile_warn("writing to output %d times resulted in 0 bytes written", loop);
18731873- ret = -1;
18741874- s->err = EAGAIN;
18751875- goto exit;
24582458+ while (left > 0) {
24592459+ nwritten = write(s->dst_fd, ptr, MIN(left, oBlocksize));
24602460+ switch (nwritten) {
24612461+ case 0:
24622462+ if (++loop > 5) {
24632463+ copyfile_warn("writing to output %d times resulted in 0 bytes written", loop);
24642464+ ret = -1;
24652465+ s->err = EAGAIN;
24662466+ goto exit;
24672467+ }
24682468+ break;
24692469+ case -1:
24702470+ copyfile_warn("writing to output file got error");
24712471+ if (status) {
24722472+ int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
24732473+ if (rv == COPYFILE_SKIP) { // Skip the data copy
24742474+ ret = 0;
24752475+ goto exit;
24762476+ }
24772477+ if (rv == COPYFILE_CONTINUE) { // Retry the write
24782478+ errno = 0;
24792479+ continue;
24802480+ }
24812481+ }
24822482+ ret = -1;
24832483+ goto exit;
24842484+ default:
24852485+ left -= nwritten;
24862486+ ptr = ((char*)ptr) + nwritten;
24872487+ loop = 0;
24882488+ break;
18762489 }
18771877- break;
18781878- case -1:
18791879- copyfile_warn("writing to output file got error");
24902490+ s->totalCopied += nwritten;
18802491 if (status) {
18811881- int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
18821882- if (rv == COPYFILE_SKIP) { // Skip the data copy
18831883- ret = 0;
24922492+ int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
24932493+ if (rv == COPYFILE_QUIT) {
24942494+ ret = -1; s->err = errno = ECANCELED;
18842495 goto exit;
18852496 }
18861886- if (rv == COPYFILE_CONTINUE) { // Retry the write
18871887- errno = 0;
18881888- continue;
18891889- }
18902497 }
18911891- ret = -1;
18921892- goto exit;
18931893- default:
18941894- left -= nwritten;
18951895- ptr = ((char*)ptr) + nwritten;
18961896- loop = 0;
18971897- break;
18982498 }
18991899- s->totalCopied += nwritten;
19001900- if (status) {
19011901- int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
19021902- if (rv == COPYFILE_QUIT) {
19031903- ret = -1; s->err = errno = ECANCELED;
19041904- goto exit;
19051905- }
19061906- }
24992499+ }
25002500+ if (nread < 0)
25012501+ {
25022502+ copyfile_warn("reading from %s", s->src ? s->src : "(null src)");
25032503+ ret = -1;
25042504+ goto exit;
19072505 }
19081908- }
19091909- if (nread < 0)
19101910- {
19111911- copyfile_warn("reading from %s", s->src ? s->src : "(null src)");
19121912- ret = -1;
19131913- goto exit;
19141914- }
1915250619161916- if (ftruncate(s->dst_fd, s->totalCopied) < 0)
19171917- {
19181918- ret = -1;
19191919- goto exit;
19201920- }
25072507+ if (ftruncate(s->dst_fd, s->totalCopied) < 0)
25082508+ {
25092509+ ret = -1;
25102510+ goto exit;
25112511+ }
1921251219222513exit:
19231923- if (ret == -1)
19241924- {
19251925- s->err = errno;
19261926- }
19271927- free(bp);
19281928- return ret;
25142514+ if (ret == -1)
25152515+ {
25162516+ s->err = errno;
25172517+ }
25182518+ free(bp);
25192519+ return ret;
19292520}
1930252119312522/*
···19362527 */
19372528static int copyfile_security(copyfile_state_t s)
19382529{
19391939- int copied = 0;
19401940- struct stat sb;
19411941- acl_t acl_src = NULL, acl_tmp = NULL, acl_dst = NULL;
19421942- int ret = 0;
19431943- filesec_t tmp_fsec = NULL;
19441944- filesec_t fsec_dst = filesec_init();
25302530+ int copied = 0;
25312531+ struct stat sb;
25322532+ acl_t acl_src = NULL, acl_tmp = NULL, acl_dst = NULL;
25332533+ int ret = 0;
25342534+ filesec_t tmp_fsec = NULL;
25352535+ filesec_t fsec_dst = filesec_init();
1945253619461946- if (fsec_dst == NULL)
19471947- return -1;
25372537+ if (fsec_dst == NULL)
25382538+ return -1;
194825391949254019501950- if (COPYFILE_ACL & s->flags)
19511951- {
19521952- if (filesec_get_property(s->fsec, FILESEC_ACL, &acl_src))
25412541+ if (COPYFILE_ACL & s->flags)
19532542 {
19541954- if (errno == ENOENT)
19551955- acl_src = NULL;
19561956- else
19571957- goto error_exit;
19581958- }
25432543+ if (filesec_get_property(s->fsec, FILESEC_ACL, &acl_src))
25442544+ {
25452545+ if (errno == ENOENT)
25462546+ acl_src = NULL;
25472547+ else
25482548+ goto error_exit;
25492549+ }
1959255019601960-/* grab the destination acl
19611961- cannot assume it's empty due to inheritance
19621962-*/
19631963- if(fstatx_np(s->dst_fd, &sb, fsec_dst))
19641964- goto error_exit;
25512551+ /* grab the destination acl
25522552+ cannot assume it's empty due to inheritance
25532553+ */
25542554+ if(fstatx_np(s->dst_fd, &sb, fsec_dst))
25552555+ goto error_exit;
1965255619661966- if (filesec_get_property(fsec_dst, FILESEC_ACL, &acl_dst))
19671967- {
19681968- if (errno == ENOENT)
19691969- acl_dst = NULL;
19701970- else
19711971- goto error_exit;
19721972- }
25572557+ if (filesec_get_property(fsec_dst, FILESEC_ACL, &acl_dst))
25582558+ {
25592559+ if (errno == ENOENT)
25602560+ acl_dst = NULL;
25612561+ else
25622562+ goto error_exit;
25632563+ }
1973256419741974- if (acl_src == NULL && acl_dst == NULL)
19751975- goto no_acl;
25652565+ if (acl_src == NULL && acl_dst == NULL)
25662566+ goto no_acl;
1976256719771977- acl_tmp = acl_init(4);
19781978- if (acl_tmp == NULL)
19791979- goto error_exit;
25682568+ acl_tmp = acl_init(4);
25692569+ if (acl_tmp == NULL)
25702570+ goto error_exit;
1980257119811981- if (acl_src) {
19821982- acl_entry_t ace = NULL;
19831983- acl_entry_t tmp = NULL;
19841984- for (copied = 0;
19851985- acl_get_entry(acl_src,
19861986- ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY,
19871987- &ace) == 0;)
19881988- {
19891989- acl_flagset_t flags = { 0 };
19901990- acl_get_flagset_np(ace, &flags);
19911991- if (!acl_get_flag_np(flags, ACL_ENTRY_INHERITED))
19921992- {
19931993- if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1)
19941994- goto error_exit;
25722572+ if (acl_src) {
25732573+ acl_entry_t ace = NULL;
25742574+ acl_entry_t tmp = NULL;
25752575+ for (copied = 0;
25762576+ acl_get_entry(acl_src,
25772577+ ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY,
25782578+ &ace) == 0;)
25792579+ {
25802580+ acl_flagset_t flags = { 0 };
25812581+ acl_get_flagset_np(ace, &flags);
25822582+ if (!acl_get_flag_np(flags, ACL_ENTRY_INHERITED))
25832583+ {
25842584+ if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1)
25852585+ goto error_exit;
1995258619961996- if ((ret = acl_copy_entry(tmp, ace)) == -1)
19971997- goto error_exit;
25872587+ if ((ret = acl_copy_entry(tmp, ace)) == -1)
25882588+ goto error_exit;
1998258919991999- copyfile_debug(2, "copied acl entry from %s to %s",
20002000- s->src ? s->src : "(null src)",
20012001- s->dst ? s->dst : "(null tmp)");
20022002- copied++;
20032003- }
25902590+ copyfile_debug(2, "copied acl entry from %s to %s",
25912591+ s->src ? s->src : "(null src)",
25922592+ s->dst ? s->dst : "(null tmp)");
25932593+ copied++;
25942594+ }
25952595+ }
20042596 }
20052005- }
20062006- if (acl_dst) {
20072007- acl_entry_t ace = NULL;
20082008- acl_entry_t tmp = NULL;
20092009- acl_flagset_t flags = { 0 };
20102010- for (copied = 0;acl_get_entry(acl_dst,
20112011- ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY,
20122012- &ace) == 0;)
20132013- {
20142014- acl_get_flagset_np(ace, &flags);
20152015- if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED))
20162016- {
20172017- if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1)
20182018- goto error_exit;
25972597+ if (acl_dst) {
25982598+ acl_entry_t ace = NULL;
25992599+ acl_entry_t tmp = NULL;
26002600+ acl_flagset_t flags = { 0 };
26012601+ for (copied = 0;acl_get_entry(acl_dst,
26022602+ ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY,
26032603+ &ace) == 0;)
26042604+ {
26052605+ acl_get_flagset_np(ace, &flags);
26062606+ if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED))
26072607+ {
26082608+ if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1)
26092609+ goto error_exit;
2019261020202020- if ((ret = acl_copy_entry(tmp, ace)) == -1)
20212021- goto error_exit;
26112611+ if ((ret = acl_copy_entry(tmp, ace)) == -1)
26122612+ goto error_exit;
2022261320232023- copyfile_debug(2, "copied acl entry from %s to %s",
20242024- s->src ? s->src : "(null dst)",
20252025- s->dst ? s->dst : "(null tmp)");
20262026- copied++;
20272027- }
26142614+ copyfile_debug(2, "copied acl entry from %s to %s",
26152615+ s->src ? s->src : "(null dst)",
26162616+ s->dst ? s->dst : "(null tmp)");
26172617+ copied++;
26182618+ }
26192619+ }
26202620+ }
26212621+ if (!filesec_set_property(s->fsec, FILESEC_ACL, &acl_tmp))
26222622+ {
26232623+ copyfile_debug(3, "altered acl");
20282624 }
20292625 }
20302030- if (!filesec_set_property(s->fsec, FILESEC_ACL, &acl_tmp))
20312031- {
20322032- copyfile_debug(3, "altered acl");
26262626+no_acl:
26272627+ /*
26282628+ * The following code is attempting to ensure that only the requested
26292629+ * security information gets copied over to the destination file.
26302630+ * We essentially have four cases: COPYFILE_ACL, COPYFILE_STAT,
26312631+ * COPYFILE_(STAT|ACL), and none (in which case, we wouldn't be in
26322632+ * this function).
26332633+ *
26342634+ * If we have both flags, we copy everything; if we have ACL but not STAT,
26352635+ * we remove the POSIX information from the filesec object, and apply the
26362636+ * ACL; if we have STAT but not ACL, then we just use fchmod(), and ignore
26372637+ * the extended version.
26382638+ */
26392639+ tmp_fsec = filesec_dup(s->fsec);
26402640+ if (tmp_fsec == NULL) {
26412641+ goto error_exit;
20332642 }
20342034- }
20352035-no_acl:
20362036- /*
20372037- * The following code is attempting to ensure that only the requested
20382038- * security information gets copied over to the destination file.
20392039- * We essentially have four cases: COPYFILE_ACL, COPYFILE_STAT,
20402040- * COPYFILE_(STAT|ACL), and none (in which case, we wouldn't be in
20412041- * this function).
20422042- *
20432043- * If we have both flags, we copy everything; if we have ACL but not STAT,
20442044- * we remove the POSIX information from the filesec object, and apply the
20452045- * ACL; if we have STAT but not ACL, then we just use fchmod(), and ignore
20462046- * the extended version.
20472047- */
20482048- tmp_fsec = filesec_dup(s->fsec);
20492049- if (tmp_fsec == NULL) {
20502050- goto error_exit;
20512051- }
2052264320532053- switch (COPYFILE_SECURITY & s->flags) {
20542054- case COPYFILE_ACL:
20552055- copyfile_unset_posix_fsec(tmp_fsec);
20562056- /* FALLTHROUGH */
20572057- case COPYFILE_ACL | COPYFILE_STAT:
20582058- if (fchmodx_np(s->dst_fd, tmp_fsec) < 0) {
20592059- acl_t acl = NULL;
20602060- /*
20612061- * The call could have failed for a number of reasons, since
20622062- * it does a number of things: it changes the mode of the file,
20632063- * sets the owner and group, and applies an ACL (if one exists).
20642064- * The typical failure is going to be trying to set the group of
20652065- * the destination file to match the source file, when the process
20662066- * doesn't have permission to put files in that group. We try to
20672067- * work around this by breaking the steps out and doing them
20682068- * discretely. We don't care if the fchown fails, but we do care
20692069- * if the mode or ACL can't be set. For historical reasons, we
20702070- * simply log those failures, however.
20712071- *
20722072- * Big warning here: we may NOT have COPYFILE_STAT set, since
20732073- * we fell-through from COPYFILE_ACL. So check for the fchmod.
20742074- */
26442644+ switch (COPYFILE_SECURITY & s->flags) {
26452645+ case COPYFILE_ACL:
26462646+ copyfile_unset_posix_fsec(tmp_fsec);
26472647+ /* FALLTHROUGH */
26482648+ case COPYFILE_ACL | COPYFILE_STAT:
26492649+ if (fchmodx_np(s->dst_fd, tmp_fsec) < 0) {
26502650+ acl_t acl = NULL;
26512651+ /*
26522652+ * The call could have failed for a number of reasons, since
26532653+ * it does a number of things: it changes the mode of the file,
26542654+ * sets the owner and group, and applies an ACL (if one exists).
26552655+ * The typical failure is going to be trying to set the group of
26562656+ * the destination file to match the source file, when the process
26572657+ * doesn't have permission to put files in that group. We try to
26582658+ * work around this by breaking the steps out and doing them
26592659+ * discretely. We don't care if the fchown fails, but we do care
26602660+ * if the mode or ACL can't be set. For historical reasons, we
26612661+ * simply log those failures, however.
26622662+ *
26632663+ * Big warning here: we may NOT have COPYFILE_STAT set, since
26642664+ * we fell-through from COPYFILE_ACL. So check for the fchmod.
26652665+ */
2075266620762667#define NS(x) ((x) ? (x) : "(null string)")
20772077- if ((s->flags & COPYFILE_STAT) &&
20782078- fchmod(s->dst_fd, s->sb.st_mode) == -1) {
20792079- copyfile_warn("could not change mode of destination file %s to match source file %s", NS(s->dst), NS(s->src));
20802080- }
20812081- (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid);
20822082- if (filesec_get_property(tmp_fsec, FILESEC_ACL, &acl) == 0) {
20832083- if (acl_set_fd(s->dst_fd, acl) == -1) {
20842084- copyfile_warn("could not apply acl to destination file %s from source file %s", NS(s->dst), NS(s->src));
26682668+ if ((s->flags & COPYFILE_STAT) &&
26692669+ fchmod(s->dst_fd, s->sb.st_mode) == -1) {
26702670+ copyfile_warn("could not change mode of destination file %s to match source file %s", NS(s->dst), NS(s->src));
26712671+ }
26722672+ (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid);
26732673+ if (filesec_get_property(tmp_fsec, FILESEC_ACL, &acl) == 0) {
26742674+ if (acl_set_fd(s->dst_fd, acl) == -1) {
26752675+ copyfile_warn("could not apply acl to destination file %s from source file %s", NS(s->dst), NS(s->src));
26762676+ }
26772677+ acl_free(acl);
26782678+ }
20852679 }
20862086- acl_free(acl);
20872087- }
20882088- }
20892680#undef NS
20902090- break;
20912091- case COPYFILE_STAT:
20922092- (void)fchmod(s->dst_fd, s->sb.st_mode);
20932093- break;
20942094- }
20952095- filesec_free(tmp_fsec);
26812681+ break;
26822682+ case COPYFILE_STAT:
26832683+ (void)fchmod(s->dst_fd, s->sb.st_mode);
26842684+ break;
26852685+ }
26862686+ filesec_free(tmp_fsec);
20962687exit:
20972097- filesec_free(fsec_dst);
20982098- if (acl_src) acl_free(acl_src);
20992099- if (acl_dst) acl_free(acl_dst);
21002100- if (acl_tmp) acl_free(acl_tmp);
26882688+ filesec_free(fsec_dst);
26892689+ if (acl_src) acl_free(acl_src);
26902690+ if (acl_dst) acl_free(acl_dst);
26912691+ if (acl_tmp) acl_free(acl_tmp);
2101269221022102- return ret;
26932693+ return ret;
2103269421042695error_exit:
21052105- ret = -1;
21062106-goto exit;
26962696+ ret = -1;
26972697+ goto exit;
2107269821082699}
2109270021102701/*
21112702 * Attempt to set the destination file's stat information -- including
21122703 * flags and time-related fields -- to the source's.
27042704+ * Note that we must set file flags *last*, as setting a flag like
27052705+ * UF_IMMUTABLE can prevent us from setting other attributes.
21132706 */
21142707static int copyfile_stat(copyfile_state_t s)
21152708{
21162116- struct timeval tval[2];
21172117- unsigned int added_flags = 0, dst_flags = 0;
21182118- struct stat dst_sb;
27092709+ unsigned int added_flags = 0, dst_flags = 0;
27102710+ struct attrlist attrlist;
27112711+ struct stat dst_sb;
27122712+ struct {
27132713+ /* Order of these structs matters for setattrlist. */
27142714+ struct timespec mod_time;
27152715+ struct timespec acc_time;
27162716+ } ma_times;
2119271721202120- /*
21212121- * NFS doesn't support chflags; ignore errors as a result, since
21222122- * we don't return failure for this.
21232123- */
21242124- if (s->internal_flags & cfMakeFileInvisible)
21252125- added_flags |= UF_HIDDEN;
27182718+ /* Try to set m/atimes using setattrlist(), for nanosecond precision. */
27192719+ memset(&attrlist, 0, sizeof(attrlist));
27202720+ attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
27212721+ attrlist.commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME;
27222722+ ma_times.mod_time = s->sb.st_mtimespec;
27232723+ ma_times.acc_time = s->sb.st_atimespec;
27242724+ (void)fsetattrlist(s->dst_fd, &attrlist, &ma_times, sizeof(ma_times), 0);
2126272521272127- /*
21282128- * We need to check if SF_RESTRICTED was set on the destination
21292129- * by the kernel. If it was, don't drop it.
21302130- */
21312131- if (fstat(s->dst_fd, &dst_sb))
21322132- return -1;
21332133- if (dst_sb.st_flags & SF_RESTRICTED)
21342134- added_flags |= SF_RESTRICTED;
27262726+ /* If this fails, we don't care */
27272727+ (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid);
27282728+27292729+ /* This may have already been done in copyfile_security() */
27302730+ (void)fchmod(s->dst_fd, s->sb.st_mode & ~S_IFMT);
2135273121362136- /* Copy file flags, masking out any we don't want to preserve */
21372137- dst_flags = (s->sb.st_flags & ~COPYFILE_OMIT_FLAGS) | added_flags;
21382138- (void)fchflags(s->dst_fd, dst_flags);
27322732+ /*
27332733+ * NFS doesn't support chflags; ignore errors as a result, since
27342734+ * we don't return failure for this.
27352735+ */
27362736+ if (s->internal_flags & cfMakeFileInvisible)
27372737+ added_flags |= UF_HIDDEN;
2139273821402140- /* If this fails, we don't care */
21412141- (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid);
27392739+ /*
27402740+ * We need to check if certain flags were set on the destination
27412741+ * by the kernel. If they were, don't drop them.
27422742+ */
27432743+ if (fstat(s->dst_fd, &dst_sb))
27442744+ return -1;
27452745+ added_flags |= (dst_sb.st_flags & COPYFILE_PRESERVE_FLAGS);
2142274621432143- /* This may have already been done in copyfile_security() */
21442144- (void)fchmod(s->dst_fd, s->sb.st_mode & ~S_IFMT);
27472747+ /*
27482748+ * The caller requested that copyfile attempts to preserve UF_TRACKED
27492749+ * on the destination. This can be used to avoid dangling docIDs when
27502750+ * the copy races against a process that sets the flag on newly created
27512751+ * documents for instance.
27522752+ */
27532753+ if (s->flags & COPYFILE_PRESERVE_DST_TRACKED) {
27542754+ added_flags |= (dst_sb.st_flags & UF_TRACKED);
27552755+ }
2145275621462146- tval[0].tv_sec = s->sb.st_atime;
21472147- tval[1].tv_sec = s->sb.st_mtime;
21482148- tval[0].tv_usec = tval[1].tv_usec = 0;
21492149- (void)futimes(s->dst_fd, tval);
27572757+ /* Copy file flags, masking out any we don't want to preserve */
27582758+ dst_flags = (s->sb.st_flags & ~COPYFILE_OMIT_FLAGS) | added_flags;
27592759+ (void)fchflags(s->dst_fd, dst_flags);
2150276021512151- return 0;
27612761+ return 0;
21522762}
2153276321542764/*
···21632773 */
21642774static int copyfile_xattr(copyfile_state_t s)
21652775{
21662166- char *name;
21672167- char *namebuf, *end;
21682168- ssize_t xa_size;
21692169- void *xa_dataptr;
21702170- ssize_t bufsize = 4096;
21712171- ssize_t asize;
21722172- ssize_t nsize;
21732173- int ret = 0;
21742174- int look_for_decmpea = 0;
27762776+ char *name;
27772777+ char *namebuf, *end;
27782778+ ssize_t xa_size;
27792779+ void *xa_dataptr;
27802780+ ssize_t bufsize = 4096;
27812781+ ssize_t asize;
27822782+ ssize_t nsize;
27832783+ int ret = 0;
27842784+ int look_for_decmpea = 0;
2175278521762176- /* delete EAs on destination */
21772177- if ((nsize = flistxattr(s->dst_fd, 0, 0, 0)) > 0)
21782178- {
21792179- if ((namebuf = (char *) malloc(nsize)) == NULL)
21802180- return -1;
21812181- else
21822182- nsize = flistxattr(s->dst_fd, namebuf, nsize, 0);
27862786+ /* delete EAs on destination */
27872787+ if ((nsize = flistxattr(s->dst_fd, 0, 0, 0)) > 0)
27882788+ {
27892789+ if ((namebuf = (char *) malloc(nsize)) == NULL)
27902790+ return -1;
27912791+ else
27922792+ nsize = flistxattr(s->dst_fd, namebuf, nsize, 0);
2183279321842184- if (nsize > 0) {
21852185- /*
21862186- * With this, end points to the last byte of the allocated buffer
21872187- * This *should* be NUL, from flistxattr, but if it's not, we can
21882188- * set it anyway -- it'll result in a truncated name, which then
21892189- * shouldn't match when we get them later.
21902190- */
21912191- end = namebuf + nsize - 1;
21922192- if (*end != 0)
21932193- *end = 0;
21942194- for (name = namebuf; name <= end; name += strlen(name) + 1) {
21952195- /* If the quarantine information shows up as an EA, we skip over it */
21962196- if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0) {
21972197- continue;
27942794+ if (nsize > 0) {
27952795+ /*
27962796+ * With this, end points to the last byte of the allocated buffer
27972797+ * This *should* be NUL, from flistxattr, but if it's not, we can
27982798+ * set it anyway -- it'll result in a truncated name, which then
27992799+ * shouldn't match when we get them later.
28002800+ */
28012801+ end = namebuf + nsize - 1;
28022802+ if (*end != 0)
28032803+ *end = 0;
28042804+ for (name = namebuf; name <= end; name += strlen(name) + 1) {
28052805+ /* If the quarantine information shows up as an EA, we skip over it */
28062806+ if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0) {
28072807+ continue;
28082808+ }
28092809+ fremovexattr(s->dst_fd, name,0);
28102810+ }
21982811 }
21992199- fremovexattr(s->dst_fd, name,0);
22002200- }
22012201- }
22022202- free(namebuf);
22032203- } else
22042204- if (nsize < 0)
22052205- {
22062206- if (errno == ENOTSUP || errno == EPERM)
22072207- return 0;
22082208- else
22092209- return -1;
22102210- }
28122812+ free(namebuf);
28132813+ } else
28142814+ if (nsize < 0)
28152815+ {
28162816+ if (errno == ENOTSUP || errno == EPERM)
28172817+ return 0;
28182818+ else
28192819+ return -1;
28202820+ }
2211282122122822#ifdef DECMPFS_XATTR_NAME
22132213- if ((s->flags & COPYFILE_DATA) &&
22142214- (s->sb.st_flags & UF_COMPRESSED) &&
22152215- doesdecmpfs(s->src_fd) &&
22162216- doesdecmpfs(s->dst_fd)) {
22172217- look_for_decmpea = XATTR_SHOWCOMPRESSION;
22182218- }
28232823+ if ((s->flags & COPYFILE_DATA) &&
28242824+ (s->sb.st_flags & UF_COMPRESSED) &&
28252825+ doesdecmpfs(s->src_fd) &&
28262826+ doesdecmpfs(s->dst_fd)) {
28272827+ look_for_decmpea = XATTR_SHOWCOMPRESSION;
28282828+ }
22192829#endif
2220283022212221- /* get name list of EAs on source */
22222222- if ((nsize = flistxattr(s->src_fd, 0, 0, look_for_decmpea)) < 0)
22232223- {
22242224- if (errno == ENOTSUP || errno == EPERM)
22252225- return 0;
22262226- else
22272227- return -1;
22282228- } else
22292229- if (nsize == 0)
22302230- return 0;
22312231-22322232- if ((namebuf = (char *) malloc(nsize)) == NULL)
22332233- return -1;
22342234- else
22352235- nsize = flistxattr(s->src_fd, namebuf, nsize, look_for_decmpea);
22362236-22372237- if (nsize <= 0) {
22382238- free(namebuf);
22392239- return (int)nsize;
22402240- }
22412241-22422242- /*
22432243- * With this, end points to the last byte of the allocated buffer
22442244- * This *should* be NUL, from flistxattr, but if it's not, we can
22452245- * set it anyway -- it'll result in a truncated name, which then
22462246- * shouldn't match when we get them later.
22472247- */
22482248- end = namebuf + nsize - 1;
22492249- if (*end != 0)
22502250- *end = 0;
28312831+ /* get name list of EAs on source */
28322832+ if ((nsize = flistxattr(s->src_fd, 0, 0, look_for_decmpea)) < 0)
28332833+ {
28342834+ if (errno == ENOTSUP || errno == EPERM)
28352835+ return 0;
28362836+ else
28372837+ return -1;
28382838+ } else
28392839+ if (nsize == 0)
28402840+ return 0;
2251284122522252- if ((xa_dataptr = (void *) malloc(bufsize)) == NULL) {
22532253- free(namebuf);
22542254- return -1;
22552255- }
28422842+ if ((namebuf = (char *) malloc(nsize)) == NULL)
28432843+ return -1;
28442844+ else
28452845+ nsize = flistxattr(s->src_fd, namebuf, nsize, look_for_decmpea);
2256284622572257- for (name = namebuf; name <= end; name += strlen(name) + 1)
22582258- {
22592259- if (s->xattr_name) {
22602260- free(s->xattr_name);
22612261- s->xattr_name = NULL;
28472847+ if (nsize <= 0) {
28482848+ free(namebuf);
28492849+ return (int)nsize;
22622850 }
2263285122642264- /* If the quarantine information shows up as an EA, we skip over it */
22652265- if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0)
22662266- continue;
28522852+ /*
28532853+ * With this, end points to the last byte of the allocated buffer
28542854+ * This *should* be NUL, from flistxattr, but if it's not, we can
28552855+ * set it anyway -- it'll result in a truncated name, which then
28562856+ * shouldn't match when we get them later.
28572857+ */
28582858+ end = namebuf + nsize - 1;
28592859+ if (*end != 0)
28602860+ *end = 0;
2267286122682268- if ((xa_size = fgetxattr(s->src_fd, name, 0, 0, 0, look_for_decmpea)) < 0)
22692269- {
22702270- continue;
28622862+ if ((xa_dataptr = (void *) malloc(bufsize)) == NULL) {
28632863+ free(namebuf);
28642864+ return -1;
22712865 }
2272286622732273- if (xa_size > bufsize)
28672867+ for (name = namebuf; name <= end; name += strlen(name) + 1)
22742868 {
22752275- void *tdptr = xa_dataptr;
22762276- bufsize = xa_size;
22772277- if ((xa_dataptr =
22782278- (void *) realloc((void *) xa_dataptr, bufsize)) == NULL)
22792279- {
22802280- free(tdptr);
22812281- ret = -1;
22822282- continue;
22832283- }
22842284- }
28692869+ if (s->xattr_name) {
28702870+ free(s->xattr_name);
28712871+ s->xattr_name = NULL;
28722872+ }
2285287322862286- if ((asize = fgetxattr(s->src_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea)) < 0)
22872287- {
22882288- continue;
22892289- }
28742874+ /* If the quarantine information shows up as an EA, we skip over it */
28752875+ if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0)
28762876+ continue;
2290287722912291- if (xa_size != asize)
22922292- xa_size = asize;
28782878+ if ((xa_size = fgetxattr(s->src_fd, name, 0, 0, 0, look_for_decmpea)) < 0)
28792879+ {
28802880+ continue;
28812881+ }
28822882+28832883+ if (xa_size > bufsize)
28842884+ {
28852885+ void *tdptr = xa_dataptr;
28862886+ bufsize = xa_size;
28872887+ if ((xa_dataptr =
28882888+ (void *) realloc((void *) xa_dataptr, bufsize)) == NULL)
28892889+ {
28902890+ free(tdptr);
28912891+ ret = -1;
28922892+ continue;
28932893+ }
28942894+ }
28952895+28962896+ if ((asize = fgetxattr(s->src_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea)) < 0)
28972897+ {
28982898+ continue;
28992899+ }
29002900+29012901+ if (xa_size != asize)
29022902+ xa_size = asize;
2293290322942904#ifdef DECMPFS_XATTR_NAME
22952295- if (strncmp(name, DECMPFS_XATTR_NAME, end-name) == 0)
22962296- {
22972297- decmpfs_disk_header *hdr = xa_dataptr;
29052905+ if (strncmp(name, DECMPFS_XATTR_NAME, end-name) == 0)
29062906+ {
29072907+ decmpfs_disk_header *hdr = xa_dataptr;
2298290822992299- /*
23002300- * If the EA has the decmpfs name, but is too
23012301- * small, or doesn't have the right magic number,
23022302- * or isn't the right type, we'll just skip it.
23032303- * This means it won't end up in the destination
23042304- * file, and data copy will happen normally.
23052305- */
23062306- if ((size_t)xa_size < sizeof(decmpfs_disk_header)) {
23072307- continue;
23082308- }
23092309- if (OSSwapLittleToHostInt32(hdr->compression_magic) != DECMPFS_MAGIC) {
23102310- continue;
23112311- }
23122312- /*
23132313- * From AppleFSCompression documentation:
23142314- * "It is incumbent on the aware copy engine to identify
23152315- * the type of compression being used, and to perform an
23162316- * unaware copy of any file it does not recognize."
23172317- *
23182318- * Compression Types are defined in:
23192319- * "AppleFSCompression/Common/compressorCommon.h"
23202320- *
23212321- * Unfortunately, they don't provide a way to dynamically
23222322- * determine what possible compression_type values exist,
23232323- * so we have to update this every time a new compression_type
23242324- * is added (Types 7->10 were added in Yosemite)
23252325- *
23262326- * Ubiquity faulting file compression type 0x80000001 are
23272327- * deprecated as of Yosemite, per rdar://17714998 don't copy the
23282328- * decmpfs xattr on these files, zero byte files are safer
23292329- * than a fault nobody knows how to handle.
23302330- */
23312331- switch (OSSwapLittleToHostInt32(hdr->compression_type)) {
23322332- case 3: /* zlib-compressed data in xattr */
23332333- case 4: /* 64k chunked zlib-compressed data in resource fork */
29092909+ /*
29102910+ * If the EA has the decmpfs name, but is too
29112911+ * small, or doesn't have the right magic number,
29122912+ * or isn't the right type, we'll just skip it.
29132913+ * This means it won't end up in the destination
29142914+ * file, and data copy will happen normally.
29152915+ */
29162916+ if ((size_t)xa_size < sizeof(decmpfs_disk_header)) {
29172917+ continue;
29182918+ }
29192919+ if (OSSwapLittleToHostInt32(hdr->compression_magic) != DECMPFS_MAGIC) {
29202920+ continue;
29212921+ }
29222922+ /*
29232923+ * From AppleFSCompression documentation:
29242924+ * "It is incumbent on the aware copy engine to identify
29252925+ * the type of compression being used, and to perform an
29262926+ * unaware copy of any file it does not recognize."
29272927+ *
29282928+ * Compression Types are defined in:
29292929+ * "AppleFSCompression/Common/compressorCommon.h"
29302930+ *
29312931+ * Unfortunately, they don't provide a way to dynamically
29322932+ * determine what possible compression_type values exist,
29332933+ * so we have to update this every time a new compression_type
29342934+ * is added. Types 7->10 were added in 10.10, Types 11 & 12
29352935+ * were added in 10.11.
29362936+ *
29372937+ * Ubiquity faulting file compression type 0x80000001 are
29382938+ * deprecated as of Yosemite, per rdar://17714998 don't copy the
29392939+ * decmpfs xattr on these files, zero byte files are safer
29402940+ * than a fault nobody knows how to handle.
29412941+ */
29422942+ switch (OSSwapLittleToHostInt32(hdr->compression_type)) {
29432943+ case 3: /* zlib-compressed data in xattr */
29442944+ case 4: /* 64k chunked zlib-compressed data in resource fork */
2334294523352335- case 7: /* LZVN-compressed data in xattr */
23362336- case 8: /* 64k chunked LZVN-compressed data in resource fork */
29462946+ case 7: /* LZVN-compressed data in xattr */
29472947+ case 8: /* 64k chunked LZVN-compressed data in resource fork */
2337294823382338- case 9: /* uncompressed data in xattr (similar to but not identical to CMP_Type1) */
23392339- case 10: /* 64k chunked uncompressed data in resource fork */
29492949+ case 9: /* uncompressed data in xattr (similar to but not identical to CMP_Type1) */
29502950+ case 10: /* 64k chunked uncompressed data in resource fork */
2340295123412341- /* valid compression type, we want to copy. */
23422342- break;
23432343-23442344- case 5: /* specifies de-dup within the generation store. Don't copy decmpfs xattr. */
23452345- copyfile_debug(3, "compression_type <5> on attribute com.apple.decmpfs for src file %s is not copied.",
23462346- s->src ? s->src : "(null string)");
23472347- continue;
29522952+ case 11: /* LZFSE-compressed data in xattr */
29532953+ case 12: /* 64k chunked LZFSE-compressed data in resource fork */
29542954+29552955+ /* valid compression type, we want to copy. */
29562956+ break;
2348295723492349- case 6: /* unused */
23502350- case 0x80000001: /* faulting files are deprecated, don't copy decmpfs xattr */
23512351- default:
23522352- copyfile_warn("Invalid compression_type <%d> on attribute %s for src file %s",
29582958+ case 5: /* specifies de-dup within the generation store. Don't copy decmpfs xattr. */
29592959+ copyfile_debug(3, "compression_type <5> on attribute com.apple.decmpfs for src file %s is not copied.",
29602960+ s->src ? s->src : "(null string)");
29612961+ continue;
29622962+29632963+ case 6: /* unused */
29642964+ case 0x80000001: /* faulting files are deprecated, don't copy decmpfs xattr */
29652965+ default:
29662966+ copyfile_warn("Invalid compression_type <%d> on attribute %s for src file %s",
23532967 OSSwapLittleToHostInt32(hdr->compression_type), name, s->src ? s->src : "(null string)");
23542354- continue;
23552355- }
23562356- s->internal_flags |= cfSawDecmpEA;
23572357- }
29682968+ continue;
29692969+ }
29702970+ s->internal_flags |= cfSawDecmpEA;
29712971+ }
23582972#endif
2359297323602360- // If we have a copy intention stated, and the EA is to be ignored, we ignore it
23612361- if (s->copyIntent
23622362- && xattr_preserve_for_intent(name, s->copyIntent) == 0)
23632363- continue;
29742974+ // If we have a copy intention stated, and the EA is to be ignored, we ignore it
29752975+ if (s->copyIntent
29762976+ && xattr_preserve_for_intent(name, s->copyIntent) == 0)
29772977+ continue;
29782978+29792979+ s->xattr_name = strdup(name);
2364298023652365- s->xattr_name = strdup(name);
23662366-23672367- if (s->statuscb) {
23682368- int rv;
23692369- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
23702370- if (rv == COPYFILE_QUIT) {
23712371- s->err = ECANCELED;
23722372- goto out;
23732373- } else if (rv == COPYFILE_SKIP) {
23742374- continue;
29812981+ if (s->statuscb) {
29822982+ int rv;
29832983+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
29842984+ if (rv == COPYFILE_QUIT) {
29852985+ s->err = ECANCELED;
29862986+ goto out;
29872987+ } else if (rv == COPYFILE_SKIP) {
29882988+ continue;
29892989+ }
23752990 }
23762376- }
23772377- if (fsetxattr(s->dst_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea) < 0)
23782378- {
23792379- if (s->statuscb)
23802380- {
23812381- int rv;
23822382- if (s->xattr_name == NULL)
23832383- s->xattr_name = strdup(name);
23842384- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
23852385- if (rv == COPYFILE_QUIT)
29912991+ if (fsetxattr(s->dst_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea) < 0)
23862992 {
23872387- s->err = ECANCELED;
23882388- ret = -1;
23892389- goto out;
29932993+ int error = errno;
29942994+ if (error == EPERM && strcmp(name, XATTR_ROOT_INSTALLED_NAME) == 0) {
29952995+ //Silently ignore if we fail to set XATTR_ROOT_INSTALLED_NAME
29962996+ errno = error;
29972997+ continue;
29982998+ }
29992999+ else if (s->statuscb)
30003000+ {
30013001+ int rv;
30023002+ error = errno;
30033003+ if (s->xattr_name == NULL)
30043004+ s->xattr_name = strdup(name);
30053005+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
30063006+ if (rv == COPYFILE_QUIT)
30073007+ {
30083008+ s->err = ECANCELED;
30093009+ ret = -1;
30103010+ goto out;
30113011+ }
30123012+ }
30133013+ else
30143014+ {
30153015+ errno = error;
30163016+ ret = -1;
30173017+ copyfile_warn("could not set attributes %s on destination file descriptor", name);
30183018+ continue;
30193019+ }
23903020 }
23912391- }
23922392- else
23932393- {
23942394- ret = -1;
23952395- copyfile_warn("could not set attributes %s on destination file descriptor: %s", name, strerror(errno));
23962396- continue;
23972397- }
23982398- }
23992399- if (s->statuscb) {
24002400- int rv;
24012401- if (s->xattr_name == NULL)
24022402- s->xattr_name = strdup(name);
30213021+ if (s->statuscb) {
30223022+ int rv;
30233023+ if (s->xattr_name == NULL)
30243024+ s->xattr_name = strdup(name);
2403302524042404- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
24052405- if (rv == COPYFILE_QUIT) {
24062406- s->err = ECANCELED;
24072407- goto out;
30263026+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
30273027+ if (rv == COPYFILE_QUIT) {
30283028+ s->err = ECANCELED;
30293029+ goto out;
30303030+ }
24083031 }
24093032 }
24102410- }
24113033out:
24122412- if (namebuf)
24132413- free(namebuf);
24142414- free((void *) xa_dataptr);
24152415- if (s->xattr_name) {
24162416- free(s->xattr_name);
24172417- s->xattr_name = NULL;
24182418- }
24192419- return ret;
30343034+ if (namebuf)
30353035+ free(namebuf);
30363036+ free((void *) xa_dataptr);
30373037+ if (s->xattr_name) {
30383038+ free(s->xattr_name);
30393039+ s->xattr_name = NULL;
30403040+ }
30413041+ return ret;
24203042}
2421304324223044/*
···24243046 */
24253047int copyfile_state_get(copyfile_state_t s, uint32_t flag, void *ret)
24263048{
24272427- if (ret == NULL)
24282428- {
24292429- errno = EFAULT;
24302430- return -1;
24312431- }
30493049+ if (ret == NULL)
30503050+ {
30513051+ errno = EFAULT;
30523052+ return -1;
30533053+ }
2432305424332433- switch(flag)
24342434- {
24352435- case COPYFILE_STATE_SRC_FD:
24362436- *(int*)ret = s->src_fd;
24372437- break;
24382438- case COPYFILE_STATE_DST_FD:
24392439- *(int*)ret = s->dst_fd;
24402440- break;
24412441- case COPYFILE_STATE_SRC_FILENAME:
24422442- *(char**)ret = s->src;
24432443- break;
24442444- case COPYFILE_STATE_DST_FILENAME:
24452445- *(char**)ret = s->dst;
24462446- break;
24472447- case COPYFILE_STATE_QUARANTINE:
24482448- *(qtn_file_t*)ret = s->qinfo;
24492449- break;
30553055+ switch(flag)
30563056+ {
30573057+ case COPYFILE_STATE_SRC_FD:
30583058+ *(int*)ret = s->src_fd;
30593059+ break;
30603060+ case COPYFILE_STATE_DST_FD:
30613061+ *(int*)ret = s->dst_fd;
30623062+ break;
30633063+ case COPYFILE_STATE_SRC_FILENAME:
30643064+ *(char**)ret = s->src;
30653065+ break;
30663066+ case COPYFILE_STATE_DST_FILENAME:
30673067+ *(char**)ret = s->dst;
30683068+ break;
30693069+ case COPYFILE_STATE_QUARANTINE:
30703070+ *(qtn_file_t*)ret = s->qinfo;
30713071+ break;
24503072#if 0
24512451- case COPYFILE_STATE_STATS:
24522452- ret = s->stats.global;
24532453- break;
24542454- case COPYFILE_STATE_PROGRESS_CB:
24552455- ret = s->callbacks.progress;
24562456- break;
30733073+ case COPYFILE_STATE_STATS:
30743074+ ret = s->stats.global;
30753075+ break;
30763076+ case COPYFILE_STATE_PROGRESS_CB:
30773077+ ret = s->callbacks.progress;
30783078+ break;
24573079#endif
24583080#ifdef COPYFILE_STATE_STATUS_CB
24592459- case COPYFILE_STATE_STATUS_CB:
24602460- *(copyfile_callback_t*)ret = s->statuscb;
24612461- break;
24622462- case COPYFILE_STATE_STATUS_CTX:
24632463- *(void**)ret = s->ctx;
24642464- break;
24652465- case COPYFILE_STATE_COPIED:
24662466- *(off_t*)ret = s->totalCopied;
24672467- break;
30813081+ case COPYFILE_STATE_STATUS_CB:
30823082+ *(copyfile_callback_t*)ret = s->statuscb;
30833083+ break;
30843084+ case COPYFILE_STATE_STATUS_CTX:
30853085+ *(void**)ret = s->ctx;
30863086+ break;
30873087+ case COPYFILE_STATE_COPIED:
30883088+ *(off_t*)ret = s->totalCopied;
30893089+ break;
24683090#endif
24693091#ifdef COPYFILE_STATE_XATTRNAME
24702470- case COPYFILE_STATE_XATTRNAME:
24712471- *(char**)ret = s->xattr_name;
24722472- break;
30923092+ case COPYFILE_STATE_XATTRNAME:
30933093+ *(char**)ret = s->xattr_name;
30943094+ break;
24733095#endif
24743096#ifdef COPYFILE_STATE_INTENT
24752475- case COPYFILE_STATE_INTENT:
24762476- *(xattr_operation_intent_t*)ret = s->copyIntent;
24772477- break;
30973097+ case COPYFILE_STATE_INTENT:
30983098+ *(xattr_operation_intent_t*)ret = s->copyIntent;
30993099+ break;
24783100#endif
24792479- default:
24802480- errno = EINVAL;
24812481- ret = NULL;
24822482- return -1;
24832483- }
24842484- return 0;
31013101+ case COPYFILE_STATE_WAS_CLONED:
31023102+ *(bool *)ret = s->was_cloned;
31033103+ break;
31043104+ default:
31053105+ errno = EINVAL;
31063106+ ret = NULL;
31073107+ return -1;
31083108+ }
31093109+ return 0;
24853110}
2486311124873112/*
···24913116int copyfile_state_set(copyfile_state_t s, uint32_t flag, const void * thing)
24923117{
24933118#define copyfile_set_string(DST, SRC) \
24942494- do { \
24952495- if (SRC != NULL) { \
24962496- DST = strdup((char *)SRC); \
24972497- } else { \
24982498- if (DST != NULL) { \
24992499- free(DST); \
25002500- } \
25012501- DST = NULL; \
25022502- } \
25032503- } while (0)
31193119+ do { \
31203120+ if (SRC != NULL) { \
31213121+ DST = strdup((char *)SRC); \
31223122+ } else { \
31233123+ if (DST != NULL) { \
31243124+ free(DST); \
31253125+ } \
31263126+ DST = NULL; \
31273127+ } \
31283128+ } while (0)
2504312925052505- if (thing == NULL)
25062506- {
25072507- errno = EFAULT;
25082508- return -1;
25092509- }
31303130+ if (thing == NULL)
31313131+ {
31323132+ errno = EFAULT;
31333133+ return -1;
31343134+ }
2510313525112511- switch(flag)
25122512- {
25132513- case COPYFILE_STATE_SRC_FD:
25142514- s->src_fd = *(int*)thing;
25152515- break;
25162516- case COPYFILE_STATE_DST_FD:
25172517- s->dst_fd = *(int*)thing;
25182518- break;
25192519- case COPYFILE_STATE_SRC_FILENAME:
25202520- copyfile_set_string(s->src, thing);
25212521- break;
25222522- case COPYFILE_STATE_DST_FILENAME:
25232523- copyfile_set_string(s->dst, thing);
25242524- break;
25252525- case COPYFILE_STATE_QUARANTINE:
25262526- if (s->qinfo)
25272527- {
25282528- qtn_file_free(s->qinfo);
25292529- s->qinfo = NULL;
25302530- }
25312531- if (*(qtn_file_t*)thing)
25322532- s->qinfo = qtn_file_clone(*(qtn_file_t*)thing);
25332533- break;
31363136+ switch(flag)
31373137+ {
31383138+ case COPYFILE_STATE_SRC_FD:
31393139+ s->src_fd = *(int*)thing;
31403140+ break;
31413141+ case COPYFILE_STATE_DST_FD:
31423142+ s->dst_fd = *(int*)thing;
31433143+ break;
31443144+ case COPYFILE_STATE_SRC_FILENAME:
31453145+ copyfile_set_string(s->src, thing);
31463146+ break;
31473147+ case COPYFILE_STATE_DST_FILENAME:
31483148+ copyfile_set_string(s->dst, thing);
31493149+ break;
31503150+ case COPYFILE_STATE_QUARANTINE:
31513151+ if (s->qinfo)
31523152+ {
31533153+ qtn_file_free(s->qinfo);
31543154+ s->qinfo = NULL;
31553155+ }
31563156+ if (*(qtn_file_t*)thing)
31573157+ s->qinfo = qtn_file_clone(*(qtn_file_t*)thing);
31583158+ break;
25343159#if 0
25352535- case COPYFILE_STATE_STATS:
25362536- s->stats.global = thing;
25372537- break;
25382538- case COPYFILE_STATE_PROGRESS_CB:
25392539- s->callbacks.progress = thing;
25402540- break;
31603160+ case COPYFILE_STATE_STATS:
31613161+ s->stats.global = thing;
31623162+ break;
31633163+ case COPYFILE_STATE_PROGRESS_CB:
31643164+ s->callbacks.progress = thing;
31653165+ break;
25413166#endif
25423167#ifdef COPYFILE_STATE_STATUS_CB
25432543- case COPYFILE_STATE_STATUS_CB:
25442544- s->statuscb = (copyfile_callback_t)thing;
25452545- break;
25462546- case COPYFILE_STATE_STATUS_CTX:
25472547- s->ctx = (void*)thing;
25482548- break;
31683168+ case COPYFILE_STATE_STATUS_CB:
31693169+ s->statuscb = (copyfile_callback_t)thing;
31703170+ break;
31713171+ case COPYFILE_STATE_STATUS_CTX:
31723172+ s->ctx = (void*)thing;
31733173+ break;
25493174#endif
25503175#ifdef COPYFILE_STATE_INTENT
25512551- case COPYFILE_STATE_INTENT:
25522552- s->copyIntent = *(xattr_operation_intent_t*)thing;
25532553- break;
31763176+ case COPYFILE_STATE_INTENT:
31773177+ s->copyIntent = *(xattr_operation_intent_t*)thing;
31783178+ break;
25543179#endif
25552555- default:
25562556- errno = EINVAL;
25572557- return -1;
25582558- }
25592559- return 0;
31803180+ default:
31813181+ errno = EINVAL;
31823182+ return -1;
31833183+ }
31843184+ return 0;
25603185#undef copyfile_set_string
25613186}
25623187···25693194#define COPYFILE_OPTION(x) { #x, COPYFILE_ ## x },
2570319525713196struct {char *s; int v;} opts[] = {
25722572- COPYFILE_OPTION(ACL)
25732573- COPYFILE_OPTION(STAT)
25742574- COPYFILE_OPTION(XATTR)
25752575- COPYFILE_OPTION(DATA)
25762576- COPYFILE_OPTION(SECURITY)
25772577- COPYFILE_OPTION(METADATA)
25782578- COPYFILE_OPTION(ALL)
25792579- COPYFILE_OPTION(NOFOLLOW_SRC)
25802580- COPYFILE_OPTION(NOFOLLOW_DST)
25812581- COPYFILE_OPTION(NOFOLLOW)
25822582- COPYFILE_OPTION(EXCL)
25832583- COPYFILE_OPTION(MOVE)
25842584- COPYFILE_OPTION(UNLINK)
25852585- COPYFILE_OPTION(PACK)
25862586- COPYFILE_OPTION(UNPACK)
25872587- COPYFILE_OPTION(CHECK)
25882588- COPYFILE_OPTION(VERBOSE)
25892589- COPYFILE_OPTION(DEBUG)
25902590- {NULL, 0}
31973197+ COPYFILE_OPTION(ACL)
31983198+ COPYFILE_OPTION(STAT)
31993199+ COPYFILE_OPTION(XATTR)
32003200+ COPYFILE_OPTION(DATA)
32013201+ COPYFILE_OPTION(SECURITY)
32023202+ COPYFILE_OPTION(METADATA)
32033203+ COPYFILE_OPTION(ALL)
32043204+ COPYFILE_OPTION(NOFOLLOW_SRC)
32053205+ COPYFILE_OPTION(NOFOLLOW_DST)
32063206+ COPYFILE_OPTION(NOFOLLOW)
32073207+ COPYFILE_OPTION(EXCL)
32083208+ COPYFILE_OPTION(MOVE)
32093209+ COPYFILE_OPTION(UNLINK)
32103210+ COPYFILE_OPTION(PACK)
32113211+ COPYFILE_OPTION(UNPACK)
32123212+ COPYFILE_OPTION(CHECK)
32133213+ COPYFILE_OPTION(CLONE)
32143214+ COPYFILE_OPTION(CLONE_FORCE)
32153215+ COPYFILE_OPTION(VERBOSE)
32163216+ COPYFILE_OPTION(RECURSIVE)
32173217+ COPYFILE_OPTION(DEBUG)
32183218+ COPYFILE_OPTION(CLONE)
32193219+ COPYFILE_OPTION(CLONE_FORCE)
32203220+ COPYFILE_OPTION(DATA_SPARSE)
32213221+ {NULL, 0}
25913222};
2592322325933224int main(int c, char *v[])
25943225{
25952595- int i;
25962596- int flags = 0;
32263226+ int i, ret;
32273227+ int flags = 0;
32283228+ copyfile_state_t state = NULL;
2597322925982598- if (c < 3)
25992599- errx(1, "insufficient arguments");
32303230+ if (c < 3)
32313231+ errx(1, "insufficient arguments");
2600323226012601- while(c-- > 3)
26022602- {
26032603- for (i = 0; opts[i].s != NULL; ++i)
32333233+ while(c-- > 3)
26043234 {
26052605- if (strcasecmp(opts[i].s, v[c]) == 0)
26062606- {
26072607- printf("option %d: %s <- %d\n", c, opts[i].s, opts[i].v);
26082608- flags |= opts[i].v;
26092609- break;
26102610- }
32353235+ for (i = 0; opts[i].s != NULL; ++i)
32363236+ {
32373237+ if (strcasecmp(opts[i].s, v[c]) == 0)
32383238+ {
32393239+ printf("option %d: %s <- %d\n", c, opts[i].s, opts[i].v);
32403240+ flags |= opts[i].v;
32413241+ break;
32423242+ }
32433243+ }
32443244+ }
32453245+32463246+ if (flags & COPYFILE_DEBUG) {
32473247+ state = copyfile_state_alloc();
32483248+ state->debug = 10; // Turn on all debug statements
32493249+ }
32503250+ ret = copyfile(v[1], v[2], state, flags);
32513251+ if (state) {
32523252+ (void)copyfile_state_free(state);
26113253 }
26122612- }
2613325426142614- return copyfile(v[1], v[2], NULL, flags);
32553255+ return ret;
26153256}
26163257#endif
26173258/*
···26223263 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
26233264 */
2624326526252625-26262626-#define offsetof(type, member) ((size_t)(&((type *)0)->member))
26272627-26283266#define XATTR_MAXATTRLEN (16*1024*1024)
262932672630326826313269/*
26322632- Typical "._" AppleDouble Header File layout:
26332633- ------------------------------------------------------------
26342634- MAGIC 0x00051607
26352635- VERSION 0x00020000
26362636- FILLER 0
26372637- COUNT 2
26382638- .-- AD ENTRY[0] Finder Info Entry (must be first)
26392639- .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
26402640- | '-> FINDER INFO
26412641- | ///////////// Fixed Size Data (32 bytes)
26422642- | EXT ATTR HDR
26432643- | /////////////
26442644- | ATTR ENTRY[0] --.
26452645- | ATTR ENTRY[1] --+--.
26462646- | ATTR ENTRY[2] --+--+--.
26472647- | ... | | |
26482648- | ATTR ENTRY[N] --+--+--+--.
26492649- | ATTR DATA 0 <-' | | |
26502650- | //////////// | | |
26512651- | ATTR DATA 1 <----' | |
26522652- | ///////////// | |
26532653- | ATTR DATA 2 <-------' |
26542654- | ///////////// |
26552655- | ... |
26562656- | ATTR DATA N <----------'
26572657- | /////////////
26582658- | Attribute Free Space
26592659- |
26602660- '----> RESOURCE FORK
26612661- ///////////// Variable Sized Data
26622662- /////////////
26632663- /////////////
26642664- /////////////
26652665- /////////////
26662666- /////////////
26672667- ...
26682668- /////////////
26692669-26702670- ------------------------------------------------------------
32703270+ Typical "._" AppleDouble Header File layout:
32713271+ ------------------------------------------------------------
32723272+ MAGIC 0x00051607
32733273+ VERSION 0x00020000
32743274+ FILLER 0
32753275+ COUNT 2
32763276+ .-- AD ENTRY[0] Finder Info Entry (must be first)
32773277+ .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
32783278+ | '-> FINDER INFO
32793279+ | ///////////// Fixed Size Data (32 bytes)
32803280+ | EXT ATTR HDR
32813281+ | /////////////
32823282+ | ATTR ENTRY[0] --.
32833283+ | ATTR ENTRY[1] --+--.
32843284+ | ATTR ENTRY[2] --+--+--.
32853285+ | ... | | |
32863286+ | ATTR ENTRY[N] --+--+--+--.
32873287+ | ATTR DATA 0 <-' | | |
32883288+ | //////////// | | |
32893289+ | ATTR DATA 1 <----' | |
32903290+ | ///////////// | |
32913291+ | ATTR DATA 2 <-------' |
32923292+ | ///////////// |
32933293+ | ... |
32943294+ | ATTR DATA N <----------'
32953295+ | /////////////
32963296+ | Attribute Free Space
32973297+ |
32983298+ '----> RESOURCE FORK
32993299+ ///////////// Variable Sized Data
33003300+ /////////////
33013301+ /////////////
33023302+ /////////////
33033303+ /////////////
33043304+ /////////////
33053305+ ...
33063306+ /////////////
2671330726722672- NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
26732673- stored as part of the Finder Info. The length in the Finder
26742674- Info AppleDouble entry includes the length of the extended
26752675- attribute header, attribute entries, and attribute data.
26762676-*/
33083308+ ------------------------------------------------------------
33093309+33103310+ NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
33113311+ stored as part of the Finder Info. The length in the Finder
33123312+ Info AppleDouble entry includes the length of the extended
33133313+ attribute header, attribute entries, and attribute data.
33143314+ */
267733152678331626793317/*
···27063344#define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
27073345#define AD_AFPNAME 13 /* Short name on AFP server */
27083346#define AD_AFPINFO 14 /* AFP file info, attrib., etc */
27092709-#define AD_AFPDIRID 15 /* AFP directory ID */
33473347+#define AD_AFPDIRID 15 /* AFP directory ID */
27103348#define AD_ATTRIBUTES AD_FINDERINFO
2711334927123350···2731336927323370typedef struct apple_double_entry
27333371{
27342734- u_int32_t type; /* entry type: see list, 0 invalid */
33723372+ u_int32_t type; /* entry type: see list, 0 invalid */
27353373 u_int32_t offset; /* entry data offset from the beginning of the file. */
27363374 u_int32_t length; /* entry data length in bytes. */
27373375} __attribute__((aligned(2), packed)) apple_double_entry_t;
···27403378typedef struct apple_double_header
27413379{
27423380 u_int32_t magic; /* == ADH_MAGIC */
27432743- u_int32_t version; /* format version: 2 = 0x00020000 */
33813381+ u_int32_t version; /* format version: 2 = 0x00020000 */
27443382 u_int32_t filler[4];
27452745- u_int16_t numEntries; /* number of entries which follow */
33833383+ u_int16_t numEntries; /* number of entries which follow */
27463384 apple_double_entry_t entries[2]; /* 'finfo' & 'rsrc' always exist */
27473385 u_int8_t finfo[FINDERINFOSIZE]; /* Must start with Finder Info (32 bytes) */
27483386 u_int8_t pad[2]; /* get better alignment inside attr_header */
···27553393 u_int32_t offset; /* file offset to data */
27563394 u_int32_t length; /* size of attribute data */
27573395 u_int16_t flags;
27582758- u_int8_t namelen; /* length of name including NULL termination char */
33963396+ u_int8_t namelen; /* length of name including NULL termination char */
27593397 u_int8_t name[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
27603398} __attribute__((aligned(2), packed)) attr_entry_t;
27613399···27673405 apple_double_header_t appledouble;
27683406 u_int32_t magic; /* == ATTR_HDR_MAGIC */
27693407 u_int32_t debug_tag; /* for debugging == file id of owning file */
27702770- u_int32_t total_size; /* total size of attribute header + entries + data */
34083408+ u_int32_t total_size; /* total size of attribute header + entries + data */
27713409 u_int32_t data_start; /* file offset to attribute data area */
27723410 u_int32_t data_length; /* length of attribute data area */
27733411 u_int32_t reserved[3];
···27843422 u_int32_t fh_MapLength;
27853423 u_int8_t systemData[112];
27863424 u_int8_t appData[128];
27872787- u_int32_t mh_DataOffset;
34253425+ u_int32_t mh_DataOffset;
27883426 u_int32_t mh_MapOffset;
27892789- u_int32_t mh_DataLength;
34273427+ u_int32_t mh_DataLength;
27903428 u_int32_t mh_MapLength;
27913429 u_int32_t mh_Next;
27923430 u_int16_t mh_RefNum;
27933431 u_int8_t mh_Attr;
27942794- u_int8_t mh_InMemoryAttr;
34323432+ u_int8_t mh_InMemoryAttr;
27953433 u_int16_t mh_Types;
27963434 u_int16_t mh_Names;
27973435 u_int16_t typeCount;
27983436} __attribute__((aligned(2), packed)) rsrcfork_header_t;
27993437#define RF_FIRST_RESOURCE 256
28002800-#define RF_NULL_MAP_LENGTH 30
34383438+#define RF_NULL_MAP_LENGTH 30
28013439#define RF_EMPTY_TAG "This resource fork intentionally left blank "
2802344028033441static const rsrcfork_header_t empty_rsrcfork_header = {
···28273465#define ATTR_ALIGN 3L /* Use four-byte alignment */
2828346628293467#define ATTR_ENTRY_LENGTH(namelen) \
28302830- ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
34683468+ ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
2831346928323470#define ATTR_NEXT(ae) \
28332833- (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
34713471+ (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
2834347228353473#define XATTR_SECURITY_NAME "com.apple.acl.text"
2836347428373475/*
28382838- * Endian swap Apple Double header
34763476+ * Endian swap Apple Double header
28393477 */
28403478static void
28413479swap_adhdr(apple_double_header_t *adh)
···28683506swap_attrhdr_entry(attr_entry_t *ae)
28693507{
28703508#if BYTE_ORDER == LITTLE_ENDIAN
28712871- ae->offset = SWAP32 (ae->offset);
28722872- ae->length = SWAP32 (ae->length);
28732873- ae->flags = SWAP16 (ae->flags);
35093509+ ae->offset = SWAP32 (ae->offset);
35103510+ ae->length = SWAP32 (ae->length);
35113511+ ae->flags = SWAP16 (ae->flags);
28743512#else
28752875- (void)ae;
35133513+ (void)ae;
28763514#endif
28773515}
28783516···28843522swap_attrhdr_entries(attr_header_t *ah)
28853523{
28863524#if BYTE_ORDER == LITTLE_ENDIAN
28872887- int i;
28882888- int count;
28892889- attr_entry_t *entry;
28902890- attr_entry_t *next;
35253525+ int i;
35263526+ int count;
35273527+ attr_entry_t *entry;
35283528+ attr_entry_t *next;
2891352928922892- /* If we're in copyfile_pack, num_args is native endian,
28932893- * if we're in _unpack, num_args is big endian. Use
28942894- * the magic number to test for endianess.
28952895- */
28962896- count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs);
35303530+ /* If we're in copyfile_pack, num_args is native endian,
35313531+ * if we're in _unpack, num_args is big endian. Use
35323532+ * the magic number to test for endianess.
35333533+ */
35343534+ count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs);
2897353528982898- entry = (attr_entry_t *)(&ah[1]);
28992899- for (i = 0; i < count; i++) {
29002900- next = ATTR_NEXT(entry);
29012901- swap_attrhdr_entry(entry);
29022902- entry = next;
29032903- }
35363536+ entry = (attr_entry_t *)(&ah[1]);
35373537+ for (i = 0; i < count; i++) {
35383538+ next = ATTR_NEXT(entry);
35393539+ swap_attrhdr_entry(entry);
35403540+ entry = next;
35413541+ }
29043542#else
29052905- (void)ah;
35433543+ (void)ah;
29063544#endif
29073545}
2908354629093547/*
29102910- * Endian swap extended attributes header
35483548+ * Endian swap extended attributes header
29113549 */
29123550static void
29133551swap_attrhdr(attr_header_t *ah)
···29343572 */
29353573static int copyfile_unpack(copyfile_state_t s)
29363574{
29372937- ssize_t bytes;
29382938- void * buffer, * endptr, * dataptr = NULL;
29392939- apple_double_header_t *adhdr;
29402940- ssize_t hdrsize;
29412941- int error = 0;
29422942-29432943- if (s->sb.st_size < ATTR_MAX_HDR_SIZE)
29442944- hdrsize = (ssize_t)s->sb.st_size;
29452945- else
29462946- hdrsize = ATTR_MAX_HDR_SIZE;
29472947-29482948- buffer = calloc(1, hdrsize);
29492949- if (buffer == NULL) {
29502950- copyfile_debug(1, "copyfile_unpack: calloc(1, %zu) returned NULL", hdrsize);
29512951- error = -1;
29522952- goto exit;
29532953- } else
29542954- endptr = (char*)buffer + hdrsize;
35753575+ ssize_t bytes;
35763576+ void * buffer, * endptr, * dataptr = NULL;
35773577+ apple_double_header_t *adhdr;
35783578+ ssize_t hdrsize;
35793579+ int error = 0;
2955358029562956- bytes = pread(s->src_fd, buffer, hdrsize, 0);
35813581+ if (s->sb.st_size < ATTR_MAX_HDR_SIZE)
35823582+ hdrsize = (ssize_t)s->sb.st_size;
35833583+ else
35843584+ hdrsize = ATTR_MAX_HDR_SIZE;
2957358529582958- if (bytes < 0)
29592959- {
29602960- copyfile_debug(1, "pread returned: %zd", bytes);
29612961- error = -1;
29622962- goto exit;
29632963- }
29642964- if (bytes < hdrsize)
29652965- {
29662966- copyfile_debug(1,
29672967- "pread couldn't read entire header: %d of %d",
29682968- (int)bytes, (int)s->sb.st_size);
29692969- error = -1;
29702970- goto exit;
29712971- }
29722972- adhdr = (apple_double_header_t *)buffer;
35863586+ buffer = calloc(1, hdrsize);
35873587+ if (buffer == NULL) {
35883588+ copyfile_debug(1, "copyfile_unpack: calloc(1, %zu) returned NULL", hdrsize);
35893589+ error = -1;
35903590+ goto exit;
35913591+ } else
35923592+ endptr = (char*)buffer + hdrsize;
2973359329742974- /*
29752975- * Check for Apple Double file.
29762976- */
29772977- if ((size_t)bytes < sizeof(apple_double_header_t) - 2 ||
29782978- SWAP32(adhdr->magic) != ADH_MAGIC ||
29792979- SWAP32(adhdr->version) != ADH_VERSION ||
29802980- SWAP16(adhdr->numEntries) != 2 ||
29812981- SWAP32(adhdr->entries[0].type) != AD_FINDERINFO)
29822982- {
29832983- if (COPYFILE_VERBOSE & s->flags)
29842984- copyfile_warn("Not a valid Apple Double header");
29852985- error = -1;
29862986- goto exit;
29872987- }
29882988- swap_adhdr(adhdr);
35943594+ bytes = pread(s->src_fd, buffer, hdrsize, 0);
2989359529902990- /*
29912991- * Remove any extended attributes on the target.
29922992- */
35963596+ if (bytes < 0)
35973597+ {
35983598+ copyfile_debug(1, "pread returned: %zd", bytes);
35993599+ error = -1;
36003600+ goto exit;
36013601+ }
36023602+ if (bytes < hdrsize)
36033603+ {
36043604+ copyfile_debug(1,
36053605+ "pread couldn't read entire header: %d of %d",
36063606+ (int)bytes, (int)s->sb.st_size);
36073607+ error = -1;
36083608+ goto exit;
36093609+ }
36103610+ adhdr = (apple_double_header_t *)buffer;
2993361129942994- if ((bytes = flistxattr(s->dst_fd, 0, 0, 0)) > 0)
29952995- {
29962996- char *namebuf, *name;
29972997-29982998- if ((namebuf = (char*) malloc(bytes)) == NULL)
36123612+ /*
36133613+ * Check for Apple Double file.
36143614+ */
36153615+ if ((size_t)bytes < sizeof(apple_double_header_t) - 2 ||
36163616+ SWAP32(adhdr->magic) != ADH_MAGIC ||
36173617+ SWAP32(adhdr->version) != ADH_VERSION ||
36183618+ SWAP16(adhdr->numEntries) != 2 ||
36193619+ SWAP32(adhdr->entries[0].type) != AD_FINDERINFO)
29993620 {
30003000- s->err = ENOMEM;
30013001- goto exit;
36213621+ if (COPYFILE_VERBOSE & s->flags)
36223622+ copyfile_warn("Not a valid Apple Double header");
36233623+ error = -1;
36243624+ goto exit;
30023625 }
30033003- bytes = flistxattr(s->dst_fd, namebuf, bytes, 0);
36263626+ swap_adhdr(adhdr);
3004362730053005- if (bytes > 0)
30063006- for (name = namebuf; name < namebuf + bytes; name += strlen(name) + 1)
30073007- (void)fremovexattr(s->dst_fd, name, 0);
36283628+ /*
36293629+ * Remove any extended attributes on the target.
36303630+ */
3008363130093009- free(namebuf);
30103010- }
30113011- else if (bytes < 0)
30123012- {
30133013- if (errno != ENOTSUP && errno != EPERM)
30143014- goto exit;
30153015- }
36323632+ if ((bytes = flistxattr(s->dst_fd, 0, 0, 0)) > 0)
36333633+ {
36343634+ char *namebuf, *name;
3016363530173017- /*
30183018- * Extract the extended attributes.
30193019- *
30203020- * >>> WARNING <<<
30213021- * This assumes that the data is already in memory (not
30223022- * the case when there are lots of attributes or one of
30233023- * the attributes is very large.
30243024- */
30253025- if (adhdr->entries[0].length > FINDERINFOSIZE)
30263026- {
30273027- attr_header_t *attrhdr;
30283028- attr_entry_t *entry;
30293029- int count;
30303030- int i;
36363636+ if ((namebuf = (char*) malloc(bytes)) == NULL)
36373637+ {
36383638+ s->err = ENOMEM;
36393639+ goto exit;
36403640+ }
36413641+ bytes = flistxattr(s->dst_fd, namebuf, bytes, 0);
36423642+36433643+ if (bytes > 0)
36443644+ for (name = namebuf; name < namebuf + bytes; name += strlen(name) + 1)
36453645+ (void)fremovexattr(s->dst_fd, name, 0);
3031364630323032- if ((size_t)hdrsize < sizeof(attr_header_t)) {
30333033- copyfile_warn("bad attribute header: %zu < %zu", hdrsize, sizeof(attr_header_t));
30343034- error = -1;
30353035- goto exit;
36473647+ free(namebuf);
30363648 }
30373037-30383038- attrhdr = (attr_header_t *)buffer;
30393039- swap_attrhdr(attrhdr);
30403040- if (attrhdr->magic != ATTR_HDR_MAGIC)
36493649+ else if (bytes < 0)
30413650 {
30423042- if (COPYFILE_VERBOSE & s->flags)
30433043- copyfile_warn("bad attribute header");
30443044- error = -1;
30453045- goto exit;
36513651+ if (errno != ENOTSUP && errno != EPERM)
36523652+ goto exit;
30463653 }
30473047- count = attrhdr->num_attrs;
30483048- entry = (attr_entry_t *)&attrhdr[1];
3049365430503050- for (i = 0; i < count; i++)
36553655+ /*
36563656+ * Extract the extended attributes.
36573657+ *
36583658+ * >>> WARNING <<<
36593659+ * This assumes that the data is already in memory (not
36603660+ * the case when there are lots of attributes or one of
36613661+ * the attributes is very large.
36623662+ */
36633663+ if (adhdr->entries[0].length > FINDERINFOSIZE)
30513664 {
30523052- /*
30533053- * First we do some simple sanity checking.
30543054- * +) See if entry is within the buffer's range;
30553055- *
30563056- * +) Check the attribute name length; if it's longer than the
30573057- * maximum, we truncate it down. (We could error out as well;
30583058- * I'm not sure which is the better way to go here.)
30593059- *
30603060- * +) If, given the name length, it goes beyond the end of
30613061- * the buffer, error out.
30623062- *
30633063- * +) If the last byte isn't a NUL, make it a NUL. (Since we
30643064- * truncated the name length above, we truncate the name here.)
30653065- *
30663066- * +) If entry->offset is so large that it causes dataptr to
30673067- * go beyond the end of the buffer -- or, worse, so large that
30683068- * it wraps around! -- we error out.
30693069- *
30703070- * +) If entry->length would cause the entry to go beyond the
30713071- * end of the buffer (or, worse, wrap around to before it),
30723072- * *or* if the length is larger than the hdrsize, we error out.
30733073- * (An explanation of that: what we're checking for there is
30743074- * the small range of values such that offset+length would cause
30753075- * it to go beyond endptr, and then wrap around past buffer. We
30763076- * care about this because we are passing entry->length down to
30773077- * fgetxattr() below, and an erroneously large value could cause
30783078- * problems there. By making sure that it's less than hdrsize,
30793079- * which has already been sanity-checked above, we're safe.
30803080- * That may mean that the check against < buffer is unnecessary.)
30813081- */
30823082- if ((void*)entry >= endptr || (void*)entry < buffer) {
30833083- if (COPYFILE_VERBOSE & s->flags)
30843084- copyfile_warn("Incomplete or corrupt attribute entry");
30853085- error = -1;
30863086- s->err = EINVAL;
30873087- goto exit;
30883088- }
36653665+ attr_header_t *attrhdr;
36663666+ attr_entry_t *entry;
36673667+ int count;
36683668+ int i;
3089366930903090- if (((char*)entry + sizeof(*entry)) > (char*)endptr) {
30913091- if (COPYFILE_VERBOSE & s->flags)
30923092- copyfile_warn("Incomplete or corrupt attribute entry");
30933093- error = -1;
30943094- s->err = EINVAL;
30953095- goto exit;
30963096- }
30973097-30983098- /*
30993099- * Endian swap the entry we're looking at. Previously
31003100- * we did this swap as part of swap_attrhdr, but that
31013101- * allowed a maliciously constructed file to overrun
31023102- * our allocation. Instead do the swap after we've verified
31033103- * the entry struct is within the buffer's range.
31043104- */
31053105- swap_attrhdr_entry(entry);
36703670+ if ((size_t)hdrsize < sizeof(attr_header_t)) {
36713671+ copyfile_warn("bad attribute header: %zu < %zu", hdrsize, sizeof(attr_header_t));
36723672+ error = -1;
36733673+ goto exit;
36743674+ }
3106367531073107- if (entry->namelen < 2) {
31083108- if (COPYFILE_VERBOSE & s->flags)
31093109- copyfile_warn("Corrupt attribute entry (only %d bytes)", entry->namelen);
31103110- error = -1;
31113111- s->err = EINVAL;
31123112- goto exit;
31133113- }
36763676+ attrhdr = (attr_header_t *)buffer;
36773677+ swap_attrhdr(attrhdr);
36783678+ if (attrhdr->magic != ATTR_HDR_MAGIC)
36793679+ {
36803680+ if (COPYFILE_VERBOSE & s->flags)
36813681+ copyfile_warn("bad attribute header");
36823682+ error = -1;
36833683+ goto exit;
36843684+ }
36853685+ count = attrhdr->num_attrs;
36863686+ entry = (attr_entry_t *)&attrhdr[1];
3114368731153115- if (entry->namelen > XATTR_MAXNAMELEN + 1) {
31163116- if (COPYFILE_VERBOSE & s->flags)
31173117- copyfile_warn("Corrupt attribute entry (name length is %d bytes)", entry->namelen);
31183118- error = -1;
31193119- s->err = EINVAL;
31203120- goto exit;
31213121- }
36883688+ for (i = 0; i < count; i++)
36893689+ {
36903690+ /*
36913691+ * First we do some simple sanity checking.
36923692+ * +) See if entry is within the buffer's range;
36933693+ *
36943694+ * +) Check the attribute name length; if it's longer than the
36953695+ * maximum, we truncate it down. (We could error out as well;
36963696+ * I'm not sure which is the better way to go here.)
36973697+ *
36983698+ * +) If, given the name length, it goes beyond the end of
36993699+ * the buffer, error out.
37003700+ *
37013701+ * +) If the last byte isn't a NUL, make it a NUL. (Since we
37023702+ * truncated the name length above, we truncate the name here.)
37033703+ *
37043704+ * +) If entry->offset is so large that it causes dataptr to
37053705+ * go beyond the end of the buffer -- or, worse, so large that
37063706+ * it wraps around! -- we error out.
37073707+ *
37083708+ * +) If entry->length would cause the entry to go beyond the
37093709+ * end of the buffer (or, worse, wrap around to before it),
37103710+ * *or* if the length is larger than the hdrsize, we error out.
37113711+ * (An explanation of that: what we're checking for there is
37123712+ * the small range of values such that offset+length would cause
37133713+ * it to go beyond endptr, and then wrap around past buffer. We
37143714+ * care about this because we are passing entry->length down to
37153715+ * fgetxattr() below, and an erroneously large value could cause
37163716+ * problems there. By making sure that it's less than hdrsize,
37173717+ * which has already been sanity-checked above, we're safe.
37183718+ * That may mean that the check against < buffer is unnecessary.)
37193719+ */
37203720+ if ((void*)entry >= endptr || (void*)entry < buffer) {
37213721+ if (COPYFILE_VERBOSE & s->flags)
37223722+ copyfile_warn("Incomplete or corrupt attribute entry");
37233723+ error = -1;
37243724+ s->err = EINVAL;
37253725+ goto exit;
37263726+ }
3122372731233123- if ((void*)(entry->name + entry->namelen) > endptr) {
31243124- if (COPYFILE_VERBOSE & s->flags)
31253125- copyfile_warn("Incomplete or corrupt attribute entry");
31263126- error = -1;
31273127- s->err = EINVAL;
31283128- goto exit;
31293129- }
37283728+ if (((char*)entry + sizeof(*entry)) > (char*)endptr) {
37293729+ if (COPYFILE_VERBOSE & s->flags)
37303730+ copyfile_warn("Incomplete or corrupt attribute entry");
37313731+ error = -1;
37323732+ s->err = EINVAL;
37333733+ goto exit;
37343734+ }
3130373531313131- /* Because namelen includes the NUL, we check one byte back */
31323132- if (entry->name[entry->namelen-1] != 0) {
31333133- if (COPYFILE_VERBOSE & s->flags)
31343134- copyfile_warn("Corrupt attribute entry (name is not NUL-terminated)");
31353135- error = -1;
31363136- s->err = EINVAL;
31373137- goto exit;
31383138- }
37363736+ /*
37373737+ * Endian swap the entry we're looking at. Previously
37383738+ * we did this swap as part of swap_attrhdr, but that
37393739+ * allowed a maliciously constructed file to overrun
37403740+ * our allocation. Instead do the swap after we've verified
37413741+ * the entry struct is within the buffer's range.
37423742+ */
37433743+ swap_attrhdr_entry(entry);
3139374431403140- copyfile_debug(3, "extracting \"%s\" (%d bytes) at offset %u",
31413141- entry->name, entry->length, entry->offset);
37453745+ if (entry->namelen < 2) {
37463746+ if (COPYFILE_VERBOSE & s->flags)
37473747+ copyfile_warn("Corrupt attribute entry (only %d bytes)", entry->namelen);
37483748+ error = -1;
37493749+ s->err = EINVAL;
37503750+ goto exit;
37513751+ }
3142375231433143-#if 0
31443144- dataptr = (char *)attrhdr + entry->offset;
37533753+ if (entry->namelen > XATTR_MAXNAMELEN + 1) {
37543754+ if (COPYFILE_VERBOSE & s->flags)
37553755+ copyfile_warn("Corrupt attribute entry (name length is %d bytes)", entry->namelen);
37563756+ error = -1;
37573757+ s->err = EINVAL;
37583758+ goto exit;
37593759+ }
3145376031463146- if (dataptr > endptr || dataptr < buffer) {
31473147- copyfile_debug(1, "Entry %d overflows: offset = %u", i, entry->offset);
31483148- error = -1;
31493149- s->err = EINVAL; /* Invalid buffer */
31503150- goto exit;
31513151- }
37613761+ if ((void*)(entry->name + entry->namelen) > endptr) {
37623762+ if (COPYFILE_VERBOSE & s->flags)
37633763+ copyfile_warn("Incomplete or corrupt attribute entry");
37643764+ error = -1;
37653765+ s->err = EINVAL;
37663766+ goto exit;
37673767+ }
3152376831533153- if (((char*)dataptr + entry->length) > (char*)endptr ||
31543154- (((char*)dataptr + entry->length) < (char*)buffer) ||
31553155- (entry->length > (size_t)hdrsize)) {
31563156- if (COPYFILE_VERBOSE & s->flags)
31573157- copyfile_warn("Incomplete or corrupt attribute entry");
31583158- copyfile_debug(1, "Entry %d length overflows: offset = %u, length = %u",
31593159- i, entry->offset, entry->length);
31603160- error = -1;
31613161- s->err = EINVAL; /* Invalid buffer */
31623162- goto exit;
31633163- }
37693769+ /* Because namelen includes the NUL, we check one byte back */
37703770+ if (entry->name[entry->namelen-1] != 0) {
37713771+ if (COPYFILE_VERBOSE & s->flags)
37723772+ copyfile_warn("Corrupt attribute entry (name is not NUL-terminated)");
37733773+ error = -1;
37743774+ s->err = EINVAL;
37753775+ goto exit;
37763776+ }
3164377731653165-#else
31663166- dataptr = malloc(entry->length);
31673167- if (dataptr == NULL) {
31683168- copyfile_debug(1, "no memory for %u bytes\n", entry->length);
31693169- error = -1;
31703170- s->err = ENOMEM;
31713171- goto exit;
31723172- }
31733173- if (pread(s->src_fd, dataptr, entry->length, entry->offset) != (ssize_t)entry->length) {
31743174- copyfile_debug(1, "failed to read %u bytes at offset %u\n", entry->length, entry->offset);
31753175- error = -1;
31763176- s->err = EINVAL;
31773177- goto exit;
31783178- }
31793179-#endif
37783778+ copyfile_debug(3, "extracting \"%s\" (%d bytes) at offset %u",
37793779+ entry->name, entry->length, entry->offset);
3180378031813181- if (strcmp((char*)entry->name, XATTR_QUARANTINE_NAME) == 0)
31823182- {
31833183- qtn_file_t tqinfo = NULL;
37813781+#if 0
37823782+ dataptr = (char *)attrhdr + entry->offset;
3184378331853185- if (s->qinfo == NULL)
31863186- {
31873187- tqinfo = qtn_file_alloc();
31883188- if (tqinfo)
31893189- {
31903190- int x;
31913191- if ((x = qtn_file_init_with_data(tqinfo, dataptr, entry->length)) != 0)
31923192- {
31933193- copyfile_warn("qtn_file_init_with_data failed: %s", qtn_error(x));
31943194- qtn_file_free(tqinfo);
31953195- tqinfo = NULL;
37843784+ if (dataptr > endptr || dataptr < buffer) {
37853785+ copyfile_debug(1, "Entry %d overflows: offset = %u", i, entry->offset);
37863786+ error = -1;
37873787+ s->err = EINVAL; /* Invalid buffer */
37883788+ goto exit;
31963789 }
31973197- }
31983198- }
31993199- else
32003200- {
32013201- tqinfo = s->qinfo;
32023202- }
32033203- if (tqinfo)
32043204- {
32053205- int x;
32063206- x = qtn_file_apply_to_fd(tqinfo, s->dst_fd);
32073207- if (x != 0) {
32083208- copyfile_warn("qtn_file_apply_to_fd failed: %s", qtn_error(x));
32093209- if (s->statuscb) {
32103210- int rv;
32113211- s->xattr_name = (char*)XATTR_QUARANTINE_NAME;
32123212- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
32133213- s->xattr_name = NULL;
32143214- if (rv == COPYFILE_QUIT) {
32153215- error = s->err = x < 0 ? ENOTSUP : errno;
32163216- goto exit;
32173217- }
32183218- } else {
32193219- error = s->err = x < 0 ? ENOTSUP : errno;
32203220- goto exit;
32213221- }
37903790+37913791+ if (((char*)dataptr + entry->length) > (char*)endptr ||
37923792+ (((char*)dataptr + entry->length) < (char*)buffer) ||
37933793+ (entry->length > (size_t)hdrsize)) {
37943794+ if (COPYFILE_VERBOSE & s->flags)
37953795+ copyfile_warn("Incomplete or corrupt attribute entry");
37963796+ copyfile_debug(1, "Entry %d length overflows: offset = %u, length = %u",
37973797+ i, entry->offset, entry->length);
37983798+ error = -1;
37993799+ s->err = EINVAL; /* Invalid buffer */
38003800+ goto exit;
32223801 }
32233223- }
32243224- if (tqinfo && !s->qinfo)
32253225- {
32263226- qtn_file_free(tqinfo);
32273227- }
32283228- }
32293229- /* Look for ACL data */
32303230- else if (strcmp((char*)entry->name, XATTR_SECURITY_NAME) == 0)
32313231- {
32323232- acl_t acl;
32333233- struct stat sb;
32343234- int retry = 1;
32353235- char *tcp = dataptr;
3236380232373237- if (entry->length == 0) {
32383238- /* Not sure how we got here, but we had one case
32393239- * where it was 0. In a normal EA, we can have a 0-byte
32403240- * payload. That means nothing in this case, so we'll
32413241- * simply skip the EA.
32423242- */
32433243- error = 0;
32443244- goto acl_done;
32453245- }
32463246- /*
32473247- * acl_from_text() requires a NUL-terminated string. The ACL EA,
32483248- * however, may not be NUL-terminated. So in that case, we need to
32493249- * copy it to a +1 sized buffer, to ensure it's got a terminated string.
32503250- */
32513251- if (tcp[entry->length - 1] != 0) {
32523252- char *tmpstr = malloc(entry->length + 1);
32533253- if (tmpstr == NULL) {
38033803+#else
38043804+ dataptr = malloc(entry->length);
38053805+ if (dataptr == NULL) {
38063806+ copyfile_debug(1, "no memory for %u bytes\n", entry->length);
32543807 error = -1;
38083808+ s->err = ENOMEM;
32553809 goto exit;
32563810 }
32573257- strlcpy(tmpstr, tcp, entry->length + 1);
32583258- acl = acl_from_text(tmpstr);
32593259- free(tmpstr);
32603260- } else {
32613261- acl = acl_from_text(tcp);
32623262- }
38113811+ if (pread(s->src_fd, dataptr, entry->length, entry->offset) != (ssize_t)entry->length) {
38123812+ copyfile_debug(1, "failed to read %u bytes at offset %u\n", entry->length, entry->offset);
38133813+ error = -1;
38143814+ s->err = EINVAL;
38153815+ goto exit;
38163816+ }
38173817+#endif
3263381832643264- if (acl != NULL)
32653265- {
32663266- filesec_t fsec_tmp;
32673267-32683268- if ((fsec_tmp = filesec_init()) == NULL)
32693269- error = -1;
32703270- else if((error = fstatx_np(s->dst_fd, &sb, fsec_tmp)) < 0)
32713271- error = -1;
32723272- else if (filesec_set_property(fsec_tmp, FILESEC_ACL, &acl) < 0)
32733273- error = -1;
32743274- else {
32753275- while (fchmodx_np(s->dst_fd, fsec_tmp) < 0)
38193819+ if (strcmp((char*)entry->name, XATTR_QUARANTINE_NAME) == 0)
32763820 {
32773277- if (errno == ENOTSUP)
32783278- {
32793279- if (retry && !copyfile_unset_acl(s))
32803280- {
32813281- retry = 0;
32823282- continue;
32833283- }
32843284- }
32853285- copyfile_warn("setting security information");
32863286- error = -1;
32873287- break;
32883288- }
32893289- }
32903290- acl_free(acl);
32913291- filesec_free(fsec_tmp);
38213821+ qtn_file_t tqinfo = NULL;
3292382232933293-acl_done:
32943294- if (error == -1)
32953295- goto exit;
32963296- }
32973297- }
32983298- /* And, finally, everything else */
32993299- else
33003300- {
33013301- if (s->copyIntent ||
33023302- xattr_preserve_for_intent((char*)entry->name, s->copyIntent) == 1) {
33033303- if (s->statuscb) {
33043304- int rv;
33053305- s->xattr_name = strdup((char*)entry->name);
33063306- s->totalCopied = 0;
33073307- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
33083308- if (s->xattr_name) {
33093309- free(s->xattr_name);
33103310- s->xattr_name = NULL;
38233823+ if (s->qinfo == NULL)
38243824+ {
38253825+ tqinfo = qtn_file_alloc();
38263826+ if (tqinfo)
38273827+ {
38283828+ int x;
38293829+ if ((x = qtn_file_init_with_data(tqinfo, dataptr, entry->length)) != 0)
38303830+ {
38313831+ copyfile_warn("qtn_file_init_with_data failed: %s", qtn_error(x));
38323832+ qtn_file_free(tqinfo);
38333833+ tqinfo = NULL;
38343834+ }
38353835+ }
38363836+ }
38373837+ else
38383838+ {
38393839+ tqinfo = s->qinfo;
38403840+ }
38413841+ if (tqinfo)
38423842+ {
38433843+ int x;
38443844+ x = qtn_file_apply_to_fd(tqinfo, s->dst_fd);
38453845+ if (x != 0) {
38463846+ copyfile_warn("qtn_file_apply_to_fd failed: %s", qtn_error(x));
38473847+ if (s->statuscb) {
38483848+ int rv;
38493849+ s->xattr_name = (char*)XATTR_QUARANTINE_NAME;
38503850+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
38513851+ s->xattr_name = NULL;
38523852+ if (rv == COPYFILE_QUIT) {
38533853+ error = s->err = x < 0 ? ENOTSUP : errno;
38543854+ goto exit;
38553855+ }
38563856+ } else {
38573857+ error = s->err = x < 0 ? ENOTSUP : errno;
38583858+ goto exit;
38593859+ }
38603860+ }
33113861 }
33123312- if (rv == COPYFILE_QUIT) {
33133313- s->err = ECANCELED;
33143314- error = -1;
33153315- goto exit;
38623862+ if (tqinfo && !s->qinfo)
38633863+ {
38643864+ qtn_file_free(tqinfo);
33163865 }
33173866 }
33183318- if (fsetxattr(s->dst_fd, (char *)entry->name, dataptr, entry->length, 0, 0) == -1) {
33193319- if (COPYFILE_VERBOSE & s->flags)
33203320- copyfile_warn("error %d setting attribute %s", errno, entry->name);
33213321- if (s->statuscb) {
33223322- int rv;
33233323-33243324- s->xattr_name = strdup((char*)entry->name);
33253325- rv = (s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
33263326- if (s->xattr_name) {
33273327- free(s->xattr_name);
33283328- s->xattr_name = NULL;
33293329- }
33303330- if (rv == COPYFILE_QUIT) {
38673867+ /* Look for ACL data */
38683868+ else if (strcmp((char*)entry->name, XATTR_SECURITY_NAME) == 0)
38693869+ {
38703870+ acl_t acl;
38713871+ struct stat sb;
38723872+ int retry = 1;
38733873+ char *tcp = dataptr;
38743874+38753875+ if (entry->length == 0) {
38763876+ /* Not sure how we got here, but we had one case
38773877+ * where it was 0. In a normal EA, we can have a 0-byte
38783878+ * payload. That means nothing in this case, so we'll
38793879+ * simply skip the EA.
38803880+ */
38813881+ error = 0;
38823882+ goto acl_done;
38833883+ }
38843884+ /*
38853885+ * acl_from_text() requires a NUL-terminated string. The ACL EA,
38863886+ * however, may not be NUL-terminated. So in that case, we need to
38873887+ * copy it to a +1 sized buffer, to ensure it's got a terminated string.
38883888+ */
38893889+ if (tcp[entry->length - 1] != 0) {
38903890+ char *tmpstr = malloc(entry->length + 1);
38913891+ if (tmpstr == NULL) {
33313892 error = -1;
33323893 goto exit;
33333894 }
38953895+ // Can't use strlcpy here: tcp is not NUL-terminated!
38963896+ memcpy(tmpstr, tcp, entry->length);
38973897+ tmpstr[entry->length] = 0;
38983898+ acl = acl_from_text(tmpstr);
38993899+ free(tmpstr);
33343900 } else {
33353335- error = -1;
33363336- goto exit;
39013901+ acl = acl_from_text(tcp);
33373902 }
33383338- } else if (s->statuscb) {
33393339- int rv;
33403340- s->xattr_name = strdup((char*)entry->name);
33413341- s->totalCopied = entry->length;
33423342- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
33433343- if (s->xattr_name) {
33443344- free(s->xattr_name);
33453345- s->xattr_name = NULL;
39033903+39043904+ if (acl != NULL)
39053905+ {
39063906+ filesec_t fsec_tmp;
39073907+39083908+ if ((fsec_tmp = filesec_init()) == NULL)
39093909+ error = -1;
39103910+ else if((error = fstatx_np(s->dst_fd, &sb, fsec_tmp)) < 0)
39113911+ error = -1;
39123912+ else if (filesec_set_property(fsec_tmp, FILESEC_ACL, &acl) < 0)
39133913+ error = -1;
39143914+ else {
39153915+ while (fchmodx_np(s->dst_fd, fsec_tmp) < 0)
39163916+ {
39173917+ if (errno == ENOTSUP)
39183918+ {
39193919+ if (retry && !copyfile_unset_acl(s))
39203920+ {
39213921+ retry = 0;
39223922+ continue;
39233923+ }
33463924 }
33473347- if (rv == COPYFILE_QUIT) {
33483348- error = -1;
33493349- s->err = ECANCELED;
33503350- goto exit;
39253925+ copyfile_warn("setting security information");
39263926+ error = -1;
39273927+ break;
39283928+ }
39293929+ }
39303930+ acl_free(acl);
39313931+ filesec_free(fsec_tmp);
39323932+39333933+ acl_done:
39343934+ if (error == -1)
39353935+ goto exit;
33513936 }
33523937 }
39383938+ /* And, finally, everything else */
39393939+ else
39403940+ {
39413941+ if (s->copyIntent ||
39423942+ xattr_preserve_for_intent((char*)entry->name, s->copyIntent) == 1) {
39433943+ if (s->statuscb) {
39443944+ int rv;
39453945+ s->xattr_name = strdup((char*)entry->name);
39463946+ s->totalCopied = 0;
39473947+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
39483948+ if (s->xattr_name) {
39493949+ free(s->xattr_name);
39503950+ s->xattr_name = NULL;
39513951+ }
39523952+ if (rv == COPYFILE_QUIT) {
39533953+ s->err = ECANCELED;
39543954+ error = -1;
39553955+ goto exit;
39563956+ }
39573957+ }
39583958+ //Silently ignore failure to set XATTR_ROOT_INSTALLED_NAME
39593959+ int result = fsetxattr(s->dst_fd, (char *)entry->name, dataptr, entry->length, 0, 0);
39603960+ int errorcode = errno;
39613961+ if (result == -1 && !(errorcode == EPERM &&
39623962+ strcmp((char*)entry->name, XATTR_ROOT_INSTALLED_NAME) == 0)) {
39633963+ errno = errorcode;
39643964+ if (COPYFILE_VERBOSE & s->flags)
39653965+ copyfile_warn("error %d setting attribute %s", errorcode, entry->name);
39663966+ if (s->statuscb) {
39673967+ int rv;
39683968+39693969+ s->xattr_name = strdup((char*)entry->name);
39703970+ rv = (s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
39713971+ if (s->xattr_name) {
39723972+ free(s->xattr_name);
39733973+ s->xattr_name = NULL;
39743974+ }
39753975+ if (rv == COPYFILE_QUIT) {
39763976+ error = -1;
39773977+ goto exit;
39783978+ }
39793979+ } else {
39803980+ error = -1;
39813981+ goto exit;
39823982+ }
39833983+ } else if (s->statuscb) {
39843984+ int rv;
39853985+ errno = errorcode;
39863986+ s->xattr_name = strdup((char*)entry->name);
39873987+ s->totalCopied = entry->length;
39883988+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
39893989+ if (s->xattr_name) {
39903990+ free(s->xattr_name);
39913991+ s->xattr_name = NULL;
39923992+ }
39933993+ if (rv == COPYFILE_QUIT) {
39943994+ error = -1;
39953995+ s->err = ECANCELED;
39963996+ goto exit;
39973997+ }
39983998+ } else {
39993999+ errno = errorcode;
40004000+ }
40014001+ }
40024002+ }
40034003+ if (dataptr) {
40044004+ free(dataptr);
40054005+ dataptr = NULL;
40064006+ }
40074007+ entry = ATTR_NEXT(entry);
33534008 }
33543354- }
33553355- if (dataptr) {
33563356- free(dataptr);
33573357- dataptr = NULL;
33583358- }
33593359- entry = ATTR_NEXT(entry);
33604009 }
33613361- }
3362401033633363- /*
33643364- * Extract the Finder Info.
33653365- */
33663366- if (adhdr->entries[0].offset > (hdrsize - sizeof(emptyfinfo))) {
33673367- error = -1;
33683368- goto exit;
33693369- }
40114011+ /*
40124012+ * Extract the Finder Info.
40134013+ */
40144014+ if (adhdr->entries[0].offset > (hdrsize - sizeof(emptyfinfo))) {
40154015+ error = -1;
40164016+ goto exit;
40174017+ }
3370401833713371- if (bcmp((u_int8_t*)buffer + adhdr->entries[0].offset, emptyfinfo, sizeof(emptyfinfo)) != 0)
33723372- {
33733373- uint16_t *fFlags;
33743374- uint8_t *newFinfo;
33753375- enum { kFinderInvisibleMask = 1 << 14 };
40194019+ if (bcmp((u_int8_t*)buffer + adhdr->entries[0].offset, emptyfinfo, sizeof(emptyfinfo)) != 0)
40204020+ {
40214021+ uint16_t *fFlags;
40224022+ uint8_t *newFinfo;
40234023+ enum { kFinderInvisibleMask = 1 << 14 };
3376402433773377- newFinfo = (u_int8_t*)buffer + adhdr->entries[0].offset;
33783378- fFlags = (uint16_t*)&newFinfo[8];
33793379- copyfile_debug(3, " extracting \"%s\" (32 bytes)", XATTR_FINDERINFO_NAME);
33803380- if (s->statuscb) {
33813381- int rv;
33823382- s->xattr_name = (char*)XATTR_FINDERINFO_NAME;
33833383- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
33843384- s->xattr_name = NULL;
33853385- if (rv == COPYFILE_QUIT) {
33863386- error = -1;
33873387- s->err = ECANCELED;
33883388- goto exit;
33893389- } else if (rv == COPYFILE_SKIP) {
33903390- goto skip_fi;
33913391- }
33923392- }
33933393- error = fsetxattr(s->dst_fd, XATTR_FINDERINFO_NAME, (u_int8_t*)buffer + adhdr->entries[0].offset, sizeof(emptyfinfo), 0, 0);
33943394- if (error) {
40254025+ newFinfo = (u_int8_t*)buffer + adhdr->entries[0].offset;
40264026+ fFlags = (uint16_t*)&newFinfo[8];
40274027+ copyfile_debug(3, " extracting \"%s\" (32 bytes)", XATTR_FINDERINFO_NAME);
33954028 if (s->statuscb) {
33964029 int rv;
33973397- s->xattr_name = (char *)XATTR_FINDERINFO_NAME;
33983398- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
40304030+ s->xattr_name = (char*)XATTR_FINDERINFO_NAME;
40314031+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
33994032 s->xattr_name = NULL;
34004033 if (rv == COPYFILE_QUIT) {
34014034 error = -1;
34024035 s->err = ECANCELED;
34034036 goto exit;
40374037+ } else if (rv == COPYFILE_SKIP) {
40384038+ goto skip_fi;
34044039 }
34054040 }
34063406- goto exit;
34073407- } else if (s->statuscb) {
34083408- int rv;
34093409- s->xattr_name = (char *)XATTR_FINDERINFO_NAME;
34103410- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
34113411- s->xattr_name = NULL;
34123412- if (rv == COPYFILE_QUIT) {
34133413- error = -1;
34143414- s->err = ECANCELED;
40414041+ error = fsetxattr(s->dst_fd, XATTR_FINDERINFO_NAME, (u_int8_t*)buffer + adhdr->entries[0].offset, sizeof(emptyfinfo), 0, 0);
40424042+ if (error) {
40434043+ if (s->statuscb) {
40444044+ int rv;
40454045+ s->xattr_name = (char *)XATTR_FINDERINFO_NAME;
40464046+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
40474047+ s->xattr_name = NULL;
40484048+ if (rv == COPYFILE_QUIT) {
40494049+ error = -1;
40504050+ s->err = ECANCELED;
40514051+ goto exit;
40524052+ }
40534053+ }
34154054 goto exit;
40554055+ } else if (s->statuscb) {
40564056+ int rv;
40574057+ s->xattr_name = (char *)XATTR_FINDERINFO_NAME;
40584058+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
40594059+ s->xattr_name = NULL;
40604060+ if (rv == COPYFILE_QUIT) {
40614061+ error = -1;
40624062+ s->err = ECANCELED;
40634063+ goto exit;
40644064+ }
34164065 }
40664066+ if (SWAP16(*fFlags) & kFinderInvisibleMask)
40674067+ s->internal_flags |= cfMakeFileInvisible;
34174068 }
34183418- if (SWAP16(*fFlags) & kFinderInvisibleMask)
34193419- s->internal_flags |= cfMakeFileInvisible;
34203420- }
34214069skip_fi:
3422407034233423- /*
34243424- * Extract the Resource Fork.
34253425- */
34263426- if (adhdr->entries[1].type == AD_RESOURCE &&
34273427- adhdr->entries[1].length > 0)
34283428- {
34293429- void * rsrcforkdata = NULL;
34303430- size_t length;
34313431- off_t offset;
34323432- struct stat sb;
34333433- struct timeval tval[2];
40714071+ /*
40724072+ * Extract the Resource Fork.
40734073+ */
40744074+ if (adhdr->entries[1].type == AD_RESOURCE &&
40754075+ adhdr->entries[1].length > 0)
40764076+ {
40774077+ void * rsrcforkdata = NULL;
40784078+ size_t length;
40794079+ off_t offset;
40804080+ struct stat sb;
40814081+ struct attrlist attrlist;
40824082+ struct {
40834083+ /* Order of these structs matters for setattrlist. */
40844084+ struct timespec mod_time;
40854085+ struct timespec acc_time;
40864086+ } ma_times;
3434408734353435- length = adhdr->entries[1].length;
34363436- offset = adhdr->entries[1].offset;
34373437- rsrcforkdata = malloc(length);
40884088+ length = adhdr->entries[1].length;
40894089+ offset = adhdr->entries[1].offset;
40904090+ rsrcforkdata = malloc(length);
3438409134393439- if (rsrcforkdata == NULL) {
34403440- copyfile_debug(1, "could not allocate %zu bytes for rsrcforkdata",
34413441- length);
34423442- error = -1;
34433443- goto bad;
34443444- }
40924092+ if (rsrcforkdata == NULL) {
40934093+ copyfile_debug(1, "could not allocate %zu bytes for rsrcforkdata",
40944094+ length);
40954095+ error = -1;
40964096+ goto bad;
40974097+ }
3445409834463446- if (fstat(s->dst_fd, &sb) < 0)
34473447- {
34483448- copyfile_debug(1, "couldn't stat destination file");
34493449- error = -1;
34503450- goto bad;
34513451- }
40994099+ if (fstat(s->dst_fd, &sb) < 0)
41004100+ {
41014101+ copyfile_debug(1, "couldn't stat destination file");
41024102+ error = -1;
41034103+ goto bad;
41044104+ }
3452410534533453- bytes = pread(s->src_fd, rsrcforkdata, length, offset);
34543454- if (bytes < (ssize_t)length)
34553455- {
34563456- if (bytes == -1)
34573457- {
34583458- copyfile_debug(1, "couldn't read resource fork");
34593459- }
34603460- else
34613461- {
34623462- copyfile_debug(1,
34633463- "couldn't read resource fork (only read %d bytes of %d)",
34643464- (int)bytes, (int)length);
34653465- }
34663466- error = -1;
34673467- goto bad;
34683468- }
34693469- if (s->statuscb) {
34703470- int rv;
34713471- s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME;
34723472- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
34733473- s->xattr_name = NULL;
34743474- if (rv == COPYFILE_QUIT) {
41064106+ bytes = pread(s->src_fd, rsrcforkdata, length, offset);
41074107+ if (bytes < (ssize_t)length)
41084108+ {
41094109+ if (bytes == -1)
41104110+ {
41114111+ copyfile_debug(1, "couldn't read resource fork");
41124112+ }
41134113+ else
41144114+ {
41154115+ copyfile_debug(1,
41164116+ "couldn't read resource fork (only read %d bytes of %d)",
41174117+ (int)bytes, (int)length);
41184118+ }
34754119 error = -1;
34763476- s->err = ECANCELED;
34773477- if (rsrcforkdata)
34783478- free(rsrcforkdata);
34793479- goto exit;
34803480- } else if (rv == COPYFILE_SKIP) {
34814120 goto bad;
34824121 }
34833483- }
34843484- error = fsetxattr(s->dst_fd, XATTR_RESOURCEFORK_NAME, rsrcforkdata, bytes, 0, 0);
34853485- if (error)
34863486- {
34873487- /*
34883488- * For filesystems that do not natively support named attributes,
34893489- * the kernel creates an AppleDouble file that -- for compatabilty
34903490- * reasons -- has a resource fork containing nothing but a rsrcfork_header_t
34913491- * structure that says there are no resources. So, if fsetxattr has
34923492- * failed, and the resource fork is that empty structure, *and* the
34933493- * target file is a directory, then we do nothing with it.
34943494- */
34953495- if ((bytes == sizeof(rsrcfork_header_t)) &&
34963496- ((sb.st_mode & S_IFMT) == S_IFDIR) &&
34973497- (memcmp(rsrcforkdata, &empty_rsrcfork_header, bytes) == 0)) {
34983498- copyfile_debug(2, "not setting empty resource fork on directory");
34993499- error = errno = 0;
35003500- goto bad;
35013501- }
35023502- if (s->statuscb) {
35033503- int rv;
35043504- s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME;
35053505- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
35063506- s->xattr_name = NULL;
35073507- if (rv == COPYFILE_CONTINUE) {
35083508- error = errno = 0;
41224122+ if (s->statuscb) {
41234123+ int rv;
41244124+ s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME;
41254125+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
41264126+ s->xattr_name = NULL;
41274127+ if (rv == COPYFILE_QUIT) {
41284128+ error = -1;
41294129+ s->err = ECANCELED;
41304130+ if (rsrcforkdata)
41314131+ free(rsrcforkdata);
41324132+ goto exit;
41334133+ } else if (rv == COPYFILE_SKIP) {
41344134+ goto bad;
41354135+ }
41364136+ }
41374137+ error = fsetxattr(s->dst_fd, XATTR_RESOURCEFORK_NAME, rsrcforkdata, bytes, 0, 0);
41384138+ if (error)
41394139+ {
41404140+ /*
41414141+ * For filesystems that do not natively support named attributes,
41424142+ * the kernel creates an AppleDouble file that -- for compatabilty
41434143+ * reasons -- has a resource fork containing nothing but a rsrcfork_header_t
41444144+ * structure that says there are no resources. So, if fsetxattr has
41454145+ * failed, and the resource fork is that empty structure, *and* the
41464146+ * target file is a directory, then we do nothing with it.
41474147+ */
41484148+ if ((bytes == sizeof(rsrcfork_header_t)) &&
41494149+ ((sb.st_mode & S_IFMT) == S_IFDIR) &&
41504150+ (memcmp(rsrcforkdata, &empty_rsrcfork_header, bytes) == 0)) {
41514151+ copyfile_debug(2, "not setting empty resource fork on directory");
41524152+ error = errno = 0;
41534153+ goto bad;
41544154+ }
41554155+ if (s->statuscb) {
41564156+ int rv;
41574157+ s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME;
41584158+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
41594159+ s->xattr_name = NULL;
41604160+ if (rv == COPYFILE_CONTINUE) {
41614161+ error = errno = 0;
41624162+ goto bad;
41634163+ }
41644164+ }
41654165+ copyfile_debug(1, "error %d setting resource fork attribute", error);
41664166+ error = -1;
35094167 goto bad;
41684168+ } else if (s->statuscb) {
41694169+ int rv;
41704170+ s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME;
41714171+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
41724172+ s->xattr_name = NULL;
41734173+ if (rv == COPYFILE_QUIT) {
41744174+ error = -1;
41754175+ s->err = ECANCELED;
41764176+ if (rsrcforkdata)
41774177+ free(rsrcforkdata);
41784178+ goto exit;
41794179+ }
35104180 }
35113511- }
35123512- copyfile_debug(1, "error %d setting resource fork attribute", error);
35133513- error = -1;
35143514- goto bad;
35153515- } else if (s->statuscb) {
35163516- int rv;
35173517- s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME;
35183518- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
35193519- s->xattr_name = NULL;
35203520- if (rv == COPYFILE_QUIT) {
35213521- error = -1;
35223522- s->err = ECANCELED;
35233523- if (rsrcforkdata)
35243524- free(rsrcforkdata);
35253525- goto exit;
41814181+ copyfile_debug(3, "extracting \"%s\" (%d bytes)",
41824182+ XATTR_RESOURCEFORK_NAME, (int)length);
41834183+41844184+ if (!(s->flags & COPYFILE_STAT))
41854185+ {
41864186+ /* Try to set m/atimes using setattrlist(), for nanosecond precision. */
41874187+ memset(&attrlist, 0, sizeof(attrlist));
41884188+ attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
41894189+ attrlist.commonattr = ATTR_CMN_MODTIME | ATTR_CMN_ACCTIME;
41904190+ ma_times.mod_time = sb.st_mtimespec;
41914191+ ma_times.acc_time = sb.st_atimespec;
41924192+ if (fsetattrlist(s->dst_fd, &attrlist, &ma_times, sizeof(ma_times), 0) != 0) {
41934193+ copyfile_warn("%s: set times", s->dst ? s->dst : "(null dst)");
41944194+ }
35264195 }
41964196+ bad:
41974197+ if (rsrcforkdata)
41984198+ free(rsrcforkdata);
35274199 }
35283528- copyfile_debug(3, "extracting \"%s\" (%d bytes)",
35293529- XATTR_RESOURCEFORK_NAME, (int)length);
3530420035313531- if (!(s->flags & COPYFILE_STAT))
42014201+ if (COPYFILE_STAT & s->flags)
35324202 {
35333533- tval[0].tv_sec = sb.st_atime;
35343534- tval[1].tv_sec = sb.st_mtime;
35353535- tval[0].tv_usec = tval[1].tv_usec = 0;
35363536-35373537- if (futimes(s->dst_fd, tval))
35383538- copyfile_warn("%s: set times", s->dst ? s->dst : "(null dst)");
42034203+ error = copyfile_stat(s);
35394204 }
35403540-bad:
35413541- if (rsrcforkdata)
35423542- free(rsrcforkdata);
35433543- }
35443544-35453545- if (COPYFILE_STAT & s->flags)
35463546- {
35473547- error = copyfile_stat(s);
35483548- }
35494205exit:
35503550- if (buffer) free(buffer);
35513551- if (dataptr) free(dataptr);
35523552- return error;
42064206+ if (buffer) free(buffer);
42074207+ if (dataptr) free(dataptr);
42084208+ return error;
35534209}
3554421035554211static int copyfile_pack_quarantine(copyfile_state_t s, void **buf, ssize_t *len)
35564212{
35573557- int ret = 0;
35583558- char qbuf[QTN_SERIALIZED_DATA_MAX];
35593559- size_t qlen = sizeof(qbuf);
42134213+ int ret = 0;
42144214+ char qbuf[QTN_SERIALIZED_DATA_MAX];
42154215+ size_t qlen = sizeof(qbuf);
3560421635613561- if (s->qinfo == NULL)
35623562- {
35633563- ret = -1;
35643564- goto done;
35653565- }
42174217+ if (s->qinfo == NULL)
42184218+ {
42194219+ ret = -1;
42204220+ goto done;
42214221+ }
3566422235673567- if (qtn_file_to_data(s->qinfo, qbuf, &qlen) != 0)
35683568- {
35693569- ret = -1;
35703570- goto done;
35713571- }
42234223+ if (qtn_file_to_data(s->qinfo, qbuf, &qlen) != 0)
42244224+ {
42254225+ ret = -1;
42264226+ goto done;
42274227+ }
3572422835733573- *buf = malloc(qlen);
35743574- if (*buf)
35753575- {
35763576- memcpy(*buf, qbuf, qlen);
35773577- *len = qlen;
35783578- }
42294229+ *buf = malloc(qlen);
42304230+ if (*buf)
42314231+ {
42324232+ memcpy(*buf, qbuf, qlen);
42334233+ *len = qlen;
42344234+ }
35794235done:
35803580- return ret;
42364236+ return ret;
35814237}
3582423835834239static int copyfile_pack_acl(copyfile_state_t s, void **buf, ssize_t *len)
35844240{
35853585- int ret = 0;
35863586- acl_t acl = NULL;
35873587- char *acl_text;
42414241+ int ret = 0;
42424242+ acl_t acl = NULL;
42434243+ char *acl_text;
3588424435893589- if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) < 0)
35903590- {
35913591- if (errno != ENOENT)
42454245+ if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) < 0)
35924246 {
35933593- ret = -1;
35943594- if (COPYFILE_VERBOSE & s->flags)
35953595- copyfile_warn("getting acl");
42474247+ if (errno != ENOENT)
42484248+ {
42494249+ ret = -1;
42504250+ if (COPYFILE_VERBOSE & s->flags)
42514251+ copyfile_warn("getting acl");
42524252+ }
42534253+ *len = 0;
42544254+ goto exit;
35964255 }
35973597- *len = 0;
35983598- goto exit;
35993599- }
3600425636013601- if ((acl_text = acl_to_text(acl, len)) != NULL)
36023602- {
36033603- /*
36043604- * acl_to_text() doesn't include the NUL at the endo
36053605- * in it's count (*len). It does, however, promise to
36063606- * return a valid C string, so we need to up the count
36073607- * by 1.
36083608- */
36093609- *len = *len + 1;
36103610- *buf = malloc(*len);
36113611- if (*buf)
36123612- memcpy(*buf, acl_text, *len);
36133613- else
36143614- *len = 0;
36153615- acl_free(acl_text);
36163616- }
36173617- copyfile_debug(2, "copied acl (%ld) %p", *len, *buf);
42574257+ if ((acl_text = acl_to_text(acl, len)) != NULL)
42584258+ {
42594259+ /*
42604260+ * acl_to_text() doesn't include the NUL at the endo
42614261+ * in it's count (*len). It does, however, promise to
42624262+ * return a valid C string, so we need to up the count
42634263+ * by 1.
42644264+ */
42654265+ *len = *len + 1;
42664266+ *buf = malloc(*len);
42674267+ if (*buf)
42684268+ memcpy(*buf, acl_text, *len);
42694269+ else
42704270+ *len = 0;
42714271+ acl_free(acl_text);
42724272+ }
42734273+ copyfile_debug(2, "copied acl (%ld) %p", *len, *buf);
36184274exit:
36193619- if (acl)
36203620- acl_free(acl);
36213621- return ret;
42754275+ if (acl)
42764276+ acl_free(acl);
42774277+ return ret;
36224278}
3623427936244280static int copyfile_pack_rsrcfork(copyfile_state_t s, attr_header_t *filehdr)
36254281{
36263626- ssize_t datasize;
36273627- char *databuf = NULL;
36283628- int ret = 0;
42824282+ ssize_t datasize;
42834283+ char *databuf = NULL;
42844284+ int ret = 0;
3629428536303630-/*
36313631- * XXX
36323632- * do COPYFILE_COPY_XATTR here; no need to
36333633- * the work if we want to skip.
36343634- */
42864286+ /*
42874287+ * XXX
42884288+ * do COPYFILE_COPY_XATTR here; no need to
42894289+ * the work if we want to skip.
42904290+ */
3635429136363636- if (s->statuscb)
36373637- {
36383638- int rv;
42924292+ if (s->statuscb)
42934293+ {
42944294+ int rv;
3639429536403640- s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME;
42964296+ s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME;
3641429736423642- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
36433643- s->xattr_name = NULL;
36443644- if (rv == COPYFILE_SKIP) {
36453645- ret = 0;
36463646- goto done;
42984298+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
42994299+ s->xattr_name = NULL;
43004300+ if (rv == COPYFILE_SKIP) {
43014301+ ret = 0;
43024302+ goto done;
43034303+ }
43044304+ if (rv == COPYFILE_QUIT) {
43054305+ ret = -1;
43064306+ s->err = ECANCELED;
43074307+ goto done;
43084308+ }
36474309 }
36483648- if (rv == COPYFILE_QUIT) {
43104310+ /* Get the resource fork size */
43114311+ if ((datasize = fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, NULL, 0, 0, 0)) < 0)
43124312+ {
43134313+ if (COPYFILE_VERBOSE & s->flags)
43144314+ copyfile_warn("skipping attr \"%s\" due to error %d", XATTR_RESOURCEFORK_NAME, errno);
43154315+ return -1;
43164316+ }
43174317+43184318+ if (datasize > INT_MAX) {
43194319+ s->err = EINVAL;
36494320 ret = -1;
36503650- s->err = ECANCELED;
36514321 goto done;
36524322 }
36533653- }
36543654- /* Get the resource fork size */
36553655- if ((datasize = fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, NULL, 0, 0, 0)) < 0)
36563656- {
36573657- if (COPYFILE_VERBOSE & s->flags)
36583658- copyfile_warn("skipping attr \"%s\" due to error %d", XATTR_RESOURCEFORK_NAME, errno);
36593659- return -1;
36603660- }
3661432336623662- if (datasize > INT_MAX) {
36633663- s->err = EINVAL;
36643664- ret = -1;
36653665- goto done;
36663666- }
43244324+ if (s->statuscb) {
43254325+ int rv;
43264326+ s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME;
3667432736683668- if (s->statuscb) {
36693669- int rv;
36703670- s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME;
43284328+ s->totalCopied = 0;
43294329+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
43304330+ s->xattr_name = NULL;
43314331+ if (rv == COPYFILE_QUIT) {
43324332+ s->err = ECANCELED;
43334333+ ret = -1;
43344334+ goto done;
43354335+ }
43364336+ }
43374337+ if ((databuf = malloc(datasize)) == NULL)
43384338+ {
43394339+ copyfile_warn("malloc");
43404340+ ret = -1;
43414341+ goto done;
43424342+ }
3671434336723672- s->totalCopied = 0;
36733673- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
36743674- s->xattr_name = NULL;
36753675- if (rv == COPYFILE_QUIT) {
36763676- s->err = ECANCELED;
43444344+ if (fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, databuf, datasize, 0, 0) != datasize)
43454345+ {
43464346+ if (COPYFILE_VERBOSE & s->flags)
43474347+ copyfile_warn("couldn't read entire resource fork");
36774348 ret = -1;
36784349 goto done;
36794350 }
36803680- }
36813681- if ((databuf = malloc(datasize)) == NULL)
36823682- {
36833683- copyfile_warn("malloc");
36843684- ret = -1;
36853685- goto done;
36863686- }
3687435136883688- if (fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, databuf, datasize, 0, 0) != datasize)
36893689- {
36903690- if (COPYFILE_VERBOSE & s->flags)
36913691- copyfile_warn("couldn't read entire resource fork");
36923692- ret = -1;
36933693- goto done;
36943694- }
36953695-36963696- /* Write the resource fork to disk. */
36973697- if (pwrite(s->dst_fd, databuf, datasize, filehdr->appledouble.entries[1].offset) != datasize)
36983698- {
36993699- if (COPYFILE_VERBOSE & s->flags)
37003700- copyfile_warn("couldn't write resource fork");
37013701- }
37023702- if (s->statuscb)
37033703- {
37043704- int rv;
37053705- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
37063706- if (rv == COPYFILE_QUIT) {
37073707- ret = -1;
37083708- goto done;
43524352+ /* Write the resource fork to disk. */
43534353+ if (pwrite(s->dst_fd, databuf, datasize, filehdr->appledouble.entries[1].offset) != datasize)
43544354+ {
43554355+ if (COPYFILE_VERBOSE & s->flags)
43564356+ copyfile_warn("couldn't write resource fork");
43574357+ }
43584358+ if (s->statuscb)
43594359+ {
43604360+ int rv;
43614361+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
43624362+ if (rv == COPYFILE_QUIT) {
43634363+ ret = -1;
43644364+ goto done;
43654365+ }
37094366 }
37103710- }
37113711- copyfile_debug(3, "copied %zd bytes of \"%s\" data @ offset 0x%08x",
37123712- datasize, XATTR_RESOURCEFORK_NAME, filehdr->appledouble.entries[1].offset);
37133713- filehdr->appledouble.entries[1].length = (u_int32_t)datasize;
43674367+ copyfile_debug(3, "copied %zd bytes of \"%s\" data @ offset 0x%08x",
43684368+ datasize, XATTR_RESOURCEFORK_NAME, filehdr->appledouble.entries[1].offset);
43694369+ filehdr->appledouble.entries[1].length = (u_int32_t)datasize;
3714437037154371done:
37163716- if (ret == -1 && s->statuscb)
37173717- {
37183718- int rv;
37193719- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
37203720- if (rv == COPYFILE_CONTINUE)
37213721- ret = 0;
37223722- }
37233723- if (s->xattr_name) {
37243724- s->xattr_name = NULL;
37253725- }
37263726- if (databuf)
37273727- free(databuf);
43724372+ if (ret == -1 && s->statuscb)
43734373+ {
43744374+ int rv;
43754375+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
43764376+ if (rv == COPYFILE_CONTINUE)
43774377+ ret = 0;
43784378+ }
43794379+ if (s->xattr_name) {
43804380+ s->xattr_name = NULL;
43814381+ }
43824382+ if (databuf)
43834383+ free(databuf);
3728438437293729-/*
37303730- * XXX
37313731- * Do status callback here
37323732- * If ret == -1, then error callback
37333733- */
37343734- return ret;
43854385+ /*
43864386+ * XXX
43874387+ * Do status callback here
43884388+ * If ret == -1, then error callback
43894389+ */
43904390+ return ret;
37354391}
3736439237374393/*
···37394395 */
37404396static int copyfile_pack(copyfile_state_t s)
37414397{
37423742- char *attrnamebuf = NULL, *endnamebuf;
37433743- void *databuf = NULL;
37443744- attr_header_t *filehdr, *endfilehdr;
37453745- attr_entry_t *entry;
37463746- ssize_t listsize = 0;
37473747- char *nameptr;
37483748- size_t namelen;
37493749- size_t entrylen;
37503750- ssize_t datasize;
37513751- size_t offset = 0;
37523752- int hasrsrcfork = 0;
37533753- int error = 0;
37543754- int seenq = 0; // Have we seen any quarantine info already?
43984398+ char *attrnamebuf = NULL, *endnamebuf;
43994399+ void *databuf = NULL;
44004400+ attr_header_t *filehdr, *endfilehdr;
44014401+ attr_entry_t *entry;
44024402+ ssize_t listsize = 0;
44034403+ char *nameptr;
44044404+ size_t namelen;
44054405+ size_t entrylen;
44064406+ ssize_t datasize;
44074407+ size_t offset = 0;
44084408+ int hasrsrcfork = 0;
44094409+ int error = 0;
44104410+ int seenq = 0; // Have we seen any quarantine info already?
3755441137563756- filehdr = (attr_header_t *) calloc(1, ATTR_MAX_HDR_SIZE);
44124412+ filehdr = (attr_header_t *) calloc(1, ATTR_MAX_HDR_SIZE);
3757441337583758- if (filehdr == NULL) {
37593759- error = -1;
37603760- goto exit;
37613761- } else {
37623762- endfilehdr = (attr_header_t*)(((char*)filehdr) + ATTR_MAX_HDR_SIZE);
37633763- }
44144414+ if (filehdr == NULL) {
44154415+ error = -1;
44164416+ goto exit;
44174417+ } else {
44184418+ endfilehdr = (attr_header_t*)(((char*)filehdr) + ATTR_MAX_HDR_SIZE);
44194419+ }
3764442037653765- attrnamebuf = calloc(1, ATTR_MAX_HDR_SIZE);
37663766- if (attrnamebuf == NULL) {
37673767- error = -1;
37683768- goto exit;
37693769- } else {
37703770- endnamebuf = ((char*)attrnamebuf) + ATTR_MAX_HDR_SIZE;
37713771- }
44214421+ attrnamebuf = calloc(1, ATTR_MAX_HDR_SIZE);
44224422+ if (attrnamebuf == NULL) {
44234423+ error = -1;
44244424+ goto exit;
44254425+ } else {
44264426+ endnamebuf = ((char*)attrnamebuf) + ATTR_MAX_HDR_SIZE;
44274427+ }
3772442837733773- /*
37743774- * Fill in the Apple Double Header defaults.
37753775- */
37763776- filehdr->appledouble.magic = ADH_MAGIC;
37773777- filehdr->appledouble.version = ADH_VERSION;
37783778- filehdr->appledouble.numEntries = 2;
37793779- filehdr->appledouble.entries[0].type = AD_FINDERINFO;
37803780- filehdr->appledouble.entries[0].offset = (u_int32_t)offsetof(apple_double_header_t, finfo);
37813781- filehdr->appledouble.entries[0].length = FINDERINFOSIZE;
37823782- filehdr->appledouble.entries[1].type = AD_RESOURCE;
37833783- filehdr->appledouble.entries[1].offset = (u_int32_t)offsetof(apple_double_header_t, pad);
37843784- filehdr->appledouble.entries[1].length = 0;
37853785- bcopy(ADH_MACOSX, filehdr->appledouble.filler, sizeof(filehdr->appledouble.filler));
37863786-37873787- /*
37883788- * Fill in the initial Attribute Header.
37893789- */
37903790- filehdr->magic = ATTR_HDR_MAGIC;
37913791- filehdr->debug_tag = 0;
37923792- filehdr->data_start = (u_int32_t)sizeof(attr_header_t);
44294429+ /*
44304430+ * Fill in the Apple Double Header defaults.
44314431+ */
44324432+ filehdr->appledouble.magic = ADH_MAGIC;
44334433+ filehdr->appledouble.version = ADH_VERSION;
44344434+ filehdr->appledouble.numEntries = 2;
44354435+ filehdr->appledouble.entries[0].type = AD_FINDERINFO;
44364436+ filehdr->appledouble.entries[0].offset = (u_int32_t)__builtin_offsetof(apple_double_header_t, finfo);
44374437+ filehdr->appledouble.entries[0].length = FINDERINFOSIZE;
44384438+ filehdr->appledouble.entries[1].type = AD_RESOURCE;
44394439+ filehdr->appledouble.entries[1].offset = (u_int32_t)__builtin_offsetof(apple_double_header_t, pad);
44404440+ filehdr->appledouble.entries[1].length = 0;
44414441+ bcopy(ADH_MACOSX, filehdr->appledouble.filler, sizeof(filehdr->appledouble.filler));
3793444237943794- /*
37953795- * Collect the attribute names.
37963796- */
37973797- entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t));
44434443+ /*
44444444+ * Fill in the initial Attribute Header.
44454445+ */
44464446+ filehdr->magic = ATTR_HDR_MAGIC;
44474447+ filehdr->debug_tag = 0;
44484448+ filehdr->data_start = (u_int32_t)sizeof(attr_header_t);
3798444937993799- /*
38003800- * Test if there are acls to copy
38013801- */
38023802- if (COPYFILE_ACL & s->flags)
38033803- {
38043804- acl_t temp_acl = NULL;
38053805- if (filesec_get_property(s->fsec, FILESEC_ACL, &temp_acl) < 0)
38063806- {
38073807- copyfile_debug(2, "no acl entries found (errno = %d)", errno);
38083808- } else
38093809- {
38103810- offset = strlen(XATTR_SECURITY_NAME) + 1;
38113811- strcpy(attrnamebuf, XATTR_SECURITY_NAME);
38123812- endnamebuf = attrnamebuf + offset;
38133813- }
38143814- if (temp_acl)
38153815- acl_free(temp_acl);
38163816- }
44504450+ /*
44514451+ * Collect the attribute names.
44524452+ */
44534453+ entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t));
3817445438183818- if (COPYFILE_XATTR & s->flags)
38193819- {
38203820- ssize_t left = ATTR_MAX_HDR_SIZE - offset;
38213821- if ((listsize = flistxattr(s->src_fd, attrnamebuf + offset, left, 0)) <= 0)
44554455+ /*
44564456+ * Test if there are acls to copy
44574457+ */
44584458+ if (COPYFILE_ACL & s->flags)
38224459 {
38233823- copyfile_debug(2, "no extended attributes found (%d)", errno);
38243824- }
38253825- if (listsize > left)
38263826- {
38273827- copyfile_debug(1, "extended attribute list too long");
38283828- listsize = left;
44604460+ acl_t temp_acl = NULL;
44614461+ if (filesec_get_property(s->fsec, FILESEC_ACL, &temp_acl) < 0)
44624462+ {
44634463+ copyfile_debug(2, "no acl entries found (errno = %d)", errno);
44644464+ } else
44654465+ {
44664466+ offset = strlen(XATTR_SECURITY_NAME) + 1;
44674467+ strcpy(attrnamebuf, XATTR_SECURITY_NAME);
44684468+ endnamebuf = attrnamebuf + offset;
44694469+ }
44704470+ if (temp_acl)
44714471+ acl_free(temp_acl);
38294472 }
3830447338313831- endnamebuf = attrnamebuf + offset + (listsize > 0 ? listsize : 0);
38323832- if (endnamebuf > (attrnamebuf + ATTR_MAX_HDR_SIZE)) {
38333833- error = -1;
38343834- goto exit;
38353835- }
38363836-38373837- if (listsize > 0)
38383838- sort_xattrname_list(attrnamebuf, endnamebuf - attrnamebuf);
38393839-38403840- for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen)
44744474+ if (COPYFILE_XATTR & s->flags)
38414475 {
38423842- namelen = strlen(nameptr) + 1;
38433843- /* Skip over FinderInfo or Resource Fork names */
38443844- if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0 ||
38453845- strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0) {
38463846- continue;
38473847- }
38483848- if (strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0) {
38493849- seenq = 1;
38503850- }
44764476+ ssize_t left = ATTR_MAX_HDR_SIZE - offset;
44774477+ if ((listsize = flistxattr(s->src_fd, attrnamebuf + offset, left, 0)) <= 0)
44784478+ {
44794479+ copyfile_debug(2, "no extended attributes found (%d)", errno);
44804480+ }
44814481+ if (listsize > left)
44824482+ {
44834483+ copyfile_debug(1, "extended attribute list too long");
44844484+ listsize = left;
44854485+ }
3851448638523852- /* The system should prevent this from happening, but... */
38533853- if (namelen > XATTR_MAXNAMELEN + 1) {
38543854- namelen = XATTR_MAXNAMELEN + 1;
38553855- }
38563856- if (s->copyIntent &&
38573857- xattr_preserve_for_intent(nameptr, s->copyIntent) == 0) {
38583858- // Skip it
38593859- size_t amt = endnamebuf - (nameptr + namelen);
38603860- memmove(nameptr, nameptr + namelen, amt);
38613861- endnamebuf -= namelen;
38623862- /* Set namelen to 0 so continue doesn't miss names */
38633863- namelen = 0;
38643864- continue;
38653865- }
38663866-38673867- if (s->statuscb) {
38683868- int rv;
38693869- char eaname[namelen];
38703870- bcopy(nameptr, eaname, namelen);
38713871- eaname[namelen - 1] = 0; // Just to be sure!
38723872- s->xattr_name = eaname;
38733873- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
38743874- s->xattr_name = NULL;
38753875- if (rv == COPYFILE_QUIT) {
44874487+ endnamebuf = attrnamebuf + offset + (listsize > 0 ? listsize : 0);
44884488+ if (endnamebuf > (attrnamebuf + ATTR_MAX_HDR_SIZE)) {
38764489 error = -1;
38773877- s->err = ECANCELED;
38784490 goto exit;
38793879- } else if (rv == COPYFILE_SKIP) {
38803880- size_t amt = endnamebuf - (nameptr + namelen);
38813881- memmove(nameptr, nameptr + namelen, amt);
38823882- endnamebuf -= namelen;
38833883- /* Set namelen to 0 so continue doesn't miss names */
38843884- namelen = 0;
38853885- continue;
38864491 }
38873887- }
38883888- entry->namelen = namelen;
38893889- entry->flags = 0;
38903890- if (nameptr + namelen > endnamebuf) {
38913891- error = -1;
38923892- goto exit;
38933893- }
3894449238953895- bcopy(nameptr, &entry->name[0], namelen);
38963896- copyfile_debug(2, "copied name [%s]", entry->name);
44934493+ if (listsize > 0)
44944494+ sort_xattrname_list(attrnamebuf, endnamebuf - attrnamebuf);
3897449538983898- entrylen = ATTR_ENTRY_LENGTH(namelen);
38993899- entry = (attr_entry_t *)(((char *)entry) + entrylen);
44964496+ for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen)
44974497+ {
44984498+ namelen = strlen(nameptr) + 1;
44994499+ /* Skip over FinderInfo or Resource Fork names */
45004500+ if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0 ||
45014501+ strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0) {
45024502+ continue;
45034503+ }
45044504+ if (strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0) {
45054505+ seenq = 1;
45064506+ }
3900450739013901- if ((void*)entry >= (void*)endfilehdr) {
39023902- error = -1;
39033903- goto exit;
39043904- }
45084508+ /* The system should prevent this from happening, but... */
45094509+ if (namelen > XATTR_MAXNAMELEN + 1) {
45104510+ namelen = XATTR_MAXNAMELEN + 1;
45114511+ }
45124512+ if (s->copyIntent &&
45134513+ xattr_preserve_for_intent(nameptr, s->copyIntent) == 0) {
45144514+ // Skip it
45154515+ size_t amt = endnamebuf - (nameptr + namelen);
45164516+ memmove(nameptr, nameptr + namelen, amt);
45174517+ endnamebuf -= namelen;
45184518+ /* Set namelen to 0 so continue doesn't miss names */
45194519+ namelen = 0;
45204520+ continue;
45214521+ }
3905452239063906- /* Update the attributes header. */
39073907- filehdr->num_attrs++;
39083908- filehdr->data_start += (u_int32_t)entrylen;
39093909- }
39103910- }
45234523+ if (s->statuscb) {
45244524+ int rv;
45254525+ char eaname[namelen];
45264526+ bcopy(nameptr, eaname, namelen);
45274527+ eaname[namelen - 1] = 0; // Just to be sure!
45284528+ s->xattr_name = eaname;
45294529+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
45304530+ s->xattr_name = NULL;
45314531+ if (rv == COPYFILE_QUIT) {
45324532+ error = -1;
45334533+ s->err = ECANCELED;
45344534+ goto exit;
45354535+ } else if (rv == COPYFILE_SKIP) {
45364536+ size_t amt = endnamebuf - (nameptr + namelen);
45374537+ memmove(nameptr, nameptr + namelen, amt);
45384538+ endnamebuf -= namelen;
45394539+ /* Set namelen to 0 so continue doesn't miss names */
45404540+ namelen = 0;
45414541+ continue;
45424542+ }
45434543+ }
45444544+ entry->namelen = namelen;
45454545+ entry->flags = 0;
45464546+ if (nameptr + namelen > endnamebuf) {
45474547+ error = -1;
45484548+ goto exit;
45494549+ }
3911455039123912- /*
39133913- * If we have any quarantine data, we always pack it.
39143914- * But if we've already got it in the EA list, don't put it in again.
39153915- */
39163916- if (s->qinfo && !seenq)
39173917- {
39183918- ssize_t left = ATTR_MAX_HDR_SIZE - offset;
39193919- /* strlcpy returns number of bytes copied, but we need offset to point to the next byte */
39203920- offset += strlcpy(attrnamebuf + offset, XATTR_QUARANTINE_NAME, left) + 1;
39213921- }
45514551+ bcopy(nameptr, &entry->name[0], namelen);
45524552+ copyfile_debug(2, "copied name [%s]", entry->name);
3922455339233923- seenq = 0;
39243924- /*
39253925- * Collect the attribute data.
39263926- */
39273927- entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t));
45544554+ entrylen = ATTR_ENTRY_LENGTH(namelen);
45554555+ entry = (attr_entry_t *)(((char *)entry) + entrylen);
3928455639293929- for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen + 1)
39303930- {
39313931- namelen = strlen(nameptr);
45574557+ if ((void*)entry >= (void*)endfilehdr) {
45584558+ error = -1;
45594559+ goto exit;
45604560+ }
45614561+45624562+ /* Update the attributes header. */
45634563+ filehdr->num_attrs++;
45644564+ filehdr->data_start += (u_int32_t)entrylen;
45654565+ }
45664566+ }
3932456739333933- if (strcmp(nameptr, XATTR_SECURITY_NAME) == 0)
39343934- copyfile_pack_acl(s, &databuf, &datasize);
39353935- else if (s->qinfo && strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0)
45684568+ /*
45694569+ * If we have any quarantine data, we always pack it.
45704570+ * But if we've already got it in the EA list, don't put it in again.
45714571+ */
45724572+ if (s->qinfo && !seenq)
39364573 {
39373937- copyfile_pack_quarantine(s, &databuf, &datasize);
45744574+ ssize_t left = ATTR_MAX_HDR_SIZE - offset;
45754575+ /* strlcpy returns number of bytes copied, but we need offset to point to the next byte */
45764576+ offset += strlcpy(attrnamebuf + offset, XATTR_QUARANTINE_NAME, left) + 1;
39384577 }
39393939- /* Check for Finder Info. */
39403940- else if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0)
45784578+45794579+ seenq = 0;
45804580+ /*
45814581+ * Collect the attribute data.
45824582+ */
45834583+ entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t));
45844584+45854585+ for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen + 1)
39414586 {
39423942- if (s->statuscb)
39433943- {
39443944- int rv;
39453945- s->xattr_name = (char*)XATTR_FINDERINFO_NAME;
39463946- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
39473947- s->xattr_name = NULL;
39483948- if (rv == COPYFILE_QUIT)
45874587+ namelen = strlen(nameptr);
45884588+45894589+ if (strcmp(nameptr, XATTR_SECURITY_NAME) == 0)
45904590+ copyfile_pack_acl(s, &databuf, &datasize);
45914591+ else if (s->qinfo && strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0)
39494592 {
39503950- s->xattr_name = NULL;
39513951- s->err = ECANCELED;
39523952- error = -1;
39533953- goto exit;
45934593+ copyfile_pack_quarantine(s, &databuf, &datasize);
39544594 }
39553955- else if (rv == COPYFILE_SKIP)
45954595+ /* Check for Finder Info. */
45964596+ else if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0)
39564597 {
39573957- s->xattr_name = NULL;
39583958- continue;
45984598+ if (s->statuscb)
45994599+ {
46004600+ int rv;
46014601+ s->xattr_name = (char*)XATTR_FINDERINFO_NAME;
46024602+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
46034603+ s->xattr_name = NULL;
46044604+ if (rv == COPYFILE_QUIT)
46054605+ {
46064606+ s->xattr_name = NULL;
46074607+ s->err = ECANCELED;
46084608+ error = -1;
46094609+ goto exit;
46104610+ }
46114611+ else if (rv == COPYFILE_SKIP)
46124612+ {
46134613+ s->xattr_name = NULL;
46144614+ continue;
46154615+ }
46164616+ s->totalCopied = 0;
46174617+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
46184618+ s->xattr_name = NULL;
46194619+ if (rv == COPYFILE_QUIT)
46204620+ {
46214621+ s->err = ECANCELED;
46224622+ error = -1;
46234623+ goto exit;
46244624+ }
46254625+ }
46264626+ datasize = fgetxattr(s->src_fd, nameptr, (u_int8_t*)filehdr + filehdr->appledouble.entries[0].offset, 32, 0, 0);
46274627+ if (datasize < 0)
46284628+ {
46294629+ if (s->statuscb) {
46304630+ int rv;
46314631+ s->xattr_name = strdup(nameptr);
46324632+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
46334633+ if (s->xattr_name) {
46344634+ free(s->xattr_name);
46354635+ s->xattr_name = NULL;
46364636+ }
46374637+ if (rv == COPYFILE_QUIT) {
46384638+ error = -1;
46394639+ goto exit;
46404640+ }
46414641+ }
46424642+ if (COPYFILE_VERBOSE & s->flags)
46434643+ copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno);
46444644+ } else if (datasize != 32)
46454645+ {
46464646+ if (COPYFILE_VERBOSE & s->flags)
46474647+ copyfile_warn("unexpected size (%ld) for \"%s\"", datasize, nameptr);
46484648+ } else
46494649+ {
46504650+ if (COPYFILE_VERBOSE & s->flags)
46514651+ copyfile_warn(" copied 32 bytes of \"%s\" data @ offset 0x%08x",
46524652+ XATTR_FINDERINFO_NAME, filehdr->appledouble.entries[0].offset);
46534653+ if (s->statuscb) {
46544654+ int rv;
46554655+ s->xattr_name = strdup(nameptr);
46564656+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
46574657+ if (s->xattr_name) {
46584658+ free(s->xattr_name);
46594659+ s->xattr_name = NULL;
46604660+ }
46614661+ if (rv == COPYFILE_QUIT) {
46624662+ error = -1;
46634663+ goto exit;
46644664+ }
46654665+ }
46664666+ }
46674667+ continue; /* finder info doesn't have an attribute entry */
39594668 }
39603960- s->totalCopied = 0;
39613961- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
39623962- s->xattr_name = NULL;
39633963- if (rv == COPYFILE_QUIT)
46694669+ /* Check for Resource Fork. */
46704670+ else if (strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0)
46714671+ {
46724672+ hasrsrcfork = 1;
46734673+ continue;
46744674+ } else
39644675 {
39653965- s->err = ECANCELED;
39663966- error = -1;
39673967- goto exit;
39683968- }
39693969- }
39703970- datasize = fgetxattr(s->src_fd, nameptr, (u_int8_t*)filehdr + filehdr->appledouble.entries[0].offset, 32, 0, 0);
39713971- if (datasize < 0)
39723972- {
39733973- if (s->statuscb) {
39743974- int rv;
39753975- s->xattr_name = strdup(nameptr);
39763976- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
39773977- if (s->xattr_name) {
39783978- free(s->xattr_name);
39793979- s->xattr_name = NULL;
46764676+ /* Just a normal attribute. */
46774677+ if (s->statuscb)
46784678+ {
46794679+ int rv;
46804680+ s->xattr_name = strdup(nameptr);
46814681+ s->totalCopied = 0;
46824682+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
46834683+ if (s->xattr_name) {
46844684+ free(s->xattr_name);
46854685+ s->xattr_name = NULL;
46864686+ }
46874687+ /*
46884688+ * Due to the nature of the packed file, we can't skip at this point.
46894689+ */
46904690+ if (rv == COPYFILE_QUIT)
46914691+ {
46924692+ s->err = ECANCELED;
46934693+ error = -1;
46944694+ goto exit;
46954695+ }
39804696 }
39813981- if (rv == COPYFILE_QUIT) {
39823982- error = -1;
39833983- goto exit;
46974697+ datasize = fgetxattr(s->src_fd, nameptr, NULL, 0, 0, 0);
46984698+ if (datasize == 0)
46994699+ goto next;
47004700+ if (datasize < 0)
47014701+ {
47024702+ if (COPYFILE_VERBOSE & s->flags)
47034703+ copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno);
47044704+ if (s->statuscb)
47054705+ {
47064706+ int rv;
47074707+ s->xattr_name = strdup(nameptr);
47084708+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
47094709+ if (s->xattr_name) {
47104710+ free(s->xattr_name);
47114711+ s->xattr_name = NULL;
47124712+ }
47134713+ if (rv == COPYFILE_QUIT)
47144714+ {
47154715+ s->err = ECANCELED;
47164716+ error = -1;
47174717+ goto exit;
47184718+ }
47194719+ }
47204720+ goto next;
39844721 }
39853985- }
39863986- if (COPYFILE_VERBOSE & s->flags)
39873987- copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno);
39883988- } else if (datasize != 32)
39893989- {
39903990- if (COPYFILE_VERBOSE & s->flags)
39913991- copyfile_warn("unexpected size (%ld) for \"%s\"", datasize, nameptr);
39923992- } else
39933993- {
39943994- if (COPYFILE_VERBOSE & s->flags)
39953995- copyfile_warn(" copied 32 bytes of \"%s\" data @ offset 0x%08x",
39963996- XATTR_FINDERINFO_NAME, filehdr->appledouble.entries[0].offset);
39973997- if (s->statuscb) {
39983998- int rv;
39993999- s->xattr_name = strdup(nameptr);
40004000- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
40014001- if (s->xattr_name) {
40024002- free(s->xattr_name);
40034003- s->xattr_name = NULL;
47224722+ if (datasize > XATTR_MAXATTRLEN)
47234723+ {
47244724+ if (COPYFILE_VERBOSE & s->flags)
47254725+ copyfile_warn("skipping attr \"%s\" (too big)", nameptr);
47264726+ goto next;
40044727 }
40054005- if (rv == COPYFILE_QUIT) {
47284728+ databuf = malloc(datasize);
47294729+ if (databuf == NULL) {
40064730 error = -1;
40074007- goto exit;
47314731+ continue;
47324732+ }
47334733+ datasize = fgetxattr(s->src_fd, nameptr, databuf, datasize, 0, 0);
47344734+ if (s->statuscb) {
47354735+ int rv;
47364736+ s->xattr_name = strdup(nameptr);
47374737+ rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
47384738+ if (s->xattr_name) {
47394739+ free(s->xattr_name);
47404740+ s->xattr_name = NULL;
47414741+ }
47424742+ if (rv == COPYFILE_QUIT) {
47434743+ s->err = ECANCELED;
47444744+ error = -1;
47454745+ goto exit;
47464746+ }
40084747 }
40094009- }
40104010- }
40114011- continue; /* finder info doesn't have an attribute entry */
40124012- }
40134013- /* Check for Resource Fork. */
40144014- else if (strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0)
40154015- {
40164016- hasrsrcfork = 1;
40174017- continue;
40184018- } else
40194019- {
40204020- /* Just a normal attribute. */
40214021- if (s->statuscb)
40224022- {
40234023- int rv;
40244024- s->xattr_name = strdup(nameptr);
40254025- s->totalCopied = 0;
40264026- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
40274027- if (s->xattr_name) {
40284028- free(s->xattr_name);
40294029- s->xattr_name = NULL;
40304748 }
47494749+47504750+ entry->length = (u_int32_t)datasize;
47514751+ entry->offset = filehdr->data_start + filehdr->data_length;
47524752+47534753+ filehdr->data_length += (u_int32_t)datasize;
47544754+#if 0
40314755 /*
40324032- * Due to the nature of the packed file, we can't skip at this point.
47564756+ * >>> WARNING <<<
47574757+ * This assumes that the data is fits in memory (not
47584758+ * the case when there are lots of attributes or one of
47594759+ * the attributes is very large.
40334760 */
40344034- if (rv == COPYFILE_QUIT)
40354035- {
40364036- s->err = ECANCELED;
40374037- error = -1;
40384038- goto exit;
47614761+ if (entry->offset > ATTR_MAX_SIZE ||
47624762+ (entry->offset + datasize > ATTR_MAX_SIZE)) {
47634763+ error = 1;
47644764+ } else {
47654765+ bcopy(databuf, (char*)filehdr + entry->offset, datasize);
40394766 }
40404040- }
40414041- datasize = fgetxattr(s->src_fd, nameptr, NULL, 0, 0, 0);
40424042- if (datasize == 0)
40434043- goto next;
40444044- if (datasize < 0)
40454045- {
40464046- if (COPYFILE_VERBOSE & s->flags)
40474047- copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno);
40484048- if (s->statuscb)
40494049- {
40504050- int rv;
40514051- s->xattr_name = strdup(nameptr);
40524052- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
40534053- if (s->xattr_name) {
40544054- free(s->xattr_name);
40554055- s->xattr_name = NULL;
40564056- }
40574057- if (rv == COPYFILE_QUIT)
40584058- {
40594059- s->err = ECANCELED;
40604060- error = -1;
40614061- goto exit;
40624062- }
47674767+#else
47684768+ if (pwrite(s->dst_fd, databuf, datasize, entry->offset) != datasize) {
47694769+ error = 1;
40634770 }
40644064- goto next;
40654065- }
40664066- if (datasize > XATTR_MAXATTRLEN)
40674067- {
40684068- if (COPYFILE_VERBOSE & s->flags)
40694069- copyfile_warn("skipping attr \"%s\" (too big)", nameptr);
40704070- goto next;
40714071- }
40724072- databuf = malloc(datasize);
40734073- if (databuf == NULL) {
40744074- error = -1;
40754075- continue;
40764076- }
40774077- datasize = fgetxattr(s->src_fd, nameptr, databuf, datasize, 0, 0);
40784078- if (s->statuscb) {
40794079- int rv;
40804080- s->xattr_name = strdup(nameptr);
40814081- rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
40824082- if (s->xattr_name) {
40834083- free(s->xattr_name);
40844084- s->xattr_name = NULL;
40854085- }
40864086- if (rv == COPYFILE_QUIT) {
40874087- s->err = ECANCELED;
40884088- error = -1;
40894089- goto exit;
40904090- }
40914091- }
47714771+#endif
47724772+ free(databuf);
47734773+47744774+ copyfile_debug(3, "copied %ld bytes of \"%s\" data @ offset 0x%08x", datasize, nameptr, entry->offset);
47754775+ next:
47764776+ /* bump to next entry */
47774777+ entrylen = ATTR_ENTRY_LENGTH(entry->namelen);
47784778+ entry = (attr_entry_t *)((char *)entry + entrylen);
40924779 }
4093478040944094- entry->length = (u_int32_t)datasize;
40954095- entry->offset = filehdr->data_start + filehdr->data_length;
47814781+ /* Now we know where the resource fork data starts. */
47824782+ filehdr->appledouble.entries[1].offset = (filehdr->data_start + filehdr->data_length);
4096478340974097- filehdr->data_length += (u_int32_t)datasize;
40984098-#if 0
40994099- /*
41004100- * >>> WARNING <<<
41014101- * This assumes that the data is fits in memory (not
41024102- * the case when there are lots of attributes or one of
41034103- * the attributes is very large.
41044104- */
41054105- if (entry->offset > ATTR_MAX_SIZE ||
41064106- (entry->offset + datasize > ATTR_MAX_SIZE)) {
41074107- error = 1;
41084108- } else {
41094109- bcopy(databuf, (char*)filehdr + entry->offset, datasize);
41104110- }
41114111-#else
41124112- if (pwrite(s->dst_fd, databuf, datasize, entry->offset) != datasize) {
41134113- error = 1;
41144114- }
41154115-#endif
41164116- free(databuf);
47844784+ /* We also know the size of the "Finder Info entry. */
47854785+ filehdr->appledouble.entries[0].length =
47864786+ filehdr->appledouble.entries[1].offset - filehdr->appledouble.entries[0].offset;
4117478741184118- copyfile_debug(3, "copied %ld bytes of \"%s\" data @ offset 0x%08x", datasize, nameptr, entry->offset);
41194119-next:
41204120- /* bump to next entry */
41214121- entrylen = ATTR_ENTRY_LENGTH(entry->namelen);
41224122- entry = (attr_entry_t *)((char *)entry + entrylen);
41234123- }
47884788+ filehdr->total_size = filehdr->appledouble.entries[1].offset;
4124478941254125- /* Now we know where the resource fork data starts. */
41264126- filehdr->appledouble.entries[1].offset = (filehdr->data_start + filehdr->data_length);
41274127-41284128- /* We also know the size of the "Finder Info entry. */
41294129- filehdr->appledouble.entries[0].length =
41304130- filehdr->appledouble.entries[1].offset - filehdr->appledouble.entries[0].offset;
41314131-41324132- filehdr->total_size = filehdr->appledouble.entries[1].offset;
41334133-41344134- /* Copy Resource Fork. */
41354135- if (hasrsrcfork && (error = copyfile_pack_rsrcfork(s, filehdr)))
41364136- goto exit;
47904790+ /* Copy Resource Fork. */
47914791+ if (hasrsrcfork && (error = copyfile_pack_rsrcfork(s, filehdr)))
47924792+ goto exit;
4137479341384138- /* Write the header to disk. */
41394139- datasize = filehdr->data_start;
47944794+ /* Write the header to disk. */
47954795+ datasize = filehdr->data_start;
4140479641414141- swap_adhdr(&filehdr->appledouble);
41424142- swap_attrhdr(filehdr);
41434143- swap_attrhdr_entries(filehdr);
47974797+ swap_adhdr(&filehdr->appledouble);
47984798+ swap_attrhdr(filehdr);
47994799+ swap_attrhdr_entries(filehdr);
4144480041454145- if (pwrite(s->dst_fd, filehdr, datasize, 0) != datasize)
41464146- {
41474147- if (COPYFILE_VERBOSE & s->flags)
41484148- copyfile_warn("couldn't write file header");
41494149- error = -1;
41504150- goto exit;
41514151- }
48014801+ if (pwrite(s->dst_fd, filehdr, datasize, 0) != datasize)
48024802+ {
48034803+ if (COPYFILE_VERBOSE & s->flags)
48044804+ copyfile_warn("couldn't write file header");
48054805+ error = -1;
48064806+ goto exit;
48074807+ }
41524808exit:
41534153- if (filehdr) free(filehdr);
41544154- if (attrnamebuf) free(attrnamebuf);
48094809+ if (filehdr) free(filehdr);
48104810+ if (attrnamebuf) free(attrnamebuf);
4155481141564156- if (error)
41574157- return error;
41584158- else
41594159- return copyfile_stat(s);
48124812+ if (error)
48134813+ return error;
48144814+ else
48154815+ return copyfile_stat(s);
41604816}
···11/*
22- * Copyright (c) 2013 Apple, Inc. All rights reserved.
22+ * Copyright (c) 2013-19 Apple, Inc. All rights reserved.
33 *
44 * @APPLE_LICENSE_HEADER_START@
55 *
···11/*
22- * Copyright (c) 2013 Apple, Inc. All rights reserved.
22+ * Copyright (c) 2013-19 Apple, Inc. All rights reserved.
33 *
44 * @APPLE_LICENSE_HEADER_START@
55 *
···8888 * intention type.
8989 */
9090#define kCopyOperationPropertyNeverPreserve ((CopyOperationProperties_t)0x0004)
9191-9292-#if 0
9393-/*
9494- * These are all going to be removed, and I don't believe anyone used them.
9595- */
9696-/*
9797- * Given an extended attribute name, and a set of properties, return an
9898- * allocated C string with the name. This will return NULL on error;
9999- * errno may be set to ENOMEM if the new name cannot be allocated, or
100100- * ENAMETOOLONG if the new name is longer than the maximum for EAs (127 UTF8
101101- * characters). The caller must deallocate the return value otherwise.
102102- *
103103- * If no properties are set, it returns a copy of the EA name.
104104- *
105105- * If the EA name is in the internal list, and the properties are the same as
106106- * defined there, then it will also return an unmodified copy of the EA name.
107107- */
108108-extern char *_xattrNameWithProperties(const char *, CopyOperationProperties_t) DEPRECATED_IN_MAC_OS_X_VERSION_10_10_AND_LATER;
109109-110110-/*
111111- * Given an extended attribute name, which may or may not have properties encoded
112112- * as a suffix, return just the name of the attribute. E.g., com.example.mine#P
113113- * would return "com.example.mine". The return value will be NULL on error;
114114- * errno will be set to ENOMEM if it cannot be allocated. The caller must deallocate
115115- * the return value.
116116- */
117117-extern char *_xattrNameWithoutProperties(const char *) DEPRECATED_IN_MAC_OS_X_VERSION_10_10_AND_LATER;
118118-119119-/*
120120- * Given an EA name, return the properties. If the name is in the internal list,
121121- * those properties will be returned. Unknown property encodings are ignored.
122122- */
123123-extern CopyOperationProperties_t _xattrPropertiesFromName(const char *) DEPRECATED_IN_MAC_OS_X_VERSION_10_10_AND_LATER;
124124-#endif /* 0 */
1259112692__END_DECLS
12793
+2
src/copyfile/xattr_flags.c
···5858 { "com.apple.security.", "S", propFlagsPrefix },
5959 { XATTR_RESOURCEFORK_NAME, "PCS", 0 }, // Don't keep for safe save
6060 { XATTR_FINDERINFO_NAME, "PCS", 0 }, // Same as ResourceFork
6161+ { "com.apple.root.installed", "PC", 0}, // Don't share or sync. Copyable by entitled callers
6162 { 0, 0, 0 },
6263};
6364···6970 { "com.apple.security.", "N", propFlagsPrefix },
7071 { XATTR_RESOURCEFORK_NAME, "PCS", 0 }, // Don't keep for safe save
7172 { XATTR_FINDERINFO_NAME, "PCS", 0 }, // Same as ResourceFork
7373+ { "com.apple.root.installed", "PC", 0}, // Don't share or sync. Copyable by entitled callers
7274 { 0, 0, 0 },
7375};
7476
+7-8
src/copyfile/xattr_name_with_flags.3
···11.\"
22.\" Copyright (c) 2013 Apple Computer, Inc. All rights reserved.
33.\"
44-.Dd October 7, 2013
44+.Dd October 9, 2018
55.Dt XATTR_NAME_WITH_FLAGS 3
66.Os
77.Sh NAME
···1010.Sh LIBRARY
1111.Lb libc
1212.Sh SYNOPSIS
1313-.In xattr_properties.h
1313+.In xattr_flags.h
1414.Ft int
1515.Fn xattr_preserve_for_intent "const char *" "xattr_operation_intent_t"
1616.Ft char *
···5656intent, or 1 if it should.
5757.Pp
5858The function
5959-.Fn xattr_presere_for_intent
5959+.Fn xattr_preserve_for_intent
6060combines the functions above, and will return zero if the
6161named extended attribute should be preserved during a copy for
6262the given intent.
···7373.It Dv XATTR_OPERATION_INTENT_SAVE
7474Indicates that intent is to perform a save (perhaps as in a "safe save").
7575This differs from a copy in that the content may be changing; the destination
7676-may be over-writing or replacing the source, and som extended attributes should
7676+may be over-writing or replacing the source, and some extended attributes should
7777not be preserved during this process.
7878.It Dv XATTR_OPERATION_INTENT_SHARE
7979Indicates that the intent is to share, or export, the object. For example,
8080saving as an attachment in an email message, or placing in a public folder.
8181Sensitive information should probably not be preserved in this case.
8282.It Dv XATTR_OPERATION_INTENT_SYNC
8383-Indicates that the intent is to sync the object to a service like iCloud.
8383+Indicates that the intent is to sync the object to a service like iCloud Drive.
8484.El
8585.Sh FLAGS
8686Various flags are defined by the type
···101101source object to a destination, no matter what the given intent is.
102102.It Dv XATTR_FLAG_SYNCABLE
103103This indicates that the extended attribute should be copied when the file
104104-is synced on services like iCloud. Sync services tends to want the metadata
105105-synced to be kept to a bare minimum, and may enforce additional restrictions
106106-on the acceptable size and number of extended attributes.
104104+is synced on services like iCloud Drive. Sync services may enforce additional
105105+restrictions on the acceptable size and number of extended attributes.
107106.El
108107.Sh EXAMPLE
109108The following example is a simple function that, given an extended attribute