]> git.saurik.com Git - apple/libc.git/blame - darwin/copyfile.c
Libc-391.4.3.tar.gz
[apple/libc.git] / darwin / copyfile.c
CommitLineData
3d9156a7
A
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
46struct _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
59static int copyfile_open (copyfile_state_t);
60static int copyfile_close (copyfile_state_t);
61static int copyfile_data (copyfile_state_t);
62static int copyfile_stat (copyfile_state_t);
63static int copyfile_security (copyfile_state_t);
64static int copyfile_xattr (copyfile_state_t);
65static int copyfile_pack (copyfile_state_t);
66static int copyfile_unpack (copyfile_state_t);
67
68static copyfile_flags_t copyfile_check (copyfile_state_t);
69static 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
88int 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 }
eb1cde05 126
3d9156a7
A
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 {
0f515e26
A
146 if (!(COPYFILE_STAT & flags || COPYFILE_ACL & flags))
147 fix_perms = !copyfile_fix_perms(s, &original_fsec, 1);
148
3d9156a7
A
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 }
196exit:
197 if (fix_perms)
198 copyfile_fix_perms(s, &original_fsec, 0);
199
200 filesec_free(original_fsec);
201
eb1cde05
A
202 if (state == NULL)
203 ret -= copyfile_free(s);
3d9156a7
A
204 return ret;
205}
206
207copyfile_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
221int 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
242static int copyfile_close(copyfile_state_t s)
243{
eb1cde05 244 if (s->src_fd != -2)
3d9156a7
A
245 close(s->src_fd);
246
eb1cde05 247 if (s->dst_fd != -2 && close(s->dst_fd))
3d9156a7
A
248 {
249 copyfile_warn("close on %s", s->dst);
250 return -1;
251 }
252 return 0;
253}
254
255static 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 {
eb1cde05 264 if(statx_np(s->dst, &sb, *fsec))
3d9156a7
A
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;
319error:
320 filesec_free(*fsec);
321 return -1;
322}
323
324
325static 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
eb1cde05 394 while((s->dst_fd = openx_np(s->dst, oflags, s->fsec)) < 0)
3d9156a7 395 {
eb1cde05 396 if (EEXIST == errno)
3d9156a7 397 {
eb1cde05
A
398 oflags = oflags & ~O_CREAT;
399 continue;
3d9156a7
A
400 }
401 copyfile_warn("open on %s", s->dst);
402 return -1;
403 }
404 }
405 return 0;
406}
407
408static 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
433static 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
470static 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
eb1cde05 536 if (!filesec_set_property(s->fsec, FILESEC_ACL, &acl_dst))
3d9156a7
A
537 {
538 copyfile_debug(1, "altered acl");
539 }
540 }
541no_acl:
eb1cde05 542 if (fchmodx_np(s->dst_fd, s->fsec) < 0 && errno != ENOTSUP)
3d9156a7
A
543 copyfile_warn("setting security information: %s", s->dst);
544
545cleanup:
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
553static 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
574static 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
673struct {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
695int 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