]> git.saurik.com Git - apple/libc.git/blob - darwin/copyfile.c
449f6ee1bd2f684a2b7523dc9f31a79b64562fe1
[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/time.h>
38 #include <sys/xattr.h>
39 #include <sys/syscall.h>
40 #include <sys/param.h>
41 #include <sys/acl.h>
42 #include <libkern/OSByteOrder.h>
43 #include <membership.h>
44
45 #include <copyfile.h>
46
47 struct _copyfile_state
48 {
49 char *src;
50 char *dst;
51 int src_fd;
52 int dst_fd;
53 struct stat sb;
54 filesec_t fsec;
55 copyfile_flags_t flags;
56 void *stats;
57 uint32_t debug;
58 };
59
60 static int copyfile_open (copyfile_state_t);
61 static int copyfile_close (copyfile_state_t);
62 static int copyfile_data (copyfile_state_t);
63 static int copyfile_stat (copyfile_state_t);
64 static int copyfile_security (copyfile_state_t);
65 static int copyfile_xattr (copyfile_state_t);
66 static int copyfile_pack (copyfile_state_t);
67 static int copyfile_unpack (copyfile_state_t);
68
69 static copyfile_flags_t copyfile_check (copyfile_state_t);
70 static int copyfile_fix_perms(copyfile_state_t, filesec_t *, int);
71
72 #define COPYFILE_DEBUG (1<<31)
73
74 #ifndef _COPYFILE_TEST
75 # define copyfile_warn(str, ...) syslog(LOG_WARNING, str ": %m", ## __VA_ARGS__)
76 # define copyfile_debug(d, str, ...) \
77 if (s && (d <= s->debug)) {\
78 syslog(LOG_DEBUG, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \
79 } else
80 #else
81 #define copyfile_warn(str, ...) \
82 fprintf(stderr, "%s:%d:%s() " str ": %s\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__, (errno) ? strerror(errno) : "")
83 # define copyfile_debug(d, str, ...) \
84 if (s && (d <= s->debug)) {\
85 fprintf(stderr, "%s:%d:%s() " str "\n", __FILE__, __LINE__ , __FUNCTION__, ## __VA_ARGS__); \
86 } else
87 #endif
88
89 int copyfile(const char *src, const char *dst, copyfile_state_t state, copyfile_flags_t flags)
90 {
91 int ret = 0;
92 copyfile_state_t s = state;
93 filesec_t original_fsec;
94 int fix_perms = 0;
95
96 original_fsec = filesec_init();
97 if (s == NULL && (s = copyfile_init()) == NULL)
98 return -1;
99
100 if (src != NULL)
101 {
102 if (s->src_fd != -2 && s->src && !strncmp(src, s->src, MAXPATHLEN))
103 close(s->src_fd);
104 s->src = strdup(src);
105 }
106
107 if (dst != NULL)
108 {
109 if (s->dst_fd != -2 && s->dst && !strncmp(dst, s->dst, MAXPATHLEN))
110 close(s->dst_fd);
111 s->dst = strdup(dst);
112 }
113
114 s->flags = flags;
115
116 if (COPYFILE_DEBUG & s->flags)
117 {
118 char *e;
119 if ((e = getenv("COPYFILE_DEBUG")))
120 {
121 s->debug = strtol(e, NULL, 0);
122 if (s->debug < 1)
123 s->debug = 1;
124 }
125 copyfile_debug(1, "debug value set to: %d\n", s->debug);
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 } __attribute__((packed)) 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 } __attribute__((packed)) 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 } __attribute__((packed)) 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 } __attribute__((packed)) 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 const u_int32_t emptyfinfo[8] = {0};
955
956 static int copyfile_unpack(copyfile_state_t s)
957 {
958 ssize_t bytes;
959 void * buffer, * endptr;
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 if (buffer == NULL) {
971 copyfile_debug(1, "copyfile_unpack: calloc(1, %u) returned NULL", hdrsize);
972 error = -1;
973 goto exit;
974 } else
975 endptr = (char*)buffer + hdrsize;
976
977 bytes = pread(s->src_fd, buffer, hdrsize, 0);
978
979 if (bytes < 0)
980 {
981 copyfile_debug(1, "pread returned: %d", bytes);
982 error = -1;
983 goto exit;
984 }
985 if (bytes < hdrsize)
986 {
987 copyfile_debug(1,
988 "pread couldn't read entire header: %d of %d",
989 (int)bytes, (int)s->sb.st_size);
990 error = -1;
991 goto exit;
992 }
993 adhdr = (apple_double_header_t *)buffer;
994
995 /*
996 * Check for Apple Double file.
997 */
998 if (bytes < sizeof(apple_double_header_t) - 2 ||
999 SWAP32(adhdr->magic) != ADH_MAGIC ||
1000 SWAP32(adhdr->version) != ADH_VERSION ||
1001 SWAP16(adhdr->numEntries) != 2 ||
1002 SWAP32(adhdr->entries[0].type) != AD_FINDERINFO)
1003 {
1004 if (COPYFILE_VERBOSE & s->flags)
1005 copyfile_warn("Not a valid Apple Double header");
1006 error = -1;
1007 goto exit;
1008 }
1009 swap_adhdr(adhdr);
1010
1011 /*
1012 * Extract the extended attributes.
1013 *
1014 * >>> WARNING <<<
1015 * This assumes that the data is already in memory (not
1016 * the case when there are lots of attributes or one of
1017 * the attributes is very large.
1018 */
1019 if (adhdr->entries[0].length > FINDERINFOSIZE)
1020 {
1021 attr_header_t *attrhdr;
1022 attr_entry_t *entry;
1023 int count;
1024 int i;
1025
1026 if (hdrsize < sizeof(attr_header_t)) {
1027 copyfile_warn("bad attribute header: %u < %u", hdrsize, sizeof(attr_header_t));
1028 error = -1;
1029 goto exit;
1030 }
1031
1032 attrhdr = (attr_header_t *)buffer;
1033 swap_attrhdr(attrhdr);
1034 if (attrhdr->magic != ATTR_HDR_MAGIC)
1035 {
1036 if (COPYFILE_VERBOSE & s->flags)
1037 copyfile_warn("bad attribute header");
1038 error = -1;
1039 goto exit;
1040 }
1041 count = attrhdr->num_attrs;
1042 entry = (attr_entry_t *)&attrhdr[1];
1043
1044 for (i = 0; i < count; i++)
1045 {
1046 void * dataptr;
1047
1048 /*
1049 * First we do some simple sanity checking.
1050 * +) See if entry is within the buffer's range;
1051 *
1052 * +) Check the attribute name length; if it's longer than the
1053 * maximum, we truncate it down. (We could error out as well;
1054 * I'm not sure which is the better way to go here.)
1055 *
1056 * +) If, given the name length, it goes beyond the end of
1057 * the buffer, error out.
1058 *
1059 * +) If the last byte isn't a NUL, make it a NUL. (Since we
1060 * truncated the name length above, we truncate the name here.)
1061 *
1062 * +) If entry->offset is so large that it causes dataptr to
1063 * go beyond the end of the buffer -- or, worse, so large that
1064 * it wraps around! -- we error out.
1065 *
1066 * +) If entry->length would cause the entry to go beyond the
1067 * end of the buffer (or, worse, wrap around to before it),
1068 * *or* if the length is larger than the hdrsize, we error out.
1069 * (An explanation of that: what we're checking for there is
1070 * the small range of values such that offset+length would cause
1071 * it to go beyond endptr, and then wrap around past buffer. We
1072 * care about this because we are passing entry->length down to
1073 * fgetxattr() below, and an erroneously large value could cause
1074 * problems there. By making sure that it's less than hdrsize,
1075 * which has already been sanity-checked above, we're safe.
1076 * That may mean that the check against < buffer is unnecessary.)
1077 */
1078 if ((void*)entry >= endptr || (void*)entry < buffer) {
1079 if (COPYFILE_VERBOSE & s->flags)
1080 copyfile_warn("Incomplete or corrupt attribute entry");
1081 error = -1;
1082 goto exit;
1083 }
1084
1085 if (((void*)entry + sizeof(*entry)) > endptr) {
1086 if (COPYFILE_VERBOSE & s->flags)
1087 copyfile_warn("Incomplete or corrupt attribute entry");
1088 error = -1;
1089 goto exit;
1090 }
1091
1092 if (entry->namelen > ATTR_MAX_NAME_LEN) {
1093 entry->namelen = ATTR_MAX_NAME_LEN;
1094 }
1095 if ((void*)(entry->name + entry->namelen) >= endptr) {
1096 if (COPYFILE_VERBOSE & s->flags)
1097 copyfile_warn("Incomplete or corrupt attribute entry");
1098 error = -1;
1099 goto exit;
1100 }
1101
1102 if (entry->name[entry->namelen] != 0) {
1103 entry->name[entry->namelen] = 0;
1104 }
1105
1106 copyfile_debug(3, "extracting \"%s\" (%d bytes) at offset %u",
1107 entry->name, entry->length, entry->offset);
1108
1109 dataptr = (char *)attrhdr + entry->offset;
1110
1111 if (dataptr >= endptr || dataptr < buffer) {
1112 copyfile_debug(1, "Entry %d overflows: offset = %u", entry->offset);
1113 error = -1;
1114 goto exit;
1115 }
1116 if ((dataptr + entry->length) > endptr ||
1117 ((dataptr + entry->length) < buffer) ||
1118 (entry->length > hdrsize)) {
1119 if (COPYFILE_VERBOSE & s->flags)
1120 copyfile_warn("Incomplete or corrupt attribute entry");
1121 copyfile_debug(1, "Entry %d length overflows: dataptr = %u, offset = %u, length = %u, buffer = %u, endptr = %u",
1122 i, dataptr, entry->offset, entry->length, buffer, endptr);
1123 error = -1;
1124 goto exit;
1125 }
1126
1127 if (COPYFILE_ACL & s->flags && strcmp((char*)entry->name, XATTR_SECURITY_NAME) == 0)
1128 copyfile_debug(2, "extracting \"%s\" (%d bytes)",
1129 entry->name, entry->length);
1130 dataptr = (char *)attrhdr + entry->offset;
1131
1132 if (COPYFILE_ACL & s->flags && strncmp(entry->name, XATTR_SECURITY_NAME, strlen(XATTR_SECURITY_NAME)) == 0)
1133 {
1134 acl_t acl;
1135 if ((acl = acl_from_text(dataptr)) != NULL)
1136 {
1137 if (filesec_set_property(s->fsec, FILESEC_ACL, &acl) < 0)
1138 {
1139 acl_t acl;
1140 if ((acl = acl_from_text(dataptr)) != NULL)
1141 {
1142 if (filesec_set_property(s->fsec, FILESEC_ACL, &acl) < 0)
1143 {
1144 copyfile_debug(1, "setting acl");
1145 }
1146 else if (fchmodx_np(s->dst_fd, s->fsec) < 0 && errno != ENOTSUP)
1147 copyfile_warn("setting security information");
1148 acl_free(acl);
1149 }
1150 } else if (COPYFILE_XATTR & s->flags && (fsetxattr(s->dst_fd, entry->name, dataptr, entry->length, 0, 0))) {
1151 if (COPYFILE_VERBOSE & s->flags)
1152 copyfile_warn("error %d setting attribute %s", error, entry->name);
1153 goto exit;
1154 }
1155 else if (fchmodx_np(s->dst_fd, s->fsec) < 0 && errno != ENOTSUP)
1156 copyfile_warn("setting security information");
1157 acl_free(acl);
1158 }
1159 } else
1160 if (COPYFILE_XATTR & s->flags && (fsetxattr(s->dst_fd, entry->name, dataptr, entry->length, 0, 0))) {
1161 if (COPYFILE_VERBOSE & s->flags)
1162 copyfile_warn("error %d setting attribute %s", error, entry->name);
1163 break;
1164 }
1165 entry = ATTR_NEXT(entry);
1166 }
1167 }
1168
1169 /*
1170 * Extract the Finder Info.
1171 */
1172 if (adhdr->entries[0].offset > (hdrsize - sizeof(emptyfinfo))) {
1173 error = -1;
1174 goto exit;
1175 }
1176
1177 if (bcmp((u_int8_t*)buffer + adhdr->entries[0].offset, emptyfinfo, sizeof(emptyfinfo)) != 0)
1178 {
1179 copyfile_debug(1, " extracting \"%s\" (32 bytes)", XATTR_FINDERINFO_NAME);
1180 error = fsetxattr(s->dst_fd, XATTR_FINDERINFO_NAME, (u_int8_t*)buffer + adhdr->entries[0].offset, sizeof(emptyfinfo), 0, 0);
1181 if (error)
1182 goto exit;
1183 }
1184
1185 /*
1186 * Extract the Resource Fork.
1187 */
1188 if (adhdr->entries[1].type == AD_RESOURCE &&
1189 adhdr->entries[1].length > 0)
1190 {
1191 void * rsrcforkdata = NULL;
1192 size_t length;
1193 off_t offset;
1194
1195 length = adhdr->entries[1].length;
1196 offset = adhdr->entries[1].offset;
1197 rsrcforkdata = malloc(length);
1198
1199 if (rsrcforkdata == NULL) {
1200 copyfile_debug(1, "could not allocate %u bytes for rsrcforkdata",
1201 length);
1202 error = -1;
1203 goto bad;
1204 }
1205
1206 bytes = pread(s->src_fd, rsrcforkdata, length, offset);
1207 if (bytes < length)
1208 {
1209 if (bytes == -1)
1210 {
1211 copyfile_debug(1, "couldn't read resource fork");
1212 }
1213 else
1214 {
1215 copyfile_debug(1,
1216 "couldn't read resource fork (only read %d bytes of %d)",
1217 (int)bytes, (int)length);
1218 }
1219 error = -1;
1220 goto bad;
1221 }
1222 error = fsetxattr(s->dst_fd, XATTR_RESOURCEFORK_NAME, rsrcforkdata, bytes, 0, 0);
1223 if (error)
1224 {
1225 copyfile_debug(1, "error %d setting resource fork attribute", error);
1226 error = -1;
1227 goto bad;
1228 }
1229 copyfile_debug(1, "extracting \"%s\" (%d bytes)",
1230 XATTR_RESOURCEFORK_NAME, (int)length);
1231 bad:
1232 if (rsrcforkdata)
1233 free(rsrcforkdata);
1234 }
1235 exit:
1236 if (buffer) free(buffer);
1237 return error;
1238 }
1239
1240 static int copyfile_pack_acl(copyfile_state_t s, void **buf, ssize_t *len)
1241 {
1242 int ret = 0;
1243 acl_t acl;
1244 char *acl_text;
1245
1246 if (filesec_get_property(s->fsec, FILESEC_ACL, &acl) < 0)
1247 {
1248 if (errno != ENOENT)
1249 {
1250 ret = -1;
1251 if (COPYFILE_VERBOSE & s->flags)
1252 copyfile_warn("getting acl");
1253 }
1254 goto err;
1255 }
1256
1257 if ((acl_text = acl_to_text(acl, len)) != NULL)
1258 {
1259 *buf = malloc(*len);
1260 memcpy(*buf, acl_text, *len);
1261 acl_free(acl_text);
1262 }
1263 copyfile_debug(1, "copied acl (%ld) %p", *len, *buf);
1264 err:
1265 return ret;
1266 }
1267
1268 static int copyfile_pack_rsrcfork(copyfile_state_t s, attr_header_t *filehdr)
1269 {
1270 int datasize;
1271 char *databuf;
1272
1273 /* Get the resource fork size */
1274 if ((datasize = fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, NULL, 0, 0, 0)) < 0)
1275 {
1276 if (COPYFILE_VERBOSE & s->flags)
1277 copyfile_warn("skipping attr \"%s\" due to error %d", XATTR_RESOURCEFORK_NAME, errno);
1278 return -1;
1279 }
1280
1281 if ((databuf = malloc(datasize)) == NULL)
1282 {
1283 copyfile_warn("malloc");
1284 return -1;
1285 }
1286
1287 if (fgetxattr(s->src_fd, XATTR_RESOURCEFORK_NAME, databuf, datasize, 0, 0) != datasize)
1288 {
1289 if (COPYFILE_VERBOSE & s->flags)
1290 copyfile_warn("couldn't read entire resource fork");
1291 return -1;
1292 }
1293
1294 /* Write the resource fork to disk. */
1295 if (pwrite(s->dst_fd, databuf, datasize, filehdr->appledouble.entries[1].offset) != datasize)
1296 {
1297 if (COPYFILE_VERBOSE & s->flags)
1298 copyfile_warn("couldn't write resource fork");
1299 }
1300 copyfile_debug(1, "copied %d bytes of \"%s\" data @ offset 0x%08x",
1301 datasize, XATTR_RESOURCEFORK_NAME, filehdr->appledouble.entries[1].offset);
1302 filehdr->appledouble.entries[1].length = datasize;
1303 free(databuf);
1304
1305 return 0;
1306 }
1307
1308
1309 static int copyfile_pack(copyfile_state_t s)
1310 {
1311 char *attrnamebuf = NULL, *endnamebuf;
1312 void *databuf = NULL;
1313 attr_header_t *filehdr, *endfilehdr;
1314 attr_entry_t *entry;
1315 ssize_t listsize = 0;
1316 char *nameptr;
1317 int namelen;
1318 int entrylen;
1319 ssize_t datasize;
1320 int offset = 0;
1321 int hasrsrcfork = 0;
1322 int error = 0;
1323
1324 filehdr = (attr_header_t *) calloc(1, ATTR_MAX_SIZE);
1325 if (filehdr == NULL) {
1326 error = -1;
1327 goto exit;
1328 } else {
1329 endfilehdr = ((void*)filehdr) + ATTR_MAX_SIZE;
1330 }
1331
1332 attrnamebuf = calloc(1, ATTR_MAX_HDR_SIZE);
1333 if (attrnamebuf == NULL) {
1334 error = -1;
1335 goto exit;
1336 } else {
1337 endnamebuf = ((char*)attrnamebuf) + ATTR_MAX_HDR_SIZE;
1338 }
1339
1340 /*
1341 * Fill in the Apple Double Header defaults.
1342 */
1343 filehdr->appledouble.magic = ADH_MAGIC;
1344 filehdr->appledouble.version = ADH_VERSION;
1345 filehdr->appledouble.numEntries = 2;
1346 filehdr->appledouble.entries[0].type = AD_FINDERINFO;
1347 filehdr->appledouble.entries[0].offset = offsetof(apple_double_header_t, finfo);
1348 filehdr->appledouble.entries[0].length = FINDERINFOSIZE;
1349 filehdr->appledouble.entries[1].type = AD_RESOURCE;
1350 filehdr->appledouble.entries[1].offset = offsetof(apple_double_header_t, pad);
1351 filehdr->appledouble.entries[1].length = 0;
1352 bcopy(ADH_MACOSX, filehdr->appledouble.filler, sizeof(filehdr->appledouble.filler));
1353
1354 /*
1355 * Fill in the initial Attribute Header.
1356 */
1357 filehdr->magic = ATTR_HDR_MAGIC;
1358 filehdr->debug_tag = s->sb.st_ino;
1359 filehdr->data_start = sizeof(attr_header_t);
1360
1361 /*
1362 * Collect the attribute names.
1363 */
1364 entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t));
1365
1366 /*
1367 * Test if there are acls to copy
1368 */
1369 if (COPYFILE_ACL & s->flags)
1370 {
1371 if (filesec_get_property(s->fsec, FILESEC_ACL, &datasize) < 0)
1372 {
1373 copyfile_debug(1, "no acl entries found (%d)", datasize < 0 ? errno : 0);
1374 } else
1375 {
1376 offset = strlen(XATTR_SECURITY_NAME) + 1;
1377 strcpy(attrnamebuf, XATTR_SECURITY_NAME);
1378 }
1379 }
1380
1381 if (COPYFILE_XATTR & s->flags)
1382 {
1383 ssize_t left = ATTR_MAX_HDR_SIZE - offset;
1384 if ((listsize = flistxattr(s->src_fd, attrnamebuf + offset, left, 0)) <= 0)
1385 {
1386 copyfile_debug(1, "no extended attributes found (%d)", errno);
1387 }
1388 if (listsize > left)
1389 {
1390 copyfile_debug(1, "extended attribute list too long");
1391 listsize = ATTR_MAX_HDR_SIZE;
1392 }
1393
1394 listsize += offset;
1395 endnamebuf = attrnamebuf + listsize;
1396 if (endnamebuf > (attrnamebuf + ATTR_MAX_HDR_SIZE)) {
1397 error = -1;
1398 goto exit;
1399 }
1400
1401 for (nameptr = attrnamebuf; nameptr <endnamebuf; nameptr += namelen)
1402 {
1403 namelen = strlen(nameptr) + 1;
1404 /* Skip over FinderInfo or Resource Fork names */
1405 if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0 ||
1406 strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0)
1407 continue;
1408
1409 /* The system should prevent this from happening, but... */
1410 if (namelen > XATTR_MAXNAMELEN + 1) {
1411 namelen = XATTR_MAXNAMELEN + 1;
1412 }
1413 entry->namelen = namelen;
1414 entry->flags = 0;
1415 bcopy(nameptr, &entry->name[0], namelen);
1416 copyfile_debug(2, "copied name [%s]", entry->name);
1417
1418 entrylen = ATTR_ENTRY_LENGTH(namelen);
1419 entry = (attr_entry_t *)(((char *)entry) + entrylen);
1420
1421 if ((void*)entry > (void*)endfilehdr) {
1422 error = -1;
1423 goto exit;
1424 }
1425
1426 /* Update the attributes header. */
1427 filehdr->num_attrs++;
1428 filehdr->data_start += entrylen;
1429 }
1430 }
1431
1432 /*
1433 * Collect the attribute data.
1434 */
1435 entry = (attr_entry_t *)((char *)filehdr + sizeof(attr_header_t));
1436
1437 for (nameptr = attrnamebuf; nameptr < attrnamebuf + listsize; nameptr += namelen + 1)
1438 {
1439 namelen = strlen(nameptr);
1440
1441 if (strcmp(nameptr, XATTR_SECURITY_NAME) == 0)
1442 copyfile_pack_acl(s, &databuf, &datasize);
1443 else
1444 /* Check for Finder Info. */
1445 if (strcmp(nameptr, XATTR_FINDERINFO_NAME) == 0)
1446 {
1447 datasize = fgetxattr(s->src_fd, nameptr, (u_int8_t*)filehdr + filehdr->appledouble.entries[0].offset, 32, 0, 0);
1448 if (datasize < 0)
1449 {
1450 if (COPYFILE_VERBOSE & s->flags)
1451 copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno);
1452 } else if (datasize != 32)
1453 {
1454 if (COPYFILE_VERBOSE & s->flags)
1455 copyfile_warn("unexpected size (%ld) for \"%s\"", datasize, nameptr);
1456 } else
1457 {
1458 if (COPYFILE_VERBOSE & s->flags)
1459 copyfile_warn(" copied 32 bytes of \"%s\" data @ offset 0x%08x",
1460 XATTR_FINDERINFO_NAME, filehdr->appledouble.entries[0].offset);
1461 }
1462 continue; /* finder info doesn't have an attribute entry */
1463 } else
1464 /* Check for Resource Fork. */
1465 if (strcmp(nameptr, XATTR_RESOURCEFORK_NAME) == 0)
1466 {
1467 hasrsrcfork = 1;
1468 continue;
1469 } else
1470 {
1471 /* Just a normal attribute. */
1472 datasize = fgetxattr(s->src_fd, nameptr, NULL, 0, 0, 0);
1473 if (datasize == 0)
1474 goto next;
1475 if (datasize < 0)
1476 {
1477 if (COPYFILE_VERBOSE & s->flags)
1478 copyfile_warn("skipping attr \"%s\" due to error %d", nameptr, errno);
1479 goto next;
1480 }
1481 if (datasize > XATTR_MAXATTRLEN)
1482 {
1483 if (COPYFILE_VERBOSE & s->flags)
1484 copyfile_warn("skipping attr \"%s\" (too big)", nameptr);
1485 goto next;
1486 }
1487 databuf = malloc(datasize);
1488 if (databuf == NULL) {
1489 error = -1;
1490 continue;
1491 }
1492 datasize = fgetxattr(s->src_fd, nameptr, databuf, datasize, 0, 0);
1493 }
1494
1495 entry->length = datasize;
1496 entry->offset = filehdr->data_start + filehdr->data_length;
1497
1498 filehdr->data_length += datasize;
1499 /*
1500 * >>> WARNING <<<
1501 * This assumes that the data is fits in memory (not
1502 * the case when there are lots of attributes or one of
1503 * the attributes is very large.
1504 */
1505 if (entry->offset > ATTR_MAX_SIZE ||
1506 (entry->offset + datasize > ATTR_MAX_SIZE)) {
1507 error = -1;
1508 } else {
1509 bcopy(databuf, (char*)filehdr + entry->offset, datasize);
1510 }
1511 free(databuf);
1512
1513 copyfile_debug(1, "copied %ld bytes of \"%s\" data @ offset 0x%08x", datasize, nameptr, entry->offset);
1514 next:
1515 /* bump to next entry */
1516 entrylen = ATTR_ENTRY_LENGTH(entry->namelen);
1517 entry = (attr_entry_t *)((char *)entry + entrylen);
1518 }
1519
1520 if (filehdr->data_length > 0)
1521 {
1522 /* Now we know where the resource fork data starts. */
1523 filehdr->appledouble.entries[1].offset = (filehdr->data_start + filehdr->data_length);
1524
1525 /* We also know the size of the "Finder Info entry. */
1526 filehdr->appledouble.entries[0].length =
1527 filehdr->appledouble.entries[1].offset - filehdr->appledouble.entries[0].offset;
1528
1529 filehdr->total_size = filehdr->appledouble.entries[1].offset;
1530 }
1531
1532 /* Copy Resource Fork. */
1533 if (hasrsrcfork && (error = copyfile_pack_rsrcfork(s, filehdr)))
1534 goto exit;
1535
1536 /* Write the header to disk. */
1537 datasize = filehdr->appledouble.entries[1].offset;
1538
1539 swap_adhdr(&filehdr->appledouble);
1540 swap_attrhdr(filehdr);
1541
1542 if (pwrite(s->dst_fd, filehdr, datasize, 0) != datasize)
1543 {
1544 if (COPYFILE_VERBOSE & s->flags)
1545 copyfile_warn("couldn't write file header");
1546 error = -1;
1547 goto exit;
1548 }
1549 exit:
1550 if (filehdr) free(filehdr);
1551 if (attrnamebuf) free(attrnamebuf);
1552
1553 if (error)
1554 return error;
1555 else
1556 return copyfile_stat(s);
1557 }