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