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