]> git.saurik.com Git - apple/libc.git/blob - darwin/copyfile.c
Libc-391.2.3.tar.gz
[apple/libc.git] / darwin / copyfile.c
1 /*
2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
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/xattr.h>
38 #include <sys/syscall.h>
39 #include <sys/param.h>
40 #include <sys/acl.h>
41 #include <libkern/OSByteOrder.h>
42 #include <membership.h>
43
44 #include <copyfile.h>
45
46 struct _copyfile_state
47 {
48 char *src;
49 char *dst;
50 int src_fd;
51 int dst_fd;
52 struct stat sb;
53 filesec_t fsec;
54 copyfile_flags_t flags;
55 void *stats;
56 uint32_t debug;
57 };
58
59 static int copyfile_open (copyfile_state_t);
60 static int copyfile_close (copyfile_state_t);
61 static int copyfile_data (copyfile_state_t);
62 static int copyfile_stat (copyfile_state_t);
63 static int copyfile_security (copyfile_state_t);
64 static int copyfile_xattr (copyfile_state_t);
65 static int copyfile_pack (copyfile_state_t);
66 static int copyfile_unpack (copyfile_state_t);
67
68 static copyfile_flags_t copyfile_check (copyfile_state_t);
69 static int copyfile_fix_perms(copyfile_state_t, filesec_t *, int);
70
71 #define COPYFILE_DEBUG (1<<31)
72
73 #ifndef _COPYFILE_TEST
74 # define copyfile_warn(str, ...) syslog(LOG_WARNING, str ": %m", ## __VA_ARGS__)
75 # define copyfile_debug(d, str, ...) \
76 if (s && (d <= s->debug)) {\
77 syslog(LOG_DEBUG, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \
78 } else
79 #else
80 #define copyfile_warn(str, ...) \
81 fprintf(stderr, "%s:%d:%s() " str ": %s\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__, (errno) ? strerror(errno) : "")
82 # define copyfile_debug(d, str, ...) \
83 if (s && (d <= s->debug)) {\
84 fprintf(stderr, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \
85 } else
86 #endif
87
88 int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_flags_t flags)
89 {
90 int ret = 0;
91 copyfile_state_t s = state;
92 filesec_t original_fsec;
93 int fix_perms = 0;
94
95 original_fsec = filesec_init();
96 if (s == NULL && (s = copyfile_init()) == NULL)
97 return -1;
98
99 if (src != NULL)
100 {
101 if (s->src_fd != -2 && s->src && !strncmp(src, s->src, MAXPATHLEN))
102 close(s->src_fd);
103 s->src = strdup(src);
104 }
105
106 if (dst != NULL)
107 {
108 if (s->dst_fd != -2 && s->dst && !strncmp(dst, s->dst, MAXPATHLEN))
109 close(s->dst_fd);
110 s->dst = strdup(dst);
111 }
112
113 s->flags = flags;
114
115 if (COPYFILE_DEBUG & s->flags)
116 {
117 char *e;
118 if ((e = getenv("COPYFILE_DEBUG")))
119 {
120 s->debug = strtol(e, NULL, 0);
121 if (s->debug < 1)
122 s->debug = 1;
123 }
124 copyfile_debug(1, "debug value set to: %d\n", s->debug);
125 }
126
127 if (COPYFILE_CHECK & flags)
128 return copyfile_check(s);
129
130 if (copyfile_open(s) < 0)
131 ret = -1;
132 else
133 {
134 if (s->dst_fd == -2 || s->src_fd == -2)
135 return 0;
136
137 if (COPYFILE_PACK & flags)
138 {
139 if (copyfile_pack(s) < 0)
140 {
141 unlink(s->dst);
142 ret = -1;
143 }
144 } else if (COPYFILE_UNPACK & flags)
145 {
146 if (copyfile_unpack(s) < 0)
147 ret = -1;
148 } else
149 {
150 if (COPYFILE_SECURITY & flags)
151 {
152 if (copyfile_security(s) < 0)
153 {
154 copyfile_warn("error processing security information");
155 ret -= 1;
156 }
157 } else if (COPYFILE_UNPACK & flags)
158 {
159 fix_perms = !copyfile_fix_perms(s, &original_fsec, 1);
160 if (copyfile_unpack(s) < 0)
161 ret = -1;
162 } else
163 {
164 if (COPYFILE_SECURITY & flags)
165 {
166 copyfile_warn("error processing stat information");
167 ret -= 1;
168 }
169 }
170 fix_perms = !copyfile_fix_perms(s, &original_fsec, 1);
171
172 if (COPYFILE_XATTR & flags)
173 {
174 if (copyfile_xattr(s) < 0)
175 {
176 copyfile_warn("error processing extended attributes");
177 ret -= 1;
178 }
179 }
180 if (COPYFILE_DATA & flags)
181 {
182 if (copyfile_data(s) < 0)
183 {
184 copyfile_warn("error processing data");
185 ret = -1;
186 if (s->dst && unlink(s->dst))
187 copyfile_warn("%s: remove", s->src);
188 goto exit;
189 }
190 }
191 }
192 }
193 exit:
194 if (fix_perms)
195 copyfile_fix_perms(s, &original_fsec, 0);
196
197 filesec_free(original_fsec);
198
199 if (state == NULL)
200 ret -= copyfile_free(s);
201 return ret;
202 }
203
204 copyfile_state_t copyfile_init(void)
205 {
206 copyfile_state_t s = (copyfile_state_t) calloc(1, sizeof(struct _copyfile_state));
207
208 if (s != NULL)
209 {
210 s->src_fd = -2;
211 s->dst_fd = -2;
212 s->fsec = filesec_init();
213 }
214
215 return s;
216 }
217
218 int copyfile_free(copyfile_state_t s)
219 {
220 if (s != NULL)
221 {
222 if (s->fsec)
223 filesec_free(s->fsec);
224
225 if (s->dst)
226 free(s->dst);
227 if (s->src)
228 free(s->src);
229 if (copyfile_close(s) < 0)
230 {
231 copyfile_warn("error closing files");
232 return -1;
233 }
234 free(s);
235 }
236 return 0;
237 }
238
239 static int copyfile_close(copyfile_state_t s)
240 {
241 if (s->src_fd != -2)
242 close(s->src_fd);
243
244 if (s->dst_fd != -2 && close(s->dst_fd))
245 {
246 copyfile_warn("close on %s", s->dst);
247 return -1;
248 }
249 return 0;
250 }
251
252 static int copyfile_fix_perms(copyfile_state_t s, filesec_t *fsec, int on)
253 {
254 filesec_t tmp_fsec;
255 struct stat sb;
256 mode_t mode;
257 acl_t acl;
258
259 if (on)
260 {
261 if(statx_np(s->dst, &sb, *fsec))
262 goto error;
263
264 tmp_fsec = filesec_dup(*fsec);
265
266 if (!filesec_get_property(tmp_fsec, FILESEC_ACL, &acl))
267 {
268 acl_entry_t entry;
269 acl_permset_t permset;
270 uuid_t qual;
271
272 if (mbr_uid_to_uuid(getuid(), qual) != 0)
273 goto error;
274
275 if (acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY) == -1)
276 goto error;
277 if (acl_get_permset(entry, &permset) == -1)
278 goto error;
279 if (acl_clear_perms(permset) == -1)
280 goto error;
281 if (acl_add_perm(permset, ACL_WRITE_DATA) == -1)
282 goto error;
283 if (acl_add_perm(permset, ACL_WRITE_ATTRIBUTES) == -1)
284 goto error;
285 if (acl_add_perm(permset, ACL_WRITE_EXTATTRIBUTES) == -1)
286 goto error;
287 if (acl_set_tag_type(entry, ACL_EXTENDED_ALLOW) == -1)
288 goto error;
289
290 if(acl_set_permset(entry, permset) == -1)
291 goto error;
292 if(acl_set_qualifier(entry, qual) == -1)
293 goto error;
294
295 if (filesec_set_property(tmp_fsec, FILESEC_ACL, &acl) != 0)
296 goto error;
297 }
298
299 if (filesec_get_property(tmp_fsec, FILESEC_MODE, &mode) == 0)
300 {
301 if (~mode & S_IWUSR)
302 {
303 mode |= S_IWUSR;
304 if (filesec_set_property(tmp_fsec, FILESEC_MODE, &mode) != 0)
305 goto error;
306 }
307 }
308 if (fchmodx_np(s->dst_fd, tmp_fsec) < 0 && errno != ENOTSUP)
309 copyfile_warn("setting security information");
310 filesec_free(tmp_fsec);
311 } else
312 if (fchmodx_np(s->dst_fd, *fsec) < 0 && errno != ENOTSUP)
313 copyfile_warn("setting security information");
314
315 return 0;
316 error:
317 filesec_free(*fsec);
318 return -1;
319 }
320
321
322 static int copyfile_open(copyfile_state_t s)
323 {
324 int oflags = O_EXCL | O_CREAT;
325
326 if (s->src && s->src_fd == -2)
327 {
328 if ((COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np)
329 (s->src, &s->sb, s->fsec))
330 {
331 copyfile_warn("stat on %s", s->src);
332 return -1;
333 }
334 if ((s->src_fd = open(s->src, O_RDONLY, 0)) < 0)
335 {
336 copyfile_warn("open on %s", s->src);
337 return -1;
338 }
339 }
340
341 if (s->dst && s->dst_fd == -2)
342 {
343 if (COPYFILE_DATA & s->flags || COPYFILE_PACK & s->flags)
344 oflags |= O_WRONLY;
345
346 if (COPYFILE_ACL & ~s->flags)
347 {
348 if (filesec_set_property(s->fsec, FILESEC_ACL, NULL) == -1)
349 {
350 copyfile_debug(1, "unsetting acl attribute on %s", s->dst);
351 }
352 }
353 if (COPYFILE_STAT & ~s->flags)
354 {
355 if (filesec_set_property(s->fsec, FILESEC_MODE, NULL) == -1)
356 {
357 copyfile_debug(1, "unsetting mode attribute on %s", s->dst);
358 }
359 }
360 if (COPYFILE_PACK & s->flags)
361 {
362 mode_t m = S_IRUSR;
363 if (filesec_set_property(s->fsec, FILESEC_MODE, &m) == -1)
364 {
365 mode_t m = S_IRUSR | S_IWUSR;
366 if (filesec_set_property(s->fsec, FILESEC_MODE, &m) == -1)
367 {
368 copyfile_debug(1, "setting mode attribute on %s", s->dst);
369 }
370 }
371 if (filesec_set_property(s->fsec, FILESEC_OWNER, NULL) == -1)
372 {
373 copyfile_debug(1, "unsetting uid attribute on %s", s->dst);
374 }
375 if (filesec_set_property(s->fsec, FILESEC_UUID, NULL) == -1)
376 {
377 copyfile_debug(1, "unsetting uuid attribute on %s", s->dst);
378 }
379 if (filesec_set_property(s->fsec, FILESEC_GROUP, NULL) == -1)
380 {
381 copyfile_debug(1, "unsetting gid attribute on %s", s->dst);
382 }
383 }
384
385 if (COPYFILE_UNLINK & s->flags && unlink(s->dst) < 0)
386 {
387 copyfile_warn("%s: remove", s->dst);
388 return -1;
389 }
390
391 while((s->dst_fd = openx_np(s->dst, oflags, s->fsec)) < 0)
392 {
393 if (EEXIST == errno)
394 {
395 oflags = oflags & ~O_CREAT;
396 continue;
397 }
398 copyfile_warn("open on %s", s->dst);
399 return -1;
400 }
401 }
402 return 0;
403 }
404
405 static copyfile_flags_t copyfile_check(copyfile_state_t s)
406 {
407 acl_t acl;
408 copyfile_flags_t ret = 0;
409
410 if (!s->src)
411 return ret;
412
413 // check EAs
414 if (COPYFILE_XATTR & s->flags)
415 if (listxattr(s->src, 0, 0, 0) > 0)
416 ret |= COPYFILE_XATTR;
417
418 if (COPYFILE_ACL & s->flags)
419 {
420 (COPYFILE_NOFOLLOW_SRC & s->flags ? lstatx_np : statx_np)
421 (s->src, &s->sb, s->fsec);
422
423 if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) == 0)
424 ret |= COPYFILE_ACL;
425 }
426
427 return ret;
428 }
429
430 static int copyfile_data(copyfile_state_t s)
431 {
432 unsigned int blen;
433 char *bp;
434 int nread;
435 int ret;
436
437 if ((bp = malloc((size_t)s->sb.st_blksize)) == NULL)
438 {
439 blen = 0;
440 warnx("malloc failed");
441 return -1;
442 }
443 blen = s->sb.st_blksize;
444
445 while ((nread = read(s->src_fd, bp, (size_t)blen)) > 0)
446 {
447 if (write(s->dst_fd, bp, (size_t)nread) != nread)
448 {
449 copyfile_warn("writing to %s", s->dst);
450 return -1;
451 }
452 }
453 if (nread < 0)
454 {
455 copyfile_warn("reading from %s", s->src);
456 ret = -1;
457 }
458
459 free(bp);
460
461 if (ftruncate(s->dst_fd, s->sb.st_size) < 0)
462 ret = -1;
463
464 return ret;
465 }
466
467 static int copyfile_security(copyfile_state_t s)
468 {
469 filesec_t fsec_dst = filesec_init();
470
471 int copied = 0;
472 acl_flagset_t flags;
473 struct stat sb;
474 acl_entry_t entry_src = NULL, entry_dst = NULL;
475 acl_t acl_src, acl_dst;
476 int inited_dst = 0, inited_src = 0, ret = 0;
477
478 if (COPYFILE_ACL & s->flags)
479 {
480 if(fstatx_np(s->dst_fd, &sb, fsec_dst))
481 {
482 goto cleanup;
483 }
484
485 if (filesec_get_property(fsec_dst, FILESEC_ACL, &acl_dst))
486 {
487 if (errno == ENOENT)
488 {
489 acl_dst = acl_init(4);
490 inited_dst = 1;
491 }
492 else
493 {
494 ret = -1;
495 goto cleanup;
496 }
497 }
498
499 if (filesec_get_property(s->fsec, FILESEC_ACL, &acl_src))
500 {
501 if (errno == ENOENT)
502 {
503 if (inited_dst)
504 goto no_acl;
505 acl_dst = acl_init(4);
506 inited_src = 1;
507 }
508 else
509 {
510 ret = -1;
511 goto cleanup;
512 }
513 }
514
515 for (;acl_get_entry(acl_src,
516 entry_src == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY,
517 &entry_src) == 0;)
518 {
519 acl_get_flagset_np(entry_src, &flags);
520 if (!acl_get_flag_np(flags, ACL_ENTRY_INHERITED))
521 {
522 if ((ret = acl_create_entry(&acl_dst, &entry_dst)) == -1)
523 goto cleanup;
524
525 if ((ret = acl_copy_entry(entry_dst, entry_src)) == -1)
526 goto cleanup;
527
528 copyfile_debug(1, "copied acl entry from %s to %s", s->src, s->dst);
529 copied++;
530 }
531 }
532
533 if (!filesec_set_property(s->fsec, FILESEC_ACL, &acl_dst))
534 {
535 copyfile_debug(1, "altered acl");
536 }
537 }
538 no_acl:
539 if (fchmodx_np(s->dst_fd, s->fsec) < 0 && errno != ENOTSUP)
540 copyfile_warn("setting security information: %s", s->dst);
541
542 cleanup:
543 filesec_free(fsec_dst);
544 if (inited_src) acl_free(acl_src);
545 if (inited_dst) acl_free(acl_dst);
546
547 return ret;
548 }
549
550 static int copyfile_stat(copyfile_state_t s)
551 {
552 struct timeval tval[2];
553 /*
554 * NFS doesn't support chflags; ignore errors unless there's reason
555 * to believe we're losing bits. (Note, this still won't be right
556 * if the server supports flags and we were trying to *remove* flags
557 * on a file that we copied, i.e., that we didn't create.)
558 */
559 if (chflags(s->dst, (u_long)s->sb.st_flags))
560 if (errno != EOPNOTSUPP || s->sb.st_flags != 0)
561 copyfile_warn("%s: set flags (was: 0%07o)", s->dst, s->sb.st_flags);
562
563 tval[0].tv_sec = s->sb.st_atime;
564 tval[1].tv_sec = s->sb.st_mtime;
565 tval[0].tv_usec = tval[1].tv_usec = 0;
566 if (utimes(s->dst, tval))
567 copyfile_warn("%s: set times", s->dst);
568 return 0;
569 }
570
571 static int copyfile_xattr(copyfile_state_t s)
572 {
573 char *name;
574 char *namebuf;
575 size_t xa_size;
576 void *xa_dataptr;
577 size_t bufsize = 4096;
578 ssize_t asize;
579 ssize_t nsize;
580 int ret = 0;
581 int flags = 0;
582
583 if (COPYFILE_NOFOLLOW_SRC & s->flags)
584 flags |= XATTR_NOFOLLOW;
585
586 /* delete EAs on destination */
587 if ((nsize = flistxattr(s->dst_fd, 0, 0, 0)) > 0)
588 {
589 if ((namebuf = (char *) malloc(nsize)) == NULL)
590 return -1;
591 else
592 nsize = flistxattr(s->dst_fd, namebuf, nsize, 0);
593
594 if (nsize > 0)
595 for (name = namebuf; name < namebuf + nsize; name += strlen(name) + 1)
596 fremovexattr(s->dst_fd, name,flags);
597
598 free(namebuf);
599 } else if (nsize < 0)
600 {
601 if (errno == ENOTSUP)
602 return 0;
603 else
604 return -1;
605 }
606
607 /* get name list of EAs on source */
608 if ((nsize = flistxattr(s->src_fd, 0, 0, 0)) < 0)
609 {
610 if (errno == ENOTSUP)
611 return 0;
612 else
613 return -1;
614 } else if (nsize == 0)
615 return 0;
616
617 if ((namebuf = (char *) malloc(nsize)) == NULL)
618 return -1;
619 else
620 nsize = flistxattr(s->src_fd, namebuf, nsize, 0);
621
622 if (nsize <= 0)
623 return nsize;
624
625 if ((xa_dataptr = (void *) malloc(bufsize)) == NULL)
626 return -1;
627
628 for (name = namebuf; name < namebuf + nsize; name += strlen(name) + 1)
629 {
630 if ((xa_size = fgetxattr(s->src_fd, name, 0, 0, 0, flags)) < 0)
631 {
632 ret = -1;
633 continue;
634 }
635
636 if (xa_size > bufsize)
637 {
638 bufsize = xa_size;
639 if ((xa_dataptr =
640 (void *) realloc((void *) xa_dataptr, bufsize)) == NULL)
641 {
642 ret = -1;
643 continue;
644 }
645 }
646
647 if ((asize = fgetxattr(s->src_fd, name, xa_dataptr, xa_size, 0, flags)) < 0)
648 {
649 ret = -1;
650 continue;
651 }
652
653 if (xa_size != asize)
654 xa_size = asize;
655
656 if (fsetxattr(s->dst_fd, name, xa_dataptr, xa_size, 0, flags) < 0)
657 {
658 ret = -1;
659 continue;
660 }
661 }
662 free((void *) xa_dataptr);
663 return ret;
664 }
665
666
667 #ifdef _COPYFILE_TEST
668 #define COPYFILE_OPTION(x) { #x, COPYFILE_ ## x },
669
670 struct {char *s; int v;} opts[] = {
671 COPYFILE_OPTION(ACL)
672 COPYFILE_OPTION(STAT)
673 COPYFILE_OPTION(XATTR)
674 COPYFILE_OPTION(DATA)
675 COPYFILE_OPTION(SECURITY)
676 COPYFILE_OPTION(METADATA)
677 COPYFILE_OPTION(ALL)
678 COPYFILE_OPTION(NOFOLLOW_SRC)
679 COPYFILE_OPTION(NOFOLLOW_DST)
680 COPYFILE_OPTION(NOFOLLOW)
681 COPYFILE_OPTION(EXCL)
682 COPYFILE_OPTION(MOVE)
683 COPYFILE_OPTION(UNLINK)
684 COPYFILE_OPTION(PACK)
685 COPYFILE_OPTION(UNPACK)
686 COPYFILE_OPTION(CHECK)
687 COPYFILE_OPTION(VERBOSE)
688 COPYFILE_OPTION(DEBUG)
689 {NULL, 0}
690 };
691
692 int main(int c, char *v[])
693 {
694 int i;
695 int flags = 0;
696
697 if (c < 3)
698 errx(1, "insufficient arguments");
699
700 while(c-- > 3)
701 {
702 for (i = 0; opts[i].s != NULL; ++i)
703 {
704 if (strcasecmp(opts[i].s, v[c]) == 0)
705 {
706 printf("option %d: %s <- %d\n", c, opts[i].s, opts[i].v);
707 flags |= opts[i].v;
708 break;
709 }
710 }
711 }
712
713 return copyfile(v[1], v[2], NULL, flags);
714 }
715 #endif
716 /*
717 * Apple Double Create
718 *
719 * Create an Apple Double "._" file from a file's extented attributes
720 *
721 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
722 */
723
724
725 #define offsetof(type, member) ((size_t)(&((type *)0)->member))
726
727 #define XATTR_MAXATTRLEN (4*1024)
728
729
730 /*
731 Typical "._" AppleDouble Header File layout:
732 ------------------------------------------------------------
733 MAGIC 0x00051607
734 VERSION 0x00020000
735 FILLER 0
736 COUNT 2
737 .-- AD ENTRY[0] Finder Info Entry (must be first)
738 .--+-- AD ENTRY[1] Resource Fork Entry (must be last)
739 | '-> FINDER INFO
740 | ///////////// Fixed Size Data (32 bytes)
741 | EXT ATTR HDR
742 | /////////////
743 | ATTR ENTRY[0] --.
744 | ATTR ENTRY[1] --+--.
745 | ATTR ENTRY[2] --+--+--.
746 | ... | | |
747 | ATTR ENTRY[N] --+--+--+--.
748 | ATTR DATA 0 <-' | | |
749 | //////////// | | |
750 | ATTR DATA 1 <----' | |
751 | ///////////// | |
752 | ATTR DATA 2 <-------' |
753 | ///////////// |
754 | ... |
755 | ATTR DATA N <----------'
756 | /////////////
757 | Attribute Free Space
758 |
759 '----> RESOURCE FORK
760 ///////////// Variable Sized Data
761 /////////////
762 /////////////
763 /////////////
764 /////////////
765 /////////////
766 ...
767 /////////////
768
769 ------------------------------------------------------------
770
771 NOTE: The EXT ATTR HDR, ATTR ENTRY's and ATTR DATA's are
772 stored as part of the Finder Info. The length in the Finder
773 Info AppleDouble entry includes the length of the extended
774 attribute header, attribute entries, and attribute data.
775 */
776
777
778 /*
779 * On Disk Data Structures
780 *
781 * Note: Motorola 68K alignment and big-endian.
782 *
783 * See RFC 1740 for additional information about the AppleDouble file format.
784 *
785 */
786
787 #define ADH_MAGIC 0x00051607
788 #define ADH_VERSION 0x00020000
789 #define ADH_MACOSX "Mac OS X "
790
791 /*
792 * AppleDouble Entry ID's
793 */
794 #define AD_DATA 1 /* Data fork */
795 #define AD_RESOURCE 2 /* Resource fork */
796 #define AD_REALNAME 3 /* FileÕs name on home file system */
797 #define AD_COMMENT 4 /* Standard Mac comment */
798 #define AD_ICONBW 5 /* Mac black & white icon */
799 #define AD_ICONCOLOR 6 /* Mac color icon */
800 #define AD_UNUSED 7 /* Not used */
801 #define AD_FILEDATES 8 /* File dates; create, modify, etc */
802 #define AD_FINDERINFO 9 /* Mac Finder info & extended info */
803 #define AD_MACINFO 10 /* Mac file info, attributes, etc */
804 #define AD_PRODOSINFO 11 /* Pro-DOS file info, attrib., etc */
805 #define AD_MSDOSINFO 12 /* MS-DOS file info, attributes, etc */
806 #define AD_AFPNAME 13 /* Short name on AFP server */
807 #define AD_AFPINFO 14 /* AFP file info, attrib., etc */
808 #define AD_AFPDIRID 15 /* AFP directory ID */
809 #define AD_ATTRIBUTES AD_FINDERINFO
810
811
812 #define ATTR_FILE_PREFIX "._"
813 #define ATTR_HDR_MAGIC 0x41545452 /* 'ATTR' */
814
815 #define ATTR_BUF_SIZE 4096 /* default size of the attr file and how much we'll grow by */
816
817 /* Implementation Limits */
818 #define ATTR_MAX_SIZE (128*1024) /* 128K maximum attribute data size */
819 #define ATTR_MAX_NAME_LEN 128
820 #define ATTR_MAX_HDR_SIZE (65536+18)
821
822 /*
823 * Note: ATTR_MAX_HDR_SIZE is the largest attribute header
824 * size supported (including the attribute entries). All of
825 * the attribute entries must reside within this limit.
826 */
827
828
829 #pragma options align=mac68k
830
831 #define FINDERINFOSIZE 32
832
833 typedef struct apple_double_entry
834 {
835 u_int32_t type; /* entry type: see list, 0 invalid */
836 u_int32_t offset; /* entry data offset from the beginning of the file. */
837 u_int32_t length; /* entry data length in bytes. */
838 } apple_double_entry_t;
839
840
841 typedef struct apple_double_header
842 {
843 u_int32_t magic; /* == ADH_MAGIC */
844 u_int32_t version; /* format version: 2 = 0x00020000 */
845 u_int32_t filler[4];
846 u_int16_t numEntries; /* number of entries which follow */
847 apple_double_entry_t entries[2]; /* 'finfo' & 'rsrc' always exist */
848 u_int8_t finfo[FINDERINFOSIZE]; /* Must start with Finder Info (32 bytes) */
849 u_int8_t pad[2]; /* get better alignment inside attr_header */
850 } apple_double_header_t;
851
852
853 /* Entries are aligned on 4 byte boundaries */
854 typedef struct attr_entry
855 {
856 u_int32_t offset; /* file offset to data */
857 u_int32_t length; /* size of attribute data */
858 u_int16_t flags;
859 u_int8_t namelen; /* length of name including NULL termination char */
860 u_int8_t name[1]; /* NULL-terminated UTF-8 name (up to 128 bytes max) */
861 } attr_entry_t;
862
863
864 /* Header + entries must fit into 64K */
865 typedef struct attr_header
866 {
867 apple_double_header_t appledouble;
868 u_int32_t magic; /* == ATTR_HDR_MAGIC */
869 u_int32_t debug_tag; /* for debugging == file id of owning file */
870 u_int32_t total_size; /* total size of attribute header + entries + data */
871 u_int32_t data_start; /* file offset to attribute data area */
872 u_int32_t data_length; /* length of attribute data area */
873 u_int32_t reserved[3];
874 u_int16_t flags;
875 u_int16_t num_attrs;
876 } attr_header_t;
877
878
879 #pragma options align=reset
880
881 #define SWAP16(x) OSSwapBigToHostInt16(x)
882 #define SWAP32(x) OSSwapBigToHostInt32(x)
883 #define SWAP64(x) OSSwapBigToHostInt64(x)
884
885 #define ATTR_ALIGN 3L /* Use four-byte alignment */
886
887 #define ATTR_ENTRY_LENGTH(namelen) \
888 ((sizeof(attr_entry_t) - 1 + (namelen) + ATTR_ALIGN) & (~ATTR_ALIGN))
889
890 #define ATTR_NEXT(ae) \
891 (attr_entry_t *)((u_int8_t *)(ae) + ATTR_ENTRY_LENGTH((ae)->namelen))
892
893 #define XATTR_SECURITY_NAME "com.apple.acl.text"
894
895 /*
896 * Endian swap Apple Double header
897 */
898 static void
899 swap_adhdr(apple_double_header_t *adh)
900 {
901 #if BYTE_ORDER == LITTLE_ENDIAN
902 int count;
903 int i;
904
905 count = (adh->magic == ADH_MAGIC) ? adh->numEntries : SWAP16(adh->numEntries);
906
907 adh->magic = SWAP32 (adh->magic);
908 adh->version = SWAP32 (adh->version);
909 adh->numEntries = SWAP16 (adh->numEntries);
910
911 for (i = 0; i < count; i++)
912 {
913 adh->entries[i].type = SWAP32 (adh->entries[i].type);
914 adh->entries[i].offset = SWAP32 (adh->entries[i].offset);
915 adh->entries[i].length = SWAP32 (adh->entries[i].length);
916 }
917 #endif
918 }
919
920 /*
921 * Endian swap extended attributes header
922 */
923 static void
924 swap_attrhdr(attr_header_t *ah)
925 {
926 #if BYTE_ORDER == LITTLE_ENDIAN
927 attr_entry_t *ae;
928 int count;
929 int i;
930
931 count = (ah->magic == ATTR_HDR_MAGIC) ? ah->num_attrs : SWAP16(ah->num_attrs);
932
933 ah->magic = SWAP32 (ah->magic);
934 ah->debug_tag = SWAP32 (ah->debug_tag);
935 ah->total_size = SWAP32 (ah->total_size);
936 ah->data_start = SWAP32 (ah->data_start);
937 ah->data_length = SWAP32 (ah->data_length);
938 ah->flags = SWAP16 (ah->flags);
939 ah->num_attrs = SWAP16 (ah->num_attrs);
940
941 ae = (attr_entry_t *)(&ah[1]);
942 for (i = 0; i < count; i++, ae++)
943 {
944 ae->offset = SWAP32 (ae->offset);
945 ae->length = SWAP32 (ae->length);
946 ae->flags = SWAP16 (ae->flags);
947 }
948 #endif
949 }
950
951 static u_int32_t emptyfinfo[8] = {0};
952
953 static int copyfile_unpack(copyfile_state_t s)
954 {
955 int bytes;
956 void * buffer;
957 apple_double_header_t *adhdr;
958 size_t hdrsize;
959 int error = 0;
960
961 if (s->sb.st_size < ATTR_MAX_HDR_SIZE)
962 hdrsize = s->sb.st_size;
963 else
964 hdrsize = ATTR_MAX_HDR_SIZE;
965
966 buffer = calloc(1, hdrsize);
967 bytes = pread(s->src_fd, buffer, hdrsize, 0);
968
969 if (bytes < 0)
970 {
971 copyfile_debug(1, "pread returned: %d", bytes);
972 error = -1;
973 goto exit;
974 }
975 if (bytes < hdrsize)
976 {
977 copyfile_debug(1,
978 "pread couldn't read entire header: %d of %d",
979 (int)bytes, (int)s->sb.st_size);
980 error = -1;
981 goto exit;
982 }
983 adhdr = (apple_double_header_t *)buffer;
984
985 /*
986 * Check for Apple Double file.
987 */
988 if (bytes < sizeof(apple_double_header_t) - 2 ||
989 SWAP32(adhdr->magic) != ADH_MAGIC ||
990 SWAP32(adhdr->version) != ADH_VERSION ||
991 SWAP16(adhdr->numEntries) != 2 ||
992 SWAP32(adhdr->entries[0].type) != AD_FINDERINFO)
993 {
994 if (COPYFILE_VERBOSE & s->flags)
995 copyfile_warn("Not a valid Apple Double header");
996 error = -1;
997 goto exit;
998 }
999 swap_adhdr(adhdr);
1000
1001 /*
1002 * Extract the extended attributes.
1003 *
1004 * >>> WARNING <<<
1005 * This assumes that the data is already in memory (not
1006 * the case when there are lots of attributes or one of
1007 * the attributes is very large.
1008 */
1009 if (adhdr->entries[0].length > FINDERINFOSIZE)
1010 {
1011 attr_header_t *attrhdr;
1012 attr_entry_t *entry;
1013 int count;
1014 int i;
1015
1016 attrhdr = (attr_header_t *)buffer;
1017 swap_attrhdr(attrhdr);
1018 if (attrhdr->magic != ATTR_HDR_MAGIC)
1019 {
1020 if (COPYFILE_VERBOSE & s->flags)
1021 copyfile_warn("bad attribute header");
1022 error = -1;
1023 goto exit;
1024 }
1025 count = attrhdr->num_attrs;
1026 entry = (attr_entry_t *)&attrhdr[1];
1027 for (i = 0; i < count; i++)
1028 {
1029 void * dataptr;
1030
1031 copyfile_debug(2, "extracting \"%s\" (%d bytes)",
1032 entry->name, entry->length);
1033 dataptr = (char *)attrhdr + entry->offset;
1034
1035 if (COPYFILE_ACL & s->flags && strncmp(entry->name, XATTR_SECURITY_NAME, strlen(XATTR_SECURITY_NAME)) == 0)
1036 {
1037 acl_t acl;
1038 if ((acl = acl_from_text(dataptr)) != NULL)
1039 {
1040 if (filesec_set_property(s->fsec, FILESEC_ACL, &acl) < 0)
1041 {
1042 acl_t acl;
1043 if ((acl = acl_from_text(dataptr)) != NULL)
1044 {
1045 if (filesec_set_property(s->fsec, FILESEC_ACL, &acl) < 0)
1046 {
1047 copyfile_debug(1, "setting acl");
1048 }
1049 else if (fchmodx_np(s->dst_fd, s->fsec) < 0 && errno != ENOTSUP)
1050 copyfile_warn("setting security information");
1051 acl_free(acl);
1052 }
1053 } else
1054 if (COPYFILE_XATTR & s->flags && (fsetxattr(s->dst_fd, entry->name, dataptr, entry->length, 0, 0))) {
1055 if (COPYFILE_VERBOSE & s->flags)
1056 copyfile_warn("error %d setting attribute %s", error, entry->name);
1057 goto exit;
1058 }
1059 else if (fchmodx_np(s->dst_fd, s->fsec) < 0 && errno != ENOTSUP)
1060 copyfile_warn("setting security information");
1061 acl_free(acl);
1062 }
1063 } else
1064 if (COPYFILE_XATTR & s->flags && (fsetxattr(s->dst_fd, entry->name, dataptr, entry->length, 0, 0))) {
1065 if (COPYFILE_VERBOSE & s->flags)
1066 copyfile_warn("error %d setting attribute %s", error, entry->name);
1067 break;
1068 }
1069 entry = ATTR_NEXT(entry);
1070 }
1071 }
1072
1073 /*
1074 * Extract the Finder Info.
1075 */
1076 if (bcmp((u_int8_t*)buffer + adhdr->entries[0].offset, emptyfinfo, sizeof(emptyfinfo)) != 0)
1077 {
1078 copyfile_debug(1, " extracting \"%s\" (32 bytes)", XATTR_FINDERINFO_NAME);
1079 error = fsetxattr(s->dst_fd, XATTR_FINDERINFO_NAME, (u_int8_t*)buffer + adhdr->entries[0].offset, sizeof(emptyfinfo), 0, 0);
1080 if (error)
1081 goto exit;
1082 }
1083
1084 /*
1085 * Extract the Resource Fork.
1086 */
1087 if (adhdr->entries[1].type == AD_RESOURCE &&
1088 adhdr->entries[1].length > 0)
1089 {
1090 void * rsrcforkdata;
1091 size_t length;
1092 off_t offset;
1093
1094 length = adhdr->entries[1].length;
1095 offset = adhdr->entries[1].offset;
1096 rsrcforkdata = malloc(length);
1097
1098 bytes = pread(s->src_fd, rsrcforkdata, length, offset);
1099 if (bytes < length)
1100 {
1101 if (bytes == -1)
1102 {
1103 copyfile_debug(1, "couldn't read resource fork");
1104 }
1105 else
1106 {
1107 copyfile_debug(1,
1108 "couldn't read resource fork (only read %d bytes of %d)",
1109 (int)bytes, (int)length);
1110 }
1111 error = -1;
1112 goto exit;
1113 }
1114 error = fsetxattr(s->dst_fd, XATTR_RESOURCEFORK_NAME, rsrcforkdata, bytes, 0, 0);
1115 if (error)
1116 {
1117 copyfile_debug(1, "error %d setting resource fork attribute", error);
1118 error = -1;
1119 goto exit;
1120 }
1121 copyfile_debug(1, "extracting \"%s\" (%d bytes)",
1122 XATTR_RESOURCEFORK_NAME, (int)length);
1123 free(rsrcforkdata);
1124 }
1125 exit:
1126 free(buffer);
1127 return error;
1128 }
1129
1130 static int copyfile_pack_acl(copyfile_state_t s, void **buf, ssize_t *len)
1131 {
1132 int ret = 0;
1133 acl_t acl;
1134 char *acl_text;
1135
1136 if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) < 0)
1137 {
1138 if (errno != ENOENT)
1139 {
1140 ret = -1;
1141 if (COPYFILE_VERBOSE & s->flags)
1142 copyfile_warn("getting acl");
1143 }
1144 goto err;
1145 }
1146
1147 if ((acl_text = acl_to_text(acl, len)) != NULL)
1148 {
1149 *buf = malloc(*len);
1150 memcpy(*buf, acl_text, *len);
1151 acl_free(acl_text);
1152 }
1153 copyfile_debug(1, "copied acl (%ld) %p", *len, *buf);
1154 err:
1155 return ret;
1156 }
1157
1158 static int copyfile_pack_rsrcfork(copyfile_state_t s, attr_header_t *filehdr)
1159 {
1160 int datasize;
1161 char *databuf;
1162
1163 /* Get the resource fork size */
1164 if ((datasize = fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, NULL, 0, 0, 0)) < 0)
1165 {
1166 if (COPYFILE_VERBOSE & s->flags)
1167 copyfile_warn("skipping attr \"%s\" due to error %d", XATTR_RESOURCEFORK_NAME, errno);
1168 return -1;
1169 }
1170
1171 if ((databuf = malloc(datasize)) == NULL)
1172 {
1173 copyfile_warn("malloc");
1174 return -1;
1175 }
1176
1177 if (fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, databuf, datasize, 0, 0) != datasize)
1178 {
1179 if (COPYFILE_VERBOSE & s->flags)
1180 copyfile_warn("couldn't read entire resource fork");
1181 return -1;
1182 }
1183
1184 /* Write the resource fork to disk. */
1185 if (pwrite(s->dst_fd, databuf, datasize, filehdr->appledouble.entries[1].offset) != datasize)
1186 {
1187 if (COPYFILE_VERBOSE & s->flags)
1188 copyfile_warn("couldn't write resource fork");
1189 }
1190 copyfile_debug(1, "copied %d bytes of \"%s\" data @ offset 0x%08x",
1191 datasize, XATTR_RESOURCEFORK_NAME, filehdr->appledouble.entries[1].offset);
1192 filehdr->appledouble.entries[1].length = datasize;
1193 free(databuf);
1194
1195 return 0;
1196 }
1197
1198
1199 static int copyfile_pack(copyfile_state_t s)
1200 {
1201 char *attrnamebuf;
1202 void *databuf;
1203 attr_header_t *filehdr;
1204 attr_entry_t *entry;
1205 ssize_t listsize;
1206 char *nameptr;
1207 int namelen;
1208 int entrylen;
1209 ssize_t datasize;
1210 int offset = 0;
1211 int hasrsrcfork = 0;
1212 int error = 0;
1213
1214 filehdr = (attr_header_t *) calloc(1, ATTR_MAX_SIZE);
1215 attrnamebuf = calloc(1, ATTR_MAX_HDR_SIZE);
1216
1217 /*
1218 * Fill in the Apple Double Header defaults.
1219 */
1220 filehdr->appledouble.magic = SWAP32 (ADH_MAGIC);
1221 filehdr->appledouble.version = SWAP32 (ADH_VERSION);
1222 filehdr->appledouble.numEntries = SWAP16 (2);
1223 filehdr->appledouble.entries[0].type = SWAP32 (AD_FINDERINFO);
1224 filehdr->appledouble.entries[0].offset = SWAP32 (offsetof(apple_double_header_t, finfo));
1225 filehdr->appledouble.entries[0].length = SWAP32 (FINDERINFOSIZE);
1226 filehdr->appledouble.entries[1].type = SWAP32 (AD_RESOURCE);
1227 filehdr->appledouble.entries[1].offset = SWAP32 (offsetof(apple_double_header_t, pad));
1228 filehdr->appledouble.entries[1].length = 0;
1229 bcopy(ADH_MACOSX, filehdr->appledouble.filler, sizeof(filehdr->appledouble.filler));
1230
1231 /*
1232 * Fill in the initial Attribute Header.
1233 */
1234 filehdr->magic = SWAP32 (ATTR_HDR_MAGIC);
1235 filehdr->debug_tag = SWAP32 (s->sb.st_ino);
1236 filehdr->data_start = SWAP32 (sizeof(attr_header_t));
1237
1238 /*
1239 * Collect the attribute names.
1240 */
1241 entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t));
1242
1243 /*
1244 * Test if there are acls to copy
1245 */
1246 if (COPYFILE_ACL & s->flags)
1247 {
1248 if (filesec_get_property(s->fsec, FILESEC_ACL, &datasize) < 0)
1249 {
1250 copyfile_debug(1, "no acl entries found (%d)", datasize < 0 ? errno : 0);
1251 } else
1252 {
1253 offset = strlen(XATTR_SECURITY_NAME) + 1;
1254 strcpy(attrnamebuf, XATTR_SECURITY_NAME);
1255 }
1256 }
1257
1258 if (COPYFILE_XATTR & s->flags)
1259 {
1260 if ((listsize = flistxattr(s->src_fd, attrnamebuf + offset, ATTR_MAX_HDR_SIZE, 0)) <= 0)
1261 {
1262 copyfile_debug(1, "no extended attributes found (%d)", errno);
1263 }
1264 if (listsize > ATTR_MAX_HDR_SIZE)
1265 {
1266 copyfile_debug(1, "extended attribute list too long");
1267 listsize = ATTR_MAX_HDR_SIZE;
1268 }
1269
1270 listsize += offset;
1271
1272 for (nameptr = attrnamebuf; nameptr < attrnamebuf + listsize; nameptr += namelen)
1273 {
1274 namelen = strlen(nameptr) + 1;
1275 /* Skip over FinderInfo or Resource Fork names */
1276 if (strncmp(nameptr, XATTR_FINDERINFO_NAME, strlen(XATTR_FINDERINFO_NAME)) == 0 ||
1277 strncmp(nameptr, XATTR_RESOURCEFORK_NAME, strlen(XATTR_RESOURCEFORK_NAME)) == 0)
1278 continue;
1279
1280 entry->namelen = namelen;
1281 entry->flags = 0;
1282 bcopy(nameptr, &entry->name[0], namelen);
1283 copyfile_debug(2, "copied name [%s]", entry->name);
1284
1285 entrylen = ATTR_ENTRY_LENGTH(namelen);
1286 entry = (attr_entry_t *)(((char *)entry) + entrylen);
1287
1288 /* Update the attributes header. */
1289 filehdr->num_attrs++;
1290 filehdr->data_start += entrylen;
1291 }
1292 }
1293
1294 /*
1295 * Collect the attribute data.
1296 */
1297 entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t));
1298
1299 for (nameptr = attrnamebuf; nameptr < attrnamebuf + listsize; nameptr += namelen + 1)
1300 {
1301 nameptr = nameptr;
1302 namelen = strlen(nameptr);
1303
1304 if (strncmp(nameptr, XATTR_SECURITY_NAME, strlen(XATTR_SECURITY_NAME)) == 0)
1305 copyfile_pack_acl(s, &databuf, &datasize);
1306 else
1307 /* Check for Finder Info. */
1308 if (strncmp(nameptr, XATTR_FINDERINFO_NAME, strlen(XATTR_FINDERINFO_NAME)) == 0)
1309 {
1310 datasize = fgetxattr(s->src_fd, nameptr, (u_int8_t*)filehdr + filehdr->appledouble.entries[0].offset, 32, 0, 0);
1311 if (datasize < 0)
1312 {
1313 if (COPYFILE_VERBOSE & s->flags)
1314 copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno);
1315 } else if (datasize != 32)
1316 {
1317 if (COPYFILE_VERBOSE & s->flags)
1318 copyfile_warn("unexpected size (%ld) for \"%s\"", datasize, nameptr);
1319 } else
1320 {
1321 if (COPYFILE_VERBOSE & s->flags)
1322 copyfile_warn(" copied 32 bytes of \"%s\" data @ offset 0x%08x",
1323 XATTR_FINDERINFO_NAME, filehdr->appledouble.entries[0].offset);
1324 }
1325 continue; /* finder info doesn't have an attribute entry */
1326 } else
1327 /* Check for Resource Fork. */
1328 if (strncmp(nameptr, XATTR_RESOURCEFORK_NAME, strlen(XATTR_RESOURCEFORK_NAME)) == 0)
1329 {
1330 hasrsrcfork = 1;
1331 continue;
1332 } else
1333 {
1334 /* Just a normal attribute. */
1335 datasize = fgetxattr(s->src_fd, nameptr, NULL, 0, 0, 0);
1336 if (datasize == 0)
1337 goto next;
1338 if (datasize < 0)
1339 {
1340 if (COPYFILE_VERBOSE & s->flags)
1341 copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno);
1342 goto next;
1343 }
1344 if (datasize > XATTR_MAXATTRLEN)
1345 {
1346 if (COPYFILE_VERBOSE & s->flags)
1347 copyfile_warn("skipping attr \"%s\" (too big)", nameptr);
1348 goto next;
1349 }
1350 databuf = malloc(datasize);
1351 datasize = fgetxattr(s->src_fd, nameptr, databuf, datasize, 0, 0);
1352 }
1353
1354 entry->length = datasize;
1355 entry->offset = filehdr->data_start + filehdr->data_length;
1356
1357 filehdr->data_length += datasize;
1358 /*
1359 * >>> WARNING <<<
1360 * This assumes that the data is fits in memory (not
1361 * the case when there are lots of attributes or one of
1362 * the attributes is very large.
1363 */
1364 bcopy(databuf, (char*)filehdr + entry->offset, datasize);
1365 free(databuf);
1366
1367 copyfile_debug(1, "copied %ld bytes of \"%s\" data @ offset 0x%08x", datasize, nameptr, entry->offset);
1368 next:
1369 /* bump to next entry */
1370 entrylen = ATTR_ENTRY_LENGTH(entry->namelen);
1371 entry = (attr_entry_t *)((char *)entry + entrylen);
1372 }
1373
1374 if (filehdr->data_length > 0)
1375 {
1376 /* Now we know where the resource fork data starts. */
1377 filehdr->appledouble.entries[1].offset = (filehdr->data_start + filehdr->data_length);
1378
1379 /* We also know the size of the "Finder Info entry. */
1380 filehdr->appledouble.entries[0].length =
1381 filehdr->appledouble.entries[1].offset - filehdr->appledouble.entries[0].offset;
1382
1383 filehdr->total_size = SWAP32 (filehdr->appledouble.entries[1].offset);
1384 }
1385
1386 /* Copy Resource Fork. */
1387 if (hasrsrcfork && (error = copyfile_pack_rsrcfork(s, filehdr)))
1388 goto exit;
1389
1390 /* Write the header to disk. */
1391 datasize = filehdr->appledouble.entries[1].offset;
1392
1393 if (pwrite(s->dst_fd, filehdr, datasize, 0) != datasize)
1394 {
1395 if (COPYFILE_VERBOSE & s->flags)
1396 copyfile_warn("couldn't write file header");
1397 error = -1;
1398 goto exit;
1399 }
1400 exit:
1401 free(filehdr);
1402 free(attrnamebuf);
1403
1404 if (error)
1405 return error;
1406 else
1407 return copyfile_stat(s);
1408 }