]> git.saurik.com Git - apple/copyfile.git/blame - copyfile.c
copyfile-138.tar.gz
[apple/copyfile.git] / copyfile.c
CommitLineData
b3aa0442 1/*
3f81d1c4 2 * Copyright (c) 2004-2010 Apple, Inc. All rights reserved.
b3aa0442
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
1f4df596 5 *
b3aa0442
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
1f4df596 12 *
b3aa0442
A
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
1f4df596 20 *
b3aa0442
A
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <err.h>
25#include <errno.h>
26#include <sys/types.h>
27#include <sys/acl.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <stdint.h>
32#include <syslog.h>
33#include <unistd.h>
34#include <fcntl.h>
35#include <sys/errno.h>
36#include <sys/stat.h>
37#include <sys/time.h>
38#include <sys/xattr.h>
3f81d1c4 39#include <sys/attr.h>
b3aa0442
A
40#include <sys/syscall.h>
41#include <sys/param.h>
42#include <sys/mount.h>
43#include <sys/acl.h>
44#include <libkern/OSByteOrder.h>
45#include <membership.h>
659640e4
A
46#include <fts.h>
47#include <libgen.h>
1f4df596 48#include <sys/clonefile.h>
913df16a
A
49#include <System/sys/content_protection.h>
50
3f81d1c4
A
51#ifdef VOL_CAP_FMT_DECMPFS_COMPRESSION
52# include <Kernel/sys/decmpfs.h>
53#endif
54
05745112 55#include <TargetConditionals.h>
11f767b3 56#if !TARGET_OS_IPHONE
b3aa0442
A
57#include <quarantine.h>
58
59#define XATTR_QUARANTINE_NAME qtn_xattr_name
11f767b3 60#else /* TARGET_OS_IPHONE */
05745112
A
61#define qtn_file_t void *
62#define QTN_SERIALIZED_DATA_MAX 0
63static void * qtn_file_alloc(void) { return NULL; }
64static int qtn_file_init_with_fd(void *x, int y) { return -1; }
11f767b3
A
65static int qtn_file_init_with_path(void *x, const char *path) { return -1; }
66static int qtn_file_init_with_data(void *x, const void *data, size_t len) { return -1; }
05745112 67static void qtn_file_free(void *x) { return; }
3f81d1c4 68static int qtn_file_apply_to_fd(void *x, int y) { return 0; }
05745112
A
69static char *qtn_error(int x) { return NULL; }
70static int qtn_file_to_data(void *x, char *y, size_t z) { return -1; }
71static void *qtn_file_clone(void *x) { return NULL; }
1f4df596
A
72static uint32_t qtn_file_get_flags(void *x) { return 0; }
73static int qtn_file_set_flags(void *x, uint32_t flags) { return 0; }
05745112 74#define XATTR_QUARANTINE_NAME "figgledidiggledy"
1f4df596 75#define QTN_FLAG_DO_NOT_TRANSLOCATE 0
11f767b3 76#endif /* TARGET_OS_IPHONE */
b3aa0442
A
77
78#include "copyfile.h"
11f767b3 79#include "copyfile_private.h"
618b37c8 80#include "xattr_flags.h"
b3aa0442 81
659640e4 82enum cfInternalFlags {
913df16a
A
83 cfDelayAce = 1 << 0, /* set if ACE shouldn't be set until post-order traversal */
84 cfMakeFileInvisible = 1 << 1, /* set if kFinderInvisibleMask is on src */
85 cfSawDecmpEA = 1 << 2, /* set if we've seen a com.apple.decmpfs xattr */
86 cfSrcProtSupportValid = 1 << 3, /* set if cfSrcSupportsCProtect is valid */
87 cfSrcSupportsCProtect = 1 << 4, /* set if src supports MNT_CPROTECT */
88 cfDstProtSupportValid = 1 << 5, /* set if cfDstSupportsCProtect is valid */
89 cfDstSupportsCProtect = 1 << 6, /* set if dst supports MNT_CPROTECT */
659640e4
A
90};
91
913df16a
A
92#define COPYFILE_MNT_CPROTECT_MASK (cfSrcProtSupportValid | cfSrcSupportsCProtect | cfDstProtSupportValid | cfDstSupportsCProtect)
93
b3aa0442
A
94/*
95 * The state structure keeps track of
96 * the source filename, the destination filename, their
97 * associated file-descriptors, the stat infomration for the
98 * source file, the security information for the source file,
99 * the flags passed in for the copy, a pointer to place statistics
100 * (not currently implemented), debug flags, and a pointer to callbacks
101 * (not currently implemented).
102 */
103struct _copyfile_state
104{
1f4df596
A
105 char *src;
106 char *dst;
107 int src_fd;
108 int dst_fd;
109 struct stat sb;
110 filesec_t fsec;
111 copyfile_flags_t flags;
112 unsigned int internal_flags;
113 void *stats;
114 uint32_t debug;
115 copyfile_callback_t statuscb;
116 void *ctx;
117 qtn_file_t qinfo; /* Quarantine information -- probably NULL */
118 filesec_t original_fsec;
119 filesec_t permissive_fsec;
120 off_t totalCopied;
121 int err;
122 char *xattr_name;
123 xattr_operation_intent_t copyIntent;
124 bool was_cloned;
b3aa0442
A
125};
126
913df16a
A
127#define GET_PROT_CLASS(fd) fcntl((fd), F_GETPROTECTIONCLASS)
128#define SET_PROT_CLASS(fd, prot_class) fcntl((fd), F_SETPROTECTIONCLASS, (prot_class))
129
659640e4 130struct acl_entry {
1f4df596 131 u_int32_t ae_magic;
659640e4 132#define _ACL_ENTRY_MAGIC 0xac1ac101
1f4df596
A
133 u_int32_t ae_tag;
134 guid_t ae_applicable;
135 u_int32_t ae_flags;
136 u_int32_t ae_perms;
659640e4
A
137};
138
1f4df596
A
139#define PACE(ace) \
140 do { \
141 struct acl_entry *__t = (struct acl_entry*)(ace); \
142 fprintf(stderr, "%s(%d): " #ace " = { flags = %#x, perms = %#x }\n", __FUNCTION__, __LINE__, __t->ae_flags, __t->ae_perms); \
659640e4
A
143 } while (0)
144
145#define PACL(ace) \
146 do { \
147 ssize_t __l; char *__cp = acl_to_text(ace, &__l); \
148 fprintf(stderr, "%s(%d): " #ace " = %s\n", __FUNCTION__, __LINE__, __cp ? __cp : "(null)"); \
149 } while (0)
150
151static int
152acl_compare_permset_np(acl_permset_t p1, acl_permset_t p2)
153{
154 struct pm { u_int32_t ap_perms; } *ps1, *ps2;
155 ps1 = (struct pm*) p1;
156 ps2 = (struct pm*) p2;
157
1f4df596 158 return ((ps1->ap_perms == ps2->ap_perms) ? 1 : 0);
659640e4
A
159}
160
3f81d1c4
A
161
162static int
163doesdecmpfs(int fd) {
164#ifdef DECMPFS_XATTR_NAME
165 int rv;
166 struct attrlist attrs;
167 char volroot[MAXPATHLEN + 1];
168 struct statfs sfs;
169 struct {
170 uint32_t length;
171 vol_capabilities_attr_t volAttrs;
172 } volattrs;
173
174 (void)fstatfs(fd, &sfs);
175 strlcpy(volroot, sfs.f_mntonname, sizeof(volroot));
176
177 memset(&attrs, 0, sizeof(attrs));
178 attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
179 attrs.volattr = ATTR_VOL_CAPABILITIES;
180
181 rv = getattrlist(volroot, &attrs, &volattrs, sizeof(volattrs), 0);
182
183 if (rv != -1 &&
184 (volattrs.volAttrs.capabilities[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_DECMPFS_COMPRESSION) &&
185 (volattrs.volAttrs.valid[VOL_CAPABILITIES_FORMAT] & VOL_CAP_FMT_DECMPFS_COMPRESSION)) {
186 return 1;
187 }
188#endif
189 return 0;
190}
191
913df16a
A
192static int
193does_copy_protection(int fd)
194{
1f4df596 195 struct statfs sfs;
913df16a 196
1f4df596
A
197 if (fstatfs(fd, &sfs) == -1)
198 return -1;
913df16a 199
1f4df596 200 return ((sfs.f_flags & MNT_CPROTECT) == MNT_CPROTECT);
913df16a 201}
3f81d1c4
A
202
203static void
204sort_xattrname_list(void *start, size_t length)
205{
206 char **ptrs = NULL;
207 int nel;
208 char *tmp;
209 int indx = 0;
210
211 /* If it's not a proper C string at the end, don't do anything */
212 if (((char*)start)[length] != 0)
213 return;
214 /*
215 * In order to sort the list of names, we need to
216 * make a list of pointers to strings. To do that,
217 * we need to run through the buffer, and find the
218 * beginnings of strings.
219 */
220 nel = 10; // Most files don't have many EAs
221 ptrs = (char**)calloc(nel, sizeof(char*));
222
223 if (ptrs == NULL)
224 goto done;
225
226#ifdef DEBUG
1f4df596
A
227 {
228 char *curPtr = start;
229 while (curPtr < (char*)start + length) {
230 printf("%s\n", curPtr);
231 curPtr += strlen(curPtr) + 1;
232 }
3f81d1c4 233 }
3f81d1c4
A
234#endif
235
236 tmp = ptrs[indx++] = (char*)start;
237
238 while (tmp = memchr(tmp, 0, ((char*)start + length) - tmp)) {
239 if (indx == nel) {
240 nel += 10;
241 ptrs = realloc(ptrs, sizeof(char**) * nel);
242 if (ptrs == NULL)
243 goto done;
244 }
245 ptrs[indx++] = ++tmp;
246 }
247#ifdef DEBUG
248 printf("Unsorted:\n");
249 for (nel = 0; nel < indx-1; nel++) {
250 printf("\tEA %d = `%s'\n", nel, ptrs[nel]);
251 }
252#endif
253 qsort_b(ptrs, indx-1, sizeof(char*), ^(const void *left, const void *right) {
254 int rv;
255 char *lstr = *(char**)left, *rstr = *(char**)right;
256 rv = strcmp(lstr, rstr);
257 return rv;
258 });
259#ifdef DEBUG
260 printf("Sorted:\n");
261 for (nel = 0; nel < indx-1; nel++) {
262 printf("\tEA %d = `%s'\n", nel, ptrs[nel]);
263 }
264#endif
265 /*
266 * Now that it's sorted, we need to make a copy, so we can
267 * move the strings around into the new order. Then we
268 * copy that on top of the old buffer, and we're done.
269 */
270 char *copy = malloc(length);
271 if (copy) {
272 int i;
273 char *curPtr = copy;
274
275 for (i = 0; i < indx-1; i++) {
276 size_t len = strlen(ptrs[i]);
277 memcpy(curPtr, ptrs[i], len+1);
278 curPtr += len+1;
279 }
280 memcpy(start, copy, length);
281 free(copy);
282 }
283
284done:
285 if (ptrs)
286 free(ptrs);
287 return;
288}
289
b3aa0442
A
290/*
291 * Internally, the process is broken into a series of
292 * private functions.
293 */
294static int copyfile_open (copyfile_state_t);
295static int copyfile_close (copyfile_state_t);
296static int copyfile_data (copyfile_state_t);
297static int copyfile_stat (copyfile_state_t);
298static int copyfile_security (copyfile_state_t);
299static int copyfile_xattr (copyfile_state_t);
300static int copyfile_pack (copyfile_state_t);
301static int copyfile_unpack (copyfile_state_t);
302
303static copyfile_flags_t copyfile_check (copyfile_state_t);
304static filesec_t copyfile_fix_perms(copyfile_state_t, filesec_t *);
305static int copyfile_preamble(copyfile_state_t *s, copyfile_flags_t flags);
306static int copyfile_internal(copyfile_state_t state, copyfile_flags_t flags);
307static int copyfile_unset_posix_fsec(filesec_t);
308static int copyfile_quarantine(copyfile_state_t);
309
310#define COPYFILE_DEBUG (1<<31)
311#define COPYFILE_DEBUG_VAR "COPYFILE_DEBUG"
312
313#ifndef _COPYFILE_TEST
314# define copyfile_warn(str, ...) syslog(LOG_WARNING, str ": %m", ## __VA_ARGS__)
315# define copyfile_debug(d, str, ...) \
1f4df596
A
316 do { \
317 if (s && (d <= s->debug)) {\
318 syslog(LOG_DEBUG, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \
319 } \
320 } while (0)
b3aa0442
A
321#else
322#define copyfile_warn(str, ...) \
1f4df596 323 fprintf(stderr, "%s:%d:%s() " str ": %s\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__, (errno) ? strerror(errno) : "")
b3aa0442 324# define copyfile_debug(d, str, ...) \
1f4df596
A
325 do { \
326 if (s && (d <= s->debug)) {\
327 fprintf(stderr, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \
328 } \
329 } while(0)
b3aa0442
A
330#endif
331
332static int copyfile_quarantine(copyfile_state_t s)
333{
1f4df596 334 int rv = 0;
b3aa0442
A
335 if (s->qinfo == NULL)
336 {
1f4df596
A
337 int error;
338 s->qinfo = qtn_file_alloc();
339 if (s->qinfo == NULL)
340 {
341 rv = -1;
342 goto done;
343 }
344 if ((error = qtn_file_init_with_fd(s->qinfo, s->src_fd)) != 0)
345 {
346 qtn_file_free(s->qinfo);
347 s->qinfo = NULL;
348 rv = -1;
349 goto done;
350 }
b3aa0442 351 }
b3aa0442 352done:
1f4df596 353 return rv;
b3aa0442
A
354}
355
659640e4
A
356static int
357add_uberace(acl_t *acl)
358{
359 acl_entry_t entry;
360 acl_permset_t permset;
361 uuid_t qual;
362
363 if (mbr_uid_to_uuid(getuid(), qual) != 0)
364 goto error_exit;
365
366 /*
367 * First, we create an entry, and give it the special name
368 * of ACL_FIRST_ENTRY, thus guaranteeing it will be first.
369 * After that, we clear out all the permissions in it, and
370 * add three permissions: WRITE_DATA, WRITE_ATTRIBUTES, and
371 * WRITE_EXTATTRIBUTES. We put these into an ACE that allows
372 * the functionality, and put this into the ACL.
373 */
374 if (acl_create_entry_np(acl, &entry, ACL_FIRST_ENTRY) == -1)
375 goto error_exit;
3f81d1c4
A
376 if (acl_get_permset(entry, &permset) == -1) {
377 copyfile_warn("acl_get_permset");
659640e4 378 goto error_exit;
3f81d1c4
A
379 }
380 if (acl_clear_perms(permset) == -1) {
381 copyfile_warn("acl_clear_permset");
659640e4 382 goto error_exit;
3f81d1c4
A
383 }
384 if (acl_add_perm(permset, ACL_WRITE_DATA) == -1) {
385 copyfile_warn("add ACL_WRITE_DATA");
659640e4 386 goto error_exit;
3f81d1c4
A
387 }
388 if (acl_add_perm(permset, ACL_WRITE_ATTRIBUTES) == -1) {
389 copyfile_warn("add ACL_WRITE_ATTRIBUTES");
659640e4 390 goto error_exit;
3f81d1c4
A
391 }
392 if (acl_add_perm(permset, ACL_WRITE_EXTATTRIBUTES) == -1) {
393 copyfile_warn("add ACL_WRITE_EXTATTRIBUTES");
659640e4 394 goto error_exit;
3f81d1c4
A
395 }
396 if (acl_add_perm(permset, ACL_APPEND_DATA) == -1) {
397 copyfile_warn("add ACL_APPEND_DATA");
659640e4 398 goto error_exit;
3f81d1c4
A
399 }
400 if (acl_add_perm(permset, ACL_WRITE_SECURITY) == -1) {
401 copyfile_warn("add ACL_WRITE_SECURITY");
659640e4 402 goto error_exit;
3f81d1c4
A
403 }
404 if (acl_set_tag_type(entry, ACL_EXTENDED_ALLOW) == -1) {
405 copyfile_warn("set ACL_EXTENDED_ALLOW");
659640e4 406 goto error_exit;
3f81d1c4 407 }
659640e4 408
3f81d1c4
A
409 if(acl_set_permset(entry, permset) == -1) {
410 copyfile_warn("acl_set_permset");
659640e4 411 goto error_exit;
3f81d1c4
A
412 }
413 if(acl_set_qualifier(entry, qual) == -1) {
414 copyfile_warn("acl_set_qualifier");
659640e4 415 goto error_exit;
3f81d1c4 416 }
659640e4
A
417
418 return 0;
419error_exit:
420 return -1;
421}
422
423static int
424is_uberace(acl_entry_t ace)
425{
426 int retval = 0;
427 acl_permset_t perms, tperms;
428 acl_t tacl;
429 acl_entry_t tentry;
430 acl_tag_t tag;
3f81d1c4 431 guid_t *qual = NULL;
659640e4
A
432 uuid_t myuuid;
433
434 // Who am I, and who is the ACE for?
435 mbr_uid_to_uuid(geteuid(), myuuid);
436 qual = (guid_t*)acl_get_qualifier(ace);
437
438 // Need to create a temporary acl, so I can get the uberace template.
439 tacl = acl_init(1);
440 if (tacl == NULL) {
441 goto done;
442 }
443 add_uberace(&tacl);
444 if (acl_get_entry(tacl, ACL_FIRST_ENTRY, &tentry) != 0) {
445 goto done;
446 }
447 acl_get_permset(tentry, &tperms);
448
449 // Now I need to get
450 acl_get_tag_type(ace, &tag);
451 acl_get_permset(ace, &perms);
452
453 if (tag == ACL_EXTENDED_ALLOW &&
454 (memcmp(qual, myuuid, sizeof(myuuid)) == 0) &&
455 acl_compare_permset_np(tperms, perms))
456 retval = 1;
457
458done:
459
3f81d1c4
A
460 if (qual)
461 acl_free(qual);
462
659640e4
A
463 if (tacl)
464 acl_free(tacl);
465
466 return retval;
467}
468
469static void
470remove_uberace(int fd, struct stat *sbuf)
471{
472 filesec_t fsec = NULL;
473 acl_t acl = NULL;
474 acl_entry_t entry;
475 struct stat sb;
476
477 fsec = filesec_init();
478 if (fsec == NULL) {
479 goto noacl;
480 }
481
482 if (fstatx_np(fd, &sb, fsec) != 0) {
483 if (errno == ENOTSUP)
484 goto noacl;
485 goto done;
486 }
487
488 if (filesec_get_property(fsec, FILESEC_ACL, &acl) != 0) {
489 goto done;
490 }
491
492 if (acl_get_entry(acl, ACL_FIRST_ENTRY, &entry) == 0) {
493 if (is_uberace(entry))
494 {
495 mode_t m = sbuf->st_mode & ~S_IFMT;
496
497 if (acl_delete_entry(acl, entry) != 0 ||
498 filesec_set_property(fsec, FILESEC_ACL, &acl) != 0 ||
499 filesec_set_property(fsec, FILESEC_MODE, &m) != 0 ||
500 fchmodx_np(fd, fsec) != 0)
501 goto noacl;
502 }
503 }
504
505done:
506 if (acl)
507 acl_free(acl);
508 if (fsec)
509 filesec_free(fsec);
510 return;
511
512noacl:
513 fchmod(fd, sbuf->st_mode & ~S_IFMT);
514 goto done;
515}
516
517static void
518reset_security(copyfile_state_t s)
519{
1f4df596
A
520 /* If we haven't reset the file security information
521 * (COPYFILE_SECURITY is not set in flags)
522 * restore back the permissions the file had originally
523 *
524 * One of the reasons this seems so complicated is that
525 * it is partially at odds with copyfile_security().
526 *
527 * Simplisticly, we are simply trying to make sure we
528 * only copy what was requested, and that we don't stomp
529 * on what wasn't requested.
530 */
659640e4
A
531
532#ifdef COPYFILE_RECURSIVE
533 if (s->dst_fd > -1) {
534 struct stat sbuf;
535
536 if (s->src_fd > -1 && (s->flags & COPYFILE_STAT))
537 fstat(s->src_fd, &sbuf);
538 else
539 fstat(s->dst_fd, &sbuf);
1f4df596 540
659640e4
A
541 if (!(s->internal_flags & cfDelayAce))
542 remove_uberace(s->dst_fd, &sbuf);
543 }
544#else
1f4df596
A
545 if (s->permissive_fsec && (s->flags & COPYFILE_SECURITY) != COPYFILE_SECURITY) {
546 if (s->flags & COPYFILE_ACL) {
547 /* Just need to reset the BSD information -- mode, owner, group */
548 (void)fchown(s->dst_fd, s->dst_sb.st_uid, s->dst_sb.st_gid);
549 (void)fchmod(s->dst_fd, s->dst_sb.st_mode);
550 } else {
551 /*
552 * flags is either COPYFILE_STAT, or neither; if it's
553 * neither, then we restore both ACL and POSIX permissions;
554 * if it's STAT, however, then we only want to restore the
555 * ACL (which may be empty). We do that by removing the
556 * POSIX information from the filesec object.
557 */
558 if (s->flags & COPYFILE_STAT) {
559 copyfile_unset_posix_fsec(s->original_fsec);
560 }
561 if (fchmodx_np(s->dst_fd, s->original_fsec) < 0 && errno != ENOTSUP)
562 copyfile_warn("restoring security information");
659640e4 563 }
659640e4 564 }
659640e4 565
1f4df596
A
566 if (s->permissive_fsec) {
567 filesec_free(s->permissive_fsec);
568 s->permissive_fsec = NULL;
569 }
659640e4 570
1f4df596
A
571 if (s->original_fsec) {
572 filesec_free(s->original_fsec);
573 s->original_fsec = NULL;
574 }
659640e4
A
575#endif
576
1f4df596 577 return;
659640e4
A
578}
579
580/*
581 * copytree -- recursively copy a hierarchy.
582 *
583 * Unlike normal copyfile(), copytree() can copy an entire hierarchy.
584 * Care is taken to keep the ACLs set up correctly, in addition to the
585 * normal copying that is done. (When copying a hierarchy, we can't
586 * get rid of the "allow-all-writes" ACE on a directory until we're done
587 * copying the *contents* of the directory.)
588 *
589 * The other big difference from copyfile (for the moment) is that copytree()
590 * will use a call-back function to pass along information about what is
591 * about to be copied, and whether or not it succeeded.
592 *
593 * copytree() is called from copyfile() -- but copytree() itself then calls
594 * copyfile() to copy each individual object.
595 *
596 * XXX - no effort is made to handle overlapping hierarchies at the moment.
597 *
598 */
599
600static int
601copytree(copyfile_state_t s)
602{
603 char *slash;
604 int retval = 0;
605 int (*sfunc)(const char *, struct stat *);
606 copyfile_callback_t status = NULL;
607 char srcisdir = 0, dstisdir = 0, dstexists = 0;
608 struct stat sbuf;
609 char *src, *dst;
610 const char *dstpathsep = "";
611#ifdef NOTYET
612 char srcpath[PATH_MAX * 2 + 1], dstpath[PATH_MAX * 2 + 1];
613#endif
614 char *srcroot;
615 FTS *fts = NULL;
616 FTSENT *ftsent;
617 ssize_t offset = 0;
618 const char *paths[2] = { 0 };
619 unsigned int flags = 0;
620 int fts_flags = FTS_NOCHDIR;
913df16a 621 dev_t last_dev = s->sb.st_dev;
659640e4
A
622
623 if (s == NULL) {
624 errno = EINVAL;
625 retval = -1;
626 goto done;
627 }
628 if (s->flags & (COPYFILE_MOVE | COPYFILE_UNLINK | COPYFILE_CHECK | COPYFILE_PACK | COPYFILE_UNPACK)) {
629 errno = EINVAL;
630 retval = -1;
631 goto done;
632 }
633
913df16a 634 flags = s->flags & (COPYFILE_ALL | COPYFILE_NOFOLLOW | COPYFILE_VERBOSE | COPYFILE_EXCL);
659640e4
A
635
636 paths[0] = src = s->src;
637 dst = s->dst;
638
639 if (src == NULL || dst == NULL) {
640 errno = EINVAL;
641 retval = -1;
642 goto done;
643 }
644
645 sfunc = (flags & COPYFILE_NOFOLLOW_SRC) ? lstat : stat;
646 if ((sfunc)(src, &sbuf) == -1) {
647 retval = -1;
648 goto done;
649 }
3f81d1c4 650 if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
659640e4
A
651 srcisdir = 1;
652 }
653
654 sfunc = (flags & COPYFILE_NOFOLLOW_DST) ? lstat : stat;
655 if ((sfunc)(dst, &sbuf) == -1) {
656 if (errno != ENOENT) {
657 retval = -1;
658 goto done;
659 }
660 } else {
661 dstexists = 1;
662 if ((sbuf.st_mode & S_IFMT) == S_IFDIR) {
663 dstisdir = 1;
664 }
665 }
666
667#ifdef NOTYET
668 // This doesn't handle filesystem crossing and case sensitivity
669 // So there's got to be a better way
670
671 if (realpath(src, srcpath) == NULL) {
672 retval = -1;
673 goto done;
674 }
675
676 if (realpath(dst, dstpath) == NULL &&
677 (errno == ENOENT && realpath(dirname(dst), dstpath) == NULL)) {
678 retval = -1;
679 goto done;
680 }
681 if (strstr(srcpath, dstpath) != NULL) {
682 errno = EINVAL;
683 retval = -1;
684 goto done;
685 }
686#endif
687 srcroot = basename((char*)src);
688 if (srcroot == NULL) {
689 retval = -1;
690 goto done;
691 }
692
693 /*
694 * To work on as well:
695 * We have a few cases when copying a hierarchy:
696 * 1) src is a non-directory, dst is a directory;
697 * 2) src is a non-directory, dst is a non-directory;
698 * 3) src is a non-directory, dst does not exist;
699 * 4) src is a directory, dst is a directory;
700 * 5) src is a directory, dst is a non-directory;
701 * 6) src is a directory, dst does not exist
702 *
703 * (1) copies src to dst/basename(src).
913df16a 704 * (2) fails if COPYFILE_EXCL is set, otherwise copies src to dst.
659640e4
A
705 * (3) and (6) copy src to the name dst.
706 * (4) copies the contents of src to the contents of dst.
707 * (5) is an error.
708 */
709
710 if (dstisdir) {
711 // copy /path/to/src to /path/to/dst/src
712 // Append "/" and (fts_path - strlen(basename(src))) to dst?
713 dstpathsep = "/";
714 slash = strrchr(src, '/');
715 if (slash == NULL)
716 offset = 0;
717 else
718 offset = slash - src + 1;
719 } else {
720 // copy /path/to/src to /path/to/dst
721 // append (fts_path + strlen(src)) to dst?
722 dstpathsep = "";
723 offset = strlen(src);
724 }
725
726 if (s->flags | COPYFILE_NOFOLLOW_SRC)
727 fts_flags |= FTS_PHYSICAL;
728 else
729 fts_flags |= FTS_LOGICAL;
730
731 fts = fts_open((char * const *)paths, fts_flags, NULL);
732
733 status = s->statuscb;
734 while ((ftsent = fts_read(fts)) != NULL) {
735 int rv = 0;
736 char *dstfile = NULL;
737 int cmd = 0;
738 copyfile_state_t tstate = copyfile_state_alloc();
739 if (tstate == NULL) {
740 errno = ENOMEM;
741 retval = -1;
742 break;
743 }
744 tstate->statuscb = s->statuscb;
745 tstate->ctx = s->ctx;
913df16a
A
746 if (last_dev == ftsent->fts_dev) {
747 tstate->internal_flags |= (s->internal_flags & COPYFILE_MNT_CPROTECT_MASK);
748 } else {
749 last_dev = ftsent->fts_dev;
750 }
659640e4
A
751 asprintf(&dstfile, "%s%s%s", dst, dstpathsep, ftsent->fts_path + offset);
752 if (dstfile == NULL) {
753 copyfile_state_free(tstate);
754 errno = ENOMEM;
755 retval = -1;
756 break;
757 }
758 switch (ftsent->fts_info) {
1f4df596
A
759 case FTS_D:
760 tstate->internal_flags |= cfDelayAce;
761 cmd = COPYFILE_RECURSE_DIR;
762 break;
763 case FTS_SL:
764 case FTS_SLNONE:
765 case FTS_DEFAULT:
766 case FTS_F:
767 cmd = COPYFILE_RECURSE_FILE;
768 break;
769 case FTS_DP:
770 cmd = COPYFILE_RECURSE_DIR_CLEANUP;
771 break;
772 case FTS_DNR:
773 case FTS_ERR:
774 case FTS_NS:
775 case FTS_NSOK:
776 default:
777 errno = ftsent->fts_errno;
778 if (status) {
779 rv = (*status)(COPYFILE_RECURSE_ERROR, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx);
780 if (rv == COPYFILE_SKIP || rv == COPYFILE_CONTINUE) {
781 errno = 0;
782 goto skipit;
783 }
784 if (rv == COPYFILE_QUIT) {
785 retval = -1;
786 goto stopit;
787 }
788 } else {
659640e4
A
789 retval = -1;
790 goto stopit;
791 }
1f4df596
A
792 case FTS_DOT:
793 goto skipit;
659640e4
A
794
795 }
796
797 if (cmd == COPYFILE_RECURSE_DIR || cmd == COPYFILE_RECURSE_FILE) {
798 if (status) {
799 rv = (*status)(cmd, COPYFILE_START, tstate, ftsent->fts_path, dstfile, s->ctx);
800 if (rv == COPYFILE_SKIP) {
801 if (cmd == COPYFILE_RECURSE_DIR) {
802 rv = fts_set(fts, ftsent, FTS_SKIP);
803 if (rv == -1) {
804 rv = (*status)(0, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx);
805 if (rv == COPYFILE_QUIT)
806 retval = -1;
807 }
808 }
809 goto skipit;
810 }
811 if (rv == COPYFILE_QUIT) {
812 retval = -1; errno = 0;
813 goto stopit;
814 }
815 }
3f81d1c4
A
816 int tmp_flags = (cmd == COPYFILE_RECURSE_DIR) ? (flags & ~COPYFILE_STAT) : flags;
817 rv = copyfile(ftsent->fts_path, dstfile, tstate, tmp_flags);
659640e4
A
818 if (rv < 0) {
819 if (status) {
820 rv = (*status)(cmd, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx);
821 if (rv == COPYFILE_QUIT) {
822 retval = -1;
823 goto stopit;
824 } else
825 rv = 0;
826 goto skipit;
827 } else {
828 retval = -1;
829 goto stopit;
830 }
831 }
832 if (status) {
833 rv = (*status)(cmd, COPYFILE_FINISH, tstate, ftsent->fts_path, dstfile, s->ctx);
834 if (rv == COPYFILE_QUIT) {
835 retval = -1; errno = 0;
836 goto stopit;
837 }
838 }
839 } else if (cmd == COPYFILE_RECURSE_DIR_CLEANUP) {
659640e4
A
840 if (status) {
841 rv = (*status)(cmd, COPYFILE_START, tstate, ftsent->fts_path, dstfile, s->ctx);
842 if (rv == COPYFILE_QUIT) {
843 retval = -1; errno = 0;
844 goto stopit;
845 } else if (rv == COPYFILE_SKIP) {
846 rv = 0;
847 goto skipit;
848 }
849 }
3f81d1c4
A
850 rv = copyfile(ftsent->fts_path, dstfile, tstate, (flags & COPYFILE_NOFOLLOW) | COPYFILE_STAT);
851 if (rv < 0) {
659640e4
A
852 if (status) {
853 rv = (*status)(COPYFILE_RECURSE_DIR_CLEANUP, COPYFILE_ERR, tstate, ftsent->fts_path, dstfile, s->ctx);
854 if (rv == COPYFILE_QUIT) {
855 retval = -1;
856 goto stopit;
857 } else if (rv == COPYFILE_SKIP || rv == COPYFILE_CONTINUE) {
858 if (rv == COPYFILE_CONTINUE)
859 errno = 0;
860 retval = 0;
861 goto skipit;
862 }
863 } else {
864 retval = -1;
865 goto stopit;
866 }
3f81d1c4
A
867 } else {
868 if (status) {
869 rv = (*status)(COPYFILE_RECURSE_DIR_CLEANUP, COPYFILE_FINISH, tstate, ftsent->fts_path, dstfile, s->ctx);
870 if (rv == COPYFILE_QUIT) {
871 retval = -1; errno = 0;
872 goto stopit;
873 }
874 }
659640e4 875 }
3f81d1c4 876
659640e4
A
877 rv = 0;
878 }
1f4df596
A
879 skipit:
880 stopit:
913df16a
A
881 s->internal_flags &= ~COPYFILE_MNT_CPROTECT_MASK;
882 s->internal_flags |= (tstate->internal_flags & COPYFILE_MNT_CPROTECT_MASK);
883
659640e4
A
884 copyfile_state_free(tstate);
885 free(dstfile);
886 if (retval == -1)
887 break;
888 }
889
890done:
891 if (fts)
892 fts_close(fts);
893
894 return retval;
895}
896
b3aa0442
A
897/*
898 * fcopyfile() is used to copy a source file descriptor to a destination file
899 * descriptor. This allows an application to figure out how it wants to open
900 * the files (doing various security checks, perhaps), and then just pass in
901 * the file descriptors.
902 */
903int fcopyfile(int src_fd, int dst_fd, copyfile_state_t state, copyfile_flags_t flags)
904{
1f4df596
A
905 int ret = 0;
906 copyfile_state_t s = state;
907 struct stat dst_sb;
b3aa0442 908
1f4df596
A
909 if (src_fd < 0 || dst_fd < 0)
910 {
911 errno = EINVAL;
912 return -1;
913 }
b3aa0442 914
1f4df596
A
915 if (copyfile_preamble(&s, flags) < 0)
916 return -1;
917
918 copyfile_debug(2, "set src_fd <- %d", src_fd);
919 if (s->src_fd == -2 && src_fd > -1)
920 {
921 s->src_fd = src_fd;
922 if (fstatx_np(s->src_fd, &s->sb, s->fsec) != 0)
923 {
924 if (errno == ENOTSUP || errno == EPERM)
925 fstat(s->src_fd, &s->sb);
926 else
927 {
928 copyfile_warn("fstatx_np on src fd %d", s->src_fd);
929 return -1;
930 }
931 }
932 }
933
934 /* prevent copying on unsupported types */
935 switch (s->sb.st_mode & S_IFMT)
936 {
937 case S_IFLNK:
938 case S_IFDIR:
939 case S_IFREG:
940 break;
941 default:
942 errno = ENOTSUP;
943 return -1;
944 }
945
946 copyfile_debug(2, "set dst_fd <- %d", dst_fd);
947 if (s->dst_fd == -2 && dst_fd > -1)
948 s->dst_fd = dst_fd;
949
950 (void)fstat(s->dst_fd, &dst_sb);
951 (void)fchmod(s->dst_fd, (dst_sb.st_mode & ~S_IFMT) | (S_IRUSR | S_IWUSR));
952
953 (void)copyfile_quarantine(s);
954
955 ret = copyfile_internal(s, flags);
956
957 if (ret >= 0 && !(s->flags & COPYFILE_STAT))
958 {
959 (void)fchmod(s->dst_fd, dst_sb.st_mode & ~S_IFMT);
960 }
961
962 if (s->err) {
963 errno = s->err;
964 s->err = 0;
965 }
966 if (state == NULL) {
967 int t = errno;
968 copyfile_state_free(s);
969 errno = t;
970 }
971
972 return ret;
973
974}
975
976/*
977 * This routine implements the clonefileat functionality
978 * for copyfile. There are 2 kinds of clone flags, namely
979 * 1. COPYFILE_CLONE_FORCE which is a 'force' clone flag.
980 * 2. COPYFILE_CLONE which is a 'best try' flag.
981 * In both cases, we inherit the flags provided
982 * to copyfile call and clone the file.
983 * Both these flags are equivalent to
984 * (COPYFILE_EXCL | COPYFILE_ACL | COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA)
985 * With force clone flag set, we return failure if cloning fails,
986 * however, in case of best try flag, we fallback to the copy method.
987 */
988
989static int copyfile_clone(const char *src, const char *dst, copyfile_state_t state, copyfile_flags_t flags)
990{
991 int ret = 0;
992 int cloneFlags = 0;
993 struct stat src_sb;
b3aa0442 994
1f4df596 995 if (lstat(src, &src_sb) != 0)
b3aa0442 996 {
1f4df596 997 errno = EINVAL;
b3aa0442 998 return -1;
1f4df596
A
999 }
1000
1001 if (COPYFILE_NOFOLLOW & flags)
1002 {
1003 cloneFlags = CLONE_NOFOLLOW;
1004 }
b3aa0442 1005
1f4df596
A
1006 /*
1007 * Support only for files and symbolic links.
1008 * TODO:Remove this check when support for directories is added.
1009 */
1010 if (S_ISREG(src_sb.st_mode) || S_ISLNK(src_sb.st_mode))
1011 {
1012 /*
1013 * COPYFILE_UNLINK tells us to try removing the destination
1014 * before we create it. We don't care if the file doesn't
1015 * exist, so we ignore ENOENT.
1016 */
1017 if (flags & COPYFILE_UNLINK)
1018 {
1019 if (remove(dst) < 0 && errno != ENOENT)
1020 {
1021 return -1;
1022 }
1023 }
1024 ret = clonefileat(AT_FDCWD, src, AT_FDCWD, dst, cloneFlags);
1025 if (ret == 0) {
1026 /*
1027 * We could also report the size of the single
1028 * object that was cloned. However, that's a lot
1029 * more difficult when we eventually support
1030 * cloning directories. It seems reasonable to NOT
1031 * report any bytes being "copied" in this scenario,
1032 * and let the caller figure out how they want to
1033 * deal.
1034 */
1035 if (state != NULL)
1036 state->was_cloned = true;
1037 }
1038 }
1039 else
1040 {
1041 errno = EINVAL;
1042 ret = -1;
1043 }
1044 return ret;
b3aa0442
A
1045}
1046
1047/*
1048 * the original copyfile() routine; this copies a source file to a destination
1049 * file. Note that because we need to set the names in the state variable, this
1050 * is not just the same as opening the two files, and then calling fcopyfile().
1051 * Oh, if only life were that simple!
1052 */
1053int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_flags_t flags)
1054{
1f4df596
A
1055 int ret = 0;
1056 int createdst = 0;
1057 copyfile_state_t s = state;
1058 struct stat dst_sb;
b3aa0442 1059
1f4df596
A
1060 if (src == NULL && dst == NULL)
1061 {
1062 errno = EINVAL;
1063 return -1;
1064 }
b3aa0442 1065
1f4df596 1066 if (flags & (COPYFILE_CLONE_FORCE | COPYFILE_CLONE))
b3aa0442 1067 {
1f4df596
A
1068 ret = copyfile_clone(src, dst, state, flags);
1069 if ((ret == 0) || (flags & COPYFILE_CLONE_FORCE))
1070 {
1071 return ret;
1072 }
1073 // cloning failed. Inherit clonefile flags required for
1074 // falling back to copyfile.
1075 flags = flags | (COPYFILE_EXCL | COPYFILE_ACL |
1076 COPYFILE_STAT | COPYFILE_XATTR | COPYFILE_DATA);
1077 flags = flags & (~COPYFILE_CLONE);
1078 }
1079 ret = 0;
1080 if (copyfile_preamble(&s, flags) < 0)
1081 {
1082 return -1;
1083 }
1084
1085 /*
1086 * This macro is... well, it's not the worst thing you can do with cpp, not
1087 * by a long shot. Essentially, we are setting the filename (src or dst)
1088 * in the state structure; since the structure may not have been cleared out
1089 * before being used again, we do some of the cleanup here: if the given
1090 * filename (e.g., src) is set, and state->src is not equal to that, then
1091 * we need to check to see if the file descriptor had been opened, and if so,
1092 * close it. After that, we set state->src to be a copy of the given filename,
1093 * releasing the old copy if necessary.
1094 */
1095#define COPYFILE_SET_FNAME(NAME, S) \
1096 do { \
1097 if (NAME != NULL) { \
1098 if (S->NAME != NULL && strncmp(NAME, S->NAME, MAXPATHLEN)) { \
1099 copyfile_debug(2, "replacing string %s (%s) -> (%s)", #NAME, NAME, S->NAME);\
1100 if (S->NAME##_fd != -2 && S->NAME##_fd > -1) { \
1101 copyfile_debug(4, "closing %s fd: %d", #NAME, S->NAME##_fd); \
1102 close(S->NAME##_fd); \
1103 S->NAME##_fd = -2; \
1104 } \
1105 } \
1106 if (S->NAME) { \
1107 free(S->NAME); \
1108 S->NAME = NULL; \
1109 } \
1110 if ((NAME) && (S->NAME = strdup(NAME)) == NULL) \
1111 return -1; \
1112 } \
1113 } while (0)
1114
1115 COPYFILE_SET_FNAME(src, s);
1116 COPYFILE_SET_FNAME(dst, s);
1117
1118 if (s->flags & COPYFILE_RECURSIVE) {
1119 ret = copytree(s);
1120 goto exit;
1121 }
1122
1123 /*
1124 * Get a copy of the source file's security settings
1125 */
1126 if (s->original_fsec) {
1127 filesec_free(s->original_fsec);
1128 s->original_fsec = NULL;
1129 }
1130 if ((s->original_fsec = filesec_init()) == NULL)
1131 goto error_exit;
1132
1133 if ((s->flags & COPYFILE_NOFOLLOW_DST) && lstat(s->dst, &dst_sb) == 0 &&
1134 ((dst_sb.st_mode & S_IFMT) == S_IFLNK)) {
1135 if (s->permissive_fsec)
1136 free(s->permissive_fsec);
659640e4 1137 s->permissive_fsec = NULL;
1f4df596
A
1138 } else if(statx_np(s->dst, &dst_sb, s->original_fsec) == 0)
1139 {
1140 /*
1141 * copyfile_fix_perms() will make a copy of the permission set,
1142 * and insert at the beginning an ACE that ensures we can write
1143 * to the file and set attributes.
1144 */
1145
1146 if((s->permissive_fsec = copyfile_fix_perms(s, &s->original_fsec)) != NULL)
1147 {
1148 /*
1149 * Set the permissions for the destination to our copy.
1150 * We should get ENOTSUP from any filesystem that simply
1151 * doesn't support it.
1152 */
1153 if (chmodx_np(s->dst, s->permissive_fsec) < 0 && errno != ENOTSUP)
1154 {
1155 copyfile_warn("setting security information");
1156 filesec_free(s->permissive_fsec);
1157 s->permissive_fsec = NULL;
1158 }
1159 }
1160 } else if (errno == ENOENT) {
1161 createdst = 1;
1162 }
1163
1164 /*
1165 * If COPYFILE_CHECK is set in flags, then all we are going to do
1166 * is see what kinds of things WOULD have been copied (see
1167 * copyfile_check() below). We return that value.
1168 */
1169 if (COPYFILE_CHECK & flags)
1170 {
1171 ret = copyfile_check(s);
1172 goto exit;
1173 } else if ((ret = copyfile_open(s)) < 0)
1174 goto error_exit;
b3aa0442 1175
1f4df596
A
1176 (void)fcntl(s->src_fd, F_NOCACHE, 1);
1177 (void)fcntl(s->dst_fd, F_NOCACHE, 1);
11f767b3 1178#ifdef F_SINGLE_WRITER
1f4df596 1179 (void)fcntl(s->dst_fd, F_SINGLE_WRITER, 1);
11f767b3 1180#endif
3f81d1c4 1181
1f4df596
A
1182 ret = copyfile_internal(s, flags);
1183 if (ret == -1)
1184 goto error_exit;
b3aa0442 1185
659640e4 1186#ifdef COPYFILE_RECURSIVE
1f4df596
A
1187 if (!(flags & COPYFILE_STAT)) {
1188 if (!createdst)
1189 {
1190 /* Just need to reset the BSD information -- mode, owner, group */
1191 (void)fchown(s->dst_fd, dst_sb.st_uid, dst_sb.st_gid);
1192 (void)fchmod(s->dst_fd, dst_sb.st_mode);
1193 }
b3aa0442 1194 }
659640e4
A
1195#endif
1196
1f4df596 1197 reset_security(s);
659640e4 1198
1f4df596
A
1199 if (s->src && (flags & COPYFILE_MOVE))
1200 (void)remove(s->src);
3f81d1c4 1201
b3aa0442 1202exit:
1f4df596
A
1203 if (state == NULL) {
1204 int t = errno;
1205 copyfile_state_free(s);
1206 errno = t;
1207 }
b3aa0442 1208
1f4df596 1209 return ret;
b3aa0442
A
1210
1211error_exit:
1f4df596
A
1212 ret = -1;
1213 if (s->err) {
1214 errno = s->err;
1215 s->err = 0;
1216 }
1217 goto exit;
b3aa0442
A
1218}
1219
1220/*
1221 * Shared prelude to the {f,}copyfile(). This initializes the
1222 * state variable, if necessary, and also checks for both debugging
1223 * and disabling environment variables.
1224 */
1225static int copyfile_preamble(copyfile_state_t *state, copyfile_flags_t flags)
1226{
1f4df596 1227 copyfile_state_t s;
b3aa0442 1228
1f4df596
A
1229 if (*state == NULL)
1230 {
1231 if ((*state = copyfile_state_alloc()) == NULL)
1232 return -1;
1233 }
b3aa0442 1234
1f4df596 1235 s = *state;
b3aa0442 1236
1f4df596 1237 if (COPYFILE_DEBUG & flags)
b3aa0442 1238 {
1f4df596
A
1239 char *e;
1240 if ((e = getenv(COPYFILE_DEBUG_VAR)))
1241 {
1242 errno = 0;
1243 s->debug = (uint32_t)strtol(e, NULL, 0);
b3aa0442 1244
1f4df596
A
1245 /* clamp s->debug to 1 if the environment variable is not parsable */
1246 if (s->debug == 0 && errno != 0)
1247 s->debug = 1;
1248 }
1249 copyfile_debug(2, "debug value set to: %d", s->debug);
b3aa0442 1250 }
b3aa0442
A
1251
1252#if 0
1f4df596
A
1253 /* Temporarily disabled */
1254 if (getenv(COPYFILE_DISABLE_VAR) != NULL)
1255 {
1256 copyfile_debug(1, "copyfile disabled");
1257 return 2;
1258 }
b3aa0442 1259#endif
1f4df596
A
1260 copyfile_debug(2, "setting flags: %d", s->flags);
1261 s->flags = flags;
b3aa0442 1262
1f4df596 1263 return 0;
b3aa0442
A
1264}
1265
1266/*
1267 * The guts of {f,}copyfile().
1268 * This looks through the flags in a particular order, and calls the
1269 * associated functions.
1270 */
1271static int copyfile_internal(copyfile_state_t s, copyfile_flags_t flags)
1272{
1f4df596 1273 int ret = 0;
b3aa0442 1274
1f4df596 1275 if (s->dst_fd < 0 || s->src_fd < 0)
b3aa0442 1276 {
1f4df596
A
1277 copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)", s->src_fd, s->dst_fd);
1278 s->err = EINVAL;
1279 return -1;
b3aa0442 1280 }
3f81d1c4 1281
1f4df596
A
1282 /*
1283 * COPYFILE_PACK causes us to create an Apple Double version of the
1284 * source file, and puts it into the destination file. See
1285 * copyfile_pack() below for all the gory details.
1286 */
1287 if (COPYFILE_PACK & flags)
1288 {
1289 if ((ret = copyfile_pack(s)) < 0)
1290 {
1291 if (s->dst) unlink(s->dst);
3f81d1c4
A
1292 goto exit;
1293 }
1f4df596 1294 goto exit;
3f81d1c4 1295 }
b3aa0442 1296
1f4df596
A
1297 /*
1298 * COPYFILE_UNPACK is the undoing of COPYFILE_PACK, obviously.
1299 * The goal there is to take an Apple Double file, and turn it
1300 * into a normal file (with data fork, resource fork, modes,
1301 * extended attributes, ACLs, etc.).
1302 */
1303 if (COPYFILE_UNPACK & flags)
b3aa0442 1304 {
1f4df596
A
1305 if ((ret = copyfile_unpack(s)) < 0)
1306 goto error_exit;
1307 goto exit;
b3aa0442 1308 }
b3aa0442 1309
b3aa0442 1310
b3aa0442 1311
1f4df596
A
1312 /*
1313 * If we have quarantine info set, we attempt
1314 * to apply it to dst_fd. We don't care if
1315 * it fails, not yet anyway.
1316 */
1317 if (s->qinfo)
1318 {
1319 int qr;
1320 uint32_t q_flags;
b3aa0442 1321
1f4df596
A
1322 /*
1323 * If COPYFILE_RUN_IN_PLACE is set, we need to add
1324 * QTN_FLAG_DO_NOT_TRANSLOCATE to the qinfo flags.
1325 *
1326 * On iOS, qtn_file_get_flags & qtn_file_set_flags
1327 * don't modify anything, always return 0, per static
1328 * defines at top of this file, though we should never
1329 * get here in that case as qinfo will always be NULL.
1330 */
1331 if (COPYFILE_RUN_IN_PLACE & flags)
1332 {
1333 q_flags = 0;
b3aa0442 1334
1f4df596
A
1335 q_flags = qtn_file_get_flags(s->qinfo);
1336 q_flags |= QTN_FLAG_DO_NOT_TRANSLOCATE;
b3aa0442 1337
1f4df596
A
1338 if (qtn_file_set_flags(s->qinfo, q_flags) != 0) {
1339 s->err = errno = EINVAL;
1340 goto error_exit;
1341 }
1342 }
659640e4 1343
1f4df596
A
1344 qr = qtn_file_apply_to_fd(s->qinfo, s->dst_fd);
1345 if (qr != 0) {
1346 if (s->statuscb) {
1347 int rv;
659640e4 1348
1f4df596
A
1349 s->xattr_name = (char*)XATTR_QUARANTINE_NAME;
1350 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
1351 s->xattr_name = NULL;
1352 if (rv == COPYFILE_QUIT) {
1353 s->err = errno = (qr < 0 ? ENOTSUP : qr);
1354 goto error_exit;
1355 }
1356 } else {
1357 s->err = errno = (qr < 0 ? ENOTSUP : qr);
1358 goto error_exit;
1359 }
1360 }
1361 }
b3aa0442 1362
1f4df596
A
1363 /*
1364 * COPYFILE_XATTR tells us to copy the extended attributes;
1365 * this is seperate from the extended security (aka ACLs),
1366 * however. If we succeed in this, we continue to the next
1367 * stage; if we fail, we return with an error value. Note
1368 * that we fail if the errno is ENOTSUP, but we don't print
1369 * a warning in that case.
1370 */
1371 if (COPYFILE_XATTR & flags)
1372 {
1373 if ((ret = copyfile_xattr(s)) < 0)
1374 {
1375 if (errno != ENOTSUP && errno != EPERM)
1376 copyfile_warn("error processing extended attributes");
1377 goto exit;
1378 }
1379 }
1380
1381 /*
1382 * Simialr to above, this tells us whether or not to copy
1383 * the non-meta data portion of the file. We attempt to
1384 * remove (via unlink) the destination file if we fail.
1385 */
1386 if (COPYFILE_DATA & flags)
1387 {
1388 if ((ret = copyfile_data(s)) < 0)
1389 {
1390 copyfile_warn("error processing data");
1391 if (s->dst && unlink(s->dst))
1392 copyfile_warn("%s: remove", s->src ? s->src : "(null src)");
1393 goto exit;
1394 }
1395 }
1396
1397 /*
1398 * COPYFILE_SECURITY requests that we copy the security, both
1399 * extended and mundane (that is, ACLs and POSIX).
1400 */
1401 if (COPYFILE_SECURITY & flags)
1402 {
1403 if ((ret = copyfile_security(s)) < 0)
1404 {
1405 copyfile_warn("error processing security information");
1406 goto exit;
1407 }
1408 }
1409
1410 if (COPYFILE_STAT & flags)
1411 {
1412 if ((ret = copyfile_stat(s)) < 0)
1413 {
1414 copyfile_warn("error processing POSIX information");
1415 goto exit;
1416 }
1417 }
1418
1419exit:
1420 return ret;
1421
1422error_exit:
1423 ret = -1;
1424 goto exit;
1425}
1426
1427/*
1428 * A publicly-visible routine, copyfile_state_alloc() sets up the state variable.
1429 */
1430copyfile_state_t copyfile_state_alloc(void)
1431{
1432 copyfile_state_t s = (copyfile_state_t) calloc(1, sizeof(struct _copyfile_state));
1433
1434 if (s != NULL)
1435 {
1436 s->src_fd = -2;
1437 s->dst_fd = -2;
1438 if (s->fsec) {
1439 filesec_free(s->fsec);
1440 s->fsec = NULL;
1441 }
1442 s->fsec = filesec_init();
1443 } else
1444 errno = ENOMEM;
1445
1446 return s;
1447}
1448
1449/*
1450 * copyfile_state_free() returns the memory allocated to the state structure.
1451 * It also closes the file descriptors, if they've been opened.
1452 */
1453int copyfile_state_free(copyfile_state_t s)
1454{
1455 if (s != NULL)
b3aa0442 1456 {
1f4df596
A
1457 if (s->fsec)
1458 filesec_free(s->fsec);
1459
1460 if (s->original_fsec)
1461 filesec_free(s->original_fsec);
1462
1463 if (s->permissive_fsec)
1464 filesec_free(s->permissive_fsec);
1465
1466 if (s->qinfo)
1467 qtn_file_free(s->qinfo);
1468
1469 if (copyfile_close(s) < 0)
1470 {
1471 copyfile_warn("error closing files");
1472 return -1;
1473 }
1474 if (s->xattr_name)
1475 free(s->xattr_name);
1476 if (s->dst)
1477 free(s->dst);
1478 if (s->src)
1479 free(s->src);
1480 free(s);
1481 }
1482 return 0;
b3aa0442
A
1483}
1484
1485/*
1486 * Should we worry if we can't close the source? NFS says we
1487 * should, but it's pretty late for us at this point.
1488 */
1489static int copyfile_close(copyfile_state_t s)
1490{
1f4df596
A
1491 if (s->src && s->src_fd >= 0)
1492 close(s->src_fd);
b3aa0442 1493
1f4df596
A
1494 if (s->dst && s->dst_fd >= 0) {
1495 if (close(s->dst_fd))
1496 return -1;
1497 }
b3aa0442 1498
1f4df596 1499 return 0;
b3aa0442
A
1500}
1501
1502/*
1503 * The purpose of this function is to set up a set of permissions
1504 * (ACL and traditional) that lets us write to the file. In the
1505 * case of ACLs, we do this by putting in a first entry that lets
1506 * us write data, attributes, and extended attributes. In the case
1507 * of traditional permissions, we set the S_IWUSR (user-write)
1508 * bit.
1509 */
1510static filesec_t copyfile_fix_perms(copyfile_state_t s __unused, filesec_t *fsec)
1511{
1f4df596
A
1512 filesec_t ret_fsec = NULL;
1513 mode_t mode;
1514 acl_t acl = NULL;
b3aa0442 1515
1f4df596
A
1516 if ((ret_fsec = filesec_dup(*fsec)) == NULL)
1517 goto error_exit;
b3aa0442 1518
1f4df596
A
1519 if (filesec_get_property(ret_fsec, FILESEC_ACL, &acl) == 0)
1520 {
659640e4 1521#ifdef COPYFILE_RECURSIVE
1f4df596
A
1522 if (add_uberace(&acl))
1523 goto error_exit;
659640e4 1524#else
1f4df596
A
1525 acl_entry_t entry;
1526 acl_permset_t permset;
1527 uuid_t qual;
b3aa0442 1528
1f4df596
A
1529 if (mbr_uid_to_uuid(getuid(), qual) != 0)
1530 goto error_exit;
b3aa0442 1531
1f4df596
A
1532 /*
1533 * First, we create an entry, and give it the special name
1534 * of ACL_FIRST_ENTRY, thus guaranteeing it will be first.
1535 * After that, we clear out all the permissions in it, and
1536 * add three permissions: WRITE_DATA, WRITE_ATTRIBUTES, and
1537 * WRITE_EXTATTRIBUTES. We put these into an ACE that allows
1538 * the functionality, and put this into the ACL.
1539 */
1540 if (acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY) == -1)
1541 goto error_exit;
1542 if (acl_get_permset(entry, &permset) == -1)
1543 goto error_exit;
1544 if (acl_clear_perms(permset) == -1)
1545 goto error_exit;
1546 if (acl_add_perm(permset, ACL_WRITE_DATA) == -1)
1547 goto error_exit;
1548 if (acl_add_perm(permset, ACL_WRITE_ATTRIBUTES) == -1)
1549 goto error_exit;
1550 if (acl_add_perm(permset, ACL_WRITE_EXTATTRIBUTES) == -1)
1551 goto error_exit;
1552 if (acl_set_tag_type(entry, ACL_EXTENDED_ALLOW) == -1)
1553 goto error_exit;
1554
1555 if(acl_set_permset(entry, permset) == -1)
1556 goto error_exit;
1557 if(acl_set_qualifier(entry, qual) == -1)
1558 goto error_exit;
659640e4 1559#endif
b3aa0442 1560
1f4df596
A
1561 if (filesec_set_property(ret_fsec, FILESEC_ACL, &acl) != 0)
1562 goto error_exit;
1563 }
1564
1565 /*
1566 * This is for the normal, mundane, POSIX permission model.
1567 * We make sure that we can write to the file.
1568 */
1569 if (filesec_get_property(ret_fsec, FILESEC_MODE, &mode) == 0)
b3aa0442 1570 {
1f4df596
A
1571 if ((mode & (S_IWUSR | S_IRUSR)) != (S_IWUSR | S_IRUSR))
1572 {
1573 mode |= S_IWUSR|S_IRUSR;
1574 if (filesec_set_property(ret_fsec, FILESEC_MODE, &mode) != 0)
1575 goto error_exit;
1576 }
b3aa0442 1577 }
b3aa0442
A
1578
1579exit:
1f4df596
A
1580 if (acl)
1581 acl_free(acl);
b3aa0442 1582
1f4df596 1583 return ret_fsec;
b3aa0442
A
1584
1585error_exit:
1f4df596
A
1586 if (ret_fsec)
1587 {
1588 filesec_free(ret_fsec);
1589 ret_fsec = NULL;
1590 }
1591 goto exit;
b3aa0442
A
1592}
1593
1594/*
1595 * Used to clear out the BSD/POSIX security information from
1596 * a filesec
1597 */
1598static int
1599copyfile_unset_posix_fsec(filesec_t fsec)
1600{
1601 (void)filesec_set_property(fsec, FILESEC_OWNER, _FILESEC_UNSET_PROPERTY);
1602 (void)filesec_set_property(fsec, FILESEC_GROUP, _FILESEC_UNSET_PROPERTY);
1603 (void)filesec_set_property(fsec, FILESEC_MODE, _FILESEC_UNSET_PROPERTY);
1604 return 0;
1605}
1606
1607/*
1608 * Used to remove acl information from a filesec_t
1609 * Unsetting the acl alone in Tiger was insufficient
1610 */
1611static int copyfile_unset_acl(copyfile_state_t s)
1612{
1f4df596
A
1613 int ret = 0;
1614 if (filesec_set_property(s->fsec, FILESEC_ACL, NULL) == -1)
1615 {
1616 copyfile_debug(5, "unsetting acl attribute on %s", s->dst ? s->dst : "(null dst)");
1617 ++ret;
1618 }
1619 if (filesec_set_property(s->fsec, FILESEC_UUID, NULL) == -1)
1620 {
1621 copyfile_debug(5, "unsetting uuid attribute on %s", s->dst ? s->dst : "(null dst)");
1622 ++ret;
1623 }
1624 if (filesec_set_property(s->fsec, FILESEC_GRPUUID, NULL) == -1)
1625 {
1626 copyfile_debug(5, "unsetting group uuid attribute on %s", s->dst ? s->dst : "(null dst)");
1627 ++ret;
1628 }
1629 return ret;
b3aa0442
A
1630}
1631
1632/*
1633 * copyfile_open() does what one expects: it opens up the files
1634 * given in the state structure, if they're not already open.
1635 * It also does some type validation, to ensure that we only
1636 * handle file types we know about.
1637 */
1638static int copyfile_open(copyfile_state_t s)
1639{
1f4df596
A
1640 int oflags = O_EXCL | O_CREAT | O_WRONLY;
1641 int islnk = 0, isdir = 0, isreg = 0;
1642 int osrc = 0, dsrc = 0;
1643 int prot_class = PROTECTION_CLASS_DEFAULT;
1644 int set_cprot_explicit = 0;
1645 int error = 0;
1646
1647 if (s->src && s->src_fd == -2)
b3aa0442 1648 {
1f4df596
A
1649 if ((COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np)
1650 (s->src, &s->sb, s->fsec))
1651 {
1652 copyfile_warn("stat on %s", s->src);
b3aa0442
A
1653 return -1;
1654 }
1f4df596
A
1655
1656 /* prevent copying on unsupported types */
1657 switch (s->sb.st_mode & S_IFMT)
1658 {
1659 case S_IFLNK:
1660 islnk = 1;
1661 if ((size_t)s->sb.st_size > SIZE_T_MAX) {
1662 s->err = ENOMEM; /* too big for us to copy */
1663 return -1;
1664 }
1665 osrc = O_SYMLINK;
1666 break;
1667 case S_IFDIR:
1668 isdir = 1;
1669 break;
1670 case S_IFREG:
1671 isreg = 1;
1672 break;
1673 default:
1674 if (!(strcmp(s->src, "/dev/null") == 0 && (s->flags & COPYFILE_METADATA))) {
1675 s->err = ENOTSUP;
1676 return -1;
1677 }
659640e4 1678 }
b3aa0442 1679 /*
1f4df596
A
1680 * If we're packing, then we are actually
1681 * creating a file, no matter what the source
1682 * was.
b3aa0442 1683 */
1f4df596
A
1684 if (s->flags & COPYFILE_PACK) {
1685 /*
1686 * O_SYMLINK and O_NOFOLLOW are not compatible options:
1687 * if the file is a symlink, and O_NOFOLLOW is specified,
1688 * open will return ELOOP, whether or not O_SYMLINK is set.
1689 * However, we know whether or not it was a symlink from
1690 * the stat above (although there is a potentiaal for a race
1691 * condition here, but it will err on the side of returning
1692 * ELOOP from open).
1693 */
1694 if (!islnk)
1695 osrc = (s->flags & COPYFILE_NOFOLLOW_SRC) ? O_NOFOLLOW : 0;
1696 isdir = islnk = 0;
659640e4 1697 }
b3aa0442 1698
1f4df596 1699 if ((s->src_fd = open(s->src, O_RDONLY | osrc , 0)) < 0)
913df16a 1700 {
1f4df596 1701 copyfile_warn("open on %s", s->src);
913df16a
A
1702 return -1;
1703 }
1f4df596
A
1704 copyfile_debug(2, "open successful on source (%s)", s->src);
1705
1706 (void)copyfile_quarantine(s);
913df16a
A
1707 }
1708
1f4df596 1709 if (s->dst && s->dst_fd == -2)
913df16a 1710 {
1f4df596
A
1711 /*
1712 * COPYFILE_UNLINK tells us to try removing the destination
1713 * before we create it. We don't care if the file doesn't
1714 * exist, so we ignore ENOENT.
1715 */
1716 if (COPYFILE_UNLINK & s->flags)
913df16a 1717 {
1f4df596
A
1718 if (remove(s->dst) < 0 && errno != ENOENT)
1719 {
1720 copyfile_warn("%s: remove", s->dst);
1721 return -1;
1722 }
913df16a 1723 }
913df16a 1724
1f4df596
A
1725 if (s->flags & COPYFILE_NOFOLLOW_DST) {
1726 struct stat st;
b3aa0442 1727
1f4df596
A
1728 dsrc = O_NOFOLLOW;
1729 if (lstat(s->dst, &st) != -1) {
1730 if ((st.st_mode & S_IFMT) == S_IFLNK)
1731 dsrc = O_SYMLINK;
1732 }
b3aa0442 1733 }
1f4df596
A
1734
1735 if (!(s->internal_flags & cfSrcProtSupportValid))
1736 {
1737 if ((error = does_copy_protection(s->src_fd)) > 0)
1738 {
1739 s->internal_flags |= cfSrcSupportsCProtect;
1740 }
1741 else if (error < 0)
1742 {
1743 copyfile_warn("does_copy_protection failed on (%s) with error <%d>", s->src, errno);
b3aa0442
A
1744 return -1;
1745 }
1f4df596 1746 s->internal_flags |= cfSrcProtSupportValid;
b3aa0442 1747 }
b3aa0442 1748
1f4df596
A
1749 /* copy protection is only valid for regular files and directories. */
1750 if ((isreg || isdir) && (s->internal_flags & cfSrcSupportsCProtect))
1751 {
1752 prot_class = GET_PROT_CLASS(s->src_fd);
1753 if (prot_class < 0)
1754 {
1755 copyfile_warn("GET_PROT_CLASS failed on (%s) with error <%d>", s->src, errno);
b3aa0442
A
1756 return -1;
1757 }
1758 }
659640e4 1759
1f4df596
A
1760 if (islnk) {
1761 size_t sz = (size_t)s->sb.st_size + 1;
1762 char *bp;
913df16a 1763
1f4df596
A
1764 bp = calloc(1, sz);
1765 if (bp == NULL) {
1766 copyfile_warn("cannot allocate %zd bytes", sz);
1767 return -1;
913df16a 1768 }
1f4df596
A
1769 if (readlink(s->src, bp, sz-1) == -1) {
1770 copyfile_warn("cannot readlink %s", s->src);
1771 free(bp);
1772 return -1;
1773 }
1774 if (symlink(bp, s->dst) == -1) {
1775 if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) {
1776 copyfile_warn("Cannot make symlink %s", s->dst);
1777 free(bp);
1778 return -1;
1779 }
1780 }
1781 free(bp);
1782 s->dst_fd = open(s->dst, O_RDONLY | O_SYMLINK);
1783 if (s->dst_fd == -1) {
1784 copyfile_warn("Cannot open symlink %s for reading", s->dst);
1785 return -1;
1786 }
1787 } else if (isdir) {
1788 mode_t mode;
1789 mode = (s->sb.st_mode & ~S_IFMT) | S_IRWXU;
1790
1791 if (mkdir(s->dst, mode) == -1) {
1792 if (errno != EEXIST || (s->flags & COPYFILE_EXCL)) {
1793 copyfile_warn("Cannot make directory %s", s->dst);
1794 return -1;
1795 }
1796 }
1797 s->dst_fd = open(s->dst, O_RDONLY | dsrc);
1798 if (s->dst_fd == -1) {
1799 copyfile_warn("Cannot open directory %s for reading", s->dst);
913df16a
A
1800 return -1;
1801 }
1f4df596
A
1802 set_cprot_explicit = 1;
1803 } else while((s->dst_fd = open_dprotected_np(s->dst, oflags | dsrc, prot_class, 0, s->sb.st_mode | S_IWUSR)) < 0)
1804 {
1805 /*
1806 * We set S_IWUSR because fsetxattr does not -- at the time this comment
1807 * was written -- allow one to set an extended attribute on a file descriptor
1808 * for a read-only file, even if the file descriptor is opened for writing.
1809 * This will only matter if the file does not already exist.
1810 */
1811 switch(errno)
1812 {
1813 case EEXIST:
1814 copyfile_debug(3, "open failed, retrying (%s)", s->dst);
1815 if (s->flags & COPYFILE_EXCL)
1816 break;
1817 oflags = oflags & ~O_CREAT;
1818 /* if O_CREAT isn't set in open_dprotected_np, it won't set protection class.
1819 * Set the flag here so we know to do it later.
1820 */
1821 set_cprot_explicit = 1;
1822 if (s->flags & (COPYFILE_PACK | COPYFILE_DATA))
1823 {
1824 copyfile_debug(4, "truncating existing file (%s)", s->dst);
1825 oflags |= O_TRUNC;
1826 }
1827 continue;
1828 case EACCES:
1829 if(chmod(s->dst, (s->sb.st_mode | S_IWUSR) & ~S_IFMT) == 0)
1830 continue;
1831 else {
1832 /*
1833 * If we're trying to write to a directory to which we don't
1834 * have access, the create above would have failed, but chmod
1835 * here would have given us ENOENT. But the real error is
1836 * still one of access, so we change the errno we're reporting.
1837 * This could cause confusion with a race condition.
1838 */
1839
1840 if (errno == ENOENT)
1841 errno = EACCES;
1842 break;
1843 }
1844 case EISDIR:
1845 copyfile_debug(3, "open failed because it is a directory (%s)", s->dst);
1846 if (((s->flags & COPYFILE_EXCL) ||
1847 (!isdir && (s->flags & COPYFILE_DATA)))
1848 && !(s->flags & COPYFILE_UNPACK))
1849 break;
1850 oflags = (oflags & ~(O_WRONLY|O_CREAT|O_TRUNC)) | O_RDONLY;
1851 continue;
1852 }
1853 copyfile_warn("open on %s", s->dst);
1854 return -1;
913df16a 1855 }
1f4df596 1856 copyfile_debug(2, "open successful on destination (%s)", s->dst);
913df16a 1857
1f4df596 1858 if (s->internal_flags & cfSrcSupportsCProtect)
913df16a 1859 {
1f4df596 1860 if (!(s->internal_flags & cfDstProtSupportValid))
913df16a 1861 {
1f4df596
A
1862 if ((error = does_copy_protection(s->dst_fd)) > 0)
1863 {
1864 s->internal_flags |= cfDstSupportsCProtect;
1865 }
1866 else if (error < 0)
1867 {
1868 copyfile_warn("does_copy_protection failed on (%s) with error <%d>", s->dst, errno);
1869 return -1;
1870 }
1871 s->internal_flags |= cfDstProtSupportValid;
1872 }
1873
1874 if ((isreg || isdir)
1875 && set_cprot_explicit
1876 && (s->internal_flags & cfDstSupportsCProtect))
1877 {
1878 /* Protection class is set in open_dprotected_np for regular files that aren't truncated.
1879 * We set the protection class here for truncated files and directories.
1880 */
1881 if (SET_PROT_CLASS(s->dst_fd, prot_class) != 0)
1882 {
1883 copyfile_warn("SET_PROT_CLASS failed on (%s) with error <%d>", s->dst, errno);
1884 return -1;
1885 }
913df16a
A
1886 }
1887 }
1888 }
b3aa0442 1889
1f4df596
A
1890 if (s->dst_fd < 0 || s->src_fd < 0)
1891 {
1892 copyfile_debug(1, "file descriptors not open (src: %d, dst: %d)",
1893 s->src_fd, s->dst_fd);
1894 s->err = EINVAL;
1895 return -1;
1896 }
1897 return 0;
b3aa0442
A
1898}
1899
1900
1901/*
1902 * copyfile_check(), as described above, essentially tells you
1903 * what you'd have to copy, if you wanted it to copy the things
1904 * you asked it to copy.
1905 * In other words, if you pass in COPYFILE_ALL, and the file in
1906 * question had no extended attributes but did have an ACL, you'd
1907 * get back COPYFILE_ACL.
1908 */
1909static copyfile_flags_t copyfile_check(copyfile_state_t s)
1910{
1f4df596
A
1911 acl_t acl = NULL;
1912 copyfile_flags_t ret = 0;
1913 int nofollow = (s->flags & COPYFILE_NOFOLLOW_SRC);
1914 qtn_file_t qinfo;
b3aa0442 1915
1f4df596 1916 if (!s->src)
b3aa0442 1917 {
1f4df596
A
1918 s->err = EINVAL;
1919 return -1;
b3aa0442
A
1920 }
1921
1f4df596
A
1922 /* check EAs */
1923 if (COPYFILE_XATTR & s->flags)
1924 if (listxattr(s->src, 0, 0, nofollow ? XATTR_NOFOLLOW : 0) > 0)
1925 {
1926 ret |= COPYFILE_XATTR;
1927 }
b3aa0442 1928
1f4df596
A
1929 if (COPYFILE_ACL & s->flags)
1930 {
1931 (COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np)
1932 (s->src, &s->sb, s->fsec);
b3aa0442 1933
1f4df596
A
1934 if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) == 0)
1935 ret |= COPYFILE_ACL;
1936 }
b3aa0442 1937
1f4df596 1938 copyfile_debug(2, "check result: %d (%s)", ret, s->src);
b3aa0442 1939
1f4df596
A
1940 if (acl)
1941 acl_free(acl);
b3aa0442 1942
1f4df596
A
1943 if (s->qinfo) {
1944 /* If the state has had quarantine info set already, we use that */
1945 ret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL);
1946 } else {
1947 qinfo = qtn_file_alloc();
b3aa0442 1948 /*
1f4df596
A
1949 * For quarantine information, we need to see if the source file
1950 * has any. Since it may be a symlink, however, and we may, or
1951 * not be following, *and* there's no qtn* routine which can optionally
1952 * follow or not follow a symlink, we need to instead work around
1953 * this limitation.
b3aa0442 1954 */
1f4df596
A
1955 if (qinfo) {
1956 int fd;
1957 int qret = 0;
1958 struct stat sbuf;
1959
1960 /*
1961 * If we care about not following symlinks, *and* the file exists
1962 * (which is to say, lstat doesn't return an error), *and* the file
1963 * is a symlink, then we open it up (with O_SYMLINK), and use
1964 * qtn_file_init_with_fd(); if none of that is true, however, then
1965 * we can simply use qtn_file_init_with_path().
1966 */
1967 if (nofollow
1968 && lstat(s->src, &sbuf) == 0
b3aa0442 1969 && ((sbuf.st_mode & S_IFMT) == S_IFLNK)) {
1f4df596
A
1970 fd = open(s->src, O_RDONLY | O_SYMLINK);
1971 if (fd != -1) {
1972 if (!qtn_file_init_with_fd(qinfo, fd)) {
1973 qret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL);
1974 }
1975 close(fd);
1976 }
1977 } else {
1978 if (!qtn_file_init_with_path(qinfo, s->src)) {
b3aa0442
A
1979 qret |= ((s->flags & COPYFILE_XATTR) ? COPYFILE_XATTR : COPYFILE_ACL);
1980 }
b3aa0442 1981 }
1f4df596
A
1982 qtn_file_free(qinfo);
1983 ret |= qret;
b3aa0442 1984 }
b3aa0442 1985 }
1f4df596 1986 return ret;
b3aa0442
A
1987}
1988
1989/*
1990 * Attempt to copy the data section of a file. Using blockisize
1991 * is not necessarily the fastest -- it might be desirable to
1992 * specify a blocksize, somehow. But it's a size that should be
1993 * guaranteed to work.
1994 */
1995static int copyfile_data(copyfile_state_t s)
1996{
1f4df596
A
1997 size_t blen;
1998 char *bp = 0;
1999 ssize_t nread;
2000 int ret = 0;
2001 size_t iBlocksize = 0;
2002 size_t oBlocksize = 0;
2003 const size_t onegig = 1 << 30;
2004 struct statfs sfs;
2005 copyfile_callback_t status = s->statuscb;
2006
2007 /* Unless it's a normal file, we don't copy. For now, anyway */
2008 if ((s->sb.st_mode & S_IFMT) != S_IFREG)
2009 return 0;
b3aa0442 2010
3f81d1c4 2011#ifdef VOL_CAP_FMT_DECMPFS_COMPRESSION
1f4df596
A
2012 if (s->internal_flags & cfSawDecmpEA) {
2013 if (s->sb.st_flags & UF_COMPRESSED) {
2014 if ((s->flags & COPYFILE_STAT) == 0) {
2015 if (fchflags(s->dst_fd, UF_COMPRESSED) == 0) {
2016 goto exit;
2017 }
2018 }
3f81d1c4 2019 }
3f81d1c4 2020 }
3f81d1c4 2021#endif
b3aa0442 2022
1f4df596
A
2023 if (fstatfs(s->src_fd, &sfs) == -1) {
2024 iBlocksize = s->sb.st_blksize;
2025 } else {
2026 iBlocksize = sfs.f_iosize;
2027 }
659640e4 2028
1f4df596
A
2029 /* Work-around for 6453525, limit blocksize to 1G */
2030 if (iBlocksize > onegig) {
2031 iBlocksize = onegig;
2032 }
b3aa0442 2033
1f4df596
A
2034 if ((bp = malloc(iBlocksize)) == NULL)
2035 return -1;
b3aa0442 2036
1f4df596
A
2037 if (fstatfs(s->dst_fd, &sfs) == -1 || sfs.f_iosize == 0) {
2038 oBlocksize = iBlocksize;
2039 } else {
2040 oBlocksize = sfs.f_iosize;
2041 if (oBlocksize > onegig)
2042 oBlocksize = onegig;
2043 }
2044
2045 blen = iBlocksize;
2046
2047 s->totalCopied = 0;
2048 /* If supported, do preallocation for Xsan / HFS volumes */
2049#ifdef F_PREALLOCATE
2050 {
2051 fstore_t fst;
2052
2053 fst.fst_flags = 0;
2054 fst.fst_posmode = F_PEOFPOSMODE;
2055 fst.fst_offset = 0;
2056 fst.fst_length = s->sb.st_size;
2057 /* Ignore errors; this is merely advisory. */
2058 (void)fcntl(s->dst_fd, F_PREALLOCATE, &fst);
2059 }
2060#endif
2061
2062 while ((nread = read(s->src_fd, bp, blen)) > 0)
2063 {
2064 ssize_t nwritten;
2065 size_t left = nread;
2066 void *ptr = bp;
2067 int loop = 0;
2068
2069 while (left > 0) {
2070 nwritten = write(s->dst_fd, ptr, MIN(left, oBlocksize));
2071 switch (nwritten) {
2072 case 0:
2073 if (++loop > 5) {
2074 copyfile_warn("writing to output %d times resulted in 0 bytes written", loop);
2075 ret = -1;
2076 s->err = EAGAIN;
2077 goto exit;
2078 }
2079 break;
2080 case -1:
2081 copyfile_warn("writing to output file got error");
2082 if (status) {
2083 int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
2084 if (rv == COPYFILE_SKIP) { // Skip the data copy
2085 ret = 0;
2086 goto exit;
2087 }
2088 if (rv == COPYFILE_CONTINUE) { // Retry the write
2089 errno = 0;
2090 continue;
2091 }
2092 }
2093 ret = -1;
659640e4 2094 goto exit;
1f4df596
A
2095 default:
2096 left -= nwritten;
2097 ptr = ((char*)ptr) + nwritten;
2098 loop = 0;
2099 break;
659640e4 2100 }
1f4df596
A
2101 s->totalCopied += nwritten;
2102 if (status) {
2103 int rv = (*status)(COPYFILE_COPY_DATA, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
2104 if (rv == COPYFILE_QUIT) {
2105 ret = -1; s->err = errno = ECANCELED;
2106 goto exit;
2107 }
659640e4
A
2108 }
2109 }
b3aa0442 2110 }
1f4df596
A
2111 if (nread < 0)
2112 {
2113 copyfile_warn("reading from %s", s->src ? s->src : "(null src)");
2114 ret = -1;
2115 goto exit;
2116 }
b3aa0442 2117
1f4df596
A
2118 if (ftruncate(s->dst_fd, s->totalCopied) < 0)
2119 {
2120 ret = -1;
2121 goto exit;
2122 }
b3aa0442
A
2123
2124exit:
1f4df596
A
2125 if (ret == -1)
2126 {
2127 s->err = errno;
2128 }
2129 free(bp);
2130 return ret;
b3aa0442
A
2131}
2132
2133/*
2134 * copyfile_security() will copy the ACL set, and the
2135 * POSIX set. Complexities come when dealing with
2136 * inheritied permissions, and when dealing with both
2137 * POSIX and ACL permissions.
2138 */
2139static int copyfile_security(copyfile_state_t s)
2140{
1f4df596
A
2141 int copied = 0;
2142 struct stat sb;
2143 acl_t acl_src = NULL, acl_tmp = NULL, acl_dst = NULL;
2144 int ret = 0;
2145 filesec_t tmp_fsec = NULL;
2146 filesec_t fsec_dst = filesec_init();
2147
2148 if (fsec_dst == NULL)
2149 return -1;
b3aa0442
A
2150
2151
1f4df596 2152 if (COPYFILE_ACL & s->flags)
b3aa0442 2153 {
1f4df596
A
2154 if (filesec_get_property(s->fsec, FILESEC_ACL, &acl_src))
2155 {
2156 if (errno == ENOENT)
2157 acl_src = NULL;
2158 else
2159 goto error_exit;
2160 }
b3aa0442 2161
1f4df596
A
2162 /* grab the destination acl
2163 cannot assume it's empty due to inheritance
2164 */
2165 if(fstatx_np(s->dst_fd, &sb, fsec_dst))
2166 goto error_exit;
b3aa0442 2167
1f4df596
A
2168 if (filesec_get_property(fsec_dst, FILESEC_ACL, &acl_dst))
2169 {
2170 if (errno == ENOENT)
2171 acl_dst = NULL;
2172 else
2173 goto error_exit;
2174 }
659640e4 2175
1f4df596
A
2176 if (acl_src == NULL && acl_dst == NULL)
2177 goto no_acl;
659640e4 2178
1f4df596
A
2179 acl_tmp = acl_init(4);
2180 if (acl_tmp == NULL)
2181 goto error_exit;
3f81d1c4 2182
1f4df596
A
2183 if (acl_src) {
2184 acl_entry_t ace = NULL;
2185 acl_entry_t tmp = NULL;
2186 for (copied = 0;
2187 acl_get_entry(acl_src,
2188 ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY,
2189 &ace) == 0;)
2190 {
2191 acl_flagset_t flags = { 0 };
2192 acl_get_flagset_np(ace, &flags);
2193 if (!acl_get_flag_np(flags, ACL_ENTRY_INHERITED))
2194 {
2195 if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1)
2196 goto error_exit;
2197
2198 if ((ret = acl_copy_entry(tmp, ace)) == -1)
2199 goto error_exit;
2200
2201 copyfile_debug(2, "copied acl entry from %s to %s",
2202 s->src ? s->src : "(null src)",
2203 s->dst ? s->dst : "(null tmp)");
2204 copied++;
2205 }
2206 }
659640e4 2207 }
1f4df596
A
2208 if (acl_dst) {
2209 acl_entry_t ace = NULL;
2210 acl_entry_t tmp = NULL;
2211 acl_flagset_t flags = { 0 };
2212 for (copied = 0;acl_get_entry(acl_dst,
2213 ace == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY,
2214 &ace) == 0;)
2215 {
2216 acl_get_flagset_np(ace, &flags);
2217 if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED))
2218 {
2219 if ((ret = acl_create_entry(&acl_tmp, &tmp)) == -1)
2220 goto error_exit;
2221
2222 if ((ret = acl_copy_entry(tmp, ace)) == -1)
2223 goto error_exit;
2224
2225 copyfile_debug(2, "copied acl entry from %s to %s",
2226 s->src ? s->src : "(null dst)",
2227 s->dst ? s->dst : "(null tmp)");
2228 copied++;
2229 }
2230 }
2231 }
2232 if (!filesec_set_property(s->fsec, FILESEC_ACL, &acl_tmp))
3f81d1c4 2233 {
1f4df596 2234 copyfile_debug(3, "altered acl");
3f81d1c4 2235 }
b3aa0442 2236 }
b3aa0442 2237no_acl:
1f4df596
A
2238 /*
2239 * The following code is attempting to ensure that only the requested
2240 * security information gets copied over to the destination file.
2241 * We essentially have four cases: COPYFILE_ACL, COPYFILE_STAT,
2242 * COPYFILE_(STAT|ACL), and none (in which case, we wouldn't be in
2243 * this function).
2244 *
2245 * If we have both flags, we copy everything; if we have ACL but not STAT,
2246 * we remove the POSIX information from the filesec object, and apply the
2247 * ACL; if we have STAT but not ACL, then we just use fchmod(), and ignore
2248 * the extended version.
2249 */
2250 tmp_fsec = filesec_dup(s->fsec);
2251 if (tmp_fsec == NULL) {
2252 goto error_exit;
2253 }
2254
2255 switch (COPYFILE_SECURITY & s->flags) {
2256 case COPYFILE_ACL:
2257 copyfile_unset_posix_fsec(tmp_fsec);
2258 /* FALLTHROUGH */
2259 case COPYFILE_ACL | COPYFILE_STAT:
2260 if (fchmodx_np(s->dst_fd, tmp_fsec) < 0) {
2261 acl_t acl = NULL;
2262 /*
2263 * The call could have failed for a number of reasons, since
2264 * it does a number of things: it changes the mode of the file,
2265 * sets the owner and group, and applies an ACL (if one exists).
2266 * The typical failure is going to be trying to set the group of
2267 * the destination file to match the source file, when the process
2268 * doesn't have permission to put files in that group. We try to
2269 * work around this by breaking the steps out and doing them
2270 * discretely. We don't care if the fchown fails, but we do care
2271 * if the mode or ACL can't be set. For historical reasons, we
2272 * simply log those failures, however.
2273 *
2274 * Big warning here: we may NOT have COPYFILE_STAT set, since
2275 * we fell-through from COPYFILE_ACL. So check for the fchmod.
2276 */
659640e4
A
2277
2278#define NS(x) ((x) ? (x) : "(null string)")
1f4df596
A
2279 if ((s->flags & COPYFILE_STAT) &&
2280 fchmod(s->dst_fd, s->sb.st_mode) == -1) {
2281 copyfile_warn("could not change mode of destination file %s to match source file %s", NS(s->dst), NS(s->src));
2282 }
2283 (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid);
2284 if (filesec_get_property(tmp_fsec, FILESEC_ACL, &acl) == 0) {
2285 if (acl_set_fd(s->dst_fd, acl) == -1) {
2286 copyfile_warn("could not apply acl to destination file %s from source file %s", NS(s->dst), NS(s->src));
2287 }
2288 acl_free(acl);
2289 }
659640e4 2290 }
659640e4 2291#undef NS
1f4df596
A
2292 break;
2293 case COPYFILE_STAT:
2294 (void)fchmod(s->dst_fd, s->sb.st_mode);
2295 break;
2296 }
2297 filesec_free(tmp_fsec);
b3aa0442 2298exit:
1f4df596
A
2299 filesec_free(fsec_dst);
2300 if (acl_src) acl_free(acl_src);
2301 if (acl_dst) acl_free(acl_dst);
2302 if (acl_tmp) acl_free(acl_tmp);
b3aa0442 2303
1f4df596 2304 return ret;
b3aa0442
A
2305
2306error_exit:
1f4df596
A
2307 ret = -1;
2308 goto exit;
b3aa0442
A
2309
2310}
2311
2312/*
2313 * Attempt to set the destination file's stat information -- including
2314 * flags and time-related fields -- to the source's.
2315 */
2316static int copyfile_stat(copyfile_state_t s)
2317{
1f4df596
A
2318 struct timeval tval[2];
2319 unsigned int added_flags = 0, dst_flags = 0;
2320 struct stat dst_sb;
2321
2322 /*
2323 * NFS doesn't support chflags; ignore errors as a result, since
2324 * we don't return failure for this.
2325 */
2326 if (s->internal_flags & cfMakeFileInvisible)
2327 added_flags |= UF_HIDDEN;
2328
2329 /*
2330 * We need to check if SF_RESTRICTED was set on the destination
2331 * by the kernel. If it was, don't drop it.
2332 */
2333 if (fstat(s->dst_fd, &dst_sb))
2334 return -1;
2335 if (dst_sb.st_flags & SF_RESTRICTED)
2336 added_flags |= SF_RESTRICTED;
618b37c8 2337
1f4df596
A
2338 /* Copy file flags, masking out any we don't want to preserve */
2339 dst_flags = (s->sb.st_flags & ~COPYFILE_OMIT_FLAGS) | added_flags;
2340 (void)fchflags(s->dst_fd, dst_flags);
b3aa0442 2341
1f4df596
A
2342 /* If this fails, we don't care */
2343 (void)fchown(s->dst_fd, s->sb.st_uid, s->sb.st_gid);
b3aa0442 2344
1f4df596
A
2345 /* This may have already been done in copyfile_security() */
2346 (void)fchmod(s->dst_fd, s->sb.st_mode & ~S_IFMT);
b3aa0442 2347
1f4df596
A
2348 tval[0].tv_sec = s->sb.st_atime;
2349 tval[1].tv_sec = s->sb.st_mtime;
2350 tval[0].tv_usec = tval[1].tv_usec = 0;
2351 (void)futimes(s->dst_fd, tval);
3f81d1c4 2352
1f4df596 2353 return 0;
b3aa0442
A
2354}
2355
2356/*
2357 * Similar to copyfile_security() in some ways; this
2358 * routine copies the extended attributes from the source,
2359 * and sets them on the destination.
2360 * The procedure is pretty simple, even if it is verbose:
2361 * for each named attribute on the destination, get its name, and
2362 * remove it. We should have none after that.
2363 * For each named attribute on the source, get its name, get its
2364 * data, and set it on the destination.
2365 */
2366static int copyfile_xattr(copyfile_state_t s)
2367{
1f4df596
A
2368 char *name;
2369 char *namebuf, *end;
2370 ssize_t xa_size;
2371 void *xa_dataptr;
2372 ssize_t bufsize = 4096;
2373 ssize_t asize;
2374 ssize_t nsize;
2375 int ret = 0;
2376 int look_for_decmpea = 0;
2377
2378 /* delete EAs on destination */
2379 if ((nsize = flistxattr(s->dst_fd, 0, 0, 0)) > 0)
2380 {
2381 if ((namebuf = (char *) malloc(nsize)) == NULL)
2382 return -1;
2383 else
2384 nsize = flistxattr(s->dst_fd, namebuf, nsize, 0);
2385
2386 if (nsize > 0) {
2387 /*
2388 * With this, end points to the last byte of the allocated buffer
2389 * This *should* be NUL, from flistxattr, but if it's not, we can
2390 * set it anyway -- it'll result in a truncated name, which then
2391 * shouldn't match when we get them later.
2392 */
2393 end = namebuf + nsize - 1;
2394 if (*end != 0)
2395 *end = 0;
2396 for (name = namebuf; name <= end; name += strlen(name) + 1) {
2397 /* If the quarantine information shows up as an EA, we skip over it */
2398 if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0) {
2399 continue;
2400 }
2401 fremovexattr(s->dst_fd, name,0);
2402 }
2403 }
2404 free(namebuf);
2405 } else
2406 if (nsize < 0)
2407 {
2408 if (errno == ENOTSUP || errno == EPERM)
2409 return 0;
2410 else
2411 return -1;
b3aa0442 2412 }
b3aa0442 2413
3f81d1c4 2414#ifdef DECMPFS_XATTR_NAME
1f4df596
A
2415 if ((s->flags & COPYFILE_DATA) &&
2416 (s->sb.st_flags & UF_COMPRESSED) &&
2417 doesdecmpfs(s->src_fd) &&
2418 doesdecmpfs(s->dst_fd)) {
2419 look_for_decmpea = XATTR_SHOWCOMPRESSION;
2420 }
3f81d1c4
A
2421#endif
2422
1f4df596
A
2423 /* get name list of EAs on source */
2424 if ((nsize = flistxattr(s->src_fd, 0, 0, look_for_decmpea)) < 0)
2425 {
2426 if (errno == ENOTSUP || errno == EPERM)
2427 return 0;
2428 else
2429 return -1;
2430 } else
2431 if (nsize == 0)
2432 return 0;
b3aa0442 2433
1f4df596
A
2434 if ((namebuf = (char *) malloc(nsize)) == NULL)
2435 return -1;
2436 else
2437 nsize = flistxattr(s->src_fd, namebuf, nsize, look_for_decmpea);
b3aa0442 2438
1f4df596
A
2439 if (nsize <= 0) {
2440 free(namebuf);
2441 return (int)nsize;
3c5890b1
A
2442 }
2443
1f4df596
A
2444 /*
2445 * With this, end points to the last byte of the allocated buffer
2446 * This *should* be NUL, from flistxattr, but if it's not, we can
2447 * set it anyway -- it'll result in a truncated name, which then
2448 * shouldn't match when we get them later.
2449 */
2450 end = namebuf + nsize - 1;
2451 if (*end != 0)
2452 *end = 0;
b3aa0442 2453
1f4df596
A
2454 if ((xa_dataptr = (void *) malloc(bufsize)) == NULL) {
2455 free(namebuf);
2456 return -1;
b3aa0442
A
2457 }
2458
1f4df596 2459 for (name = namebuf; name <= end; name += strlen(name) + 1)
b3aa0442 2460 {
1f4df596
A
2461 if (s->xattr_name) {
2462 free(s->xattr_name);
2463 s->xattr_name = NULL;
2464 }
b3aa0442 2465
1f4df596
A
2466 /* If the quarantine information shows up as an EA, we skip over it */
2467 if (strncmp(name, XATTR_QUARANTINE_NAME, end - name) == 0)
2468 continue;
b3aa0442 2469
1f4df596
A
2470 if ((xa_size = fgetxattr(s->src_fd, name, 0, 0, 0, look_for_decmpea)) < 0)
2471 {
2472 continue;
2473 }
b3aa0442 2474
1f4df596
A
2475 if (xa_size > bufsize)
2476 {
2477 void *tdptr = xa_dataptr;
2478 bufsize = xa_size;
2479 if ((xa_dataptr =
2480 (void *) realloc((void *) xa_dataptr, bufsize)) == NULL)
2481 {
2482 free(tdptr);
2483 ret = -1;
2484 continue;
2485 }
2486 }
2487
2488 if ((asize = fgetxattr(s->src_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea)) < 0)
2489 {
618b37c8 2490 continue;
1f4df596 2491 }
618b37c8 2492
1f4df596
A
2493 if (xa_size != asize)
2494 xa_size = asize;
2495
2496#ifdef DECMPFS_XATTR_NAME
2497 if (strncmp(name, DECMPFS_XATTR_NAME, end-name) == 0)
2498 {
2499 decmpfs_disk_header *hdr = xa_dataptr;
2500
2501 /*
2502 * If the EA has the decmpfs name, but is too
2503 * small, or doesn't have the right magic number,
2504 * or isn't the right type, we'll just skip it.
2505 * This means it won't end up in the destination
2506 * file, and data copy will happen normally.
2507 */
2508 if ((size_t)xa_size < sizeof(decmpfs_disk_header)) {
2509 continue;
2510 }
2511 if (OSSwapLittleToHostInt32(hdr->compression_magic) != DECMPFS_MAGIC) {
2512 continue;
2513 }
2514 /*
2515 * From AppleFSCompression documentation:
2516 * "It is incumbent on the aware copy engine to identify
2517 * the type of compression being used, and to perform an
2518 * unaware copy of any file it does not recognize."
2519 *
2520 * Compression Types are defined in:
2521 * "AppleFSCompression/Common/compressorCommon.h"
2522 *
2523 * Unfortunately, they don't provide a way to dynamically
2524 * determine what possible compression_type values exist,
2525 * so we have to update this every time a new compression_type
2526 * is added. Types 7->10 were added in 10.10, Types 11 & 12
2527 * were added in 10.11.
2528 *
2529 * Ubiquity faulting file compression type 0x80000001 are
2530 * deprecated as of Yosemite, per rdar://17714998 don't copy the
2531 * decmpfs xattr on these files, zero byte files are safer
2532 * than a fault nobody knows how to handle.
2533 */
2534 switch (OSSwapLittleToHostInt32(hdr->compression_type)) {
2535 case 3: /* zlib-compressed data in xattr */
2536 case 4: /* 64k chunked zlib-compressed data in resource fork */
2537
2538 case 7: /* LZVN-compressed data in xattr */
2539 case 8: /* 64k chunked LZVN-compressed data in resource fork */
2540
2541 case 9: /* uncompressed data in xattr (similar to but not identical to CMP_Type1) */
2542 case 10: /* 64k chunked uncompressed data in resource fork */
2543
2544 case 11: /* LZFSE-compressed data in xattr */
2545 case 12: /* 64k chunked LZFSE-compressed data in resource fork */
2546
2547 /* valid compression type, we want to copy. */
2548 break;
2549
2550 case 5: /* specifies de-dup within the generation store. Don't copy decmpfs xattr. */
2551 copyfile_debug(3, "compression_type <5> on attribute com.apple.decmpfs for src file %s is not copied.",
2552 s->src ? s->src : "(null string)");
2553 continue;
2554
2555 case 6: /* unused */
2556 case 0x80000001: /* faulting files are deprecated, don't copy decmpfs xattr */
2557 default:
2558 copyfile_warn("Invalid compression_type <%d> on attribute %s for src file %s",
618b37c8 2559 OSSwapLittleToHostInt32(hdr->compression_type), name, s->src ? s->src : "(null string)");
1f4df596
A
2560 continue;
2561 }
2562 s->internal_flags |= cfSawDecmpEA;
2563 }
3f81d1c4
A
2564#endif
2565
1f4df596
A
2566 // If we have a copy intention stated, and the EA is to be ignored, we ignore it
2567 if (s->copyIntent
2568 && xattr_preserve_for_intent(name, s->copyIntent) == 0)
3f81d1c4 2569 continue;
1f4df596
A
2570
2571 s->xattr_name = strdup(name);
2572
2573 if (s->statuscb) {
2574 int rv;
2575 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
2576 if (rv == COPYFILE_QUIT) {
2577 s->err = ECANCELED;
2578 goto out;
2579 } else if (rv == COPYFILE_SKIP) {
2580 continue;
2581 }
3f81d1c4 2582 }
1f4df596 2583 if (fsetxattr(s->dst_fd, name, xa_dataptr, xa_size, 0, look_for_decmpea) < 0)
3f81d1c4 2584 {
1f4df596
A
2585 if (s->statuscb)
2586 {
2587 int rv;
2588 if (s->xattr_name == NULL)
2589 s->xattr_name = strdup(name);
2590 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
2591 if (rv == COPYFILE_QUIT)
2592 {
2593 s->err = ECANCELED;
2594 ret = -1;
2595 goto out;
2596 }
2597 }
2598 else
2599 {
2600 ret = -1;
2601 copyfile_warn("could not set attributes %s on destination file descriptor: %s", name, strerror(errno));
2602 continue;
2603 }
3f81d1c4 2604 }
1f4df596
A
2605 if (s->statuscb) {
2606 int rv;
2607 if (s->xattr_name == NULL)
2608 s->xattr_name = strdup(name);
3f81d1c4 2609
1f4df596
A
2610 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
2611 if (rv == COPYFILE_QUIT) {
2612 s->err = ECANCELED;
2613 goto out;
2614 }
3f81d1c4 2615 }
b3aa0442 2616 }
3f81d1c4 2617out:
1f4df596
A
2618 if (namebuf)
2619 free(namebuf);
2620 free((void *) xa_dataptr);
2621 if (s->xattr_name) {
2622 free(s->xattr_name);
2623 s->xattr_name = NULL;
2624 }
2625 return ret;
b3aa0442
A
2626}
2627
2628/*
2629 * API interface into getting data from the opaque data type.
2630 */
2631int copyfile_state_get(copyfile_state_t s, uint32_t flag, void *ret)
2632{
1f4df596
A
2633 if (ret == NULL)
2634 {
2635 errno = EFAULT;
2636 return -1;
2637 }
2638
2639 switch(flag)
2640 {
2641 case COPYFILE_STATE_SRC_FD:
2642 *(int*)ret = s->src_fd;
2643 break;
2644 case COPYFILE_STATE_DST_FD:
2645 *(int*)ret = s->dst_fd;
2646 break;
2647 case COPYFILE_STATE_SRC_FILENAME:
2648 *(char**)ret = s->src;
2649 break;
2650 case COPYFILE_STATE_DST_FILENAME:
2651 *(char**)ret = s->dst;
2652 break;
2653 case COPYFILE_STATE_QUARANTINE:
2654 *(qtn_file_t*)ret = s->qinfo;
2655 break;
b3aa0442 2656#if 0
1f4df596
A
2657 case COPYFILE_STATE_STATS:
2658 ret = s->stats.global;
2659 break;
2660 case COPYFILE_STATE_PROGRESS_CB:
2661 ret = s->callbacks.progress;
2662 break;
659640e4
A
2663#endif
2664#ifdef COPYFILE_STATE_STATUS_CB
1f4df596
A
2665 case COPYFILE_STATE_STATUS_CB:
2666 *(copyfile_callback_t*)ret = s->statuscb;
2667 break;
2668 case COPYFILE_STATE_STATUS_CTX:
2669 *(void**)ret = s->ctx;
2670 break;
2671 case COPYFILE_STATE_COPIED:
2672 *(off_t*)ret = s->totalCopied;
2673 break;
3f81d1c4
A
2674#endif
2675#ifdef COPYFILE_STATE_XATTRNAME
1f4df596
A
2676 case COPYFILE_STATE_XATTRNAME:
2677 *(char**)ret = s->xattr_name;
2678 break;
11f767b3
A
2679#endif
2680#ifdef COPYFILE_STATE_INTENT
1f4df596
A
2681 case COPYFILE_STATE_INTENT:
2682 *(xattr_operation_intent_t*)ret = s->copyIntent;
2683 break;
b3aa0442 2684#endif
1f4df596
A
2685 case COPYFILE_STATE_WAS_CLONED:
2686 *(bool *)ret = s->was_cloned;
2687 break;
2688 default:
2689 errno = EINVAL;
2690 ret = NULL;
2691 return -1;
2692 }
2693 return 0;
b3aa0442
A
2694}
2695
2696/*
2697 * Public API for setting state data (remember that the state is
2698 * an opaque data type).
2699 */
2700int copyfile_state_set(copyfile_state_t s, uint32_t flag, const void * thing)
2701{
2702#define copyfile_set_string(DST, SRC) \
1f4df596
A
2703 do { \
2704 if (SRC != NULL) { \
2705 DST = strdup((char *)SRC); \
2706 } else { \
2707 if (DST != NULL) { \
2708 free(DST); \
2709 } \
2710 DST = NULL; \
2711 } \
2712 } while (0)
2713
2714 if (thing == NULL)
2715 {
2716 errno = EFAULT;
2717 return -1;
2718 }
2719
2720 switch(flag)
2721 {
2722 case COPYFILE_STATE_SRC_FD:
2723 s->src_fd = *(int*)thing;
2724 break;
2725 case COPYFILE_STATE_DST_FD:
2726 s->dst_fd = *(int*)thing;
2727 break;
2728 case COPYFILE_STATE_SRC_FILENAME:
2729 copyfile_set_string(s->src, thing);
2730 break;
2731 case COPYFILE_STATE_DST_FILENAME:
2732 copyfile_set_string(s->dst, thing);
2733 break;
2734 case COPYFILE_STATE_QUARANTINE:
2735 if (s->qinfo)
2736 {
2737 qtn_file_free(s->qinfo);
2738 s->qinfo = NULL;
2739 }
2740 if (*(qtn_file_t*)thing)
2741 s->qinfo = qtn_file_clone(*(qtn_file_t*)thing);
2742 break;
2743#if 0
2744 case COPYFILE_STATE_STATS:
2745 s->stats.global = thing;
2746 break;
2747 case COPYFILE_STATE_PROGRESS_CB:
2748 s->callbacks.progress = thing;
2749 break;
2750#endif
2751#ifdef COPYFILE_STATE_STATUS_CB
2752 case COPYFILE_STATE_STATUS_CB:
2753 s->statuscb = (copyfile_callback_t)thing;
2754 break;
2755 case COPYFILE_STATE_STATUS_CTX:
2756 s->ctx = (void*)thing;
2757 break;
2758#endif
11f767b3 2759#ifdef COPYFILE_STATE_INTENT
1f4df596
A
2760 case COPYFILE_STATE_INTENT:
2761 s->copyIntent = *(xattr_operation_intent_t*)thing;
2762 break;
b3aa0442 2763#endif
1f4df596
A
2764 default:
2765 errno = EINVAL;
2766 return -1;
2767 }
2768 return 0;
b3aa0442
A
2769#undef copyfile_set_string
2770}
2771
2772
2773/*
2774 * Make this a standalone program for testing purposes by
2775 * defining _COPYFILE_TEST.
2776 */
2777#ifdef _COPYFILE_TEST
2778#define COPYFILE_OPTION(x) { #x, COPYFILE_ ## x },
2779
2780struct {char *s; int v;} opts[] = {
1f4df596
A
2781 COPYFILE_OPTION(ACL)
2782 COPYFILE_OPTION(STAT)
2783 COPYFILE_OPTION(XATTR)
2784 COPYFILE_OPTION(DATA)
2785 COPYFILE_OPTION(SECURITY)
2786 COPYFILE_OPTION(METADATA)
2787 COPYFILE_OPTION(ALL)
2788 COPYFILE_OPTION(NOFOLLOW_SRC)
2789 COPYFILE_OPTION(NOFOLLOW_DST)
2790 COPYFILE_OPTION(NOFOLLOW)
2791 COPYFILE_OPTION(EXCL)
2792 COPYFILE_OPTION(MOVE)
2793 COPYFILE_OPTION(UNLINK)
2794 COPYFILE_OPTION(PACK)
2795 COPYFILE_OPTION(UNPACK)
2796 COPYFILE_OPTION(CHECK)
2797 COPYFILE_OPTION(VERBOSE)
2798 COPYFILE_OPTION(DEBUG)
2799 {NULL, 0}
b3aa0442
A
2800};
2801
2802int main(int c, char *v[])
2803{
1f4df596
A
2804 int i;
2805 int flags = 0;
b3aa0442 2806
1f4df596
A
2807 if (c < 3)
2808 errx(1, "insufficient arguments");
b3aa0442 2809
1f4df596 2810 while(c-- > 3)
b3aa0442 2811 {
1f4df596
A
2812 for (i = 0; opts[i].s != NULL; ++i)
2813 {
2814 if (strcasecmp(opts[i].s, v[c]) == 0)
2815 {
2816 printf("option %d: %s <- %d\n", c, opts[i].s, opts[i].v);
2817 flags |= opts[i].v;
2818 break;
2819 }
2820 }
b3aa0442 2821 }
b3aa0442 2822
1f4df596 2823 return copyfile(v[1], v[2], NULL, flags);
b3aa0442
A
2824}
2825#endif
2826/*
2827 * Apple Double Create
2828 *
2829 * Create an Apple Double "._" file from a file's extented attributes
2830 *
2831 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
2832 */
2833
2834
2835#define offsetof(type, member) ((size_t)(&((type *)0)->member))
2836
11f767b3 2837#define XATTR_MAXATTRLEN (16*1024*1024)
b3aa0442
A
2838
2839
2840/*
1f4df596
A
2841 Typical "._" AppleDouble Header File layout:
2842 ------------------------------------------------------------
2843 MAGIC 0x00051607
2844 VERSION 0x00020000
2845 FILLER 0
2846 COUNT 2
2847 .-- AD ENTRY[0] Finder Info Entry (must be first)
2848 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
2849 | '-> FINDER INFO
2850 | ///////////// Fixed Size Data (32 bytes)
2851 | EXT ATTR HDR
2852 | /////////////
2853 | ATTR ENTRY[0] --.
2854 | ATTR ENTRY[1] --+--.
2855 | ATTR ENTRY[2] --+--+--.
2856 | ... | | |
2857 | ATTR ENTRY[N] --+--+--+--.
2858 | ATTR DATA 0 <-' | | |
2859 | //////////// | | |
2860 | ATTR DATA 1 <----' | |
2861 | ///////////// | |
2862 | ATTR DATA 2 <-------' |
2863 | ///////////// |
2864 | ... |
2865 | ATTR DATA N <----------'
2866 | /////////////
2867 | Attribute Free Space
2868 |
2869 '----> RESOURCE FORK
2870 ///////////// Variable Sized Data
2871 /////////////
2872 /////////////
2873 /////////////
2874 /////////////
2875 /////////////
2876 ...
2877 /////////////
2878
2879 ------------------------------------------------------------
2880
2881 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
2882 stored as part of the Finder Info. The length in the Finder
2883 Info AppleDouble entry includes the length of the extended
2884 attribute header, attribute entries, and attribute data.
2885 */
b3aa0442
A
2886
2887
2888/*
2889 * On Disk Data Structures
2890 *
2891 * Note: Motorola 68K alignment and big-endian.
2892 *
2893 * See RFC 1740 for additional information about the AppleDouble file format.
2894 *
2895 */
2896
2897#define ADH_MAGIC 0x00051607
2898#define ADH_VERSION 0x00020000
2899#define ADH_MACOSX "Mac OS X "
2900
2901/*
2902 * AppleDouble Entry ID's
2903 */
2904#define AD_DATA 1 /* Data fork */
2905#define AD_RESOURCE 2 /* Resource fork */
2906#define AD_REALNAME 3 /* File's name on home file system */
2907#define AD_COMMENT 4 /* Standard Mac comment */
2908#define AD_ICONBW 5 /* Mac black & white icon */
2909#define AD_ICONCOLOR 6 /* Mac color icon */
2910#define AD_UNUSED 7 /* Not used */
2911#define AD_FILEDATES 8 /* File dates; create, modify, etc */
2912#define AD_FINDERINFO 9 /* Mac Finder info & extended info */
2913#define AD_MACINFO 10 /* Mac file info, attributes, etc */
2914#define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
2915#define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
2916#define AD_AFPNAME 13 /* Short name on AFP server */
2917#define AD_AFPINFO 14 /* AFP file info, attrib., etc */
1f4df596 2918#define AD_AFPDIRID 15 /* AFP directory ID */
b3aa0442
A
2919#define AD_ATTRIBUTES AD_FINDERINFO
2920
2921
2922#define ATTR_FILE_PREFIX "._"
2923#define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
2924
2925#define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
2926
2927/* Implementation Limits */
11f767b3 2928#define ATTR_MAX_SIZE (16*1024*1024) /* 16 megabyte maximum attribute data size */
b3aa0442
A
2929#define ATTR_MAX_NAME_LEN 128
2930#define ATTR_MAX_HDR_SIZE (65536+18)
2931
2932/*
2933 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
2934 * size supported (including the attribute entries). All of
2935 * the attribute entries must reside within this limit.
2936 */
2937
2938
2939#define FINDERINFOSIZE 32
2940
2941typedef struct apple_double_entry
2942{
1f4df596 2943 u_int32_t type; /* entry type: see list, 0 invalid */
b3aa0442
A
2944 u_int32_t offset; /* entry data offset from the beginning of the file. */
2945 u_int32_t length; /* entry data length in bytes. */
2946} __attribute__((aligned(2), packed)) apple_double_entry_t;
2947
2948
2949typedef struct apple_double_header
2950{
2951 u_int32_t magic; /* == ADH_MAGIC */
1f4df596 2952 u_int32_t version; /* format version: 2 = 0x00020000 */
b3aa0442 2953 u_int32_t filler[4];
1f4df596 2954 u_int16_t numEntries; /* number of entries which follow */
b3aa0442
A
2955 apple_double_entry_t entries[2]; /* 'finfo' & 'rsrc' always exist */
2956 u_int8_t finfo[FINDERINFOSIZE]; /* Must start with Finder Info (32 bytes) */
2957 u_int8_t pad[2]; /* get better alignment inside attr_header */
2958} __attribute__((aligned(2), packed)) apple_double_header_t;
2959
2960
2961/* Entries are aligned on 4 byte boundaries */
2962typedef struct attr_entry
2963{
2964 u_int32_t offset; /* file offset to data */
2965 u_int32_t length; /* size of attribute data */
2966 u_int16_t flags;
1f4df596 2967 u_int8_t namelen; /* length of name including NULL termination char */
b3aa0442
A
2968 u_int8_t name[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
2969} __attribute__((aligned(2), packed)) attr_entry_t;
2970
2971
2972
2973/* Header + entries must fit into 64K */
2974typedef struct attr_header
2975{
2976 apple_double_header_t appledouble;
2977 u_int32_t magic; /* == ATTR_HDR_MAGIC */
2978 u_int32_t debug_tag; /* for debugging == file id of owning file */
1f4df596 2979 u_int32_t total_size; /* total size of attribute header + entries + data */
b3aa0442
A
2980 u_int32_t data_start; /* file offset to attribute data area */
2981 u_int32_t data_length; /* length of attribute data area */
2982 u_int32_t reserved[3];
2983 u_int16_t flags;
2984 u_int16_t num_attrs;
2985} __attribute__((aligned(2), packed)) attr_header_t;
2986
2987/* Empty Resource Fork Header */
2988/* This comes by way of xnu's vfs_xattr.c */
2989typedef struct rsrcfork_header {
2990 u_int32_t fh_DataOffset;
2991 u_int32_t fh_MapOffset;
2992 u_int32_t fh_DataLength;
2993 u_int32_t fh_MapLength;
2994 u_int8_t systemData[112];
2995 u_int8_t appData[128];
1f4df596 2996 u_int32_t mh_DataOffset;
b3aa0442 2997 u_int32_t mh_MapOffset;
1f4df596 2998 u_int32_t mh_DataLength;
b3aa0442
A
2999 u_int32_t mh_MapLength;
3000 u_int32_t mh_Next;
3001 u_int16_t mh_RefNum;
3002 u_int8_t mh_Attr;
1f4df596 3003 u_int8_t mh_InMemoryAttr;
b3aa0442
A
3004 u_int16_t mh_Types;
3005 u_int16_t mh_Names;
3006 u_int16_t typeCount;
31f0c9c4 3007} __attribute__((aligned(2), packed)) rsrcfork_header_t;
b3aa0442 3008#define RF_FIRST_RESOURCE 256
1f4df596 3009#define RF_NULL_MAP_LENGTH 30
b3aa0442
A
3010#define RF_EMPTY_TAG "This resource fork intentionally left blank "
3011
3012static const rsrcfork_header_t empty_rsrcfork_header = {
3013 OSSwapHostToBigInt32(RF_FIRST_RESOURCE), // fh_DataOffset
3014 OSSwapHostToBigInt32(RF_FIRST_RESOURCE), // fh_MapOffset
3015 0, // fh_DataLength
3016 OSSwapHostToBigInt32(RF_NULL_MAP_LENGTH), // fh_MapLength
3017 { RF_EMPTY_TAG, }, // systemData
3018 { 0 }, // appData
3019 OSSwapHostToBigInt32(RF_FIRST_RESOURCE), // mh_DataOffset
3020 OSSwapHostToBigInt32(RF_FIRST_RESOURCE), // mh_MapOffset
3021 0, // mh_DataLength
3022 OSSwapHostToBigInt32(RF_NULL_MAP_LENGTH), // mh_MapLength
3023 0, // mh_Next
3024 0, // mh_RefNum
3025 0, // mh_Attr
3026 0, // mh_InMemoryAttr
3027 OSSwapHostToBigInt16(RF_NULL_MAP_LENGTH - 2), // mh_Types
3028 OSSwapHostToBigInt16(RF_NULL_MAP_LENGTH), // mh_Names
3029 OSSwapHostToBigInt16(-1), // typeCount
3030};
3031
3032#define SWAP16(x) OSSwapBigToHostInt16(x)
3033#define SWAP32(x) OSSwapBigToHostInt32(x)
3034#define SWAP64(x) OSSwapBigToHostInt64(x)
3035
3036#define ATTR_ALIGN 3L /* Use four-byte alignment */
3037
3038#define ATTR_ENTRY_LENGTH(namelen) \
1f4df596 3039 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
b3aa0442
A
3040
3041#define ATTR_NEXT(ae) \
1f4df596 3042 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
b3aa0442
A
3043
3044#define XATTR_SECURITY_NAME "com.apple.acl.text"
3045
3046/*
1f4df596 3047 * Endian swap Apple Double header
b3aa0442
A
3048 */
3049static void
3050swap_adhdr(apple_double_header_t *adh)
3051{
3052#if BYTE_ORDER == LITTLE_ENDIAN
3053 int count;
3054 int i;
3055
3056 count = (adh->magic == ADH_MAGIC) ? adh->numEntries : SWAP16(adh->numEntries);
3057
3058 adh->magic = SWAP32 (adh->magic);
3059 adh->version = SWAP32 (adh->version);
3060 adh->numEntries = SWAP16 (adh->numEntries);
3061
3062 for (i = 0; i < count; i++)
3063 {
3064 adh->entries[i].type = SWAP32 (adh->entries[i].type);
3065 adh->entries[i].offset = SWAP32 (adh->entries[i].offset);
3066 adh->entries[i].length = SWAP32 (adh->entries[i].length);
3067 }
3068#else
3069 (void)adh;
3070#endif
3071}
3072
3073/*
44fb9055 3074 * Endian swap a single attr_entry_t
b3aa0442
A
3075 */
3076static void
44fb9055 3077swap_attrhdr_entry(attr_entry_t *ae)
b3aa0442
A
3078{
3079#if BYTE_ORDER == LITTLE_ENDIAN
1f4df596
A
3080 ae->offset = SWAP32 (ae->offset);
3081 ae->length = SWAP32 (ae->length);
3082 ae->flags = SWAP16 (ae->flags);
44fb9055 3083#else
1f4df596 3084 (void)ae;
44fb9055
A
3085#endif
3086}
b3aa0442 3087
44fb9055
A
3088/*
3089 * For a validated/endian swapped attr_header_t*
3090 * ah, endian swap all of the entries.
3091 */
3092static void
3093swap_attrhdr_entries(attr_header_t *ah)
3094{
3095#if BYTE_ORDER == LITTLE_ENDIAN
1f4df596
A
3096 int i;
3097 int count;
3098 attr_entry_t *entry;
3099 attr_entry_t *next;
3100
3101 /* If we're in copyfile_pack, num_args is native endian,
3102 * if we're in _unpack, num_args is big endian. Use
3103 * the magic number to test for endianess.
3104 */
3105 count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs);
3106
3107 entry = (attr_entry_t *)(&ah[1]);
3108 for (i = 0; i < count; i++) {
3109 next = ATTR_NEXT(entry);
3110 swap_attrhdr_entry(entry);
3111 entry = next;
3112 }
44fb9055 3113#else
1f4df596 3114 (void)ah;
44fb9055
A
3115#endif
3116}
b3aa0442 3117
44fb9055 3118/*
1f4df596 3119 * Endian swap extended attributes header
44fb9055
A
3120 */
3121static void
3122swap_attrhdr(attr_header_t *ah)
3123{
3124#if BYTE_ORDER == LITTLE_ENDIAN
b3aa0442
A
3125 ah->magic = SWAP32 (ah->magic);
3126 ah->debug_tag = SWAP32 (ah->debug_tag);
3127 ah->total_size = SWAP32 (ah->total_size);
3128 ah->data_start = SWAP32 (ah->data_start);
3129 ah->data_length = SWAP32 (ah->data_length);
3130 ah->flags = SWAP16 (ah->flags);
3131 ah->num_attrs = SWAP16 (ah->num_attrs);
b3aa0442
A
3132#else
3133 (void)ah;
3134#endif
3135}
3136
3137static const u_int32_t emptyfinfo[8] = {0};
3138
3139/*
3140 * Given an Apple Double file in src, turn it into a
3141 * normal file (possibly with multiple forks, EAs, and
3142 * ACLs) in dst.
3143 */
3144static int copyfile_unpack(copyfile_state_t s)
3145{
1f4df596
A
3146 ssize_t bytes;
3147 void * buffer, * endptr, * dataptr = NULL;
3148 apple_double_header_t *adhdr;
3149 ssize_t hdrsize;
3150 int error = 0;
3151
3152 if (s->sb.st_size < ATTR_MAX_HDR_SIZE)
3153 hdrsize = (ssize_t)s->sb.st_size;
3154 else
3155 hdrsize = ATTR_MAX_HDR_SIZE;
b3aa0442 3156
1f4df596
A
3157 buffer = calloc(1, hdrsize);
3158 if (buffer == NULL) {
3159 copyfile_debug(1, "copyfile_unpack: calloc(1, %zu) returned NULL", hdrsize);
3160 error = -1;
3161 goto exit;
3162 } else
3163 endptr = (char*)buffer + hdrsize;
b3aa0442 3164
1f4df596 3165 bytes = pread(s->src_fd, buffer, hdrsize, 0);
b3aa0442 3166
1f4df596 3167 if (bytes < 0)
b3aa0442 3168 {
1f4df596 3169 copyfile_debug(1, "pread returned: %zd", bytes);
b3aa0442
A
3170 error = -1;
3171 goto exit;
3172 }
1f4df596 3173 if (bytes < hdrsize)
b3aa0442 3174 {
1f4df596
A
3175 copyfile_debug(1,
3176 "pread couldn't read entire header: %d of %d",
3177 (int)bytes, (int)s->sb.st_size);
3178 error = -1;
3179 goto exit;
b3aa0442 3180 }
1f4df596 3181 adhdr = (apple_double_header_t *)buffer;
b3aa0442 3182
1f4df596
A
3183 /*
3184 * Check for Apple Double file.
3185 */
3186 if ((size_t)bytes < sizeof(apple_double_header_t) - 2 ||
3187 SWAP32(adhdr->magic) != ADH_MAGIC ||
3188 SWAP32(adhdr->version) != ADH_VERSION ||
3189 SWAP16(adhdr->numEntries) != 2 ||
3190 SWAP32(adhdr->entries[0].type) != AD_FINDERINFO)
b3aa0442 3191 {
b3aa0442 3192 if (COPYFILE_VERBOSE & s->flags)
1f4df596 3193 copyfile_warn("Not a valid Apple Double header");
b3aa0442
A
3194 error = -1;
3195 goto exit;
1f4df596
A
3196 }
3197 swap_adhdr(adhdr);
b3aa0442 3198
1f4df596
A
3199 /*
3200 * Remove any extended attributes on the target.
3201 */
b3aa0442 3202
1f4df596
A
3203 if ((bytes = flistxattr(s->dst_fd, 0, 0, 0)) > 0)
3204 {
3205 char *namebuf, *name;
b3aa0442 3206
1f4df596
A
3207 if ((namebuf = (char*) malloc(bytes)) == NULL)
3208 {
3209 s->err = ENOMEM;
3210 goto exit;
3211 }
3212 bytes = flistxattr(s->dst_fd, namebuf, bytes, 0);
b3aa0442 3213
1f4df596
A
3214 if (bytes > 0)
3215 for (name = namebuf; name < namebuf + bytes; name += strlen(name) + 1)
3216 (void)fremovexattr(s->dst_fd, name, 0);
3217
3218 free(namebuf);
3219 }
3220 else if (bytes < 0)
3221 {
3222 if (errno != ENOTSUP && errno != EPERM)
3223 goto exit;
3224 }
3225
3226 /*
3227 * Extract the extended attributes.
3228 *
3229 * >>> WARNING <<<
3230 * This assumes that the data is already in memory (not
3231 * the case when there are lots of attributes or one of
3232 * the attributes is very large.
3233 */
3234 if (adhdr->entries[0].length > FINDERINFOSIZE)
3235 {
3236 attr_header_t *attrhdr;
3237 attr_entry_t *entry;
3238 int count;
3239 int i;
3240
3241 if ((size_t)hdrsize < sizeof(attr_header_t)) {
3242 copyfile_warn("bad attribute header: %zu < %zu", hdrsize, sizeof(attr_header_t));
3243 error = -1;
3244 goto exit;
3245 }
3246
3247 attrhdr = (attr_header_t *)buffer;
3248 swap_attrhdr(attrhdr);
3249 if (attrhdr->magic != ATTR_HDR_MAGIC)
3250 {
3251 if (COPYFILE_VERBOSE & s->flags)
3252 copyfile_warn("bad attribute header");
3253 error = -1;
3254 goto exit;
3255 }
3256 count = attrhdr->num_attrs;
3257 entry = (attr_entry_t *)&attrhdr[1];
3258
3259 for (i = 0; i < count; i++)
3260 {
3261 /*
3262 * First we do some simple sanity checking.
3263 * +) See if entry is within the buffer's range;
3264 *
3265 * +) Check the attribute name length; if it's longer than the
3266 * maximum, we truncate it down. (We could error out as well;
3267 * I'm not sure which is the better way to go here.)
3268 *
3269 * +) If, given the name length, it goes beyond the end of
3270 * the buffer, error out.
3271 *
3272 * +) If the last byte isn't a NUL, make it a NUL. (Since we
3273 * truncated the name length above, we truncate the name here.)
3274 *
3275 * +) If entry->offset is so large that it causes dataptr to
3276 * go beyond the end of the buffer -- or, worse, so large that
3277 * it wraps around! -- we error out.
3278 *
3279 * +) If entry->length would cause the entry to go beyond the
3280 * end of the buffer (or, worse, wrap around to before it),
3281 * *or* if the length is larger than the hdrsize, we error out.
3282 * (An explanation of that: what we're checking for there is
3283 * the small range of values such that offset+length would cause
3284 * it to go beyond endptr, and then wrap around past buffer. We
3285 * care about this because we are passing entry->length down to
3286 * fgetxattr() below, and an erroneously large value could cause
3287 * problems there. By making sure that it's less than hdrsize,
3288 * which has already been sanity-checked above, we're safe.
3289 * That may mean that the check against < buffer is unnecessary.)
3290 */
3291 if ((void*)entry >= endptr || (void*)entry < buffer) {
3292 if (COPYFILE_VERBOSE & s->flags)
3293 copyfile_warn("Incomplete or corrupt attribute entry");
3294 error = -1;
3295 s->err = EINVAL;
3296 goto exit;
3297 }
3298
3299 if (((char*)entry + sizeof(*entry)) > (char*)endptr) {
3300 if (COPYFILE_VERBOSE & s->flags)
3301 copyfile_warn("Incomplete or corrupt attribute entry");
3302 error = -1;
3303 s->err = EINVAL;
3304 goto exit;
3305 }
3306
3307 /*
3308 * Endian swap the entry we're looking at. Previously
3309 * we did this swap as part of swap_attrhdr, but that
3310 * allowed a maliciously constructed file to overrun
3311 * our allocation. Instead do the swap after we've verified
3312 * the entry struct is within the buffer's range.
3313 */
3314 swap_attrhdr_entry(entry);
3315
3316 if (entry->namelen < 2) {
3317 if (COPYFILE_VERBOSE & s->flags)
3318 copyfile_warn("Corrupt attribute entry (only %d bytes)", entry->namelen);
3319 error = -1;
3320 s->err = EINVAL;
3321 goto exit;
3322 }
3323
3324 if (entry->namelen > XATTR_MAXNAMELEN + 1) {
3325 if (COPYFILE_VERBOSE & s->flags)
3326 copyfile_warn("Corrupt attribute entry (name length is %d bytes)", entry->namelen);
3327 error = -1;
3328 s->err = EINVAL;
3329 goto exit;
3330 }
3331
3332 if ((void*)(entry->name + entry->namelen) > endptr) {
3333 if (COPYFILE_VERBOSE & s->flags)
3334 copyfile_warn("Incomplete or corrupt attribute entry");
3335 error = -1;
3336 s->err = EINVAL;
3337 goto exit;
3338 }
3339
3340 /* Because namelen includes the NUL, we check one byte back */
3341 if (entry->name[entry->namelen-1] != 0) {
3342 if (COPYFILE_VERBOSE & s->flags)
3343 copyfile_warn("Corrupt attribute entry (name is not NUL-terminated)");
3344 error = -1;
3345 s->err = EINVAL;
3346 goto exit;
3347 }
b3aa0442 3348
1f4df596
A
3349 copyfile_debug(3, "extracting \"%s\" (%d bytes) at offset %u",
3350 entry->name, entry->length, entry->offset);
b3aa0442 3351
11f767b3 3352#if 0
1f4df596 3353 dataptr = (char *)attrhdr + entry->offset;
b3aa0442 3354
1f4df596
A
3355 if (dataptr > endptr || dataptr < buffer) {
3356 copyfile_debug(1, "Entry %d overflows: offset = %u", i, entry->offset);
3357 error = -1;
3358 s->err = EINVAL; /* Invalid buffer */
3359 goto exit;
3360 }
11f767b3 3361
1f4df596
A
3362 if (((char*)dataptr + entry->length) > (char*)endptr ||
3363 (((char*)dataptr + entry->length) < (char*)buffer) ||
3364 (entry->length > (size_t)hdrsize)) {
3365 if (COPYFILE_VERBOSE & s->flags)
3366 copyfile_warn("Incomplete or corrupt attribute entry");
3367 copyfile_debug(1, "Entry %d length overflows: offset = %u, length = %u",
3368 i, entry->offset, entry->length);
3369 error = -1;
3370 s->err = EINVAL; /* Invalid buffer */
3371 goto exit;
3372 }
b3aa0442 3373
11f767b3 3374#else
1f4df596
A
3375 dataptr = malloc(entry->length);
3376 if (dataptr == NULL) {
3377 copyfile_debug(1, "no memory for %u bytes\n", entry->length);
3378 error = -1;
3379 s->err = ENOMEM;
3380 goto exit;
3381 }
3382 if (pread(s->src_fd, dataptr, entry->length, entry->offset) != (ssize_t)entry->length) {
3383 copyfile_debug(1, "failed to read %u bytes at offset %u\n", entry->length, entry->offset);
3384 error = -1;
3385 s->err = EINVAL;
3386 goto exit;
3387 }
3388#endif
b3aa0442 3389
1f4df596 3390 if (strcmp((char*)entry->name, XATTR_QUARANTINE_NAME) == 0)
b3aa0442 3391 {
1f4df596
A
3392 qtn_file_t tqinfo = NULL;
3393
3394 if (s->qinfo == NULL)
3395 {
3396 tqinfo = qtn_file_alloc();
3397 if (tqinfo)
3398 {
3399 int x;
3400 if ((x = qtn_file_init_with_data(tqinfo, dataptr, entry->length)) != 0)
3401 {
3402 copyfile_warn("qtn_file_init_with_data failed: %s", qtn_error(x));
3403 qtn_file_free(tqinfo);
3404 tqinfo = NULL;
3405 }
3406 }
3407 }
3408 else
3409 {
3410 tqinfo = s->qinfo;
3411 }
3412 if (tqinfo)
3413 {
3414 int x;
3415 x = qtn_file_apply_to_fd(tqinfo, s->dst_fd);
3416 if (x != 0) {
3417 copyfile_warn("qtn_file_apply_to_fd failed: %s", qtn_error(x));
3418 if (s->statuscb) {
3419 int rv;
3420 s->xattr_name = (char*)XATTR_QUARANTINE_NAME;
3421 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
3422 s->xattr_name = NULL;
3423 if (rv == COPYFILE_QUIT) {
3424 error = s->err = x < 0 ? ENOTSUP : errno;
3425 goto exit;
3426 }
3427 } else {
3428 error = s->err = x < 0 ? ENOTSUP : errno;
3429 goto exit;
3430 }
3431 }
3432 }
3433 if (tqinfo && !s->qinfo)
3434 {
3435 qtn_file_free(tqinfo);
3436 }
b3aa0442 3437 }
1f4df596
A
3438 /* Look for ACL data */
3439 else if (strcmp((char*)entry->name, XATTR_SECURITY_NAME) == 0)
3440 {
3441 acl_t acl;
3442 struct stat sb;
3443 int retry = 1;
3444 char *tcp = dataptr;
3445
3446 if (entry->length == 0) {
3447 /* Not sure how we got here, but we had one case
3448 * where it was 0. In a normal EA, we can have a 0-byte
3449 * payload. That means nothing in this case, so we'll
3450 * simply skip the EA.
3451 */
3452 error = 0;
3453 goto acl_done;
3454 }
3455 /*
3456 * acl_from_text() requires a NUL-terminated string. The ACL EA,
3457 * however, may not be NUL-terminated. So in that case, we need to
3458 * copy it to a +1 sized buffer, to ensure it's got a terminated string.
3459 */
3460 if (tcp[entry->length - 1] != 0) {
3461 char *tmpstr = malloc(entry->length + 1);
3462 if (tmpstr == NULL) {
3463 error = -1;
3f81d1c4
A
3464 goto exit;
3465 }
1f4df596
A
3466 strlcpy(tmpstr, tcp, entry->length + 1);
3467 acl = acl_from_text(tmpstr);
3468 free(tmpstr);
3f81d1c4 3469 } else {
1f4df596 3470 acl = acl_from_text(tcp);
3f81d1c4 3471 }
b3aa0442 3472
1f4df596
A
3473 if (acl != NULL)
3474 {
3475 filesec_t fsec_tmp;
b3aa0442 3476
1f4df596
A
3477 if ((fsec_tmp = filesec_init()) == NULL)
3478 error = -1;
3479 else if((error = fstatx_np(s->dst_fd, &sb, fsec_tmp)) < 0)
3480 error = -1;
3481 else if (filesec_set_property(fsec_tmp, FILESEC_ACL, &acl) < 0)
3482 error = -1;
3483 else {
3484 while (fchmodx_np(s->dst_fd, fsec_tmp) < 0)
3485 {
b3aa0442 3486 if (errno == ENOTSUP)
1f4df596
A
3487 {
3488 if (retry && !copyfile_unset_acl(s))
3489 {
3490 retry = 0;
3491 continue;
3492 }
3493 }
b3aa0442
A
3494 copyfile_warn("setting security information");
3495 error = -1;
3496 break;
1f4df596
A
3497 }
3498 }
3499 acl_free(acl);
3500 filesec_free(fsec_tmp);
3501
3502 acl_done:
3503 if (error == -1)
3504 goto exit;
3505 }
3506 }
3507 /* And, finally, everything else */
3508 else
3509 {
3510 if (s->copyIntent ||
3511 xattr_preserve_for_intent((char*)entry->name, s->copyIntent) == 1) {
3512 if (s->statuscb) {
3513 int rv;
3514 s->xattr_name = strdup((char*)entry->name);
3515 s->totalCopied = 0;
3516 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
3517 if (s->xattr_name) {
3518 free(s->xattr_name);
3519 s->xattr_name = NULL;
3520 }
3521 if (rv == COPYFILE_QUIT) {
3522 s->err = ECANCELED;
3523 error = -1;
3524 goto exit;
3525 }
3526 }
3527 if (fsetxattr(s->dst_fd, (char *)entry->name, dataptr, entry->length, 0, 0) == -1) {
3528 if (COPYFILE_VERBOSE & s->flags)
3529 copyfile_warn("error %d setting attribute %s", errno, entry->name);
3530 if (s->statuscb) {
3531 int rv;
3532
3533 s->xattr_name = strdup((char*)entry->name);
3534 rv = (s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
3535 if (s->xattr_name) {
3536 free(s->xattr_name);
3537 s->xattr_name = NULL;
3538 }
3539 if (rv == COPYFILE_QUIT) {
3540 error = -1;
3541 goto exit;
3542 }
3543 } else {
3544 error = -1;
3545 goto exit;
3546 }
3547 } else if (s->statuscb) {
3548 int rv;
3549 s->xattr_name = strdup((char*)entry->name);
3550 s->totalCopied = entry->length;
3551 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
3552 if (s->xattr_name) {
3553 free(s->xattr_name);
3554 s->xattr_name = NULL;
3555 }
3556 if (rv == COPYFILE_QUIT) {
3557 error = -1;
3558 s->err = ECANCELED;
3559 goto exit;
3560 }
3561 }
3562 }
3563 }
3564 if (dataptr) {
3565 free(dataptr);
3566 dataptr = NULL;
b3aa0442 3567 }
1f4df596
A
3568 entry = ATTR_NEXT(entry);
3569 }
3570 }
b3aa0442 3571
1f4df596
A
3572 /*
3573 * Extract the Finder Info.
3574 */
3575 if (adhdr->entries[0].offset > (hdrsize - sizeof(emptyfinfo))) {
3576 error = -1;
3577 goto exit;
3578 }
3579
3580 if (bcmp((u_int8_t*)buffer + adhdr->entries[0].offset, emptyfinfo, sizeof(emptyfinfo)) != 0)
3581 {
3582 uint16_t *fFlags;
3583 uint8_t *newFinfo;
3584 enum { kFinderInvisibleMask = 1 << 14 };
3585
3586 newFinfo = (u_int8_t*)buffer + adhdr->entries[0].offset;
3587 fFlags = (uint16_t*)&newFinfo[8];
3588 copyfile_debug(3, " extracting \"%s\" (32 bytes)", XATTR_FINDERINFO_NAME);
3589 if (s->statuscb) {
3590 int rv;
3591 s->xattr_name = (char*)XATTR_FINDERINFO_NAME;
3592 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
3593 s->xattr_name = NULL;
3594 if (rv == COPYFILE_QUIT) {
3595 error = -1;
3596 s->err = ECANCELED;
3597 goto exit;
3598 } else if (rv == COPYFILE_SKIP) {
3599 goto skip_fi;
3600 }
b3aa0442 3601 }
1f4df596
A
3602 error = fsetxattr(s->dst_fd, XATTR_FINDERINFO_NAME, (u_int8_t*)buffer + adhdr->entries[0].offset, sizeof(emptyfinfo), 0, 0);
3603 if (error) {
3f81d1c4
A
3604 if (s->statuscb) {
3605 int rv;
1f4df596
A
3606 s->xattr_name = (char *)XATTR_FINDERINFO_NAME;
3607 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
3608 s->xattr_name = NULL;
3f81d1c4
A
3609 if (rv == COPYFILE_QUIT) {
3610 error = -1;
1f4df596 3611 s->err = ECANCELED;
3f81d1c4
A
3612 goto exit;
3613 }
3f81d1c4 3614 }
3f81d1c4 3615 goto exit;
1f4df596 3616 } else if (s->statuscb) {
3f81d1c4 3617 int rv;
11f767b3 3618 s->xattr_name = (char *)XATTR_FINDERINFO_NAME;
1f4df596 3619 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
3f81d1c4
A
3620 s->xattr_name = NULL;
3621 if (rv == COPYFILE_QUIT) {
3622 error = -1;
3623 s->err = ECANCELED;
3624 goto exit;
3625 }
3626 }
1f4df596
A
3627 if (SWAP16(*fFlags) & kFinderInvisibleMask)
3628 s->internal_flags |= cfMakeFileInvisible;
3f81d1c4 3629 }
3f81d1c4 3630skip_fi:
b3aa0442 3631
1f4df596
A
3632 /*
3633 * Extract the Resource Fork.
3634 */
3635 if (adhdr->entries[1].type == AD_RESOURCE &&
3636 adhdr->entries[1].length > 0)
3637 {
3638 void * rsrcforkdata = NULL;
3639 size_t length;
3640 off_t offset;
3641 struct stat sb;
3642 struct timeval tval[2];
b3aa0442 3643
1f4df596
A
3644 length = adhdr->entries[1].length;
3645 offset = adhdr->entries[1].offset;
3646 rsrcforkdata = malloc(length);
b3aa0442 3647
1f4df596
A
3648 if (rsrcforkdata == NULL) {
3649 copyfile_debug(1, "could not allocate %zu bytes for rsrcforkdata",
3650 length);
3651 error = -1;
3652 goto bad;
3653 }
b3aa0442 3654
1f4df596
A
3655 if (fstat(s->dst_fd, &sb) < 0)
3656 {
3657 copyfile_debug(1, "couldn't stat destination file");
3f81d1c4 3658 error = -1;
3f81d1c4
A
3659 goto bad;
3660 }
1f4df596
A
3661
3662 bytes = pread(s->src_fd, rsrcforkdata, length, offset);
3663 if (bytes < (ssize_t)length)
3664 {
3665 if (bytes == -1)
3666 {
3667 copyfile_debug(1, "couldn't read resource fork");
3668 }
3669 else
3670 {
3671 copyfile_debug(1,
3672 "couldn't read resource fork (only read %d bytes of %d)",
3673 (int)bytes, (int)length);
3674 }
3675 error = -1;
3f81d1c4
A
3676 goto bad;
3677 }
1f4df596
A
3678 if (s->statuscb) {
3679 int rv;
3680 s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME;
3681 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
3682 s->xattr_name = NULL;
3683 if (rv == COPYFILE_QUIT) {
3684 error = -1;
3685 s->err = ECANCELED;
3686 if (rsrcforkdata)
3687 free(rsrcforkdata);
3688 goto exit;
3689 } else if (rv == COPYFILE_SKIP) {
3690 goto bad;
3691 }
3692 }
3693 error = fsetxattr(s->dst_fd, XATTR_RESOURCEFORK_NAME, rsrcforkdata, bytes, 0, 0);
3694 if (error)
3695 {
3696 /*
3697 * For filesystems that do not natively support named attributes,
3698 * the kernel creates an AppleDouble file that -- for compatabilty
3699 * reasons -- has a resource fork containing nothing but a rsrcfork_header_t
3700 * structure that says there are no resources. So, if fsetxattr has
3701 * failed, and the resource fork is that empty structure, *and* the
3702 * target file is a directory, then we do nothing with it.
3703 */
3704 if ((bytes == sizeof(rsrcfork_header_t)) &&
3705 ((sb.st_mode & S_IFMT) == S_IFDIR) &&
3706 (memcmp(rsrcforkdata, &empty_rsrcfork_header, bytes) == 0)) {
3707 copyfile_debug(2, "not setting empty resource fork on directory");
3708 error = errno = 0;
3709 goto bad;
3710 }
3711 if (s->statuscb) {
3712 int rv;
3713 s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME;
3714 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
3715 s->xattr_name = NULL;
3716 if (rv == COPYFILE_CONTINUE) {
3717 error = errno = 0;
3718 goto bad;
3719 }
3720 }
3721 copyfile_debug(1, "error %d setting resource fork attribute", error);
3f81d1c4 3722 error = -1;
1f4df596
A
3723 goto bad;
3724 } else if (s->statuscb) {
3725 int rv;
3726 s->xattr_name = (char *)XATTR_RESOURCEFORK_NAME;
3727 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
3728 s->xattr_name = NULL;
3729 if (rv == COPYFILE_QUIT) {
3730 error = -1;
3731 s->err = ECANCELED;
3732 if (rsrcforkdata)
3733 free(rsrcforkdata);
3734 goto exit;
3735 }
3736 }
3737 copyfile_debug(3, "extracting \"%s\" (%d bytes)",
3738 XATTR_RESOURCEFORK_NAME, (int)length);
3739
3740 if (!(s->flags & COPYFILE_STAT))
3741 {
3742 tval[0].tv_sec = sb.st_atime;
3743 tval[1].tv_sec = sb.st_mtime;
3744 tval[0].tv_usec = tval[1].tv_usec = 0;
3745
3746 if (futimes(s->dst_fd, tval))
3747 copyfile_warn("%s: set times", s->dst ? s->dst : "(null dst)");
3f81d1c4 3748 }
1f4df596
A
3749 bad:
3750 if (rsrcforkdata)
3751 free(rsrcforkdata);
b3aa0442 3752 }
b3aa0442 3753
1f4df596 3754 if (COPYFILE_STAT & s->flags)
b3aa0442 3755 {
1f4df596
A
3756 error = copyfile_stat(s);
3757 }
b3aa0442 3758exit:
1f4df596
A
3759 if (buffer) free(buffer);
3760 if (dataptr) free(dataptr);
3761 return error;
b3aa0442
A
3762}
3763
3764static int copyfile_pack_quarantine(copyfile_state_t s, void **buf, ssize_t *len)
3765{
1f4df596
A
3766 int ret = 0;
3767 char qbuf[QTN_SERIALIZED_DATA_MAX];
3768 size_t qlen = sizeof(qbuf);
b3aa0442 3769
1f4df596
A
3770 if (s->qinfo == NULL)
3771 {
3772 ret = -1;
3773 goto done;
3774 }
b3aa0442 3775
1f4df596
A
3776 if (qtn_file_to_data(s->qinfo, qbuf, &qlen) != 0)
3777 {
3778 ret = -1;
3779 goto done;
3780 }
3781
3782 *buf = malloc(qlen);
3783 if (*buf)
3784 {
3785 memcpy(*buf, qbuf, qlen);
3786 *len = qlen;
3787 }
b3aa0442 3788done:
1f4df596 3789 return ret;
b3aa0442
A
3790}
3791
3792static int copyfile_pack_acl(copyfile_state_t s, void **buf, ssize_t *len)
3793{
1f4df596
A
3794 int ret = 0;
3795 acl_t acl = NULL;
3796 char *acl_text;
b3aa0442 3797
1f4df596 3798 if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) < 0)
b3aa0442 3799 {
1f4df596
A
3800 if (errno != ENOENT)
3801 {
3802 ret = -1;
3803 if (COPYFILE_VERBOSE & s->flags)
3804 copyfile_warn("getting acl");
3805 }
3806 *len = 0;
3807 goto exit;
b3aa0442 3808 }
b3aa0442 3809
1f4df596
A
3810 if ((acl_text = acl_to_text(acl, len)) != NULL)
3811 {
3812 /*
3813 * acl_to_text() doesn't include the NUL at the endo
3814 * in it's count (*len). It does, however, promise to
3815 * return a valid C string, so we need to up the count
3816 * by 1.
3817 */
3818 *len = *len + 1;
3819 *buf = malloc(*len);
3820 if (*buf)
3821 memcpy(*buf, acl_text, *len);
3822 else
3823 *len = 0;
3824 acl_free(acl_text);
3825 }
3826 copyfile_debug(2, "copied acl (%ld) %p", *len, *buf);
b3aa0442 3827exit:
1f4df596
A
3828 if (acl)
3829 acl_free(acl);
3830 return ret;
b3aa0442
A
3831}
3832
3833static int copyfile_pack_rsrcfork(copyfile_state_t s, attr_header_t *filehdr)
3834{
1f4df596
A
3835 ssize_t datasize;
3836 char *databuf = NULL;
3837 int ret = 0;
b3aa0442 3838
1f4df596
A
3839 /*
3840 * XXX
3841 * do COPYFILE_COPY_XATTR here; no need to
3842 * the work if we want to skip.
3843 */
3f81d1c4 3844
1f4df596
A
3845 if (s->statuscb)
3846 {
3847 int rv;
3f81d1c4 3848
1f4df596 3849 s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME;
3f81d1c4 3850
1f4df596
A
3851 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
3852 s->xattr_name = NULL;
3853 if (rv == COPYFILE_SKIP) {
3854 ret = 0;
3855 goto done;
3856 }
3857 if (rv == COPYFILE_QUIT) {
3858 ret = -1;
3859 s->err = ECANCELED;
3860 goto done;
3861 }
3f81d1c4 3862 }
1f4df596
A
3863 /* Get the resource fork size */
3864 if ((datasize = fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, NULL, 0, 0, 0)) < 0)
3865 {
3866 if (COPYFILE_VERBOSE & s->flags)
3867 copyfile_warn("skipping attr \"%s\" due to error %d", XATTR_RESOURCEFORK_NAME, errno);
3868 return -1;
3869 }
3870
3871 if (datasize > INT_MAX) {
3872 s->err = EINVAL;
3f81d1c4 3873 ret = -1;
3f81d1c4
A
3874 goto done;
3875 }
b3aa0442 3876
1f4df596
A
3877 if (s->statuscb) {
3878 int rv;
3879 s->xattr_name = (char*)XATTR_RESOURCEFORK_NAME;
b3aa0442 3880
1f4df596
A
3881 s->totalCopied = 0;
3882 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
3883 s->xattr_name = NULL;
3884 if (rv == COPYFILE_QUIT) {
3885 s->err = ECANCELED;
3886 ret = -1;
3887 goto done;
3888 }
3889 }
3890 if ((databuf = malloc(datasize)) == NULL)
3891 {
3892 copyfile_warn("malloc");
3893 ret = -1;
3894 goto done;
3895 }
3f81d1c4 3896
1f4df596
A
3897 if (fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, databuf, datasize, 0, 0) != datasize)
3898 {
3899 if (COPYFILE_VERBOSE & s->flags)
3900 copyfile_warn("couldn't read entire resource fork");
3f81d1c4
A
3901 ret = -1;
3902 goto done;
3903 }
b3aa0442 3904
1f4df596
A
3905 /* Write the resource fork to disk. */
3906 if (pwrite(s->dst_fd, databuf, datasize, filehdr->appledouble.entries[1].offset) != datasize)
3907 {
3908 if (COPYFILE_VERBOSE & s->flags)
3909 copyfile_warn("couldn't write resource fork");
3f81d1c4 3910 }
1f4df596
A
3911 if (s->statuscb)
3912 {
3913 int rv;
3914 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
3915 if (rv == COPYFILE_QUIT) {
3916 ret = -1;
3917 goto done;
3918 }
3919 }
3920 copyfile_debug(3, "copied %zd bytes of \"%s\" data @ offset 0x%08x",
3921 datasize, XATTR_RESOURCEFORK_NAME, filehdr->appledouble.entries[1].offset);
3922 filehdr->appledouble.entries[1].length = (u_int32_t)datasize;
b3aa0442
A
3923
3924done:
1f4df596
A
3925 if (ret == -1 && s->statuscb)
3926 {
3927 int rv;
3928 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
3929 if (rv == COPYFILE_CONTINUE)
3930 ret = 0;
3931 }
3932 if (s->xattr_name) {
3933 s->xattr_name = NULL;
3934 }
3935 if (databuf)
3936 free(databuf);
b3aa0442 3937
1f4df596
A
3938 /*
3939 * XXX
3940 * Do status callback here
3941 * If ret == -1, then error callback
3942 */
3943 return ret;
b3aa0442
A
3944}
3945
3946/*
3947 * The opposite of copyfile_unpack(), obviously.
3948 */
3949static int copyfile_pack(copyfile_state_t s)
3950{
1f4df596
A
3951 char *attrnamebuf = NULL, *endnamebuf;
3952 void *databuf = NULL;
3953 attr_header_t *filehdr, *endfilehdr;
3954 attr_entry_t *entry;
3955 ssize_t listsize = 0;
3956 char *nameptr;
3957 size_t namelen;
3958 size_t entrylen;
3959 ssize_t datasize;
3960 size_t offset = 0;
3961 int hasrsrcfork = 0;
3962 int error = 0;
3963 int seenq = 0; // Have we seen any quarantine info already?
3964
3965 filehdr = (attr_header_t *) calloc(1, ATTR_MAX_HDR_SIZE);
3966
3967 if (filehdr == NULL) {
3968 error = -1;
3969 goto exit;
3970 } else {
3971 endfilehdr = (attr_header_t*)(((char*)filehdr) + ATTR_MAX_HDR_SIZE);
b3aa0442
A
3972 }
3973
1f4df596
A
3974 attrnamebuf = calloc(1, ATTR_MAX_HDR_SIZE);
3975 if (attrnamebuf == NULL) {
b3aa0442
A
3976 error = -1;
3977 goto exit;
1f4df596
A
3978 } else {
3979 endnamebuf = ((char*)attrnamebuf) + ATTR_MAX_HDR_SIZE;
b3aa0442
A
3980 }
3981
1f4df596
A
3982 /*
3983 * Fill in the Apple Double Header defaults.
3984 */
3985 filehdr->appledouble.magic = ADH_MAGIC;
3986 filehdr->appledouble.version = ADH_VERSION;
3987 filehdr->appledouble.numEntries = 2;
3988 filehdr->appledouble.entries[0].type = AD_FINDERINFO;
3989 filehdr->appledouble.entries[0].offset = (u_int32_t)offsetof(apple_double_header_t, finfo);
3990 filehdr->appledouble.entries[0].length = FINDERINFOSIZE;
3991 filehdr->appledouble.entries[1].type = AD_RESOURCE;
3992 filehdr->appledouble.entries[1].offset = (u_int32_t)offsetof(apple_double_header_t, pad);
3993 filehdr->appledouble.entries[1].length = 0;
3994 bcopy(ADH_MACOSX, filehdr->appledouble.filler, sizeof(filehdr->appledouble.filler));
3995
3996 /*
3997 * Fill in the initial Attribute Header.
3998 */
3999 filehdr->magic = ATTR_HDR_MAGIC;
4000 filehdr->debug_tag = 0;
4001 filehdr->data_start = (u_int32_t)sizeof(attr_header_t);
4002
4003 /*
4004 * Collect the attribute names.
4005 */
4006 entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t));
3f81d1c4 4007
1f4df596
A
4008 /*
4009 * Test if there are acls to copy
4010 */
4011 if (COPYFILE_ACL & s->flags)
b3aa0442 4012 {
1f4df596
A
4013 acl_t temp_acl = NULL;
4014 if (filesec_get_property(s->fsec, FILESEC_ACL, &temp_acl) < 0)
4015 {
4016 copyfile_debug(2, "no acl entries found (errno = %d)", errno);
4017 } else
4018 {
4019 offset = strlen(XATTR_SECURITY_NAME) + 1;
4020 strcpy(attrnamebuf, XATTR_SECURITY_NAME);
4021 endnamebuf = attrnamebuf + offset;
3f81d1c4 4022 }
1f4df596
A
4023 if (temp_acl)
4024 acl_free(temp_acl);
b3aa0442 4025 }
1f4df596
A
4026
4027 if (COPYFILE_XATTR & s->flags)
b3aa0442 4028 {
1f4df596
A
4029 ssize_t left = ATTR_MAX_HDR_SIZE - offset;
4030 if ((listsize = flistxattr(s->src_fd, attrnamebuf + offset, left, 0)) <= 0)
3f81d1c4 4031 {
1f4df596 4032 copyfile_debug(2, "no extended attributes found (%d)", errno);
3f81d1c4 4033 }
1f4df596 4034 if (listsize > left)
3f81d1c4 4035 {
1f4df596
A
4036 copyfile_debug(1, "extended attribute list too long");
4037 listsize = left;
3f81d1c4 4038 }
1f4df596
A
4039
4040 endnamebuf = attrnamebuf + offset + (listsize > 0 ? listsize : 0);
4041 if (endnamebuf > (attrnamebuf + ATTR_MAX_HDR_SIZE)) {
4042 error = -1;
4043 goto exit;
3f81d1c4 4044 }
1f4df596
A
4045
4046 if (listsize > 0)
4047 sort_xattrname_list(attrnamebuf, endnamebuf - attrnamebuf);
4048
4049 for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen)
4050 {
4051 namelen = strlen(nameptr) + 1;
4052 /* Skip over FinderInfo or Resource Fork names */
4053 if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0 ||
4054 strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0) {
4055 continue;
4056 }
4057 if (strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0) {
4058 seenq = 1;
4059 }
4060
4061 /* The system should prevent this from happening, but... */
4062 if (namelen > XATTR_MAXNAMELEN + 1) {
4063 namelen = XATTR_MAXNAMELEN + 1;
4064 }
4065 if (s->copyIntent &&
4066 xattr_preserve_for_intent(nameptr, s->copyIntent) == 0) {
4067 // Skip it
4068 size_t amt = endnamebuf - (nameptr + namelen);
4069 memmove(nameptr, nameptr + namelen, amt);
4070 endnamebuf -= namelen;
4071 /* Set namelen to 0 so continue doesn't miss names */
4072 namelen = 0;
4073 continue;
4074 }
4075
4076 if (s->statuscb) {
4077 int rv;
4078 char eaname[namelen];
4079 bcopy(nameptr, eaname, namelen);
4080 eaname[namelen - 1] = 0; // Just to be sure!
4081 s->xattr_name = eaname;
4082 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
3f81d1c4 4083 s->xattr_name = NULL;
1f4df596
A
4084 if (rv == COPYFILE_QUIT) {
4085 error = -1;
4086 s->err = ECANCELED;
4087 goto exit;
4088 } else if (rv == COPYFILE_SKIP) {
4089 size_t amt = endnamebuf - (nameptr + namelen);
4090 memmove(nameptr, nameptr + namelen, amt);
4091 endnamebuf -= namelen;
4092 /* Set namelen to 0 so continue doesn't miss names */
4093 namelen = 0;
4094 continue;
4095 }
3f81d1c4 4096 }
1f4df596
A
4097 entry->namelen = namelen;
4098 entry->flags = 0;
4099 if (nameptr + namelen > endnamebuf) {
3f81d1c4
A
4100 error = -1;
4101 goto exit;
4102 }
1f4df596
A
4103
4104 bcopy(nameptr, &entry->name[0], namelen);
4105 copyfile_debug(2, "copied name [%s]", entry->name);
4106
4107 entrylen = ATTR_ENTRY_LENGTH(namelen);
4108 entry = (attr_entry_t *)(((char *)entry) + entrylen);
4109
4110 if ((void*)entry >= (void*)endfilehdr) {
3f81d1c4
A
4111 error = -1;
4112 goto exit;
4113 }
1f4df596
A
4114
4115 /* Update the attributes header. */
4116 filehdr->num_attrs++;
4117 filehdr->data_start += (u_int32_t)entrylen;
4118 }
b3aa0442 4119 }
1f4df596
A
4120
4121 /*
4122 * If we have any quarantine data, we always pack it.
4123 * But if we've already got it in the EA list, don't put it in again.
4124 */
4125 if (s->qinfo && !seenq)
b3aa0442 4126 {
1f4df596
A
4127 ssize_t left = ATTR_MAX_HDR_SIZE - offset;
4128 /* strlcpy returns number of bytes copied, but we need offset to point to the next byte */
4129 offset += strlcpy(attrnamebuf + offset, XATTR_QUARANTINE_NAME, left) + 1;
4130 }
4131
4132 seenq = 0;
4133 /*
4134 * Collect the attribute data.
4135 */
4136 entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t));
4137
4138 for (nameptr = attrnamebuf; nameptr < endnamebuf; nameptr += namelen + 1)
b3aa0442 4139 {
1f4df596
A
4140 namelen = strlen(nameptr);
4141
4142 if (strcmp(nameptr, XATTR_SECURITY_NAME) == 0)
4143 copyfile_pack_acl(s, &databuf, &datasize);
4144 else if (s->qinfo && strcmp(nameptr, XATTR_QUARANTINE_NAME) == 0)
3f81d1c4 4145 {
1f4df596 4146 copyfile_pack_quarantine(s, &databuf, &datasize);
3f81d1c4 4147 }
1f4df596
A
4148 /* Check for Finder Info. */
4149 else if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0)
3f81d1c4 4150 {
1f4df596
A
4151 if (s->statuscb)
4152 {
4153 int rv;
4154 s->xattr_name = (char*)XATTR_FINDERINFO_NAME;
4155 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_START, s, s->src, s->dst, s->ctx);
4156 s->xattr_name = NULL;
4157 if (rv == COPYFILE_QUIT)
4158 {
4159 s->xattr_name = NULL;
4160 s->err = ECANCELED;
4161 error = -1;
4162 goto exit;
4163 }
4164 else if (rv == COPYFILE_SKIP)
4165 {
4166 s->xattr_name = NULL;
4167 continue;
4168 }
4169 s->totalCopied = 0;
4170 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
4171 s->xattr_name = NULL;
4172 if (rv == COPYFILE_QUIT)
4173 {
4174 s->err = ECANCELED;
4175 error = -1;
4176 goto exit;
4177 }
4178 }
4179 datasize = fgetxattr(s->src_fd, nameptr, (u_int8_t*)filehdr + filehdr->appledouble.entries[0].offset, 32, 0, 0);
4180 if (datasize < 0)
4181 {
4182 if (s->statuscb) {
4183 int rv;
4184 s->xattr_name = strdup(nameptr);
4185 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
4186 if (s->xattr_name) {
4187 free(s->xattr_name);
4188 s->xattr_name = NULL;
4189 }
4190 if (rv == COPYFILE_QUIT) {
4191 error = -1;
4192 goto exit;
4193 }
4194 }
4195 if (COPYFILE_VERBOSE & s->flags)
4196 copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno);
4197 } else if (datasize != 32)
4198 {
4199 if (COPYFILE_VERBOSE & s->flags)
4200 copyfile_warn("unexpected size (%ld) for \"%s\"", datasize, nameptr);
4201 } else
4202 {
4203 if (COPYFILE_VERBOSE & s->flags)
4204 copyfile_warn(" copied 32 bytes of \"%s\" data @ offset 0x%08x",
4205 XATTR_FINDERINFO_NAME, filehdr->appledouble.entries[0].offset);
4206 if (s->statuscb) {
4207 int rv;
4208 s->xattr_name = strdup(nameptr);
4209 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
4210 if (s->xattr_name) {
4211 free(s->xattr_name);
4212 s->xattr_name = NULL;
4213 }
4214 if (rv == COPYFILE_QUIT) {
4215 error = -1;
4216 goto exit;
4217 }
4218 }
4219 }
4220 continue; /* finder info doesn't have an attribute entry */
3f81d1c4 4221 }
1f4df596
A
4222 /* Check for Resource Fork. */
4223 else if (strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0)
4224 {
4225 hasrsrcfork = 1;
4226 continue;
4227 } else
4228 {
4229 /* Just a normal attribute. */
4230 if (s->statuscb)
4231 {
4232 int rv;
4233 s->xattr_name = strdup(nameptr);
4234 s->totalCopied = 0;
4235 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_PROGRESS, s, s->src, s->dst, s->ctx);
4236 if (s->xattr_name) {
4237 free(s->xattr_name);
4238 s->xattr_name = NULL;
4239 }
4240 /*
4241 * Due to the nature of the packed file, we can't skip at this point.
4242 */
4243 if (rv == COPYFILE_QUIT)
4244 {
4245 s->err = ECANCELED;
4246 error = -1;
4247 goto exit;
4248 }
4249 }
4250 datasize = fgetxattr(s->src_fd, nameptr, NULL, 0, 0, 0);
4251 if (datasize == 0)
4252 goto next;
4253 if (datasize < 0)
4254 {
4255 if (COPYFILE_VERBOSE & s->flags)
4256 copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno);
4257 if (s->statuscb)
4258 {
4259 int rv;
4260 s->xattr_name = strdup(nameptr);
4261 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_ERR, s, s->src, s->dst, s->ctx);
4262 if (s->xattr_name) {
4263 free(s->xattr_name);
4264 s->xattr_name = NULL;
4265 }
4266 if (rv == COPYFILE_QUIT)
4267 {
4268 s->err = ECANCELED;
4269 error = -1;
4270 goto exit;
4271 }
4272 }
4273 goto next;
4274 }
4275 if (datasize > XATTR_MAXATTRLEN)
4276 {
4277 if (COPYFILE_VERBOSE & s->flags)
4278 copyfile_warn("skipping attr \"%s\" (too big)", nameptr);
4279 goto next;
4280 }
4281 databuf = malloc(datasize);
4282 if (databuf == NULL) {
4283 error = -1;
4284 continue;
4285 }
4286 datasize = fgetxattr(s->src_fd, nameptr, databuf, datasize, 0, 0);
4287 if (s->statuscb) {
4288 int rv;
4289 s->xattr_name = strdup(nameptr);
4290 rv = (*s->statuscb)(COPYFILE_COPY_XATTR, COPYFILE_FINISH, s, s->src, s->dst, s->ctx);
4291 if (s->xattr_name) {
4292 free(s->xattr_name);
4293 s->xattr_name = NULL;
4294 }
4295 if (rv == COPYFILE_QUIT) {
4296 s->err = ECANCELED;
4297 error = -1;
4298 goto exit;
4299 }
4300 }
3f81d1c4 4301 }
b3aa0442 4302
1f4df596
A
4303 entry->length = (u_int32_t)datasize;
4304 entry->offset = filehdr->data_start + filehdr->data_length;
b3aa0442 4305
1f4df596 4306 filehdr->data_length += (u_int32_t)datasize;
11f767b3 4307#if 0
1f4df596
A
4308 /*
4309 * >>> WARNING <<<
4310 * This assumes that the data is fits in memory (not
4311 * the case when there are lots of attributes or one of
4312 * the attributes is very large.
4313 */
4314 if (entry->offset > ATTR_MAX_SIZE ||
4315 (entry->offset + datasize > ATTR_MAX_SIZE)) {
4316 error = 1;
4317 } else {
4318 bcopy(databuf, (char*)filehdr + entry->offset, datasize);
4319 }
11f767b3 4320#else
1f4df596
A
4321 if (pwrite(s->dst_fd, databuf, datasize, entry->offset) != datasize) {
4322 error = 1;
4323 }
11f767b3 4324#endif
1f4df596 4325 free(databuf);
b3aa0442 4326
1f4df596
A
4327 copyfile_debug(3, "copied %ld bytes of \"%s\" data @ offset 0x%08x", datasize, nameptr, entry->offset);
4328 next:
4329 /* bump to next entry */
4330 entrylen = ATTR_ENTRY_LENGTH(entry->namelen);
4331 entry = (attr_entry_t *)((char *)entry + entrylen);
4332 }
b3aa0442 4333
1f4df596
A
4334 /* Now we know where the resource fork data starts. */
4335 filehdr->appledouble.entries[1].offset = (filehdr->data_start + filehdr->data_length);
b3aa0442 4336
1f4df596
A
4337 /* We also know the size of the "Finder Info entry. */
4338 filehdr->appledouble.entries[0].length =
4339 filehdr->appledouble.entries[1].offset - filehdr->appledouble.entries[0].offset;
4340
4341 filehdr->total_size = filehdr->appledouble.entries[1].offset;
4342
4343 /* Copy Resource Fork. */
4344 if (hasrsrcfork && (error = copyfile_pack_rsrcfork(s, filehdr)))
4345 goto exit;
4346
4347 /* Write the header to disk. */
4348 datasize = filehdr->data_start;
4349
4350 swap_adhdr(&filehdr->appledouble);
4351 swap_attrhdr(filehdr);
4352 swap_attrhdr_entries(filehdr);
4353
4354 if (pwrite(s->dst_fd, filehdr, datasize, 0) != datasize)
4355 {
4356 if (COPYFILE_VERBOSE & s->flags)
4357 copyfile_warn("couldn't write file header");
4358 error = -1;
4359 goto exit;
4360 }
b3aa0442 4361exit:
1f4df596
A
4362 if (filehdr) free(filehdr);
4363 if (attrnamebuf) free(attrnamebuf);
b3aa0442 4364
1f4df596
A
4365 if (error)
4366 return error;
4367 else
4368 return copyfile_stat(s);
b3aa0442 4369}