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